Compare commits
19 commits
a94ca1ed7e
...
4896cd5c97
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4896cd5c97 | ||
![]() |
ad35a7e956 | ||
![]() |
3bba2c2f14 | ||
![]() |
2d10da420f | ||
![]() |
437bed4be0 | ||
![]() |
c3d8571ba3 | ||
![]() |
c99af66f5f | ||
![]() |
6dd00be368 | ||
![]() |
1c5e9ea9c2 | ||
![]() |
22b3524206 | ||
![]() |
a893955b03 | ||
![]() |
a1851c5911 | ||
![]() |
4c8d408fc7 | ||
![]() |
9e2a8d5604 | ||
![]() |
5f6e3806b3 | ||
![]() |
47acbe98bb | ||
![]() |
fe4bb30704 | ||
![]() |
79eea09e64 | ||
![]() |
4b6d90366c |
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 18 KiB |
|
@ -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");
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 " +
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
20
Ghidra/Processors/x86/data/patterns/x86gcc_prepatterns.xml
Normal 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>
|
|
@ -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
|
||||||
|
|