diff --git a/Ghidra/Configurations/Public_Release/src/global/docs/WhatsNew.md b/Ghidra/Configurations/Public_Release/src/global/docs/WhatsNew.md index d77182f758..4cf4fcc37a 100644 --- a/Ghidra/Configurations/Public_Release/src/global/docs/WhatsNew.md +++ b/Ghidra/Configurations/Public_Release/src/global/docs/WhatsNew.md @@ -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 vulnerabilities in networks and systems. -# What's coming in Ghidra 12.0 -This is a preview of what is coming in the future Ghidra 12.0 release. +# What's New in Ghidra 12.0 +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 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 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 - 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 -and repositories containing such files are used by older version of Ghidra or the Ghidra Server. +with it a new file storage type has been introduced which can create some incompatibilities if +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. -With 12.0 the ability to establish internal folder and file links has been introduced. The new -storage format avoids the use of a database and relies only on a light-weight property file. Internal +Previously only external folder and file links were supported through the use of a Ghidra URL. With +12.0 the ability to establish internal folder and file links has been introduced. The new storage +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 -a folder or file to have the same pathname, some ambiguities can result. It is highly recommended that -the use of conflicting folder and file pathnames be avoided. +a folder or file to have the same pathname, some ambiguities can result. It is highly recommended +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 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`, `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 -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 11.4 is fully backward compatible with project data from previous releases. However, programs -and data type archives which are created or modified in 11.4 will not be usable by an earlier Ghidra -version. +## PyGhidra +PyGhidra 3.0.0 (compatible with Ghidra 12.0 and later) introduces many new Python-specific API +methods with the goal of making the most common Ghidra tasks quick and easy, such as opening a +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 +methods. Below is an example program that showcases some of the new API functionality. See the +PyGhidra library README for more information. +```python +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 -(3.9 to 3.13 supported) installed on your system. + # Walk a Ghidra release zip file, load every decompiler binary, and save them to the project + 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 -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. + # Analyze the windows decompiler program for a maximum of 10 seconds + with pyghidra.program_context(project, "/win_x86_64/decompile.exe") as program: + analysis_props = pyghidra.analysis_properties(program) + 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 -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. + # Load some bytes as a new program + ByteArrayCls = jpype.JArray(jpype.JByte) + my_bytes = ByteArrayCls(b"\xaa\xbb\xcc\xdd\xee\xff") + loader = pyghidra.program_loader().project(project).source(my_bytes).name("my_bytes") + loader = loader.loaders("BinaryLoader").language("DATA:LE:64:default") + with loader.load() as load_results: + load_results.save(pyghidra.monitor()) -**NOTE:** Ghidra Server: The Ghidra 11.x server is compatible with Ghidra 9.2 and later Ghidra -clients. Ghidra 11.x clients are compatible with all 10.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. + # Run a GhidraScript + pyghidra.ghidra_script(f"{os.environ['GHIDRA_SCRIPTS_DIR']}/HelloWorldScript.java", project) +``` +## 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 -of Ghidra elements and replacing that text with a different text sequence. Using this feature, many different -Ghidra elements can be renamed all at once including labels, functions, name-spaces, parameters, data-types, -field names, and enum values. This feature also supports regular expressions (including capture groups). -After initiating a search and replace, a results table is displayed with a list of items that match the -search. From this table, the replace actions can be applied in bulk or individually, one item at a time -as they are reviewed. - -## 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. +Extensions that currently integrate via inheritance can continue to do so, but will still need to +apply some minimal changes to satisfy interface and constructor changes. The developers of such +extensions ought to consider porting their integrations to the compositional/callback-based +mechanism. A careful assessment may be required depending on the nature of the extension. Extensions +that merely integrate with emulation should consider the compositional/callback-based mechanism. +Extensions that incorporate new domains (e.g. Z3) or novel behaviors (e.g. JIT) should continue +using inheritance. ## Other Improvements - + Many calling conventions for various processors/compilers have been improved using the more flexible decompiler rules - when the data types for parameters and return values are known. - + Upgraded many 3rd party dependencies to address potential bugs and CVE's, including jars for Bouncy Castle, - Apache Commons Compress, Apache Commons Lang3, Apache Commons IO, protobuf, and JUnit. + + Added the ability to toggle the displaying of function variables (parameters and locals) that are + normally displayed just below the function signature. The variables display can be turned on/off + globally or individually per function. ## Additional Bug Fixes and Enhancements Numerous other new features, improvements, and bug fixes are fully listed in the diff --git a/Ghidra/Features/Base/data/PEFunctionsThatDoNotReturn b/Ghidra/Features/Base/data/PEFunctionsThatDoNotReturn index 4ef288a838..115bf6ff95 100644 --- a/Ghidra/Features/Base/data/PEFunctionsThatDoNotReturn +++ b/Ghidra/Features/Base/data/PEFunctionsThatDoNotReturn @@ -18,3 +18,11 @@ longjmp quick_exit RpcRaiseException 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 diff --git a/Ghidra/Features/Base/ghidra_scripts/FixupNoReturnFunctionsScript.java b/Ghidra/Features/Base/ghidra_scripts/FixupNoReturnFunctionsScript.java index 49b7b136c8..a5af7ec64b 100644 --- a/Ghidra/Features/Base/ghidra_scripts/FixupNoReturnFunctionsScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/FixupNoReturnFunctionsScript.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * 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.program.model.address.*; import ghidra.program.model.block.*; +import ghidra.program.model.lang.Processor; import ghidra.program.model.listing.*; import ghidra.program.model.symbol.*; import ghidra.util.exception.CancelledException; @@ -48,10 +49,15 @@ import ghidra.util.exception.CancelledException; public class FixupNoReturnFunctionsScript extends GhidraScript { IssueEntries entryList = null; + + private final static String X86_NAME = "x86"; + boolean isX86; @Override public void run() throws Exception { Program cp = currentProgram; + + isX86 = checkForX86(cp); 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) { StringColumnDisplay explanationColumn = new StringColumnDisplay() { @Override @@ -373,10 +384,22 @@ public class FixupNoReturnFunctionsScript extends GhidraScript { // // FunctionManager functionManager = currentProgram.getFunctionManager(); - FunctionIterator functionIter = functionManager.getFunctions(true); + AddressSet set = new AddressSet(); HashSet suspectNoReturnFunctions = new HashSet(); + 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 suspectNoReturnFunctions) throws CancelledException { while (functionIter.hasNext()) { Function candidateNoReturnfunction = functionIter.next(); noReturnEntries.setMessage("Checking function: " + candidateNoReturnfunction.getName()); @@ -428,8 +451,6 @@ public class FixupNoReturnFunctionsScript extends GhidraScript { suspectNoReturnFunctions.add(candidateNoReturnfunction); } } - - return set; } private boolean testCalledFunctionsNonReturning(Function candidateNonReturningFunction, @@ -462,8 +483,8 @@ public class FixupNoReturnFunctionsScript extends GhidraScript { FunctionManager funcManager = currentProgram.getFunctionManager(); Listing listing = currentProgram.getListing(); while (fallThru != null) { - if (funcManager.getFunctionAt(fallThru) != null) { - + Function fallThruFunction = funcManager.getFunctionAt(fallThru); + if (fallThruFunction != null) { NoReturnLocations location = new NoReturnLocations(currentProgram, ref.getToAddress(), ref.getFromAddress(), "Function defined after call"); dialog.add(location); @@ -490,11 +511,15 @@ public class FixupNoReturnFunctionsScript extends GhidraScript { // or references. This is especially true if there is only one // example for a calling reference. if (callingFunc != null) { + Address fromAddress = reference.getFromAddress(); Function function = - funcManager.getFunctionContaining(reference.getFromAddress()); - if (callingFunc.equals(function)) { + funcManager.getFunctionContaining(fromAddress); + // 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, - ref.getToAddress(), ref.getFromAddress(), + ref.getToAddress(), fromAddress, "Data Reference from same function after call"); dialog.add(location); return true; @@ -517,6 +542,15 @@ public class FixupNoReturnFunctionsScript extends GhidraScript { dialog.add(location); 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; if (block.getFlowType().isFallthrough()) { CodeBlockReferenceIterator dests = block.getDestinations(monitor); diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Search/images/MemorySearchProvider.png b/Ghidra/Features/Base/src/main/help/help/topics/Search/images/MemorySearchProvider.png index 86aa4931eb..0bb8d69c1a 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/Search/images/MemorySearchProvider.png and b/Ghidra/Features/Base/src/main/help/help/topics/Search/images/MemorySearchProvider.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Search/images/MemorySearchProviderWithOptionsOn.png b/Ghidra/Features/Base/src/main/help/help/topics/Search/images/MemorySearchProviderWithOptionsOn.png index 87bf8867af..0b57358cbf 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/Search/images/MemorySearchProviderWithOptionsOn.png and b/Ghidra/Features/Base/src/main/help/help/topics/Search/images/MemorySearchProviderWithOptionsOn.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Search/images/MemorySearchProviderWithScanPanelOn.png b/Ghidra/Features/Base/src/main/help/help/topics/Search/images/MemorySearchProviderWithScanPanelOn.png index 94a13ce3ba..3290880690 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/Search/images/MemorySearchProviderWithScanPanelOn.png and b/Ghidra/Features/Base/src/main/help/help/topics/Search/images/MemorySearchProviderWithScanPanelOn.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Search/images/SearchMemoryRegex.png b/Ghidra/Features/Base/src/main/help/help/topics/Search/images/SearchMemoryRegex.png index 0b77e07997..75f1cc6eac 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/Search/images/SearchMemoryRegex.png and b/Ghidra/Features/Base/src/main/help/help/topics/Search/images/SearchMemoryRegex.png differ diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/FindNoReturnFunctionsAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/FindNoReturnFunctionsAnalyzer.java index 06b87089d7..b2af9b487a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/FindNoReturnFunctionsAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/FindNoReturnFunctionsAnalyzer.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * 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.program.model.address.*; import ghidra.program.model.block.*; -import ghidra.program.model.lang.GhidraLanguagePropertyKeys; -import ghidra.program.model.lang.Language; +import ghidra.program.model.lang.*; import ghidra.program.model.listing.*; import ghidra.program.model.pcode.PcodeOp; 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 nextFunction = null; // last return nextFunction + + private final static String X86_NAME = "x86"; + boolean isX86; public FindNoReturnFunctionsAnalyzer() { this(NAME, DESCRIPTION, AnalyzerType.INSTRUCTION_ANALYZER); @@ -105,6 +107,8 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer { this.monitor = monitor; this.reasonList = new ArrayList<>(); lastGetNextFuncAddress = null; + + isX86 = checkForX86(program); monitor.setMessage("NoReturn - Finding non-returning functions"); @@ -149,6 +153,11 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer { } return true; } + + private boolean checkForX86(Program cp) { + return cp.getLanguage().getProcessor().equals( + Processor.findOrPossiblyCreateProcessor(X86_NAME)); + } /** * repair any damaged locations @@ -358,7 +367,10 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer { } // 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) { int count = 1; @@ -558,6 +570,17 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer { 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 fallThru = null; if (instr.getFlowType().isFallthrough()) { @@ -566,7 +589,39 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer { } 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. * 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(); RefType refType = reference.getReferenceType(); if (refType.isRead() || refType.isWrite()) { - // look at function the reference is coming from - // is the function the same as the call is in + // Check function the reference is coming from + // is the same function as the call is in // This is a better indicator of non-returning // Random references from another function could be bad disassembly // 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 // then this check will do nothing if (callingFunc != null) { + Address fromAddress = reference.getFromAddress(); Function function = - funcManager.getFunctionContaining(reference.getFromAddress()); - if (callingFunc.equals(function)) { + funcManager.getFunctionContaining(fromAddress); + // 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 = new NoReturnLocations(calledAddr, reference.getToAddress(), "Data Reference from same function after call"); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java index 779f97ce88..0f1de137df 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java @@ -541,13 +541,26 @@ public class PeLoader extends AbstractPeDebugLoader { return; } - AddressFactory af = program.getAddressFactory(); - AddressSpace space = af.getDefaultAddressSpace(); + AddressSpace space = program.getAddressFactory().getDefaultAddressSpace(); SymbolTable symTable = program.getSymbolTable(); - Listing listing = program.getListing(); 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(); + 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) { if (monitor.isCancelled()) { return; @@ -555,65 +568,51 @@ public class PeLoader extends AbstractPeDebugLoader { Address address = space.getAddress(export.getAddress()); 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()) { - try { - listing.createData(address, TerminatedStringDataType.dataType, -1); - Data data = listing.getDataAt(address); - if (data != null) { - Object obj = data.getValue(); - if (obj instanceof String) { - String str = (String) obj; - int dotpos = str.indexOf('.'); + Data data = + PeUtils.createData(program, address, TerminatedStringDataType.dataType, log); + if (extAddr != null && data != null && data.getValue() instanceof String str) { + int dotpos = str.indexOf('.'); + if (dotpos < 0) { + dotpos = 0; // TODO + } + String libName = str.substring(0, dotpos) + ".dll"; + String extSymbolName = str.substring(dotpos + 1); - if (dotpos < 0) { - dotpos = 0;//TODO - } - - // get the name of the dll - String dllName = str.substring(0, dotpos) + ".dll"; - - // get the name of the symbol - String expName = str.substring(dotpos + 1); - - try { - refManager.addExternalReference(address, dllName.toUpperCase(), - expName, null, SourceType.IMPORTED, 0, RefType.DATA); - } - catch (DuplicateNameException e) { - log.appendMsg("External location not created: " + e.getMessage()); - } - catch (InvalidInputException e) { - log.appendMsg("External location not created: " + e.getMessage()); - } - } + try { + symTable.addExternalEntryPoint(extAddr); + Function function = funcManager.createFunction(export.getName(), extAddr, + new AddressSet(extAddr), SourceType.IMPORTED); + ExternalLocation loc = extManager.addExtLocation(libName.toUpperCase(), + extSymbolName, null, SourceType.IMPORTED); + function.setThunkedFunction(loc.createFunction()); + symTable.createLabel(extAddr, + SymbolUtilities.ORDINAL_PREFIX + export.getOrdinal(), + SourceType.IMPORTED); + refManager.addMemoryReference(address, extAddr, RefType.DATA, + SourceType.IMPORTED, 0); + setComment(CommentType.PLATE, extAddr, export.getComment()); + } + catch (InvalidInputException | DuplicateNameException + | OverlappingFunctionException 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 } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemoryScanControlPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemoryScanControlPanel.java index 82e8522eb6..eac955f614 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemoryScanControlPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemoryScanControlPanel.java @@ -15,7 +15,6 @@ */ package ghidra.features.base.memsearch.gui; -import java.awt.BorderLayout; import java.awt.FlowLayout; import javax.swing.*; @@ -38,20 +37,30 @@ public class MemoryScanControlPanel extends JPanel { private JButton scanButton; MemoryScanControlPanel(MemorySearchProvider provider) { - super(new BorderLayout()); + super(); + + setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS)); + setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0)); - add(buildButtonPanel(), BorderLayout.CENTER); + scanButton = new JButton("Scan Values"); + scanButton.setMnemonic('V'); + scanButton.setEnabled(false); scanButton.setToolTipText("Refreshes byte values of current results and eliminates " + "those that don't meet the selected change criteria"); + + add(scanButton); + add(Box.createHorizontalStrut(20)); + add(buildButtonPanel()); + HelpService helpService = Help.getHelpService(); helpService.registerHelp(this, new HelpLocation(HelpTopics.SEARCH, "Scan_Controls")); - add(scanButton, BorderLayout.WEST); + scanButton.addActionListener(e -> provider.scan(selectedScanner)); } private JComponent buildButtonPanel() { - JPanel panel = new JPanel(new FlowLayout()); + JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEADING)); ButtonGroup buttonGroup = new ButtonGroup(); for (Scanner scanner : Scanner.values()) { GRadioButton button = new GRadioButton(scanner.getName()); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemorySearchControlPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemorySearchControlPanel.java index 4a6e8691e9..cf6c1180d3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemorySearchControlPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemorySearchControlPanel.java @@ -24,14 +24,17 @@ import java.util.List; import javax.swing.*; import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; import javax.swing.text.*; import docking.DockingUtils; import docking.menu.ButtonState; import docking.menu.MultiStateButton; import docking.widgets.PopupWindow; +import docking.widgets.combobox.GComboBox; import docking.widgets.combobox.GhidraComboBox; import docking.widgets.label.GDLabel; +import docking.widgets.label.GLabel; import docking.widgets.list.GComboBoxCellRenderer; import generic.theme.GThemeDefaults.Colors.Messages; import ghidra.features.base.memsearch.combiner.Combiner; @@ -89,9 +92,11 @@ class MemorySearchControlPanel extends JPanel { searchButton = new MultiStateButton(initialSearchButtonStates); searchButton .setStateChangedListener(state -> model.setMatchCombiner(state.getClientData())); + searchButton.setMnemonic('S'); searchButton.addActionListener(e -> search()); panel.add(searchButton, BorderLayout.WEST); selectionCheckbox = new JCheckBox("Selection Only"); + selectionCheckbox.setMnemonic('O'); selectionCheckbox.setSelected(model.isSearchSelectionOnly()); selectionCheckbox.setEnabled(model.hasSelection()); selectionCheckbox @@ -132,9 +137,10 @@ class MemorySearchControlPanel extends JPanel { if (!formatComboBox.getSelectedItem().equals(searchFormat)) { formatComboBox.setSelectedItem(searchFormat); } + selectionCheckbox.setSelected(model.isSearchSelectionOnly()); selectionCheckbox.setEnabled(model.hasSelection()); - searchInputField.setToolTipText(searchFormat.getToolTip()); + searchInputField.setToolTipText("Search Text: " + searchFormat.getToolTip()); String text = searchInputField.getText(); String convertedText = searchFormat.convertText(text, oldSettings, model.getSettings()); @@ -144,23 +150,53 @@ class MemorySearchControlPanel extends JPanel { } 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.setName("HexSequenceField"); + hexSearchSequenceField.setName("Hex Sequence Field"); Border outerBorder = BorderFactory.createLoweredBevelBorder(); Border innerBorder = BorderFactory.createEmptyBorder(0, 4, 0, 4); Border border = BorderFactory.createCompoundBorder(outerBorder, innerBorder); hexSearchSequenceField.setBorder(border); - JPanel panel = new JPanel(new PairLayout(2, 10)); - panel.add(buildSearchFormatCombo()); - panel.add(searchInputField); - JLabel byteSequenceLabel = new JLabel("Byte Sequence:", SwingConstants.RIGHT); - byteSequenceLabel.setToolTipText( - "This field shows the byte sequence that will be search (if applicable)"); + panel.add(hexSearchSequenceField, BorderLayout.CENTER); + int spaceWidth = formatComboBox.getPreferredSize().width; + panel.add(Box.createHorizontalStrut(spaceWidth), BorderLayout.EAST); + + return panel; + } + + 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; } @@ -181,7 +217,7 @@ class MemorySearchControlPanel extends JPanel { updateCombo(); searchInputField.setAutoCompleteEnabled(false); // this interferes with validation searchInputField.setEditable(true); - searchInputField.setToolTipText(model.getSearchFormat().getToolTip()); + searchInputField.setToolTipText("Search Text: " + model.getSearchFormat().getToolTip()); searchInputField.setDocument(new RestrictedInputDocument()); searchInputField.addActionListener(ev -> search()); JTextField searchTextField = searchInputField.getTextField(); @@ -220,11 +256,14 @@ class MemorySearchControlPanel extends JPanel { } private JComponent buildSearchFormatCombo() { - formatComboBox = new JComboBox<>(SearchFormat.ALL); + formatComboBox = new GComboBox<>(SearchFormat.ALL); formatComboBox.setSelectedItem(model.getSearchFormat()); formatComboBox.addItemListener(this::formatComboChanged); - formatComboBox.setToolTipText("The selected format will determine how to " + - "interpret text typed into the input field"); + formatComboBox.setToolTipText("Search Format: how to interpret search text"); + Border inside = formatComboBox.getBorder(); + CompoundBorder paddingBorder = + BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0), inside); + formatComboBox.setBorder(paddingBorder); return formatComboBox; } @@ -252,7 +291,7 @@ class MemorySearchControlPanel extends JPanel { currentMatcher = byteMatcher; String text = currentMatcher.getDescription(); hexSearchSequenceField.setText(text); - hexSearchSequenceField.setToolTipText(currentMatcher.getToolTip()); + hexSearchSequenceField.setToolTipText("Search as hex: " + currentMatcher.getToolTip()); updateSearchButton(); provider.setByteMatcher(byteMatcher); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemorySearchOptionsPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemorySearchOptionsPanel.java index 4b5585254b..489f68d27e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemorySearchOptionsPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemorySearchOptionsPanel.java @@ -29,6 +29,7 @@ import javax.swing.text.*; import docking.widgets.checkbox.GCheckBox; import docking.widgets.combobox.GComboBox; +import docking.widgets.label.GLabel; import ghidra.app.util.HelpTopics; import ghidra.docking.util.LookAndFeelUtils; import ghidra.features.base.memsearch.bytesource.SearchRegion; @@ -57,7 +58,7 @@ class MemorySearchOptionsPanel extends JPanel { super(new BorderLayout()); 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. isNimbus = LookAndFeelUtils.isUsingNimbusUI(); @@ -93,9 +94,22 @@ class MemorySearchOptionsPanel extends JPanel { JPanel panel = new JPanel(new VerticalLayout(3)); panel.setBorder(createBorder("Search Region Filter")); + boolean accelerator = true; List choices = model.getMemoryRegionChoices(); for (SearchRegion region : choices) { 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.setSelected(model.isSelectedRegion(region)); checkbox.addItemListener(e -> model.selectRegion(region, checkbox.isSelected())); @@ -109,13 +123,15 @@ class MemorySearchOptionsPanel extends JPanel { panel.setBorder(createBorder("Decimal Options")); 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"); innerPanel.add(label); Integer[] decimalSizes = new Integer[] { 1, 2, 3, 4, 5, 6, 7, 8, 16 }; int decimalByteSize = model.getDecimalByteSize(); decimalByteSizeCombo = new GComboBox<>(decimalSizes); + label.setLabelFor(decimalByteSizeCombo); decimalByteSizeCombo.setSelectedItem(decimalByteSize); decimalByteSizeCombo.addItemListener(this::byteSizeComboChanged); decimalByteSizeCombo.setToolTipText("Size of decimal values in bytes"); @@ -123,6 +139,7 @@ class MemorySearchOptionsPanel extends JPanel { panel.add(innerPanel); decimalUnsignedCheckbox = new GCheckBox("Unsigned"); + decimalUnsignedCheckbox.setMnemonic('U'); decimalUnsignedCheckbox.setToolTipText( "Sets whether decimal values should be interpreted as unsigned values"); decimalUnsignedCheckbox.addActionListener( @@ -145,8 +162,11 @@ class MemorySearchOptionsPanel extends JPanel { JPanel panel = new JPanel(new VerticalLayout(5)); panel.setBorder(createBorder("Code Type Filter")); GCheckBox instructionsCheckBox = new GCheckBox("Instructions"); + instructionsCheckBox.setMnemonic('I'); GCheckBox definedDataCheckBox = new GCheckBox("Defined Data"); + definedDataCheckBox.setMnemonic('D'); GCheckBox undefinedDataCheckBox = new GCheckBox("Undefined Data"); + undefinedDataCheckBox.setMnemonic('U'); instructionsCheckBox.setToolTipText( "If selected, include matches found in instructions"); definedDataCheckBox.setToolTipText( @@ -185,9 +205,15 @@ class MemorySearchOptionsPanel extends JPanel { alignField.setToolTipText( "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(new JLabel("Alignment:")); + panel.add(alignmentLabel); panel.add(alignField); return panel; @@ -215,19 +241,23 @@ class MemorySearchOptionsPanel extends JPanel { charsetCombo.setToolTipText("Character encoding for translating strings to bytes"); 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"); innerPanel.add(label); innerPanel.add(charsetCombo); panel.add(innerPanel); caseSensitiveCheckbox = new GCheckBox("Case Sensitive"); + caseSensitiveCheckbox.setMnemonic('n'); caseSensitiveCheckbox.setSelected(model.isCaseSensitive()); caseSensitiveCheckbox.setToolTipText("Allows for case sensitive searching."); caseSensitiveCheckbox.addActionListener( e -> model.setCaseSensitive(caseSensitiveCheckbox.isSelected())); escapeSequencesCheckbox = new GCheckBox("Escape Sequences"); + escapeSequencesCheckbox.setMnemonic('c'); escapeSequencesCheckbox.setSelected(model.useEscapeSequences()); escapeSequencesCheckbox.setToolTipText( "Allows specifying control characters using escape sequences " + diff --git a/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemorySearchProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemorySearchProvider.java index 95060011d9..a572739c9b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemorySearchProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/gui/MemorySearchProvider.java @@ -698,10 +698,18 @@ public class MemorySearchProvider extends ComponentProviderAdapter } @Override - protected ActionContext createContext(Component sourceComponent, Object contextObject) { + protected ActionContext createContext(Component focusedComponent, Object contextObject) { ActionContext context = new NavigatableActionContext(this, navigatable); 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; } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/data/DefaultProjectDataTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/data/DefaultProjectDataTest.java index 4f468c2d00..9be87dcce7 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/data/DefaultProjectDataTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/framework/data/DefaultProjectDataTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -339,11 +339,7 @@ public class DefaultProjectDataTest extends AbstractGhidraHeadedIntegrationTest sharedFS.createFolder("/", "b"); flushFileSystemEvents(); // wait for FileSystemListener callback to update folder assertEquals(3, root.getFolders().length); - assertEventsSize(3); - - 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); + assertEventsSize(0); sharedFS.createFolder("/b", "subB"); 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 sharedFS.renameFolder("/", "a", "bigA"); 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(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); + checkEvent(events.get(0), "Folder Added", null, "/bigA", null, null, null); // 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"); flushFileSystemEvents(); // wait for FileSystemListener callback to update folder - assertEventsSize(4); - checkEvent(events.get(0), "Folder Added", null, "/a/x", null, null, null); - checkEvent(events.get(1), "Folder Added", null, "/a/y", 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); + assertEventsSize(2); + checkEvent(events.get(0), "Folder Removed", "/a", null, null, null, "y"); + checkEvent(events.get(1), "Folder Added", null, "/a/bigY", null, null, null); } @@ -532,12 +523,9 @@ public class DefaultProjectDataTest extends AbstractGhidraHeadedIntegrationTest assertNull(root.getFolder("c")); assertNotNull(root.getFolder("bigC")); - assertEventsSize(5); - 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); - checkEvent(events.get(3), "Folder Removed", "/", null, null, null, "c"); - checkEvent(events.get(4), "Folder Added", null, "/bigC", null, null, null); + assertEventsSize(2); + checkEvent(events.get(0), "Folder Removed", "/", null, null, null, "c"); + checkEvent(events.get(1), "Folder Added", null, "/bigC", null, null, null); } @Test @@ -614,12 +602,9 @@ public class DefaultProjectDataTest extends AbstractGhidraHeadedIntegrationTest sharedFS.moveFolder("/", "a", "/c"); 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(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); + checkEvent(events.get(0), "Folder Added", null, "/c/a", null, null, null); // versioned folder was moved to /c/a, but private folder /a should still exist diff --git a/Ghidra/Features/BytePatterns/src/main/java/ghidra/app/analyzers/FunctionStartAnalyzer.java b/Ghidra/Features/BytePatterns/src/main/java/ghidra/app/analyzers/FunctionStartAnalyzer.java index 554642c0a4..c02e18321e 100644 --- a/Ghidra/Features/BytePatterns/src/main/java/ghidra/app/analyzers/FunctionStartAnalyzer.java +++ b/Ghidra/Features/BytePatterns/src/main/java/ghidra/app/analyzers/FunctionStartAnalyzer.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * 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 validCodeMax = VALID_INSTRUCTIONS_NO_MAX; private String label = null; - private boolean isThunk = false; // true if this function should be turned into a thunk - private boolean noreturn = false; // true to set function non-returning - boolean validFunction = false; // must be defined at a function + 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 String sectionName = null; // required section name + boolean validFunction = false; // must be defined at a function private boolean contiguous = true; // require validcode instructions be contiguous @Override @@ -225,6 +226,14 @@ public class FunctionStartAnalyzer extends AbstractAnalyzer implements PatternFa } 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 * 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; break; + case "section": + sectionName = attrValue; + break; + case "noreturn": noreturn = true; break; @@ -816,7 +829,14 @@ public class FunctionStartAnalyzer extends AbstractAnalyzer implements PatternFa AutoAnalysisManager analysisManager = AutoAnalysisManager.getAnalysisManager(program); 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); diff --git a/Ghidra/Features/BytePatterns/src/main/java/ghidra/app/analyzers/FunctionStartPreFuncAnalyzer.java b/Ghidra/Features/BytePatterns/src/main/java/ghidra/app/analyzers/FunctionStartPreFuncAnalyzer.java index 2c40ab2bab..024d279e69 100644 --- a/Ghidra/Features/BytePatterns/src/main/java/ghidra/app/analyzers/FunctionStartPreFuncAnalyzer.java +++ b/Ghidra/Features/BytePatterns/src/main/java/ghidra/app/analyzers/FunctionStartPreFuncAnalyzer.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -44,7 +44,7 @@ public class FunctionStartPreFuncAnalyzer extends FunctionStartAnalyzer { public FunctionStartPreFuncAnalyzer() { super(FUNCTION_START_PRE_SEARCH, DESCRIPTION, AnalyzerType.BYTE_ANALYZER); - setPriority(AnalysisPriority.BLOCK_ANALYSIS.after()); + setPriority(AnalysisPriority.BLOCK_ANALYSIS.before()); setDefaultEnablement(true); setSupportsOneTimeAnalysis(); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/DetachedWindowNode.java b/Ghidra/Framework/Docking/src/main/java/docking/DetachedWindowNode.java index 5a8584d83f..1a1066427d 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/DetachedWindowNode.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/DetachedWindowNode.java @@ -83,10 +83,26 @@ class DetachedWindowNode extends WindowNode { 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) { 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; } @@ -372,6 +388,7 @@ class DetachedWindowNode extends WindowNode { private Rectangle getNewBounds(Window newWindow) { Rectangle updatedBounds = new Rectangle(restoreBounds); + restoreBounds = null; if (updatedBounds.isEmpty()) { // No bounds to restore; pick something reasonable window.pack(); @@ -379,20 +396,8 @@ class DetachedWindowNode extends WindowNode { updatedBounds.height = d.height; 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); - WindowUtilities.ensureEntirelyOnScreen(newWindow, updatedBounds); return updatedBounds; diff --git a/Ghidra/Framework/Docking/src/main/java/docking/RootNode.java b/Ghidra/Framework/Docking/src/main/java/docking/RootNode.java index b3e3655ac5..2ed477d953 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/RootNode.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/RootNode.java @@ -241,7 +241,7 @@ class RootNode extends WindowNode { Point location = loc == null ? new Point() : loc; Dimension size = placeholderSize == null ? new Dimension() : placeholderSize; - windowNode.setInitialBounds(new Rectangle(location, size)); + windowNode.setWindowContentsBounds(new Rectangle(location, size)); detachedWindows.add(windowNode); placeholder.getNode().add(placeholder); diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/GhidraFolderData.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/GhidraFolderData.java index b23e02aa08..d7685ee830 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/GhidraFolderData.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/data/GhidraFolderData.java @@ -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 notifyAdd true if listener should be notified about newly discovered subfolders * @param monitor recursion task monitor - break from recursion if cancelled * @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 @@ -630,7 +632,7 @@ class GhidraFolderData { GhidraFolderData folderData = addFolderData(folderName); if (folderData != null) { folderList.add(folderName); - if (visited) { + if (notifyAdd) { listener.domainFolderAdded(folderData.getDomainFolder()); } } @@ -678,7 +680,13 @@ class GhidraFolderData { 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(); @@ -744,7 +752,7 @@ class GhidraFolderData { FolderItem versionedFolderItem = versionedItemMap.get(fileName); GhidraFileData fileData = addFileData(fileName, localFolderItem, versionedFolderItem); - if (visited) { + if (notifyAdd) { listener.domainFileAdded(fileData.getDomainFile()); } } @@ -791,6 +799,7 @@ class GhidraFolderData { return; } + boolean notifyAdd = visited; visited = true; try { @@ -812,13 +821,13 @@ class GhidraFolderData { // FIXME: If forced we should be refreshing folder/file lists - refreshFiles(monitor); + refreshFiles(notifyAdd, monitor); if (monitor != null && monitor.isCancelled()) { return; // break-out from recursion on cancel } - refreshFolders(recursive, monitor); + refreshFolders(recursive, notifyAdd, monitor); } } diff --git a/Ghidra/Processors/x86/certification.manifest b/Ghidra/Processors/x86/certification.manifest index 8d64f3e60b..1b6dd3dfb3 100644 --- a/Ghidra/Processors/x86/certification.manifest +++ b/Ghidra/Processors/x86/certification.manifest @@ -92,5 +92,6 @@ data/patterns/x86-64gcc_patterns.xml||GHIDRA||||END| data/patterns/x86-64win_patterns.xml||GHIDRA||||END| data/patterns/x86delphi_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_prepatterns.xml||GHIDRA||||END| diff --git a/Ghidra/Processors/x86/data/patterns/prepatternconstraints.xml b/Ghidra/Processors/x86/data/patterns/prepatternconstraints.xml index ea92c2ab1c..51d98bbe1e 100644 --- a/Ghidra/Processors/x86/data/patterns/prepatternconstraints.xml +++ b/Ghidra/Processors/x86/data/patterns/prepatternconstraints.xml @@ -7,6 +7,15 @@ x86win_prepatterns.xml + + x86gcc_prepatterns.xml + - + + + + x86gcc_prepatterns.xml + + + diff --git a/Ghidra/Processors/x86/data/patterns/x86gcc_prepatterns.xml b/Ghidra/Processors/x86/data/patterns/x86gcc_prepatterns.xml new file mode 100644 index 0000000000..190acf223f --- /dev/null +++ b/Ghidra/Processors/x86/data/patterns/x86gcc_prepatterns.xml @@ -0,0 +1,20 @@ + + + + + 0xff25........ + 0x68......00 + 0xe9......ff + + + + + + + 0xf3 0x0f 0x1e 0x1a + 0xf2 0xff 0x25 .. .. .. .. + + + + + diff --git a/Ghidra/application.properties b/Ghidra/application.properties index c37f49ffc1..1401682b29 100644 --- a/Ghidra/application.properties +++ b/Ghidra/application.properties @@ -1,5 +1,5 @@ application.name=Ghidra -application.version=12.0 +application.version=12.1 application.release.name=DEV application.layout.version=3 application.gradle.min=8.5