Compare commits

...

19 commits

Author SHA1 Message Date
Ryan Kurtz
4896cd5c97 Merge remote-tracking branch 'origin/GP-5900_ryanmkurtz_pe-export-forwarding--SQUASHED' 2025-09-16 05:57:21 -04:00
Ryan Kurtz
ad35a7e956 GP-5900: PeLoader now creates thunks in EXTERNAL block for forwarded exported functions 2025-09-16 05:56:14 -04:00
Ryan Kurtz
3bba2c2f14 GP-0: Upping master to 12.1 2025-09-15 13:49:29 -04:00
Ryan Kurtz
2d10da420f Merge remote-tracking branch 'origin/Ghidra_12.0' 2025-09-15 13:48:46 -04:00
Ryan Kurtz
437bed4be0 GP-0: Updating WhatsNew 2025-09-15 13:30:39 -04:00
ghidra1
c3d8571ba3 Merge remote-tracking branch 'origin/Ghidra_12.0' 2025-09-15 10:31:49 -04:00
ghidra1
c99af66f5f GP-5908 Corrected excessive folder change notifications on first visit
to parent
2025-09-15 10:06:20 -04:00
Ryan Kurtz
6dd00be368 Merge branch
'GP-5979_dragonmacher-search-memory-accessibility--SQUASHED' (Closes #8264)
2025-09-15 09:53:46 -04:00
dragonmacher
1c5e9ea9c2 GP-5979: Accessibility - Updates to the Search Memory provider 2025-09-15 09:52:36 -04:00
Ryan Kurtz
22b3524206 Merge remote-tracking branch 'origin/GP-5763_emteere_NoReturnExternalsINT3--SQUASHED' 2025-09-15 09:46:20 -04:00
Ryan Kurtz
a893955b03 Merge remote-tracking branch 'origin/patch' 2025-09-15 09:45:43 -04:00
Ryan Kurtz
a1851c5911 Merge remote-tracking branch 'origin/GP-5985_emteere_MoreNonReturningPEFuncs' into patch 2025-09-15 09:42:53 -04:00
Ryan Kurtz
4c8d408fc7 Merge remote-tracking branch 'origin/GP-5526_emteere_plt_thunkPatterns' into patch 2025-09-15 09:40:48 -04:00
Ryan Kurtz
9e2a8d5604 Merge remote-tracking branch 'origin/GP-1-dragonmacher-window-growing-issue--SQUASHED' 2025-09-15 09:33:44 -04:00
Ryan Kurtz
5f6e3806b3 GP-0: Updating WhatsNew 2025-09-15 08:41:50 -04:00
emteere
47acbe98bb GP-5763 Check for INT3 after calls as a non-returning indicator. Also only consider data refereces from the same function. 2025-09-12 20:05:16 +00:00
dragonmacher
fe4bb30704 Fixed detached windows growing every time they get shown 2025-09-12 12:39:44 -04:00
emteere
79eea09e64 GP-5985 added several more known non-returning PE functions 2025-09-10 23:06:54 +00:00
emteere
4b6d90366c GP-5526 Added section tag to function start patterns. New thunk patterns
in x86 gcc .plt section.  Changed priority of pre analyzer and
disassembly.
2025-09-10 22:55:14 +00:00
23 changed files with 533 additions and 283 deletions

View file

@ -15,8 +15,33 @@ applied Ghidra SRE capabilities to a variety of problems that involve analyzing
generating deep insights for NSA analysts who seek a better understanding of potential generating deep insights for NSA analysts who seek a better understanding of potential
vulnerabilities in networks and systems. vulnerabilities in networks and systems.
# What's coming in Ghidra 12.0 # What's New in Ghidra 12.0
This is a preview of what is coming in the future Ghidra 12.0 release. This release includes new features, enhancements, performance improvements, quite a few bug fixes,
and many pull-request contributions. Thanks to all those who have contributed their time, thoughts,
and code. The Ghidra user community thanks you too!
### The not-so-fine print: Please Read!
Ghidra 12.0 is fully backward compatible with project data from previous releases. However, programs
and data type archives which are created or modified in 12.0 will not be usable by an earlier Ghidra
version.
**IMPORTANT:** Ghidra 12.0 requires at minimum JDK 21 to run.
**IMPORTANT:** To use the Debugger or do a full source distribution build, you will need Python3
(3.9 to 3.13 supported) installed on your system.
**NOTE:** There have been reports of certain features causing the XWindows server to crash. A fix
for `CVE-2024-31083` in X.org software in April 2024 introduced a regression, which has been fixed
in xwayland 23.2.6 and xorg-server 21.1.13. If you experience any crashing of Ghidra, most likely
causing a full logout, check if your xorg-server has been updated to at least the noted version.
**NOTE:** Each build distribution will include native components (e.g., decompiler) for at least one
platform (e.g., Windows x86-64). If you have another platform that is not included in the build
distribution, you can build native components for your platform directly from the distribution.
See the *Getting Started* document for additional information. Users running with older shared
libraries and operating systems (e.g., CentOS 7.x) may also run into compatibility errors when
launching native executables such as the Decompiler and GNU Demangler which may necessitate a
rebuild of native components.
**NOTE:** Ghidra Server: The Ghidra 12.0 server is compatible with Ghidra 9.2 and later Ghidra **NOTE:** Ghidra Server: The Ghidra 12.0 server is compatible with Ghidra 9.2 and later Ghidra
clients although the presence of any newer link-files within a repository may not be handled properly clients although the presence of any newer link-files within a repository may not be handled properly
@ -24,18 +49,38 @@ by client versions prior to 12.0 which lack support for the new storage format.
which introduce new link-files into a project will not be able to add such files into version which introduce new link-files into a project will not be able to add such files into version
control if connected to older Ghidra Server versions. control if connected to older Ghidra Server versions.
**NOTE:** Ghidra Server: The Ghidra 12.x server is compatible with Ghidra 9.2 and later Ghidra
clients although the presence of any newer link-files within a repository may not be handled
properly by client versions prior to 12.0 which lack support for the new storage format. Ghidra 12.0
clients which introduce new link-files into a project will not be able to add such files into
version control if connected to older Ghidra Server versions. Ghidra 12.x clients are compatible
with all 0.x and 9.x servers. Although, due to potential Java version differences, it is
recommended that Ghidra Server installations older than 10.2 be upgraded. Those using 10.2 and newer
should not need a server upgrade.
**NOTE:** Programs imported with a Ghidra beta version or code built directly from source code
outside of a release tag may not be compatible, and may have flaws that won't be corrected by using
this new release. Any programs analyzed from a beta or other local master source build should be
considered experimental and re-imported and analyzed with a release version.
Programs imported with previous release versions should upgrade correctly through various automatic
upgrade mechanisms. However, there may be improvements or bug fixes in the import and analysis
process that will provide better results than prior Ghidra versions. You might consider comparing a
fresh import of any program you will continue to reverse engineer to see if the latest Ghidra
provides better results.
## Project Link Files ## Project Link Files
Support for link-files within a Ghidra Project has been significantly expanded with this release and Support for link-files within a Ghidra Project has been significantly expanded with this release and
with it a new file storage type has been introduced which can create some incompatibilities if projects with it a new file storage type has been introduced which can create some incompatibilities if
and repositories containing such files are used by older version of Ghidra or the Ghidra Server. projects and repositories containing such files are used by older version of Ghidra or the Ghidra
Server.
Previously only external folder and file links were supported through the use of a Ghidra URL. Previously only external folder and file links were supported through the use of a Ghidra URL. With
With 12.0 the ability to establish internal folder and file links has been introduced. The new 12.0 the ability to establish internal folder and file links has been introduced. The new storage
storage format avoids the use of a database and relies only on a light-weight property file. Internal format avoids the use of a database and relies only on a light-weight property file. Internal
project links also allow for either absolute or relative links. Due to the fact that Ghidra allows project links also allow for either absolute or relative links. Due to the fact that Ghidra allows
a folder or file to have the same pathname, some ambiguities can result. It is highly recommended that a folder or file to have the same pathname, some ambiguities can result. It is highly recommended
the use of conflicting folder and file pathnames be avoided. that the use of conflicting folder and file pathnames be avoided.
The use of internally linked folders and files allows batch import processing to more accurately The use of internally linked folders and files allows batch import processing to more accurately
reflect the native file-system and its use of symbolic links which allow for the same content to reflect the native file-system and its use of symbolic links which allow for the same content to
@ -49,126 +94,96 @@ Additional Ghidra API methods have been provided or refined on the following cla
link-files: `DomainFolder`, `DomainFile`, `LinkFile`, `LinkHandler`, `DomainFileFilter`, link-files: `DomainFolder`, `DomainFile`, `LinkFile`, `LinkHandler`, `DomainFileFilter`,
`DomainFileIterator`, etc. `DomainFileIterator`, etc.
...TO BE CONTINUED... ...TO BE CONTINUED...
## Filesystem Mirroring
An option has been added to mirror the local filesystem when importing programs and their libraries.
Programs and libraries that exist on the local filesystem as symbolic links will have both their
corresponding link file and resolved program file mirrored in the project. Filesystem mirroring
can also be used in headless mode with the new `-mirror` command line option.
# What's New in Ghidra 11.4 ## PyGhidra
This release includes new features, enhancements, performance improvements, quite a few bug fixes, PyGhidra 3.0.0 (compatible with Ghidra 12.0 and later) introduces many new Python-specific API
and many pull-request contributions. Thanks to all those who have contributed their time, thoughts, methods with the goal of making the most common Ghidra tasks quick and easy, such as opening a
and code. The Ghidra user community thanks you too! project, getting a program, running a GhidraScript, etc. Legacy API fuctions such as
`pyghidra.open_program()` and `pyghidra_run_script()` have been deprecated in favor of the new
### The not-so-fine print: Please Read! methods. Below is an example program that showcases some of the new API functionality. See the
Ghidra 11.4 is fully backward compatible with project data from previous releases. However, programs PyGhidra library README for more information.
and data type archives which are created or modified in 11.4 will not be usable by an earlier Ghidra ```python
version. import os, jpype, pyghidra
pyghidra.start()
**IMPORTANT:** Ghidra 11.4 requires at minimum JDK 21 to run. # Open/create a project
with pyghidra.open_project(os.environ["GHIDRA_PROJECT_DIR"], "ExampleProject", create=True) as project:
**IMPORTANT:** To use the Debugger or do a full source distribution build, you will need Python3 # Walk a Ghidra release zip file, load every decompiler binary, and save them to the project
(3.9 to 3.13 supported) installed on your system. with pyghidra.open_filesystem(f"{os.environ['DOWNLOADS_DIR']}/ghidra_11.4_PUBLIC_20250620.zip") as fs:
loader = pyghidra.program_loader().project(project)
for f in fs.files(lambda f: "os/" in f.path and f.name.startswith("decompile")):
loader = loader.source(f.getFSRL()).projectFolderPath("/" + f.parentFile.name)
with loader.load() as load_results:
load_results.save(pyghidra.monitor())
**NOTE:** There have been reports of certain features causing the XWindows server to crash. A fix # Analyze the windows decompiler program for a maximum of 10 seconds
for `CVE-2024-31083` in X.org software in April 2024 introduced a regression, which has been fixed with pyghidra.program_context(project, "/win_x86_64/decompile.exe") as program:
in xwayland 23.2.6 and xorg-server 21.1.13. If you experience any crashing of Ghidra, most likely analysis_props = pyghidra.analysis_properties(program)
causing a full logout, check if your xorg-server has been updated to at least the noted version. with pyghidra.transaction(program):
analysis_props.setBoolean("Non-Returning Functions - Discovered", False)
analysis_log = pyghidra.analyze(program, pyghidra.monitor(10))
program.save("Analyzed", pyghidra.monitor())
# Walk the project and set a property in each decompiler program
def set_property(domain_file, program):
with pyghidra.transaction(program):
program_info = pyghidra.program_info(program)
program_info.setString("PyGhidra Property", "Set by PyGhidra!")
program.save("Setting property", pyghidra.monitor())
pyghidra.walk_programs(project, set_property, program_filter=lambda f, p: p.name.startswith("decompile"))
**NOTE:** Each build distribution will include native components (e.g., decompiler) for at least one # Load some bytes as a new program
platform (e.g., Windows x86-64). If you have another platform that is not included in the build ByteArrayCls = jpype.JArray(jpype.JByte)
distribution, you can build native components for your platform directly from the distribution. my_bytes = ByteArrayCls(b"\xaa\xbb\xcc\xdd\xee\xff")
See the *Getting Started* document for additional information. Users running with older shared libraries loader = pyghidra.program_loader().project(project).source(my_bytes).name("my_bytes")
and operating systems (e.g., CentOS 7.x) may also run into compatibility errors when launching loader = loader.loaders("BinaryLoader").language("DATA:LE:64:default")
native executables such as the Decompiler and GNU Demangler which may necessitate a rebuild of with loader.load() as load_results:
native components. load_results.save(pyghidra.monitor())
**NOTE:** Ghidra Server: The Ghidra 11.x server is compatible with Ghidra 9.2 and later Ghidra # Run a GhidraScript
clients. Ghidra 11.x clients are compatible with all 10.x and 9.x servers. Although, due to pyghidra.ghidra_script(f"{os.environ['GHIDRA_SCRIPTS_DIR']}/HelloWorldScript.java", project)
potential Java version differences, it is recommended that Ghidra Server installations older than ```
10.2 be upgraded. Those using 10.2 and newer should not need a server upgrade.
**NOTE:** Programs imported with a Ghidra beta version or code built directly from source code
outside of a release tag may not be compatible, and may have flaws that won't be corrected by using
this new release. Any programs analyzed from a beta or other local master source build should be
considered experimental and re-imported and analyzed with a release version.
Programs imported with previous release versions should upgrade correctly through various automatic
upgrade mechanisms. However, there may be improvements or bug fixes in the import and analysis
process that will provide better results than prior Ghidra versions. You might consider comparing a
fresh import of any program you will continue to reverse engineer to see if the latest Ghidra
provides better results.
## Z3 Concolic Emulation and Symbolic Summary
We've added an experimental Z3-based symbolic emulator, which runs as a "auxilliary" domain to the
concrete emulator, effectively constructing what is commonly called a "concolic" emulator. The
symbolic emulator creates Z3 expressions and branching constraints, but it only follows the path
determined by concrete emulation. This is most easily accessed by installing the "SymbolicSummaryZ3"
extension (**File** → **Install Extensions**) and then enabling the `Z3SummaryPlugin` in the
Debugger or Emulator tool, which includes a GUI for viewing and sorting through the results. Before
using the Z3 emulator, you must download and install z3-4.13.0 from https://github.com/Z3Prover/z3.
Depending on your platform, you may need to build it from source. Other versions may work, but our
current test configuration uses 4.13.0.
## Search ## Emulation API
The `PcodeEmulator` and related API has undergone substantial changes in preparation for integrating
our JIT-accelerated emulator into the GUI. Please see the **Notable API Changes** section of our
[Change History](ChangeHistory.md). The goal is to facilitate integration by composition; whereas,
it had previously required inheritance, which is now considered poor design. Essentially, we've
introduced a set of callbacks that integrators can use to detect when certain things have happened
in emulation, as well as offer some control of machine-state behavior, e.g., to facilitate lazily
loading from a snapshot.
A new "Search and Replace" feature allows searching for string patterns in a wide variety Extensions that currently integrate via inheritance can continue to do so, but will still need to
of Ghidra elements and replacing that text with a different text sequence. Using this feature, many different apply some minimal changes to satisfy interface and constructor changes. The developers of such
Ghidra elements can be renamed all at once including labels, functions, name-spaces, parameters, data-types, extensions ought to consider porting their integrations to the compositional/callback-based
field names, and enum values. This feature also supports regular expressions (including capture groups). mechanism. A careful assessment may be required depending on the nature of the extension. Extensions
After initiating a search and replace, a results table is displayed with a list of items that match the that merely integrate with emulation should consider the compositional/callback-based mechanism.
search. From this table, the replace actions can be applied in bulk or individually, one item at a time Extensions that incorporate new domains (e.g. Z3) or novel behaviors (e.g. JIT) should continue
as they are reviewed. using inheritance.
## Taint Engine Support
Extended support for using taint engines, particularly CTADL (https://github.com/sandialabs/ctadl)
and AngryGhidra (https://github.com/Nalen98/AngryGhidra), from the decompiler. Allows users to mark
pcode varnodes as sources and sinks, displaying paths from sources to sinks as both address selections
in the disassembly and token selections in the decompiler.
## Dockerized Ghidra
A new capability to build a docker image that demonstrates Ghidra's various entrypoint executions for `headless`,
`ghidra-server`, `bsim-server`, `bsim`, `pyghidra`, and `gui` within the docker container has been included. The Docker
image can be used as is, or can be tailored to your workflow needs. Configuration such as the base
image (linux distro), additional packages, and more is possible using Docker.
See the `docker/README.md` for information about building a docker image for Ghidra and running within the Ghidra container.
## Binary Formats
+ New loaders for the a.out and OMF-51 binary file formats.
+ Support for Mach-O "re-exports".
+ New ability to load Mach-O binaries directly from a Universal Binary without needing to open the File System Browser.
+ DWARF will now load external debug files during analysis as is done for PDB files.
## Debugger
There have been numerous improvements, extensions for new targets, better launching and configuration, and bug fixes to the debugger.
## Analysis Speed
Constant and Stack analysis time has been greatly decreased through algorithm improvements and better threading. There has been additional
work to loosen locking of the program database where possible. By locking only when necessary, multiple threads can better analyze the program
and interaction with the GUI during analysis should be more responsive.
## Golang
Golang binary analysis analysis has been improved.
+ Analysis has been improved to model closures, interface methods, and generic functions more accurately.
+ Function signatures for core golang library functions are automatically applied.
+ Decompilation results are improved by filtering some verbose golang garbage collection function logic.
+ Addressed finding the Golang bootstrap information in stripped PE binaries.
## BSim
PostgreSQL for BSim has been updated to version 15.13 and the JDBC driver to 42.7.6. This resolves issues with building PostgreSQL
server on newer releases of Linux and compiler toolchains which compile with -std=c23 option by default. In addition,
building of PostgreSQL for linux_arm_64 and mac_arm_64 based platforms is supported.
+ BSim is now installed in the default Codebrowser tool.
+ Function names now update in BSim search results overview if the name is changed elsewhere in Ghidra.
## Processors
+ Enhanced support for the x86 AVX-512 processor extension with additional instruction support - including the BF16, FP16 and VNNI extensions.
+ Implemented many AARCH64 Neon instruction semantics to improve decompilation.
+ Upgraded pcodetest framework scripts to python3 and improved command-line options.
## Other Improvements ## Other Improvements
+ Many calling conventions for various processors/compilers have been improved using the more flexible decompiler rules + Added the ability to toggle the displaying of function variables (parameters and locals) that are
when the data types for parameters and return values are known. normally displayed just below the function signature. The variables display can be turned on/off
+ Upgraded many 3rd party dependencies to address potential bugs and CVE's, including jars for Bouncy Castle, globally or individually per function.
Apache Commons Compress, Apache Commons Lang3, Apache Commons IO, protobuf, and JUnit.
## Additional Bug Fixes and Enhancements ## Additional Bug Fixes and Enhancements
Numerous other new features, improvements, and bug fixes are fully listed in the Numerous other new features, improvements, and bug fixes are fully listed in the

View file

@ -18,3 +18,11 @@ longjmp
quick_exit quick_exit
RpcRaiseException RpcRaiseException
terminate terminate
___raise_securityfailure
___report_rangecheckfailure
?_Xregex_error@std@@YAXW4error_type@regex_constant@1@@Z
?_Xbad_alloc@std@@YAXXZ
?_Xlength_error@std@@YAXPBD@Z
?_Xout_of_range@std@@YAXPBD@Z
?_Xbad_function_call@std@@YAXXZ
?terminate@@YAXXZ

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -41,6 +41,7 @@ import ghidra.app.script.GhidraScript;
import ghidra.app.tablechooser.*; import ghidra.app.tablechooser.*;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.block.*; import ghidra.program.model.block.*;
import ghidra.program.model.lang.Processor;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
@ -48,10 +49,15 @@ import ghidra.util.exception.CancelledException;
public class FixupNoReturnFunctionsScript extends GhidraScript { public class FixupNoReturnFunctionsScript extends GhidraScript {
IssueEntries entryList = null; IssueEntries entryList = null;
private final static String X86_NAME = "x86";
boolean isX86;
@Override @Override
public void run() throws Exception { public void run() throws Exception {
Program cp = currentProgram; Program cp = currentProgram;
isX86 = checkForX86(cp);
TableChooserExecutor executor = createTableExecutor(); TableChooserExecutor executor = createTableExecutor();
@ -89,6 +95,11 @@ public class FixupNoReturnFunctionsScript extends GhidraScript {
} }
} }
private boolean checkForX86(Program cp) {
return cp.getLanguage().getProcessor().equals(
Processor.findOrPossiblyCreateProcessor(X86_NAME));
}
private void configureTableColumns(TableChooserDialog dialog) { private void configureTableColumns(TableChooserDialog dialog) {
StringColumnDisplay explanationColumn = new StringColumnDisplay() { StringColumnDisplay explanationColumn = new StringColumnDisplay() {
@Override @Override
@ -373,10 +384,22 @@ public class FixupNoReturnFunctionsScript extends GhidraScript {
// //
// //
FunctionManager functionManager = currentProgram.getFunctionManager(); FunctionManager functionManager = currentProgram.getFunctionManager();
FunctionIterator functionIter = functionManager.getFunctions(true);
AddressSet set = new AddressSet(); AddressSet set = new AddressSet();
HashSet<Function> suspectNoReturnFunctions = new HashSet<Function>(); HashSet<Function> suspectNoReturnFunctions = new HashSet<Function>();
FunctionIterator functionIter = functionManager.getFunctions(true);
checkFunctions(cp, functionIter, noReturnEntries, set, suspectNoReturnFunctions);
FunctionIterator externalFunctionIter = functionManager.getExternalFunctions();
checkFunctions(cp, externalFunctionIter, noReturnEntries, set, suspectNoReturnFunctions);
return set;
}
public void checkFunctions(Program cp, FunctionIterator functionIter,
IssueEntries noReturnEntries, AddressSet set,
HashSet<Function> suspectNoReturnFunctions) throws CancelledException {
while (functionIter.hasNext()) { while (functionIter.hasNext()) {
Function candidateNoReturnfunction = functionIter.next(); Function candidateNoReturnfunction = functionIter.next();
noReturnEntries.setMessage("Checking function: " + candidateNoReturnfunction.getName()); noReturnEntries.setMessage("Checking function: " + candidateNoReturnfunction.getName());
@ -428,8 +451,6 @@ public class FixupNoReturnFunctionsScript extends GhidraScript {
suspectNoReturnFunctions.add(candidateNoReturnfunction); suspectNoReturnFunctions.add(candidateNoReturnfunction);
} }
} }
return set;
} }
private boolean testCalledFunctionsNonReturning(Function candidateNonReturningFunction, private boolean testCalledFunctionsNonReturning(Function candidateNonReturningFunction,
@ -462,8 +483,8 @@ public class FixupNoReturnFunctionsScript extends GhidraScript {
FunctionManager funcManager = currentProgram.getFunctionManager(); FunctionManager funcManager = currentProgram.getFunctionManager();
Listing listing = currentProgram.getListing(); Listing listing = currentProgram.getListing();
while (fallThru != null) { while (fallThru != null) {
if (funcManager.getFunctionAt(fallThru) != null) { Function fallThruFunction = funcManager.getFunctionAt(fallThru);
if (fallThruFunction != null) {
NoReturnLocations location = new NoReturnLocations(currentProgram, NoReturnLocations location = new NoReturnLocations(currentProgram,
ref.getToAddress(), ref.getFromAddress(), "Function defined after call"); ref.getToAddress(), ref.getFromAddress(), "Function defined after call");
dialog.add(location); dialog.add(location);
@ -490,11 +511,15 @@ public class FixupNoReturnFunctionsScript extends GhidraScript {
// or references. This is especially true if there is only one // or references. This is especially true if there is only one
// example for a calling reference. // example for a calling reference.
if (callingFunc != null) { if (callingFunc != null) {
Address fromAddress = reference.getFromAddress();
Function function = Function function =
funcManager.getFunctionContaining(reference.getFromAddress()); funcManager.getFunctionContaining(fromAddress);
if (callingFunc.equals(function)) { // The reference must come from an address within this function
// before this function call (reference fromAddress)
// this should get rid of spurious data references from other functions
if ((fromAddress.compareTo(fallThru) < 0) && callingFunc.equals(function)) {
NoReturnLocations location = new NoReturnLocations(currentProgram, NoReturnLocations location = new NoReturnLocations(currentProgram,
ref.getToAddress(), ref.getFromAddress(), ref.getToAddress(), fromAddress,
"Data Reference from same function after call"); "Data Reference from same function after call");
dialog.add(location); dialog.add(location);
return true; return true;
@ -517,6 +542,15 @@ public class FixupNoReturnFunctionsScript extends GhidraScript {
dialog.add(location); dialog.add(location);
return true; return true;
} }
if (isX86) {
Instruction fallInstr = listing.getInstructionContaining(fallThru);
if (fallInstr != null && fallInstr.getMnemonicString().equals("INT3")) {
NoReturnLocations location = new NoReturnLocations(currentProgram,
ref.getToAddress(), ref.getFromAddress(), "INT3 interrupt after call");
dialog.add(location);
return true;
}
}
fallThru = null; fallThru = null;
if (block.getFlowType().isFallthrough()) { if (block.getFlowType().isFallthrough()) {
CodeBlockReferenceIterator dests = block.getDestinations(monitor); CodeBlockReferenceIterator dests = block.getDestinations(monitor);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Before After
Before After

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -25,8 +25,7 @@ import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.block.*; import ghidra.program.model.block.*;
import ghidra.program.model.lang.GhidraLanguagePropertyKeys; import ghidra.program.model.lang.*;
import ghidra.program.model.lang.Language;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode; import ghidra.program.model.pcode.Varnode;
@ -79,6 +78,9 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer {
private Address lastGetNextFuncAddress = null; // last addr used for getNextFunction() private Address lastGetNextFuncAddress = null; // last addr used for getNextFunction()
private Address nextFunction = null; // last return nextFunction private Address nextFunction = null; // last return nextFunction
private final static String X86_NAME = "x86";
boolean isX86;
public FindNoReturnFunctionsAnalyzer() { public FindNoReturnFunctionsAnalyzer() {
this(NAME, DESCRIPTION, AnalyzerType.INSTRUCTION_ANALYZER); this(NAME, DESCRIPTION, AnalyzerType.INSTRUCTION_ANALYZER);
@ -105,6 +107,8 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer {
this.monitor = monitor; this.monitor = monitor;
this.reasonList = new ArrayList<>(); this.reasonList = new ArrayList<>();
lastGetNextFuncAddress = null; lastGetNextFuncAddress = null;
isX86 = checkForX86(program);
monitor.setMessage("NoReturn - Finding non-returning functions"); monitor.setMessage("NoReturn - Finding non-returning functions");
@ -149,6 +153,11 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer {
} }
return true; return true;
} }
private boolean checkForX86(Program cp) {
return cp.getLanguage().getProcessor().equals(
Processor.findOrPossiblyCreateProcessor(X86_NAME));
}
/** /**
* repair any damaged locations * repair any damaged locations
@ -358,7 +367,10 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer {
} }
// detected a calling issue, check other instructions calling the same place // detected a calling issue, check other instructions calling the same place
Address[] flows = inst.getFlows(); Address[] flows = getAllFlows(inst);
if (flows == null) {
continue;
}
for (Address target : flows) { for (Address target : flows) {
int count = 1; int count = 1;
@ -558,6 +570,17 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer {
return true; return true;
} }
// on x86 INT3 after a call indicates a non-returning call from alignment padding
if (isX86) {
Instruction fallInstr = listing.getInstructionContaining(fallThru);
if (fallInstr != null && fallInstr.getMnemonicString().equals("INT3")) {
NoReturnLocations location =
new NoReturnLocations(target, fallThru, "INT3 interrupt after call");
reasonList.add(location);
return true;
}
}
// get the next instruction in fallthru chain // get the next instruction in fallthru chain
fallThru = null; fallThru = null;
if (instr.getFlowType().isFallthrough()) { if (instr.getFlowType().isFallthrough()) {
@ -566,7 +589,39 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer {
} }
return false; return false;
} }
/**
* Get all flows that have already been added to instruction.
* If there are none and this is an indirect, get the function at
* the end of the read.
* @param callInst
* @return all flows
*/
private Address[] getAllFlows(Instruction callInst) {
Address[] flows = callInst.getFlows();
if (flows != null && flows.length > 0) {
return flows;
}
FlowType flowType = callInst.getFlowType();
if (!flowType.isCall() || !flowType.isIndirect()) {
return flows;
}
// if haven't found any flows yet, check for a read of a location that refers
// to a function.
Reference[] referencesFrom = callInst.getReferencesFrom();
for (Reference reference : referencesFrom) {
if (reference.getReferenceType().isRead()) {
Function functionAt = program.getFunctionManager().getFunctionAt(reference.getToAddress());
if (functionAt != null) {
flows = new Address[1];
flows[0] = reference.getToAddress();
return flows;
}
}
}
return flows;
}
/** /**
* Return true if fallThru address has inconsistent (data/call) references to it. * Return true if fallThru address has inconsistent (data/call) references to it.
* Adds the reason for non-returning reason to no return locations list. * Adds the reason for non-returning reason to no return locations list.
@ -585,8 +640,8 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer {
Reference reference = refIterTo.next(); Reference reference = refIterTo.next();
RefType refType = reference.getReferenceType(); RefType refType = reference.getReferenceType();
if (refType.isRead() || refType.isWrite()) { if (refType.isRead() || refType.isWrite()) {
// look at function the reference is coming from // Check function the reference is coming from
// is the function the same as the call is in // is the same function as the call is in
// This is a better indicator of non-returning // This is a better indicator of non-returning
// Random references from another function could be bad disassembly // Random references from another function could be bad disassembly
// or references. This is especially true if there is only one // or references. This is especially true if there is only one
@ -595,9 +650,13 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer {
// TODO: if this is done before functions are created from calls // TODO: if this is done before functions are created from calls
// then this check will do nothing // then this check will do nothing
if (callingFunc != null) { if (callingFunc != null) {
Address fromAddress = reference.getFromAddress();
Function function = Function function =
funcManager.getFunctionContaining(reference.getFromAddress()); funcManager.getFunctionContaining(fromAddress);
if (callingFunc.equals(function)) { // The reference must come from an address within this function
// before this the function call (reference fromAddress)
// this should get rid of considering spurious/bad data references from other functions
if ((fromAddress.compareTo(addr) < 0) && callingFunc.equals(function)) {
NoReturnLocations location = NoReturnLocations location =
new NoReturnLocations(calledAddr, reference.getToAddress(), new NoReturnLocations(calledAddr, reference.getToAddress(),
"Data Reference from same function after call"); "Data Reference from same function after call");

View file

@ -541,13 +541,26 @@ public class PeLoader extends AbstractPeDebugLoader {
return; return;
} }
AddressFactory af = program.getAddressFactory(); AddressSpace space = program.getAddressFactory().getDefaultAddressSpace();
AddressSpace space = af.getDefaultAddressSpace();
SymbolTable symTable = program.getSymbolTable(); SymbolTable symTable = program.getSymbolTable();
Listing listing = program.getListing();
ReferenceManager refManager = program.getReferenceManager(); ReferenceManager refManager = program.getReferenceManager();
ExternalManager extManager = program.getExternalManager();
FunctionManager funcManager = program.getFunctionManager();
// If we have any forwarders, set up the EXTERNAL block
ExportInfo[] exports = edd.getExports(); ExportInfo[] exports = edd.getExports();
Address extAddr = null;
long forwardedCount = Arrays.stream(exports).filter(ExportInfo::isForwarded).count();
if (forwardedCount > 0) {
try {
extAddr = AbstractProgramLoader.addExternalBlock(program,
forwardedCount * program.getDefaultPointerSize(), log);
}
catch (Exception e) {
log.appendException(e);
}
}
for (ExportInfo export : exports) { for (ExportInfo export : exports) {
if (monitor.isCancelled()) { if (monitor.isCancelled()) {
return; return;
@ -555,65 +568,51 @@ public class PeLoader extends AbstractPeDebugLoader {
Address address = space.getAddress(export.getAddress()); Address address = space.getAddress(export.getAddress());
setComment(CommentType.PRE, address, export.getComment()); setComment(CommentType.PRE, address, export.getComment());
symTable.addExternalEntryPoint(address);
String name = export.getName();
try {
symTable.createLabel(address, name, SourceType.IMPORTED);
}
catch (InvalidInputException e) {
// Don't create invalid symbol
}
try {
symTable.createLabel(address, SymbolUtilities.ORDINAL_PREFIX + export.getOrdinal(),
SourceType.IMPORTED);
}
catch (InvalidInputException e) {
// Don't create invalid symbol
}
// When exported symbol is a forwarder,
// a string exists at the address of the export
// Therefore, create a string data object to prevent
// disassembler from attempting to create
// code here. If code was created, it would be incorrect
// and offcut.
if (export.isForwarded()) { if (export.isForwarded()) {
try { Data data =
listing.createData(address, TerminatedStringDataType.dataType, -1); PeUtils.createData(program, address, TerminatedStringDataType.dataType, log);
Data data = listing.getDataAt(address); if (extAddr != null && data != null && data.getValue() instanceof String str) {
if (data != null) { int dotpos = str.indexOf('.');
Object obj = data.getValue(); if (dotpos < 0) {
if (obj instanceof String) { dotpos = 0; // TODO
String str = (String) obj; }
int dotpos = str.indexOf('.'); String libName = str.substring(0, dotpos) + ".dll";
String extSymbolName = str.substring(dotpos + 1);
if (dotpos < 0) { try {
dotpos = 0;//TODO symTable.addExternalEntryPoint(extAddr);
} Function function = funcManager.createFunction(export.getName(), extAddr,
new AddressSet(extAddr), SourceType.IMPORTED);
// get the name of the dll ExternalLocation loc = extManager.addExtLocation(libName.toUpperCase(),
String dllName = str.substring(0, dotpos) + ".dll"; extSymbolName, null, SourceType.IMPORTED);
function.setThunkedFunction(loc.createFunction());
// get the name of the symbol symTable.createLabel(extAddr,
String expName = str.substring(dotpos + 1); SymbolUtilities.ORDINAL_PREFIX + export.getOrdinal(),
SourceType.IMPORTED);
try { refManager.addMemoryReference(address, extAddr, RefType.DATA,
refManager.addExternalReference(address, dllName.toUpperCase(), SourceType.IMPORTED, 0);
expName, null, SourceType.IMPORTED, 0, RefType.DATA); setComment(CommentType.PLATE, extAddr, export.getComment());
} }
catch (DuplicateNameException e) { catch (InvalidInputException | DuplicateNameException
log.appendMsg("External location not created: " + e.getMessage()); | OverlappingFunctionException e) {
} log.appendMsg("External location not created: " + e.getMessage());
catch (InvalidInputException e) { }
log.appendMsg("External location not created: " + e.getMessage()); finally {
} extAddr = extAddr.add(program.getDefaultPointerSize());
}
} }
} }
catch (CodeUnitInsertionException e) { }
// Nothing to do...just continue on else {
symTable.addExternalEntryPoint(address);
try {
symTable.createLabel(address, export.getName(), SourceType.IMPORTED);
symTable.createLabel(address,
SymbolUtilities.ORDINAL_PREFIX + export.getOrdinal(), SourceType.IMPORTED);
}
catch (InvalidInputException e) {
// Don't create invalid symbol
} }
} }
} }

View file

@ -15,7 +15,6 @@
*/ */
package ghidra.features.base.memsearch.gui; package ghidra.features.base.memsearch.gui;
import java.awt.BorderLayout;
import java.awt.FlowLayout; import java.awt.FlowLayout;
import javax.swing.*; import javax.swing.*;
@ -38,20 +37,30 @@ public class MemoryScanControlPanel extends JPanel {
private JButton scanButton; private JButton scanButton;
MemoryScanControlPanel(MemorySearchProvider provider) { MemoryScanControlPanel(MemorySearchProvider provider) {
super(new BorderLayout()); super();
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0)); setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
add(buildButtonPanel(), BorderLayout.CENTER);
scanButton = new JButton("Scan Values"); scanButton = new JButton("Scan Values");
scanButton.setMnemonic('V');
scanButton.setEnabled(false);
scanButton.setToolTipText("Refreshes byte values of current results and eliminates " + scanButton.setToolTipText("Refreshes byte values of current results and eliminates " +
"those that don't meet the selected change criteria"); "those that don't meet the selected change criteria");
add(scanButton);
add(Box.createHorizontalStrut(20));
add(buildButtonPanel());
HelpService helpService = Help.getHelpService(); HelpService helpService = Help.getHelpService();
helpService.registerHelp(this, new HelpLocation(HelpTopics.SEARCH, "Scan_Controls")); helpService.registerHelp(this, new HelpLocation(HelpTopics.SEARCH, "Scan_Controls"));
add(scanButton, BorderLayout.WEST);
scanButton.addActionListener(e -> provider.scan(selectedScanner)); scanButton.addActionListener(e -> provider.scan(selectedScanner));
} }
private JComponent buildButtonPanel() { private JComponent buildButtonPanel() {
JPanel panel = new JPanel(new FlowLayout()); JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEADING));
ButtonGroup buttonGroup = new ButtonGroup(); ButtonGroup buttonGroup = new ButtonGroup();
for (Scanner scanner : Scanner.values()) { for (Scanner scanner : Scanner.values()) {
GRadioButton button = new GRadioButton(scanner.getName()); GRadioButton button = new GRadioButton(scanner.getName());

View file

@ -24,14 +24,17 @@ import java.util.List;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.Border; import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.text.*; import javax.swing.text.*;
import docking.DockingUtils; import docking.DockingUtils;
import docking.menu.ButtonState; import docking.menu.ButtonState;
import docking.menu.MultiStateButton; import docking.menu.MultiStateButton;
import docking.widgets.PopupWindow; import docking.widgets.PopupWindow;
import docking.widgets.combobox.GComboBox;
import docking.widgets.combobox.GhidraComboBox; import docking.widgets.combobox.GhidraComboBox;
import docking.widgets.label.GDLabel; import docking.widgets.label.GDLabel;
import docking.widgets.label.GLabel;
import docking.widgets.list.GComboBoxCellRenderer; import docking.widgets.list.GComboBoxCellRenderer;
import generic.theme.GThemeDefaults.Colors.Messages; import generic.theme.GThemeDefaults.Colors.Messages;
import ghidra.features.base.memsearch.combiner.Combiner; import ghidra.features.base.memsearch.combiner.Combiner;
@ -89,9 +92,11 @@ class MemorySearchControlPanel extends JPanel {
searchButton = new MultiStateButton<Combiner>(initialSearchButtonStates); searchButton = new MultiStateButton<Combiner>(initialSearchButtonStates);
searchButton searchButton
.setStateChangedListener(state -> model.setMatchCombiner(state.getClientData())); .setStateChangedListener(state -> model.setMatchCombiner(state.getClientData()));
searchButton.setMnemonic('S');
searchButton.addActionListener(e -> search()); searchButton.addActionListener(e -> search());
panel.add(searchButton, BorderLayout.WEST); panel.add(searchButton, BorderLayout.WEST);
selectionCheckbox = new JCheckBox("Selection Only"); selectionCheckbox = new JCheckBox("Selection Only");
selectionCheckbox.setMnemonic('O');
selectionCheckbox.setSelected(model.isSearchSelectionOnly()); selectionCheckbox.setSelected(model.isSearchSelectionOnly());
selectionCheckbox.setEnabled(model.hasSelection()); selectionCheckbox.setEnabled(model.hasSelection());
selectionCheckbox selectionCheckbox
@ -132,9 +137,10 @@ class MemorySearchControlPanel extends JPanel {
if (!formatComboBox.getSelectedItem().equals(searchFormat)) { if (!formatComboBox.getSelectedItem().equals(searchFormat)) {
formatComboBox.setSelectedItem(searchFormat); formatComboBox.setSelectedItem(searchFormat);
} }
selectionCheckbox.setSelected(model.isSearchSelectionOnly()); selectionCheckbox.setSelected(model.isSearchSelectionOnly());
selectionCheckbox.setEnabled(model.hasSelection()); selectionCheckbox.setEnabled(model.hasSelection());
searchInputField.setToolTipText(searchFormat.getToolTip()); searchInputField.setToolTipText("Search Text: " + searchFormat.getToolTip());
String text = searchInputField.getText(); String text = searchInputField.getText();
String convertedText = searchFormat.convertText(text, oldSettings, model.getSettings()); String convertedText = searchFormat.convertText(text, oldSettings, model.getSettings());
@ -144,23 +150,53 @@ class MemorySearchControlPanel extends JPanel {
} }
private JComponent buildLeftSearchInputPanel() { private JComponent buildLeftSearchInputPanel() {
createSearchInputField();
JPanel searchInputPanel = createSearchInputPanel();
GLabel searchLabel = new GLabel("Search Text:");
searchLabel.setDisplayedMnemonic('T');
searchLabel.setLabelFor(searchInputField);
JLabel bytesLabel = new GLabel("Byte Sequence:", SwingConstants.RIGHT);
bytesLabel.setToolTipText("The byte sequence that will be searched (if applicable)");
JPanel bytesPanel = createBytesPanel();
JPanel panel = new JPanel(new PairLayout(2, 10));
// row 1
panel.add(searchLabel);
panel.add(searchInputPanel);
// row 2
panel.add(bytesLabel);
panel.add(bytesPanel);
return panel;
}
private JPanel createBytesPanel() {
JPanel panel = new JPanel(new BorderLayout());
hexSearchSequenceField = new GDLabel(); hexSearchSequenceField = new GDLabel();
hexSearchSequenceField.setName("HexSequenceField"); hexSearchSequenceField.setName("Hex Sequence Field");
Border outerBorder = BorderFactory.createLoweredBevelBorder(); Border outerBorder = BorderFactory.createLoweredBevelBorder();
Border innerBorder = BorderFactory.createEmptyBorder(0, 4, 0, 4); Border innerBorder = BorderFactory.createEmptyBorder(0, 4, 0, 4);
Border border = BorderFactory.createCompoundBorder(outerBorder, innerBorder); Border border = BorderFactory.createCompoundBorder(outerBorder, innerBorder);
hexSearchSequenceField.setBorder(border); hexSearchSequenceField.setBorder(border);
JPanel panel = new JPanel(new PairLayout(2, 10)); panel.add(hexSearchSequenceField, BorderLayout.CENTER);
panel.add(buildSearchFormatCombo()); int spaceWidth = formatComboBox.getPreferredSize().width;
panel.add(searchInputField); panel.add(Box.createHorizontalStrut(spaceWidth), BorderLayout.EAST);
JLabel byteSequenceLabel = new JLabel("Byte Sequence:", SwingConstants.RIGHT);
byteSequenceLabel.setToolTipText( return panel;
"This field shows the byte sequence that will be search (if applicable)"); }
private JPanel createSearchInputPanel() {
createSearchInputField();
JPanel panel = new JPanel(new BorderLayout());
panel.add(searchInputField, BorderLayout.CENTER);
panel.add(buildSearchFormatCombo(), BorderLayout.EAST);
panel.add(byteSequenceLabel);
panel.add(hexSearchSequenceField);
return panel; return panel;
} }
@ -181,7 +217,7 @@ class MemorySearchControlPanel extends JPanel {
updateCombo(); updateCombo();
searchInputField.setAutoCompleteEnabled(false); // this interferes with validation searchInputField.setAutoCompleteEnabled(false); // this interferes with validation
searchInputField.setEditable(true); searchInputField.setEditable(true);
searchInputField.setToolTipText(model.getSearchFormat().getToolTip()); searchInputField.setToolTipText("Search Text: " + model.getSearchFormat().getToolTip());
searchInputField.setDocument(new RestrictedInputDocument()); searchInputField.setDocument(new RestrictedInputDocument());
searchInputField.addActionListener(ev -> search()); searchInputField.addActionListener(ev -> search());
JTextField searchTextField = searchInputField.getTextField(); JTextField searchTextField = searchInputField.getTextField();
@ -220,11 +256,14 @@ class MemorySearchControlPanel extends JPanel {
} }
private JComponent buildSearchFormatCombo() { private JComponent buildSearchFormatCombo() {
formatComboBox = new JComboBox<>(SearchFormat.ALL); formatComboBox = new GComboBox<>(SearchFormat.ALL);
formatComboBox.setSelectedItem(model.getSearchFormat()); formatComboBox.setSelectedItem(model.getSearchFormat());
formatComboBox.addItemListener(this::formatComboChanged); formatComboBox.addItemListener(this::formatComboChanged);
formatComboBox.setToolTipText("The selected format will determine how to " + formatComboBox.setToolTipText("Search Format: how to interpret search text");
"interpret text typed into the input field"); Border inside = formatComboBox.getBorder();
CompoundBorder paddingBorder =
BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0), inside);
formatComboBox.setBorder(paddingBorder);
return formatComboBox; return formatComboBox;
} }
@ -252,7 +291,7 @@ class MemorySearchControlPanel extends JPanel {
currentMatcher = byteMatcher; currentMatcher = byteMatcher;
String text = currentMatcher.getDescription(); String text = currentMatcher.getDescription();
hexSearchSequenceField.setText(text); hexSearchSequenceField.setText(text);
hexSearchSequenceField.setToolTipText(currentMatcher.getToolTip()); hexSearchSequenceField.setToolTipText("Search as hex: " + currentMatcher.getToolTip());
updateSearchButton(); updateSearchButton();
provider.setByteMatcher(byteMatcher); provider.setByteMatcher(byteMatcher);
} }

View file

@ -29,6 +29,7 @@ import javax.swing.text.*;
import docking.widgets.checkbox.GCheckBox; import docking.widgets.checkbox.GCheckBox;
import docking.widgets.combobox.GComboBox; import docking.widgets.combobox.GComboBox;
import docking.widgets.label.GLabel;
import ghidra.app.util.HelpTopics; import ghidra.app.util.HelpTopics;
import ghidra.docking.util.LookAndFeelUtils; import ghidra.docking.util.LookAndFeelUtils;
import ghidra.features.base.memsearch.bytesource.SearchRegion; import ghidra.features.base.memsearch.bytesource.SearchRegion;
@ -57,7 +58,7 @@ class MemorySearchOptionsPanel extends JPanel {
super(new BorderLayout()); super(new BorderLayout());
this.model = model; this.model = model;
// if the look and feel is Nimbus, the spaceing it too big, so we use less spacing // if the look and feel is Nimbus, the spacing it too big, so we use less spacing
// between elements. // between elements.
isNimbus = LookAndFeelUtils.isUsingNimbusUI(); isNimbus = LookAndFeelUtils.isUsingNimbusUI();
@ -93,9 +94,22 @@ class MemorySearchOptionsPanel extends JPanel {
JPanel panel = new JPanel(new VerticalLayout(3)); JPanel panel = new JPanel(new VerticalLayout(3));
panel.setBorder(createBorder("Search Region Filter")); panel.setBorder(createBorder("Search Region Filter"));
boolean accelerator = true;
List<SearchRegion> choices = model.getMemoryRegionChoices(); List<SearchRegion> choices = model.getMemoryRegionChoices();
for (SearchRegion region : choices) { for (SearchRegion region : choices) {
GCheckBox checkbox = new GCheckBox(region.getName()); GCheckBox checkbox = new GCheckBox(region.getName());
if (accelerator) {
// The text for the checkbox is dynamic. If the first letter is taken by a menu,
// then the accelerator may not work. At the time of writing, the first letter of
// the first option seems not to conflict with the other accelerators in the parent
// dialog.
String name = region.getName();
char c = name.charAt(0);
checkbox.setMnemonic(c);
accelerator = false;
}
checkbox.setToolTipText(region.getDescription()); checkbox.setToolTipText(region.getDescription());
checkbox.setSelected(model.isSelectedRegion(region)); checkbox.setSelected(model.isSelectedRegion(region));
checkbox.addItemListener(e -> model.selectRegion(region, checkbox.isSelected())); checkbox.addItemListener(e -> model.selectRegion(region, checkbox.isSelected()));
@ -109,13 +123,15 @@ class MemorySearchOptionsPanel extends JPanel {
panel.setBorder(createBorder("Decimal Options")); panel.setBorder(createBorder("Decimal Options"));
JPanel innerPanel = new JPanel(new PairLayout(5, 5)); JPanel innerPanel = new JPanel(new PairLayout(5, 5));
JLabel label = new JLabel("Size:"); GLabel label = new GLabel("Size:");
label.setDisplayedMnemonic('z');
label.setToolTipText("Size of decimal values in bytes"); label.setToolTipText("Size of decimal values in bytes");
innerPanel.add(label); innerPanel.add(label);
Integer[] decimalSizes = new Integer[] { 1, 2, 3, 4, 5, 6, 7, 8, 16 }; Integer[] decimalSizes = new Integer[] { 1, 2, 3, 4, 5, 6, 7, 8, 16 };
int decimalByteSize = model.getDecimalByteSize(); int decimalByteSize = model.getDecimalByteSize();
decimalByteSizeCombo = new GComboBox<>(decimalSizes); decimalByteSizeCombo = new GComboBox<>(decimalSizes);
label.setLabelFor(decimalByteSizeCombo);
decimalByteSizeCombo.setSelectedItem(decimalByteSize); decimalByteSizeCombo.setSelectedItem(decimalByteSize);
decimalByteSizeCombo.addItemListener(this::byteSizeComboChanged); decimalByteSizeCombo.addItemListener(this::byteSizeComboChanged);
decimalByteSizeCombo.setToolTipText("Size of decimal values in bytes"); decimalByteSizeCombo.setToolTipText("Size of decimal values in bytes");
@ -123,6 +139,7 @@ class MemorySearchOptionsPanel extends JPanel {
panel.add(innerPanel); panel.add(innerPanel);
decimalUnsignedCheckbox = new GCheckBox("Unsigned"); decimalUnsignedCheckbox = new GCheckBox("Unsigned");
decimalUnsignedCheckbox.setMnemonic('U');
decimalUnsignedCheckbox.setToolTipText( decimalUnsignedCheckbox.setToolTipText(
"Sets whether decimal values should be interpreted as unsigned values"); "Sets whether decimal values should be interpreted as unsigned values");
decimalUnsignedCheckbox.addActionListener( decimalUnsignedCheckbox.addActionListener(
@ -145,8 +162,11 @@ class MemorySearchOptionsPanel extends JPanel {
JPanel panel = new JPanel(new VerticalLayout(5)); JPanel panel = new JPanel(new VerticalLayout(5));
panel.setBorder(createBorder("Code Type Filter")); panel.setBorder(createBorder("Code Type Filter"));
GCheckBox instructionsCheckBox = new GCheckBox("Instructions"); GCheckBox instructionsCheckBox = new GCheckBox("Instructions");
instructionsCheckBox.setMnemonic('I');
GCheckBox definedDataCheckBox = new GCheckBox("Defined Data"); GCheckBox definedDataCheckBox = new GCheckBox("Defined Data");
definedDataCheckBox.setMnemonic('D');
GCheckBox undefinedDataCheckBox = new GCheckBox("Undefined Data"); GCheckBox undefinedDataCheckBox = new GCheckBox("Undefined Data");
undefinedDataCheckBox.setMnemonic('U');
instructionsCheckBox.setToolTipText( instructionsCheckBox.setToolTipText(
"If selected, include matches found in instructions"); "If selected, include matches found in instructions");
definedDataCheckBox.setToolTipText( definedDataCheckBox.setToolTipText(
@ -185,9 +205,15 @@ class MemorySearchOptionsPanel extends JPanel {
alignField.setToolTipText( alignField.setToolTipText(
"Filters out matches whose address is not divisible by the alignment value"); "Filters out matches whose address is not divisible by the alignment value");
panel.add(new JLabel("Endianess:")); GLabel endianessLabel = new GLabel("Endianess:");
endianessLabel.setLabelFor(endianessCombo);
endianessLabel.setDisplayedMnemonic('n');
GLabel alignmentLabel = new GLabel("Alignment:");
alignmentLabel.setDisplayedMnemonic('A');
alignmentLabel.setLabelFor(alignField);
panel.add(endianessLabel);
panel.add(endianessCombo); panel.add(endianessCombo);
panel.add(new JLabel("Alignment:")); panel.add(alignmentLabel);
panel.add(alignField); panel.add(alignField);
return panel; return panel;
@ -215,19 +241,23 @@ class MemorySearchOptionsPanel extends JPanel {
charsetCombo.setToolTipText("Character encoding for translating strings to bytes"); charsetCombo.setToolTipText("Character encoding for translating strings to bytes");
JPanel innerPanel = new JPanel(new PairLayout(5, 5)); JPanel innerPanel = new JPanel(new PairLayout(5, 5));
JLabel label = new JLabel("Encoding:"); GLabel label = new GLabel("Encoding:");
label.setDisplayedMnemonic('c');
label.setLabelFor(charsetCombo);
label.setToolTipText("Character encoding for translating strings to bytes"); label.setToolTipText("Character encoding for translating strings to bytes");
innerPanel.add(label); innerPanel.add(label);
innerPanel.add(charsetCombo); innerPanel.add(charsetCombo);
panel.add(innerPanel); panel.add(innerPanel);
caseSensitiveCheckbox = new GCheckBox("Case Sensitive"); caseSensitiveCheckbox = new GCheckBox("Case Sensitive");
caseSensitiveCheckbox.setMnemonic('n');
caseSensitiveCheckbox.setSelected(model.isCaseSensitive()); caseSensitiveCheckbox.setSelected(model.isCaseSensitive());
caseSensitiveCheckbox.setToolTipText("Allows for case sensitive searching."); caseSensitiveCheckbox.setToolTipText("Allows for case sensitive searching.");
caseSensitiveCheckbox.addActionListener( caseSensitiveCheckbox.addActionListener(
e -> model.setCaseSensitive(caseSensitiveCheckbox.isSelected())); e -> model.setCaseSensitive(caseSensitiveCheckbox.isSelected()));
escapeSequencesCheckbox = new GCheckBox("Escape Sequences"); escapeSequencesCheckbox = new GCheckBox("Escape Sequences");
escapeSequencesCheckbox.setMnemonic('c');
escapeSequencesCheckbox.setSelected(model.useEscapeSequences()); escapeSequencesCheckbox.setSelected(model.useEscapeSequences());
escapeSequencesCheckbox.setToolTipText( escapeSequencesCheckbox.setToolTipText(
"Allows specifying control characters using escape sequences " + "Allows specifying control characters using escape sequences " +

View file

@ -698,10 +698,18 @@ public class MemorySearchProvider extends ComponentProviderAdapter
} }
@Override @Override
protected ActionContext createContext(Component sourceComponent, Object contextObject) { protected ActionContext createContext(Component focusedComponent, Object contextObject) {
ActionContext context = new NavigatableActionContext(this, navigatable); ActionContext context = new NavigatableActionContext(this, navigatable);
context.setContextObject(contextObject); context.setContextObject(contextObject);
context.setSourceComponent(sourceComponent);
// the 'sourceComponent' will be the focused item if the focus owner is in our provider,
// otherwise it will be the main component
context.setSourceObject(focusedComponent);
// we make the source component be the table so that the 'activate filter' action works
// from anywhere in this provider
GhidraTable table = resultsPanel.getTable();
context.setSourceComponent(table);
return context; return context;
} }

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -339,11 +339,7 @@ public class DefaultProjectDataTest extends AbstractGhidraHeadedIntegrationTest
sharedFS.createFolder("/", "b"); sharedFS.createFolder("/", "b");
flushFileSystemEvents(); // wait for FileSystemListener callback to update folder flushFileSystemEvents(); // wait for FileSystemListener callback to update folder
assertEquals(3, root.getFolders().length); assertEquals(3, root.getFolders().length);
assertEventsSize(3); assertEventsSize(0);
checkEvent(events.get(0), "Folder Added", null, "/a", null, null, null);
checkEvent(events.get(1), "Folder Added", null, "/b", null, null, null);
checkEvent(events.get(2), "Folder Added", null, "/c", null, null, null);
sharedFS.createFolder("/b", "subB"); sharedFS.createFolder("/b", "subB");
flushFileSystemEvents(); // wait for FileSystemListener callback to update folder flushFileSystemEvents(); // wait for FileSystemListener callback to update folder
@ -487,12 +483,9 @@ public class DefaultProjectDataTest extends AbstractGhidraHeadedIntegrationTest
root.getFolders(); // visit root folder to receive change events for it root.getFolders(); // visit root folder to receive change events for it
sharedFS.renameFolder("/", "a", "bigA"); sharedFS.renameFolder("/", "a", "bigA");
flushFileSystemEvents(); // wait for FileSystemListener callback to update folder flushFileSystemEvents(); // wait for FileSystemListener callback to update folder
assertEventsSize(4); assertEventsSize(1);
checkEvent(events.get(0), "Folder Added", null, "/a", null, null, null); checkEvent(events.get(0), "Folder Added", null, "/bigA", null, null, null);
checkEvent(events.get(1), "Folder Added", null, "/b", null, null, null);
checkEvent(events.get(2), "Folder Added", null, "/c", null, null, null);
checkEvent(events.get(3), "Folder Added", null, "/bigA", null, null, null);
// versioned folder was renamed to /bigA, but private folder /a should still exist // versioned folder was renamed to /bigA, but private folder /a should still exist
@ -516,11 +509,9 @@ public class DefaultProjectDataTest extends AbstractGhidraHeadedIntegrationTest
sharedFS.renameFolder("/a", "y", "bigY"); sharedFS.renameFolder("/a", "y", "bigY");
flushFileSystemEvents(); // wait for FileSystemListener callback to update folder flushFileSystemEvents(); // wait for FileSystemListener callback to update folder
assertEventsSize(4); assertEventsSize(2);
checkEvent(events.get(0), "Folder Added", null, "/a/x", null, null, null); checkEvent(events.get(0), "Folder Removed", "/a", null, null, null, "y");
checkEvent(events.get(1), "Folder Added", null, "/a/y", null, null, null); checkEvent(events.get(1), "Folder Added", null, "/a/bigY", null, null, null);
checkEvent(events.get(2), "Folder Removed", "/a", null, null, null, "y");
checkEvent(events.get(3), "Folder Added", null, "/a/bigY", null, null, null);
} }
@ -532,12 +523,9 @@ public class DefaultProjectDataTest extends AbstractGhidraHeadedIntegrationTest
assertNull(root.getFolder("c")); assertNull(root.getFolder("c"));
assertNotNull(root.getFolder("bigC")); assertNotNull(root.getFolder("bigC"));
assertEventsSize(5); assertEventsSize(2);
checkEvent(events.get(0), "Folder Added", null, "/a", null, null, null); checkEvent(events.get(0), "Folder Removed", "/", null, null, null, "c");
checkEvent(events.get(1), "Folder Added", null, "/b", null, null, null); checkEvent(events.get(1), "Folder Added", null, "/bigC", null, null, null);
checkEvent(events.get(2), "Folder Added", null, "/c", null, null, null);
checkEvent(events.get(3), "Folder Removed", "/", null, null, null, "c");
checkEvent(events.get(4), "Folder Added", null, "/bigC", null, null, null);
} }
@Test @Test
@ -614,12 +602,9 @@ public class DefaultProjectDataTest extends AbstractGhidraHeadedIntegrationTest
sharedFS.moveFolder("/", "a", "/c"); sharedFS.moveFolder("/", "a", "/c");
flushFileSystemEvents(); // wait for FileSystemListener callback to update folder flushFileSystemEvents(); // wait for FileSystemListener callback to update folder
assertEventsSize(4); assertEventsSize(1);
checkEvent(events.get(0), "Folder Added", null, "/a", null, null, null); checkEvent(events.get(0), "Folder Added", null, "/c/a", null, null, null);
checkEvent(events.get(1), "Folder Added", null, "/b", null, null, null);
checkEvent(events.get(2), "Folder Added", null, "/c", null, null, null);
checkEvent(events.get(3), "Folder Added", null, "/c/a", null, null, null);
// versioned folder was moved to /c/a, but private folder /a should still exist // versioned folder was moved to /c/a, but private folder /a should still exist

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -207,9 +207,10 @@ public class FunctionStartAnalyzer extends AbstractAnalyzer implements PatternFa
private int validCodeMin = NO_VALID_INSTRUCTIONS_REQUIRED; private int validCodeMin = NO_VALID_INSTRUCTIONS_REQUIRED;
private int validCodeMax = VALID_INSTRUCTIONS_NO_MAX; private int validCodeMax = VALID_INSTRUCTIONS_NO_MAX;
private String label = null; private String label = null;
private boolean isThunk = false; // true if this function should be turned into a thunk private boolean isThunk = false; // true if this function should be turned into a thunk
private boolean noreturn = false; // true to set function non-returning private boolean noreturn = false; // true to set function non-returning
boolean validFunction = false; // must be defined at a function private String sectionName = null; // required section name
boolean validFunction = false; // must be defined at a function
private boolean contiguous = true; // require validcode instructions be contiguous private boolean contiguous = true; // require validcode instructions be contiguous
@Override @Override
@ -225,6 +226,14 @@ public class FunctionStartAnalyzer extends AbstractAnalyzer implements PatternFa
} }
protected boolean checkPreRequisites(Program program, Address addr) { protected boolean checkPreRequisites(Program program, Address addr) {
// check required section name
if (sectionName != null) {
MemoryBlock block = program.getMemory().getBlock(addr);
if (block == null || !block.getName().matches(sectionName)) {
return false;
}
}
/** /**
* If the match's mark point occurs in undefined data, schedule disassembly * If the match's mark point occurs in undefined data, schedule disassembly
* and a function start at that address. If the match's mark point occurs at an instruction, but that * and a function start at that address. If the match's mark point occurs at an instruction, but that
@ -641,6 +650,10 @@ public class FunctionStartAnalyzer extends AbstractAnalyzer implements PatternFa
isThunk = true; isThunk = true;
break; break;
case "section":
sectionName = attrValue;
break;
case "noreturn": case "noreturn":
noreturn = true; noreturn = true;
break; break;
@ -816,7 +829,14 @@ public class FunctionStartAnalyzer extends AbstractAnalyzer implements PatternFa
AutoAnalysisManager analysisManager = AutoAnalysisManager.getAnalysisManager(program); AutoAnalysisManager analysisManager = AutoAnalysisManager.getAnalysisManager(program);
if (!disassemResult.isEmpty()) { if (!disassemResult.isEmpty()) {
analysisManager.disassemble(disassemResult, AnalysisPriority.DISASSEMBLY); // disassemble known function starts now
AddressSet doNowDisassembly = disassemResult.intersect(funcResult);
// this will disassemble at this analyzers priority
analysisManager.disassemble(doNowDisassembly);
// delay disassemble of possible function starts
AddressSet delayedDisassembly = disassemResult.subtract(funcResult);
analysisManager.disassemble(delayedDisassembly, AnalysisPriority.DISASSEMBLY);
} }
analysisManager.setProtectedLocations(codeLocations); analysisManager.setProtectedLocations(codeLocations);

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -44,7 +44,7 @@ public class FunctionStartPreFuncAnalyzer extends FunctionStartAnalyzer {
public FunctionStartPreFuncAnalyzer() { public FunctionStartPreFuncAnalyzer() {
super(FUNCTION_START_PRE_SEARCH, DESCRIPTION, AnalyzerType.BYTE_ANALYZER); super(FUNCTION_START_PRE_SEARCH, DESCRIPTION, AnalyzerType.BYTE_ANALYZER);
setPriority(AnalysisPriority.BLOCK_ANALYSIS.after()); setPriority(AnalysisPriority.BLOCK_ANALYSIS.before());
setDefaultEnablement(true); setDefaultEnablement(true);
setSupportsOneTimeAnalysis(); setSupportsOneTimeAnalysis();
} }

View file

@ -83,10 +83,26 @@ class DetachedWindowNode extends WindowNode {
child = processChildElement(childElement, mgr, this, list); child = processChildElement(childElement, mgr, this, list);
} }
void setInitialBounds(Rectangle r) { // Set the bounds for the component that is being placed in this window when the window is first
// created. This is useful when dragging a component provider out of an existing window into
// its own window.
void setWindowContentsBounds(Rectangle r) {
// The rectangle will be empty when there is no size information for the component being
// added to this window node.
if (r == null) { if (r == null) {
r = new Rectangle(); r = new Rectangle();
} }
if (!r.isEmpty()) {
// We need to create window bounds from the given component bounds. The window has
// extra size for the toolbar and menus.
int nonComponentWidth = 12;
int nonComponentHeight = 120;
r.width += nonComponentWidth;
r.height += nonComponentHeight;
}
restoreBounds = r; restoreBounds = r;
} }
@ -372,6 +388,7 @@ class DetachedWindowNode extends WindowNode {
private Rectangle getNewBounds(Window newWindow) { private Rectangle getNewBounds(Window newWindow) {
Rectangle updatedBounds = new Rectangle(restoreBounds); Rectangle updatedBounds = new Rectangle(restoreBounds);
restoreBounds = null;
if (updatedBounds.isEmpty()) { if (updatedBounds.isEmpty()) {
// No bounds to restore; pick something reasonable // No bounds to restore; pick something reasonable
window.pack(); window.pack();
@ -379,20 +396,8 @@ class DetachedWindowNode extends WindowNode {
updatedBounds.height = d.height; updatedBounds.height = d.height;
updatedBounds.width = d.width; updatedBounds.width = d.width;
} }
else {
// Update the desired window bounds for the size of the component. The window size
// has to account for things like the menu and toolbars. These value were picked
// through trial-and-error.
int nonComponentWidth = 12;
int nonComponentHeight = 120;
updatedBounds.width += nonComponentWidth;
updatedBounds.height += nonComponentHeight;
}
ensureValidLocation(updatedBounds); ensureValidLocation(updatedBounds);
WindowUtilities.ensureEntirelyOnScreen(newWindow, updatedBounds); WindowUtilities.ensureEntirelyOnScreen(newWindow, updatedBounds);
return updatedBounds; return updatedBounds;

View file

@ -241,7 +241,7 @@ class RootNode extends WindowNode {
Point location = loc == null ? new Point() : loc; Point location = loc == null ? new Point() : loc;
Dimension size = placeholderSize == null ? new Dimension() : placeholderSize; Dimension size = placeholderSize == null ? new Dimension() : placeholderSize;
windowNode.setInitialBounds(new Rectangle(location, size)); windowNode.setWindowContentsBounds(new Rectangle(location, size));
detachedWindows.add(windowNode); detachedWindows.add(windowNode);
placeholder.getNode().add(placeholder); placeholder.getNode().add(placeholder);

View file

@ -558,12 +558,14 @@ class GhidraFolderData {
} }
/** /**
* Refresh set of sub-folder names and identify added/removed folders. * Refresh set of sub-folder names and notify about adds/removes if appropriate
* @param recursive recurse into visited subfolders if true * @param recursive recurse into visited subfolders if true
* @param notifyAdd true if listener should be notified about newly discovered subfolders
* @param monitor recursion task monitor - break from recursion if cancelled * @param monitor recursion task monitor - break from recursion if cancelled
* @throws IOException if an IO error occurs during the refresh * @throws IOException if an IO error occurs during the refresh
*/ */
private void refreshFolders(boolean recursive, TaskMonitor monitor) throws IOException { private void refreshFolders(boolean recursive, boolean notifyAdd, TaskMonitor monitor)
throws IOException {
// FIXME: inconsistent use of forced-recursive refresh and cached folderList // FIXME: inconsistent use of forced-recursive refresh and cached folderList
@ -630,7 +632,7 @@ class GhidraFolderData {
GhidraFolderData folderData = addFolderData(folderName); GhidraFolderData folderData = addFolderData(folderName);
if (folderData != null) { if (folderData != null) {
folderList.add(folderName); folderList.add(folderName);
if (visited) { if (notifyAdd) {
listener.domainFolderAdded(folderData.getDomainFolder()); listener.domainFolderAdded(folderData.getDomainFolder());
} }
} }
@ -678,7 +680,13 @@ class GhidraFolderData {
return map; return map;
} }
private void refreshFiles(TaskMonitor monitor) throws IOException { /**
* Refresh set of files and notify about adds/removes if appropriate
* @param notifyAdd true if listener should be notified about newly discovered files
* @param monitor return immediately if cancelled
* @throws IOException if an IO error occurs during the refresh
*/
private void refreshFiles(boolean notifyAdd, TaskMonitor monitor) throws IOException {
String path = getPathname(); String path = getPathname();
@ -744,7 +752,7 @@ class GhidraFolderData {
FolderItem versionedFolderItem = versionedItemMap.get(fileName); FolderItem versionedFolderItem = versionedItemMap.get(fileName);
GhidraFileData fileData = addFileData(fileName, localFolderItem, versionedFolderItem); GhidraFileData fileData = addFileData(fileName, localFolderItem, versionedFolderItem);
if (visited) { if (notifyAdd) {
listener.domainFileAdded(fileData.getDomainFile()); listener.domainFileAdded(fileData.getDomainFile());
} }
} }
@ -791,6 +799,7 @@ class GhidraFolderData {
return; return;
} }
boolean notifyAdd = visited;
visited = true; visited = true;
try { try {
@ -812,13 +821,13 @@ class GhidraFolderData {
// FIXME: If forced we should be refreshing folder/file lists // FIXME: If forced we should be refreshing folder/file lists
refreshFiles(monitor); refreshFiles(notifyAdd, monitor);
if (monitor != null && monitor.isCancelled()) { if (monitor != null && monitor.isCancelled()) {
return; // break-out from recursion on cancel return; // break-out from recursion on cancel
} }
refreshFolders(recursive, monitor); refreshFolders(recursive, notifyAdd, monitor);
} }
} }

View file

@ -92,5 +92,6 @@ data/patterns/x86-64gcc_patterns.xml||GHIDRA||||END|
data/patterns/x86-64win_patterns.xml||GHIDRA||||END| data/patterns/x86-64win_patterns.xml||GHIDRA||||END|
data/patterns/x86delphi_patterns.xml||GHIDRA||||END| data/patterns/x86delphi_patterns.xml||GHIDRA||||END|
data/patterns/x86gcc_patterns.xml||GHIDRA||||END| data/patterns/x86gcc_patterns.xml||GHIDRA||||END|
data/patterns/x86gcc_prepatterns.xml||GHIDRA||||END|
data/patterns/x86win_patterns.xml||GHIDRA||||END| data/patterns/x86win_patterns.xml||GHIDRA||||END|
data/patterns/x86win_prepatterns.xml||GHIDRA||||END| data/patterns/x86win_prepatterns.xml||GHIDRA||||END|

View file

@ -7,6 +7,15 @@
<compiler id="borlandcpp"> <compiler id="borlandcpp">
<patternfile>x86win_prepatterns.xml</patternfile> <patternfile>x86win_prepatterns.xml</patternfile>
</compiler> </compiler>
<compiler id="gcc">
<patternfile>x86gcc_prepatterns.xml</patternfile>
</compiler>
</language> </language>
<language id="x86:LE:64:default">
<compiler id="gcc">
<patternfile>x86gcc_prepatterns.xml</patternfile>
</compiler>
</language>
</patternconstraints> </patternconstraints>

View file

@ -0,0 +1,20 @@
<patternlist>
<pattern>
<data>
0xff25........ <!-- jmp -->
0x68......00 <!-- push -->
0xe9......ff <!-- jmp -addr -->
</data> <!-- .plt thunk -->
<funcstart thunk="true" section=".plt"/>
</pattern>
<pattern>
<data>
0xf3 0x0f 0x1e 0x1a <!-- ENDBR64 -->
0xf2 0xff 0x25 .. .. .. .. <!-- jmp -->
</data> <!-- .plt thunk -->
<funcstart thunk="true" section=".plt"/>
</pattern>
</patternlist>

View file

@ -1,5 +1,5 @@
application.name=Ghidra application.name=Ghidra
application.version=12.0 application.version=12.1
application.release.name=DEV application.release.name=DEV
application.layout.version=3 application.layout.version=3
application.gradle.min=8.5 application.gradle.min=8.5