mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-21 19:42:14 +00:00
Merge branch 'master' of gitlab.evoforge.org:ghidra/ghidra into debugger
This commit is contained in:
commit
f7984f7b76
76
.gitattributes
vendored
76
.gitattributes
vendored
@ -3,53 +3,79 @@
|
||||
|
||||
# Explicitly declare text files you want to always be normalized and converted
|
||||
# to native line endings on checkout.
|
||||
*.java text
|
||||
*.gradle text
|
||||
*.manifest text
|
||||
*.bash text
|
||||
*.c text
|
||||
*.cc text
|
||||
*.command text
|
||||
*.cpp text
|
||||
*.cspec text
|
||||
*.css text
|
||||
*.gradle text
|
||||
*.groovy text
|
||||
*.h text
|
||||
*.hh text
|
||||
*.htm text
|
||||
*.html text
|
||||
*.java text
|
||||
*.jj text
|
||||
*.js text
|
||||
*.json text
|
||||
*.jsp text
|
||||
*.jspf text
|
||||
*.jspx text
|
||||
*.l text
|
||||
*.lang text
|
||||
*.ldefs text
|
||||
*.manifest text
|
||||
*.opinion text
|
||||
*.props text
|
||||
*.properties text
|
||||
*.tld text
|
||||
*.txt text
|
||||
*.proto text
|
||||
*.pspec text
|
||||
*.py text
|
||||
*.rxg text
|
||||
*.sh text
|
||||
*.sla text
|
||||
*.tag text
|
||||
*.tld text
|
||||
*.tool text
|
||||
*.trans text
|
||||
*.txt text
|
||||
*.xml text
|
||||
*.c text
|
||||
*.h text
|
||||
*.cpp text
|
||||
*.hh text
|
||||
*.cc text
|
||||
*.y text
|
||||
|
||||
# Declare files that will always have CRLF line endings on checkout.
|
||||
*.bat text eol=crlf
|
||||
*.sln text eol=crlf
|
||||
*.vcproj text eol=crlf
|
||||
*.vcxproj text eol=crlf
|
||||
*.bat text eol=crlf
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.a binary
|
||||
*.apk binary
|
||||
*.bmp binary
|
||||
*.class binary
|
||||
*.dll binary
|
||||
*.dmg binary
|
||||
*.ear binary
|
||||
*.gif binary
|
||||
*.ico binary
|
||||
*.jar binary
|
||||
*.jpeg binary
|
||||
*.so binary
|
||||
*.war binary
|
||||
*.pdf binary
|
||||
*.exe binary
|
||||
*.lib binary
|
||||
*.sa binary
|
||||
*.gdt binary
|
||||
*.gif binary
|
||||
*.gz binary
|
||||
*.gzf binary
|
||||
*.tgz binary
|
||||
*.ico binary
|
||||
*.ipsw binary
|
||||
*.jar binary
|
||||
*.jpeg binary
|
||||
*.jpg binary
|
||||
*.lib binary
|
||||
*.o binary
|
||||
*.obj binary
|
||||
*.pdf binary
|
||||
*.png binary
|
||||
*.sa binary
|
||||
*.so binary
|
||||
*.tar binary
|
||||
*.sh binary
|
||||
|
||||
*.tgz binary
|
||||
*.war binary
|
||||
*.zip binary
|
||||
|
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -29,7 +29,8 @@ If applicable, please attach any files that caused problems or log files generat
|
||||
**Environment (please complete the following information):**
|
||||
- OS: [e.g. macOS 10.14.2]
|
||||
- Java Version: [e.g. 11.0]
|
||||
- Ghidra Version: [e.g. 9.0]
|
||||
- Ghidra Version: [e.g. 9.1.2]
|
||||
- Ghidra Origin: [e.g. official ghidra-sre.org distro, third party distro, locally built]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
@ -1,3 +1,6 @@
|
||||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
apply from: file("../gpl.gradle").getCanonicalPath()
|
||||
|
||||
if (findProject(':Generic') != null) {
|
||||
|
@ -2,6 +2,4 @@
|
||||
##MODULE IP: GPL 3
|
||||
##MODULE IP: Public Domain
|
||||
Module.manifest||Public Domain||||END|
|
||||
build.gradle||Public Domain||||END|
|
||||
data/cabextract-1.6.tar.gz||GPL 3||||END|
|
||||
settings.gradle||Public Domain||||END|
|
||||
|
@ -0,0 +1,3 @@
|
||||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
@ -1,3 +1,6 @@
|
||||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
apply from: file("../gpl.gradle").getCanonicalPath()
|
||||
|
||||
if (findProject(':Generic') != null) {
|
||||
|
@ -3,7 +3,6 @@
|
||||
##MODULE IP: LGPL 2.1
|
||||
##MODULE IP: Public Domain
|
||||
Module.manifest||Public Domain||||END|
|
||||
build.gradle||Public Domain||||END|
|
||||
data/lib/catacombae_csframework.jar||LGPL 2.1||||END|
|
||||
data/lib/catacombae_hfsx.jar||GPL 3||||END|
|
||||
data/lib/catacombae_hfsx_dmglib.jar||GPL 3||||END|
|
||||
@ -16,4 +15,3 @@ data/os/win64/llio_amd64.dll||GPL 3||||END|
|
||||
data/os/win64/llio_i386.dll||GPL 3||||END|
|
||||
data/os/win64/llio_ia64.dll||GPL 3||||END|
|
||||
data/server_memory.cfg||Public Domain||||END|
|
||||
settings.gradle||Public Domain||||END|
|
||||
|
@ -0,0 +1,3 @@
|
||||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
@ -1,3 +1,6 @@
|
||||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
apply from: file("../gpl.gradle").getCanonicalPath()
|
||||
|
||||
if (findProject(':Generic') != null) {
|
||||
|
@ -5,7 +5,5 @@
|
||||
##MODULE IP: LGPL 3.0
|
||||
##MODULE IP: Public Domain
|
||||
Module.manifest||Public Domain||||END|
|
||||
build.gradle||Public Domain||||END|
|
||||
settings.gradle||Public Domain||||END|
|
||||
src/demangler_gnu_v2_24/README.txt||Public Domain||||END|
|
||||
src/demangler_gnu_v2_33_1/README.txt||Public Domain||||END|
|
||||
|
@ -0,0 +1,3 @@
|
||||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
@ -1,3 +1,6 @@
|
||||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
// If extension module does not reside within the Ghidra GPL directory, the Ghidra installation directory
|
||||
// must be specified either by setting the GHIDRA_INSTALL_DIR environment variable or Gradle
|
||||
// project property:
|
||||
|
@ -1,3 +1,6 @@
|
||||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
/*******************************************************************************************
|
||||
* build.gradle file that applies this script must define two properties
|
||||
* 1) binutilsLocation - the folder where the original binutils.zip lives
|
||||
|
@ -4,10 +4,7 @@
|
||||
.project||GHIDRA||||END|
|
||||
Module.manifest||Public Domain||||END|
|
||||
README.txt||Public Domain||||END|
|
||||
build.gradle||Public Domain||||END|
|
||||
buildGdis.gradle||Public Domain||||END|
|
||||
data/arm_test1.s||Public Domain||||END|
|
||||
data/big.elf||Public Domain||||END|
|
||||
data/little.elf||Public Domain||||END|
|
||||
extension.properties||Public Domain||||END|
|
||||
settings.gradle||Public Domain||||END|
|
||||
|
@ -0,0 +1,3 @@
|
||||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
@ -1,5 +1,2 @@
|
||||
##VERSION: 2.0
|
||||
##MODULE IP: Public Domain
|
||||
gpl.gradle||Public Domain||||END|
|
||||
nativeBuildProperties.gradle||Public Domain||||END|
|
||||
vsconfig.gradle||GHIDRA||||END|
|
||||
|
@ -1,4 +1,6 @@
|
||||
|
||||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
// BIN_REPO only useable in full Ghidra source configuration
|
||||
project.ext.BIN_REPO = file("../../../ghidra.bin").absolutePath
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
/****************************************************************************
|
||||
* nativeBuildProperties.gradle
|
||||
*
|
||||
|
@ -1,3 +1,18 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/****************************************************************************
|
||||
* Establish Visual Studio configuration environment for Windows native builds
|
||||
*
|
||||
|
@ -1,3 +1,18 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/javaProject.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/jacocoProject.gradle"
|
||||
|
@ -1,6 +1,5 @@
|
||||
##VERSION: 2.0
|
||||
Module.manifest||GHIDRA||||END|
|
||||
build.gradle||GHIDRA||||END|
|
||||
data/PDB_SYMBOL_SERVER_URLS.pdburl||GHIDRA||||END|
|
||||
src/global/docs/ChangeHistory.html||GHIDRA||||END|
|
||||
src/global/docs/UserAgreement.html||GHIDRA||||END|
|
||||
|
@ -6,14 +6,66 @@
|
||||
</HEAD>
|
||||
|
||||
<BODY>
|
||||
<H1 align="center">Ghidra 9.2.3 Change History (March 2021)</H1>
|
||||
<blockquote><p><u>Improvements</u></p>
|
||||
<ul>
|
||||
<li><I>Analysis</I>. Added check for vftable entries in <code>.NEP</code> section and relaxed the requirement that the code must have a return. (GP-649)</li>
|
||||
<li><I>Analysis</I>. Corrected flaw in RTTI analyzer determination of size of vftables. (GP-688)</li>
|
||||
<li><I>Basic Infrastructure</I>. Updated TLS protocol preference to use the most preferred/recent version available to both sides of an SSL connection (e.g., TLSv1.3) instead of forcing use of TLSv1.2. (GP-622)</li>
|
||||
<li><I>Build</I>. Corrected build issues which had prevented users from building Ghidra on an Apple M1 (OS X, AARCH64 architecture). (GP-600, Issue #2653)</li>
|
||||
<li><I>Demangler</I>. Increased Gnu Demangler parsing performance by changing some regular expressions. (GP-705)</li>
|
||||
<li><I>Eclipse Integration</I>. Updated SleighEditor to support new endian tag on <B>define token</B> definitions. (GP-721)</li>
|
||||
<li><I>GUI</I>. Updated the Choose Data Type dialog to apply data types in the same manner as dragging types from the Data Types window. This provides users more control when choosing how to overwrite existing types. (GP-521)</li>
|
||||
<li><I>Importer:ELF</I>. Added support for ELF relocation <code>R_X86_64_IRELATIVE</code>. (GP-651, Issue #1189)</li>
|
||||
<li><I>Importer:ELF</I>. Sped up loading of ELF files with large symbol tables. (GP-697)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<blockquote><p><u>Bugs</u></p>
|
||||
<ul>
|
||||
<li><I>Analysis</I>. The RTTI analyzer now runs prior to Reference analysis so that references into vftables are not turned into code or data before the vftables are created. (GP-517)</li>
|
||||
<li><I>API</I>. <code>Funtion.getCalledFunctions(TaskMonitor)</code> and <code>Function.getCallingFunctions(TaskMonitor)</code> now support passing <code>null</code> for the task monitor parameter, which previously would have thrown an exception. (GP-589, Issue #2643)</li>
|
||||
<li><I>Data Types</I>. Corrected segmented 32-bit pointer datatype address generation for 16:16 x86 far pointers. (GP-534, Issue #2548)</li>
|
||||
<li><I>Decompiler</I>. Fixed Decompiler issue where, when a function name extends beyond the line limit, an end-of-line comment could wrap around to additional lines without including additional <code>//</code> comment indicators. (GP-473)</li>
|
||||
<li><I>Decompiler</I>. Corrected an exception that could occur when attempting to edit function signature from the Decompiler. (GP-597, Issue #2601)</li>
|
||||
<li><I>Demangler</I>. Changed return type applied to constructors by Demangler from <code>void</code> to <code>Undefined</code>, allowing the Decompiler to determine the type. (GP-790)</li>
|
||||
<li><I>DWARF</I>. Improved handling of empty DWARF compile units. (GP-743)</li>
|
||||
<li><I>DWARF</I>. Improved handling of DWARF function signatures when parameter info contains unsupported location opcodes or failed to resolve datatypes. (GP-794)</li>
|
||||
<li><I>Eclipse Integration</I>. When installing the SleighEditor into Eclipse, the plugin will now show up under the Ghidra category. Previously the <B>Group Items by Category</B> option had to be turned off before the SleighEditor would appear as a visible entry. (GP-564)</li>
|
||||
<li><I>Eclipse Integration</I>. Fixed an issue with Eclipse PyDev breakpoints not catching. (GP-668, Issue #2713)</li>
|
||||
<li><I>Eclipse Integration</I>. Fixed an Eclipse GhidraDev exception that occurred when creating a new Ghidra scripting project if a <B>~/ghidra_scripts</B> directory did not exist. (GP-669)</li>
|
||||
<li><I>Emulator</I>. Replaced Java floating point emulation to fix multiple rounding issues. (GP-357, Issue #2414)</li>
|
||||
<li><I>Graphing</I>. Fixed issue with graph filters not updating satellite view when changing edge filters. (GP-557)</li>
|
||||
<li><I>Graphing</I>. Fixed Function Graph keybindings that did not work when docked in the main Code Browser window. (GP-586, Issue #2641)</li>
|
||||
<li><I>GUI</I>. Fixed NullPointerException due to using <B>Go To</B> action when there was no open program in the Listing. (GP-66)</li>
|
||||
<li><I>GUI</I>. Fixed bug in Reference Code Viewer options that caused an exception. (GP-620, Issue #2672)</li>
|
||||
<li><I>Importer</I>. Fixed exception caused when importing previously exported XML data where the bookmark override option was turned off. (GP-667)</li>
|
||||
<li><I>Importer:ELF</I>. Fixed a NullPointerException caused by importing an ELF with an uninitialized <code>.got</code> section. (GP-360, Issue #2416)</li>
|
||||
<li><I>Importer:ELF</I>. Added Support for ELF <code>R_ARM_MOVW_ABS_NC</code> and <code>R_ARM_MOVT_ABS ELF</code> Relocations for ARM. (GP-555, Issue #2510)</li>
|
||||
<li><I>Importer:ELF</I>. Corrected ELF processing of <code>.init_array</code> and <code>.fini_array</code> which was incorrectly overadjusting entries for an image base change. (GP-699)</li>
|
||||
<li><I>Importer:Mach-O</I>. Corrected Mach-O fat-binary library import issue and resolved error related to unnamed Mach-O segment. (GP-652, Issue #2702)</li>
|
||||
<li><I>Importer:Mach-O</I>. Fixed an issue with DYLD Load Command data structures being created in the wrong locations. (GP-689, Issue #2624)</li>
|
||||
<li><I>Importer:Mach-O</I>. Fixed an exception that occurred when importing Mach-O files that define zero <code>LC_BUILD_VERSION</code> tool entries. (GP-702, Issue #2192)</li>
|
||||
<li><I>PDB</I>. Fixed createPdbXmlFiles.bat to permit spaces in the path name of Ghidra installation folder and the batch argument name. (GP-575, Issue #2167)</li>
|
||||
<li><I>PDB</I>. Fixed PDB Universal analyzer to set the run-once flag when finished. (GP-724)</li>
|
||||
<li><I>PDB</I>. Changed return type applied to constructors by PDB Universal from <code>void</code> to <code>Undefined</code>, allowing the Decompiler to determine the type. (GP-791)</li>
|
||||
<li><I>Processors</I>. Added missing <code>RFE</code> instruction in MIPS up to version R3000. (GP-33, Issue #1766)</li>
|
||||
<li><I>Processors</I>. ARM instruction <code>VMUL</code> now decodes correctly. (GP-627, Issue #2677)</li>
|
||||
<li><I>Processors</I>. Added missing <code>CFINV</code> instruction to AARCH64 processor specification and added definitions for locals in neon instructions. (GP-655, Issue #2710)</li>
|
||||
<li><I>Scripting</I>. Fixed analyzeHeadless <code><B>-scriptPath</B></code> option that didn't work for Python and other non-Java scripts located in non-default directories. (GP-528, Issue #2561)</li>
|
||||
<li><I>Scripting</I>. Fixed concurrency issue with management of scripting bundle paths. (GP-576)</li>
|
||||
<li><I>Scripting</I>. Corrected handling for Ghidra Script files which are symlinks that were broken in Ghidra 9.2. (GP-650, Issue #2698)</li>
|
||||
<li><I>Scripting</I>. Fixed the analyzeHeadless <code><B>-scriptPath</B></code> option to correctly parse <code><B>$GHIDRA_HOME</B></code> and <code><B>$USER_HOME</B></code>. (GP-781)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<H1 align="center">Ghidra 9.2.2 Change History (December 2020)</H1>
|
||||
<blockquote><p><u>Bugs</u></p>
|
||||
<ul>
|
||||
<li><I>Graphing</I>. Fixed issue with Graph filters not working and satellite view sometimes not matching graph. (GP-526)</li>
|
||||
<li><I>Importer:MachO</I>. MachO DYLD cache incorrect offset use has been fixed. (GP-550, Issue ##2560)</li>
|
||||
<li><I>Listing</I>. Fixed issue where Edit Label action (L key) did not work on primary function symbols. (GP-537)</li>
|
||||
<li><I>Importer:Mach-O</I>. Mach-O DYLD cache incorrect offset use has been fixed. (GP-550, Issue #2560)</li>
|
||||
<li><I>Listing</I>. Fixed issue where <B>Edit Label</B> action (L key) did not work on primary function symbols. (GP-537)</li>
|
||||
<li><I>Multi-User</I>. Corrected Ghidra Server build issue for version 9.2.1 which had an improperly generated <B>classpath.frag</B> file. Issue caused server to fail startup with a ClassNotFoundException. (GP-542)</li>
|
||||
<li><I>Processors</I>. The V850 JMP instruction has been corrected not to use the PC in the address calculation (GP-548, Issue #2570)</li>
|
||||
<li><I>Processors</I>. The V850 <code>JMP</code> instruction has been corrected not to use the PC in the address calculation. (GP-548, Issue #2570)</li>
|
||||
<li><I>Processors</I>. Removed erroneous VST4 variant, most likely from a copy/paste error. This fixes the ARM Thumb BL instruction disassembly with a negative offset. (GP-549, Issue #2559)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
@ -24,6 +76,7 @@
|
||||
<li><I>Analysis</I>. Updated RTTI analyzer to find <code>type_info</code> vftable when it cannot be found with its mangled name. This will enable many more Windows programs to have their RTTI structures created that were unable to be parsed in previous Ghidra versions. (GP-141)</li>
|
||||
<li><I>API</I>. Relaxed memory block naming restrictions and restored ability to have spaces in memory block names. However, if a memory block is flagged as an overlay, the associated overlay space name may be modified to ensure validity and uniqueness. The DuplicateNameException has been removed from all memory block API methods since this was entirely an overlay space concern. Memory block GUI has also been changed eliminate the duplicate block name restriction. (GP-420, Issue #2465)</li>
|
||||
<li><I>Build</I>. Eliminated the need for installation of <B>bison</B> and <B>flex</B> when performing source-based <B>gradle</B> build of Ghidra or the Decompiler module. The generated files are now included with source files and maintained in source control. A separate <code><B>gradle Decompiler:generateParsers</B></code> task, which still requires <B>bison</B> and <B>flex</B>, must be used, explicitly, when changes are made to lex/yacc source files. (GP-467)</li>
|
||||
<li><I>Graphing</I>. Fixed issue with exporting graphs to DOT format due to invalid vertex IDs. (GP-280)</li>
|
||||
<li><I>Graphing</I>. Improved graphing where it did not navigate when clicking on external function nodes. Now it will navigate to the <B>fake</B> function location in the program, which is the location of the pointer to the external function. (GP-493)</li>
|
||||
<li><I>Listing:Symbols</I>. Removed restriction for naming labels that resemble default label names. (GT-3185, Issue #1057)</li>
|
||||
<li><I>PDB</I>. Crafted PDB type ID records <code>0x1608</code> and <code>0x1609</code> with presumed <B>class</B> and <B>struct</B> types and follow-on application of these types. Also fixed up some fall-back data type logic and improved some warning messages to reflect the <B>cause</B> of the conditions. (GP-474, Issue #2523)</li>
|
||||
@ -37,7 +90,7 @@
|
||||
<li><I>Decompiler</I>. Fixed issue with the Auto Create/Fill Structure command that caused it to silently miss some pointer accesses. (GP-344)</li>
|
||||
<li><I>Decompiler</I>. Jump table recovery now takes into account encoded bits, like ARM/THUMB mode transition, that may be present in address tables. (GP-387, Issue #2420)</li>
|
||||
<li><I>Decompiler</I>. Fixed a bug in the Decompiler <B>renaming</B> action when applied to function references. (GP-477, Issue #2415)</li>
|
||||
<li><I>Decompiler</I>. Corrected 8-byte return value storage specification in compiler-spec affecting longlong and double return values. Endianess ordering of r0/r1 was incorrect. (GP-512, Issue #2547)</li>
|
||||
<li><I>Decompiler</I>. Corrected 8-byte return value storage specification in compiler-spec affecting <code>longlong</code> and <code>double</code> return values. Endianess ordering of <code>r0</code>/<code>r1</code> was incorrect. (GP-512, Issue #2547)</li>
|
||||
<li><I>Graphing</I>. Fixed the Function Graph's <B>drag-to-select-nodes</B> feature. (GP-430)</li>
|
||||
<li><I>Graphing</I>. Fixed issue where the graph in the satellite view is sometimes truncated. (GP-469)</li>
|
||||
<li><I>Graphing</I>. Fixed a stack trace issue caused by reusing a graph display window to show a graph that is larger than is allowed. (GP-492)</li>
|
||||
@ -126,7 +179,7 @@
|
||||
<li><I>Importer:ELF</I>. Added support for processing Android packed ELF Relocation Tables. (GT-3320, Issue #1192)</li>
|
||||
<li><I>Importer:ELF</I>. Added ELF import opinion for ARM BE8. (GT-3642, Issue #1187)</li>
|
||||
<li><I>Importer:ELF</I>. Added support for ELF RELR relocations, such as those produced for Android. (GP-348)</li>
|
||||
<li><I>Importer:MachO</I>. DYLD Loader can now load x86_64 DYLD from macOS. (GT-3611, Issue #1566)</li>
|
||||
<li><I>Importer:Mach-O</I>. DYLD Loader can now load x86_64 DYLD from macOS. (GT-3611, Issue #1566)</li>
|
||||
<li><I>Importer:PE</I>. Improved parsing of Microsoft ordinal map files produced with <code>DUMPBIN /EXPORTS</code> (see <B>Ghidra/Features/Base/data/symbols/README.txt</B>). (GT-3235)</li>
|
||||
<li><I>Jython</I>. Upgraded Jython to version 2.7.2. (GP-109)</li>
|
||||
<li><I>Listing</I>. In the PCode field of the Listing, accesses of varnodes in the <code>unique</code> space are now always shown with the size of the access. Fixed bug which would cause the PCode emulator to reject valid pcode in rare instances. (GP-196)</li>
|
||||
@ -282,8 +335,8 @@
|
||||
<li><I>Disassembly</I>. Corrected potential infinite loop with disassembler caused by branch to self with invalid delay slot instruction. (GT-3511, Issue #1486)</li>
|
||||
<li><I>GUI</I>. Corrected processor manual display for Microsoft Windows users, which was not displaying processor manual and was, instead, rendering a blank page in web browser. (GT-3444)</li>
|
||||
<li><I>GUI:Bitfield Editor</I>. Added field comment support to composite bitfield editor. (GT-3410)</li>
|
||||
<li><I>Importer:MachO</I>. A MachO loader regression, in Ghidra 9.1.1, when laying down symbols at the correct location, has been fixed. (GT-3487, Issue #1446)</li>
|
||||
<li><I>Multi-User:Ghidra Server</I>. Corrected Ghidra Server remote interface errors that occur when running with Java 11.0.6 (and later) release, which would throw RemoteException <code>"Method is not Remote"</code> errors. (GT-3521, Issue #1440)</li>
|
||||
<li><I>Importer:Mach-O</I>. A Mach-O loader regression, in Ghidra 9.1.1, when laying down symbols at the correct location, has been fixed. (GT-3487, Issue #1446)</li>
|
||||
<li><I>Multi-User:Ghidra Server</I>. Corrected Ghidra Server remote interface errors that occur when running with Java 11.0.6 (and later) release, which would throw RemoteException <code>Method is not Remote</code> errors. (GT-3521, Issue #1440)</li>
|
||||
<li><I>PDB</I>. Corrected PDB XML generation for zero-length classes and structures and resolved various datatype dependency issues encountered during PDB Analysis. Changed line numbers from hex to decimal. (GT-3462, Issue #1410)</li>
|
||||
<li><I>Processors</I>. Corrected mnemonic for ARM thumb <code>RSB.w</code> instruction. (GT-3420, Issue #1365)</li>
|
||||
<li><I>Processors</I>. Corrected issue in M68000 with some move instructions not creating correct array assignments. (GT-3429, Issue #1394)</li>
|
||||
@ -294,7 +347,7 @@
|
||||
<H1 align="center">Ghidra 9.1.1 Change History (December 2019)</H1>
|
||||
<blockquote><p><u>Improvements</u></p>
|
||||
<ul>
|
||||
<li><I>Importer:MachO</I>. Improved import/load time of DYLD shared cache files. (GT-3261)</li>
|
||||
<li><I>Importer:Mach-O</I>. Improved import/load time of DYLD shared cache files. (GT-3261)</li>
|
||||
<li><I>Program API</I>. Cached the addresses that correspond to executable memory to improve analysis performance. (GT-3260)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
@ -331,7 +384,7 @@
|
||||
<li><I>Eclipse Integration</I>. Added new GhidraSleighEditor Eclipse plugin in the installation directory under Extensions/Eclipse. (GT-113)</li>
|
||||
<li><I>GUI</I>. Added method for turning off table sorting by control-clicking the only sorted table column. (GT-2763, Issue #87)</li>
|
||||
<li><I>GUI</I>. Hovering on an address will now show where the byte at that address came from in the imported file. (GT-3016, Issue #154)</li>
|
||||
<li><I>Importer:MachO</I>. Added new importer/loader for DYLD-shared cache files. (GT-2343)</li>
|
||||
<li><I>Importer:Mach-O</I>. Added new importer/loader for DYLD-shared cache files. (GT-2343)</li>
|
||||
<li><I>Memory</I>. Added new API to preserve imported program's original bytes and how they map to memory blocks. (GT-2845)</li>
|
||||
<li><I>Processors</I>. Implemented Intel MCS-96 processor module. (GT-2350)</li>
|
||||
<li><I>Processors</I>. Added SH1/2/2a sleigh processor specification. (GT-3029, Issue #715)</li>
|
||||
@ -488,7 +541,7 @@
|
||||
<li><I>Listing</I>. Cursor in the listing now stays in the proper column after editing a field. (GT-3045, Issue #702)</li>
|
||||
<li><I>Listing</I>. Fixed a problem with register highlighting that could occur on certain register/sub-register combinations. (GT-3071, Issue #810)</li>
|
||||
<li><I>Multi-User</I>. Corrected terminate checkout from viewed checkout list which was always terminating first row range based upon number of selected rows and not the actual selected rows. (GT-2903)</li>
|
||||
<li><I>Multi-user</I>. Corrected ability for user to cancel checkin/checkout to Ghidra Server. (GT-3208)</li>
|
||||
<li><I>Multi-User</I>. Corrected ability for user to cancel checkin/checkout to Ghidra Server. (GT-3208)</li>
|
||||
<li><I>Multi-User:Ghidra Server</I>. Added proper Ghidra Server interface binding with new <code><B>-i</B></code> option. Corrected <code><B>-ip</B></code> option to strictly convey remote access hostname to clients. The updated server will only accept connections from Ghidra 9.1 and later clients due to the registry port now employing TLS. (GT-2685, Issue #101, #645)</li>
|
||||
<li><I>Multi-User:Ghidra Server</I>. Fixed argument-passing bug in svrAdmin script. (GT-3082, Issue #907)</li>
|
||||
<li><I>Multi-User:Merge</I>. Corrected merge problem affecting modified Function Definition datatypes which could result in a NullPointerException. (GT-2922)</li>
|
||||
@ -518,7 +571,7 @@
|
||||
<li><I>Program API</I>. Corrected parameter storage which failed to properly refresh after undo/redo. (GT-3130, Issue #960)</li>
|
||||
<li><I>Program API</I>. Corrected function parameter ordinal numbering when more than one auto-parameter is present. (GT-3214)</li>
|
||||
<li><I>Project Manager</I>. Fixed a problem with creating Ghidra projects in Windows root directories (e.g., Z:\). (GT-2585)</li>
|
||||
<li><I>Project Manager</I>. Fixed a path traversal vulnerability that could occur when restoring a malicious project archive. (GT-3001, Issue #789)</li>
|
||||
<li><I>Project Manager</I>. Fixed a path-traversal vulnerability that could occur when restoring a malicious project archive. (GT-3001, Issue #789)</li>
|
||||
<li><I>Scripting</I>. <code>GhidraScript.askDomainFile()</code> now correctly throws a CancelledException when the cancel button is clicked. (GT-2841)</li>
|
||||
<li><I>Scripting</I>. Removed deprecated scripting methods older than 5 releases. (GT-2949)</li>
|
||||
<li><I>Security</I>. Removed use of nonsecure XMLEncoder/XMLDecoder from Ghidra code base. (GT-3198, Issue #1090)</li>
|
||||
@ -531,128 +584,123 @@
|
||||
<H1 align="center">Ghidra 9.0.4 Change History (May 2019)</H1>
|
||||
<blockquote><p><u>Bugs</u></p>
|
||||
<ul>
|
||||
<li><I>Multi-User:Ghidra Server</I>. Corrected severe script error in svrAdmin.bat introduced with 9.0.3 build.</li>
|
||||
<li><I>GUI</I>. Restored the default 'p' key binding for creating pointers within the listing display.</li>
|
||||
<li><I>Multi-User:Ghidra Server</I>. Corrected severe script error in svrAdmin.bat introduced with 9.0.3 build. (GT-2874)</li>
|
||||
<li><I>GUI</I>. Restored the default 'p' key binding for creating pointers within the listing display. (GT-2854)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<H1 align="center">Ghidra 9.0.3 Change History (April 2019)</H1>
|
||||
<blockquote><p><u>New Features</u></p>
|
||||
<ul>
|
||||
<li><I>GUI</I>. Function tags are now viewable from Functions Window table using new column.</li>
|
||||
<li><I>GUI</I>. Function tags are now viewable from Functions Window table using new column. (GT-2114)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<blockquote><p><u>Improvements</u></p>
|
||||
<ul>
|
||||
<li><I>Decompiler</I>. Improved modeling of CFG on Windows 10. (Issue #340)</li>
|
||||
<li><I>Patcher</I>. Renamed patch directory to /Ghidra/patch and added README.txt that explains how the patch directory is used.</li>
|
||||
<li><I>Search</I>. Updated the Decompiler Data Type Finder to find references to inside of nested array access in a line of Decompiler C output. (Issue #416)</li>
|
||||
<li><I>Sleigh</I>. Improved error reporting for SLEIGH compiler. (Issue #364)</li>
|
||||
<li><I>Decompiler</I>. Improved modeling of CFG on Windows 10. (GT-2755, Issue #340)</li>
|
||||
<li><I>Patcher</I>. Renamed patch directory to <install dir>/Ghidra/patch and added README.txt that explains how the patch directory is used. (GT-2734)</li>
|
||||
<li><I>Search</I>. Updated the Decompiler Data Type Finder to find references inside of nested array access in a line of Decompiler C output. (GT-2756, Issue #416)</li>
|
||||
<li><I>Sleigh</I>. Improved error reporting for SLEIGH compiler. (GT-2820, Issue #364)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<blockquote><p><u>Bugs</u></p>
|
||||
<ul>
|
||||
<li><I>Analysis</I>. Code that checks for thunks no longer throws an exception if the PC is not set for the processor.</li>
|
||||
<li><I>Analysis</I>. Made a fix to enable Apply button when changing tool options. (Issue #40)</li>
|
||||
<li><I>Data Types</I>. Fixed concurrent modification exception when replacing one datatype for another that results in some other datatype being renamed. </li>
|
||||
<li><I>Decompiler</I>. Fixed dynamic variables and equates in 16-bit x86 programs. (Issue #336)</li>
|
||||
<li><I>Decompiler:Java</I>. Fixed DEX decompilation regression issue. (Issue #350)</li>
|
||||
<li><I>Eclipse Integration</I>. Fixed exception in Eclipse GhidraDev plugin that occurred when performing certain actions on a Ghidra project that was imported from a previously exported Archive File. (Issues #283, #383)</li>
|
||||
<li><I>Analysis</I>. Code that checks for thunks no longer throws an exception if the PC is not set for the processor. (GT-2730)</li>
|
||||
<li><I>Analysis</I>. Made a fix to enable Apply button when changing tool options. (GT-2801, Issue #40)</li>
|
||||
<li><I>Data Types</I>. Fixed concurrent modification exception when replacing one datatype for another that results in some other datatype being renamed. (GT-2736)</li>
|
||||
<li><I>Decompiler</I>. Fixed dynamic variables and equates in 16-bit x86 programs. (GT-2745, Issue #336)</li>
|
||||
<li><I>Decompiler:Java</I>. Fixed DEX decompilation regression issue. (GT-2743, Issue #350)</li>
|
||||
<li><I>Eclipse Integration</I>. Fixed exception in Eclipse GhidraDev plugin that occurred when performing certain actions on a Ghidra project that was imported from a previously exported Archive File. (GT-2721, Issues #283, #383)</li>
|
||||
<li><I>GUI</I>. Improved documentation on how to deal with HiDPI monitor issues in Linux. In the <I><ghidra_installation></I>/support/launch.properties file, change VMARGS=-Dsun.java2d.xrender from false to true.</li>
|
||||
<li><I>Importer</I>. Fixed an exception that occurred when batch importing APK files. (Issue #426)</li>
|
||||
<li><I>Multi-User:Ghidra Server</I>. Restored ability to execute svrAdmin script in development mode. </li>
|
||||
<li><I>Processors</I>. The 6502 Zero page indexed addressing has been corrected to only access the Zero page. (Issue #201)</li>
|
||||
<li><I>Processors</I>. The 68000 BCD arithmetic instructions now have pcode semantics that allow disassembly to continue. (Issue #227)</li>
|
||||
<li><I>Search</I>. Fixed NullPointerException in Decompiler Data Type Reference Finder. (Issue #407)</li>
|
||||
<li><I>Importer</I>. Fixed an exception that occurred when batch importing APK files. (GT-2767, Issue #426)</li>
|
||||
<li><I>Multi-User:Ghidra Server</I>. Restored ability to execute svrAdmin script in development mode. (GT-2740) </li>
|
||||
<li><I>Processors</I>. The 6502 Zero page indexed addressing has been corrected to only access the Zero page. (GT-2759, Issue #201)</li>
|
||||
<li><I>Processors</I>. The M68000 BCD arithmetic instructions now have pcode semantics that allow disassembly to continue. (GT-2807, Issue #227)</li>
|
||||
<li><I>Search</I>. Fixed NullPointerException in Decompiler Data Type Reference Finder. (GT-2754, Issue #407)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<H1 align="center">Ghidra 9.0.2 Change History (April 2019)</H1>
|
||||
<blockquote><p><u>Bugs</u></p>
|
||||
<ul>
|
||||
<li><I>Analysis</I>. Constant reference analysis boundary controls for speculative references has been fixed. Speculative references are references created from computed constants passed as parameters, stored to a location, or from indexed offsets from a register. (Issue #228)</li>
|
||||
<li><I>Decompiler</I>. Fixed rendering bug in the Decompiler when the "Find" dialog is closed. (Issue #282) </li>
|
||||
<li><I>Decompiler</I>. Fixed decompiler handling of Function Definition data types. (Issue #247) </li>
|
||||
<li><I>Decompiler</I>. Fixed "Free Varnode" exception in RuleConditionalMove. (Issue #294) </li>
|
||||
<li><I>Diff</I>. Fixed exceptions that can occur in the Diff View for programs with overlays. </li>
|
||||
<li><I>Documentation</I>. Corrected the spelling of "listener" throughout the source code. (Issue #235) </li>
|
||||
<li><I>Exporter</I>. Exporting a selection as Intel Hex will now allow a selection of any length. Previously this was restricted to multiples of 16 bytes. (Issue #260) </li>
|
||||
<li><I>GUI</I>. Fixed exception that occurs after disabling MyProgramChangesDisplayPlugin. </li>
|
||||
<li><I>GUI</I>. Updated the "Open Program" dialog to disallow file drop operations. (Issue #252)
|
||||
<li><I>Multi-User:Ghidra Server</I>. Corrected bug introduced into ghidraSvr.bat which could prevent Ghidra Server startup (Issue #279) </li>
|
||||
<li><I>Processors</I>. The ARM Thumb CMP.W and LSL instructions have been changed to correctly decode. There are still issues to work out with Unpredictable execution when Rd is the PC. (Issue #280) </li>
|
||||
<li><I>Scripting</I>. MultiInstructionMemReference script has been corrected to consider input and output registers when placing a reference on an instruction.</li>
|
||||
<li><I>Analysis</I>. Constant reference analysis boundary controls for speculative references has been fixed. Speculative references are references created from computed constants passed as parameters, stored to a location, or from indexed offsets from a register. (GT-2723, Issue #228)</li>
|
||||
<li><I>Decompiler</I>. Fixed Decompiler handling of Function Definition data types. (GT-2704, Issue #247)</li>
|
||||
<li><I>Decompiler</I>. Fixed rendering bug in the Decompiler when the "Find" dialog is closed. (GT-2716, Issue #282)</li>
|
||||
<li><I>Decompiler</I>. Fixed "Free Varnode" exception in RuleConditionalMove. (GT-2726, Issue #294)</li>
|
||||
<li><I>Diff</I>. Fixed exceptions that can occur in the Diff View for programs with overlays. (GT-2706)</li>
|
||||
<li><I>Documentation</I>. Corrected the spelling of "listener" throughout the source code. (GT-2702, Issue #235)</li>
|
||||
<li><I>Exporter</I>. Exporting a selection as Intel Hex will now allow a selection of any length. Previously this was restricted to multiples of 16 bytes. (GT-2703, Issue #260)</li>
|
||||
<li><I>GUI</I>. Fixed exception that occurs after disabling MyProgramChangesDisplayPlugin. (GT-2712)</li>
|
||||
<li><I>GUI</I>. Updated the "Open Program" dialog to disallow file drop operations. (GT-2705, Issue #252)</li>
|
||||
<li><I>Multi-User:Ghidra Server</I>. Corrected bug introduced into ghidraSvr.bat which could prevent Ghidra Server startup. (GT-2717, Issue #279)</li>
|
||||
<li><I>Processors</I>. The ARM Thumb CMP.W and LSL instructions have been changed to correctly decode. There are still issues to work out with Unpredictable execution when Rd is the PC. (GT-2722, Issue #280)</li>
|
||||
<li><I>Scripting</I>. MultiInstructionMemReference script has been corrected to consider input and output registers when placing a reference on an instruction. (GT-2723)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<blockquote><p><u>Security</u></p>
|
||||
<ul>
|
||||
<li><I>Basic Infrastructure</I>. Added a property to support/launch.properties to prevent log4j from using jansi.dll on Windows. (Issue #286) </li>
|
||||
<li><I>Basic Infrastructure</I>. Added a property to support/launch.properties to prevent log4j from using jansi.dll on Windows. (GT-2725, Issue #286)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<H1 align="center">Ghidra 9.0.1 Change History (March 2019)</H1>
|
||||
<blockquote><p><u>New Features</u></p>
|
||||
<ul>
|
||||
<li><I>Scripting</I>. Created a script to show all equates within the current selection. (Issue #111)</li>
|
||||
<li><I>Scripting</I>. Created ShowEquatesInSelectionScript to show all equates within the current selection. (GT-2651, Issue #111)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<blockquote><p><u>Improvements</u></p>
|
||||
<ul>
|
||||
<li><I>Basic Infrastructure</I>. Updated commons-compress library to version 1.18. (Issue #171)</li>
|
||||
<li><I>Eclipse Integration</I>. Ghidra now connects to the Eclipse GhidraDev plugin on 127.0.0.1 rather than localhost.</li>
|
||||
<li><I>GUI</I>. Turned on font anti-aliasing by default for Linux. (Issue #212)</li>
|
||||
<li><I>GUI</I>. Fixed Options Dialog slow scrolling speed. (Issue #27)</li>
|
||||
<li><I>Importer:ELF</I>. Corrected bug in ELF loader which can improperly process the GOT, PLT and relocations
|
||||
when multiple symbol tables exist within the ELF binary. (Issue #52)</li>
|
||||
<li><I>Multi-User:Ghidra Server</I>. Corrected the Ghidra Server service wrapper (YAJSW) configuration for
|
||||
Mac OS X to prevent a startup timeout condition which could occur.</li>
|
||||
<li><I>Processors</I>. Added ARM/Thumb SRS instruction decodes for undefined modes. (Issue #216)</li>
|
||||
<li><I>Basic Infrastructure</I>. Updated commons-compress library to version 1.18. (GT-2657, Issue #171)</li>
|
||||
<li><I>Eclipse Integration</I>. Ghidra now connects to the Eclipse GhidraDev plugin on 127.0.0.1 rather than localhost. (GT-2691)</li>
|
||||
<li><I>GUI</I>. Turned on font anti-aliasing by default for Linux. (GT-2674, Issue #212)</li>
|
||||
<li><I>GUI</I>. Fixed Options Dialog slow scrolling speed. (GT-2679, Issue #27)</li>
|
||||
<li><I>Importer:ELF</I>. Corrected bug in ELF loader which can improperly process the GOT, PLT and relocations when multiple symbol tables exist within the ELF binary. (GT-2646, Issue #52)</li>
|
||||
<li><I>Multi-User:Ghidra Server</I>. Corrected the Ghidra Server service wrapper (YAJSW) configuration for Mac OS X to prevent a startup timeout condition which could occur. (GT-2637)</li>
|
||||
<li><I>Processors</I>. Added ARM/Thumb SRS instruction decodes for undefined modes. (GT-2676, Issue #216)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<blockquote><p><u>Bugs</u></p>
|
||||
<ul>
|
||||
<li><I>API</I>. Fixed equals method on Varnode class. (Issue #97)</li>
|
||||
<li><I>API</I>. Fixed a bug in MaskImpl.comlementMask(). (Issue #187)</li>
|
||||
<li><I>Basic Infrastructure</I>. Fixed special character handling in idaxml.py. (Issue #75)</li>
|
||||
<li><I>Basic Infrastructure</I>. Ghidra now forces the locale to en_US by default. Only the en_US is currently supported.
|
||||
This fixes certain unexpected exceptions. (Issue #209)</li>
|
||||
<li><I>Diff</I>. Fixed exceptions occasionally encountered when starting a Diff session. (Issue #211)</li>
|
||||
<li><I>Documentation</I>. Fixed javadoc search box redirecting to broken links. (Issue #129)</li>
|
||||
<li><I>Function Graph</I>. Fixed Function Graph exception when generating tooltip. (Issue #65)</li>
|
||||
<li><I>GUI</I>. Updated window placement to keep windows on screen. (Issue #41)</li>
|
||||
<li><I>GUI</I>. Add/Edit References dialog now restricts users to creating refs in valid memory address spaces.</li>
|
||||
<li><I>GUI</I>. Fixed exception when exiting Ghidra while a table is being edited. (Issue #51)</li>
|
||||
<li><I>GUI</I>. Fixed some touchpad scrolling issues. (Issue #2)</li>
|
||||
<li><I>GUI</I>. Fixed stack trace in the Data Type Manager's tooltip generation. (Issue #133)</li>
|
||||
<li><I>GUI</I>. User key binding settings for the Recently Used and Define Pointer actions no longer lost after re-launching tool. (Issue #152)</li>
|
||||
<li><I>GUI</I>. Toolbar buttons now respond to fast clicking.</li>
|
||||
<li><I>Importer:MachO</I>. The MachoLoader can now find import libraries found in Universal Binary files. (Issue #136)</li>
|
||||
<li><I>Importer:PE</I>. The PeLoader now correctly parses the GuardCFFunctionTable when entries are more than 4 bytes each. (Issue #220)</li>
|
||||
<li><I>Multi-User:Ghidra Server</I>. Removed support for native OS authentication from Ghidra Server (removed modes -a2 and -a3)
|
||||
due to incompatibility with newer OS releases including Windows 10 and Windows Server 2016. Re-introduction of this will be
|
||||
considered for a future release.</li>
|
||||
<li><I>PDB</I>. Corrected NPE error when processing PDB files. (Issues #138, #188)</li>
|
||||
<li><I>Processors</I>. Added missing PowerPC VLE conditional branch instructions: e_bdnz and e_bdz. (Issue #103)</li>
|
||||
<li><I>Processors</I>. Fixed instruction semantics for several instructions and added Control Flow Enforcement, NOP variants, CMP variants, UD1, and
|
||||
prefixed call instructions to X86 processor specification. (Issues #22, #53, #158, #157)</li>
|
||||
<li><I>Processors</I>. The 68000 MOVE instruction now correctly sets the CF and VF flags. (Issue #163)</li>
|
||||
<li><I>Processors</I>. Added four missing MOVEM instruction variants to the 68000 processor. (Issue #219)</li>
|
||||
<li><I>Processors</I>. An incorrect usage of X instead of Y in indexed mode for the 6502 has been corrected.(Issue #201)</li>
|
||||
<li><I>Processors</I>. Added support for ARM Thumb half BL instruction on processor variants prior to v6. (Issue #39)</li>
|
||||
<li><I>Scripting</I>. Fixed a bug in ImportSymbolsScript.py that prevented it from running. (Issue #170)</li>
|
||||
<li><I>API</I>. Fixed equals method on Varnode class. (GT-2648, Issue #97)</li>
|
||||
<li><I>API</I>. Fixed a bug in MaskImpl.complementMask(). (GT-2694, Issue #187)</li>
|
||||
<li><I>Basic Infrastructure</I>. Fixed special character handling in idaxml.py. (GT-2669, Issue #75)</li>
|
||||
<li><I>Basic Infrastructure</I>. Ghidra now forces the locale to en_US by default. Only the en_US is currently supported. This fixes certain unexpected exceptions. (GT-2680, Issue #209)</li>
|
||||
<li><I>Diff</I>. Fixed exception occasionally encountered when starting a Diff session. (GT-2672, Issue #211)</li>
|
||||
<li><I>Documentation</I>. Fixed javadoc search box redirecting to broken links. (GT-2655, Issue #129)</li>
|
||||
<li><I>Function Graph</I>. Fixed Function Graph exception when generating tooltip. (GT-2650, Issue #65)</li>
|
||||
<li><I>GUI</I>. Updated window placement to keep windows on screen. (GT-1516, Issue #41)</li>
|
||||
<li><I>GUI</I>. Add/Edit References dialog now restricts users to creating refs in valid memory address spaces. (GT-2638)</li>
|
||||
<li><I>GUI</I>. Fixed exception when exiting Ghidra while a table is being edited. (GT-2642, Issue #51)</li>
|
||||
<li><I>GUI</I>. Fixed some touchpad scrolling issues. (GT-2647, Issue #2)</li>
|
||||
<li><I>GUI</I>. Fixed stack trace in the Data Type Manager's tooltip generation. (GT-2656, Issue #133)</li>
|
||||
<li><I>GUI</I>. User key binding settings for the Recently Used and Define Pointer actions no longer lost after re-launching tool. (GT-2659, Issue #152)</li>
|
||||
<li><I>GUI</I>. Toolbar buttons now respond to fast clicking. (GT-2689)</li>
|
||||
<li><I>Importer:Mach-O</I>. The Mach-O loader can now find import libraries found in Universal Binary files. (GT-2663, Issue #136)</li>
|
||||
<li><I>Importer:PE</I>. The PeLoader now correctly parses the GuardCFFunctionTable when table entries are more than 4 bytes each. (GT-2671, Issue #220)</li>
|
||||
<li><I>Multi-User:Ghidra Server</I>. Removed support for native OS authentication from Ghidra Server (removed modes -a2 and -a3) due to incompatibility with newer OS releases including Windows 10 and Windows Server 2016. Re-introduction of this will be considered for a future release. (GT-2653)</li>
|
||||
<li><I>PDB</I>. Corrected NullPointerException when processing PDB files. (GT-2673, Issues #138, #188)</li>
|
||||
<li><I>Processors</I>. Added missing PowerPC VLE conditional branch instructions: e_bdnz and e_bdz. (GT-2652, Issue #103)</li>
|
||||
<li><I>Processors</I>. Fixed instruction semantics for several instructions and added Control Flow Enforcement, NOP variants, CMP variants, UD1, and prefixed call instructions to X86 processor specification. (GT-2660, Issues #22, #53, #158, #157)</li>
|
||||
<li><I>Processors</I>. The M68000 MOVE instruction now correctly sets the CF and VF flags. (GT-2661, Issue #163)</li>
|
||||
<li><I>Processors</I>. Added four missing MOVEM instruction variants to the M68000 processor. (GT-2675, Issue #219)</li>
|
||||
<li><I>Processors</I>. An incorrect usage of X instead of Y in indexed mode for the 6502 has been corrected. (GT-2677, Issue #201)</li>
|
||||
<li><I>Processors</I>. PPC VLE now disassembles base PPC instructions that are valid in VLE mode. (GT-2681, Issue #127)</li>
|
||||
<li><I>Processors</I>. Added support for ARM Thumb half BL instruction on processor variants prior to v6. (GT-2684, Issue #39)</li>
|
||||
<li><I>Scripting</I>. Fixed a bug in ImportSymbolsScript.py that prevented it from running. (GT-2668, Issue #170)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
<blockquote><p><u>Security</u></p>
|
||||
<ul>
|
||||
<li><I>Basic Infrastructure</I>. Running Ghidra in debug mode no longer opens remotely accessible ports by default. (Issue #6)</li>
|
||||
<li><I>GUI</I>. The Defined Strings plugin no longer renders HTML in its table. (Issue #45)</li>
|
||||
<li><I>Project Manager</I>. Fixed an XXE vulnerability affecting projects and many other saved components. (Issue #71)</li>
|
||||
<li><I>Basic Infrastructure</I>. Running Ghidra in debug mode no longer opens remotely accessible ports by default. (GT-2641, Issue #6)</li>
|
||||
<li><I>GUI</I>. The Defined Strings plugin no longer renders HTML in its table. (GT-2686, Issue #45)</li>
|
||||
<li><I>Project Manager</I>. Fixed an XXE vulnerability affecting projects and many other saved components. (GT-2643, Issue #71)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
|
||||
|
@ -1,3 +1,18 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
apply from: "$rootProject.projectDir/gradle/distributableGhidraExtension.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/javaProject.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle"
|
||||
|
@ -2,7 +2,6 @@
|
||||
##MODULE IP: FAMFAMFAM Icons - CC 2.5
|
||||
##MODULE IP: Oxygen Icons - LGPL 3.0
|
||||
Module.manifest||GHIDRA||reviewed||END|
|
||||
build.gradle||GHIDRA||||END|
|
||||
data/ExtensionPoint.manifest||GHIDRA||||END|
|
||||
extension.properties||GHIDRA||||END|
|
||||
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
|
||||
|
@ -1,3 +1,18 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
apply from: "$rootProject.projectDir/gradle/distributableGhidraExtension.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/javaProject.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle"
|
||||
|
@ -1,14 +1,12 @@
|
||||
##VERSION: 2.0
|
||||
.project||GHIDRA||||END|
|
||||
Module.manifest||GHIDRA||||END|
|
||||
build.gradle||GHIDRA||||END|
|
||||
data/ExtensionPoint.manifest||GHIDRA||||END|
|
||||
data/LanguageMap.txt||GHIDRA||||END|
|
||||
extension.properties||GHIDRA||||END|
|
||||
pcodetest/.gitignore||GHIDRA||||END|
|
||||
pcodetest/README.txt||GHIDRA||||END|
|
||||
pcodetest/build||GHIDRA||||END|
|
||||
pcodetest/build.py||GHIDRA||||END|
|
||||
pcodetest/c_src/BIOPS.test||GHIDRA||||END|
|
||||
pcodetest/c_src/BIOPS2.test||GHIDRA||||END|
|
||||
pcodetest/c_src/BIOPS4.test||GHIDRA||||END|
|
||||
@ -28,7 +26,3 @@ pcodetest/c_src/PointerManipulation.test||GHIDRA||||END|
|
||||
pcodetest/c_src/StructUnionManipulation.test||GHIDRA||||END|
|
||||
pcodetest/c_src/misc.test||GHIDRA||||END|
|
||||
pcodetest/c_src/msp430x.ld||GHIDRA||||END|
|
||||
pcodetest/defaults.py||GHIDRA||||END|
|
||||
pcodetest/pcode_defs.py||GHIDRA||||END|
|
||||
pcodetest/pcodetest.py||GHIDRA||||END|
|
||||
pcodetest/tpp.py||GHIDRA||||END|
|
||||
|
@ -1,3 +1,18 @@
|
||||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# 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.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
|
@ -1,4 +1,18 @@
|
||||
|
||||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# 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.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
# Default values can be modified here, or (in
|
||||
# some cases) on the build command line (see ./build --help)
|
||||
|
||||
|
@ -1,4 +1,18 @@
|
||||
|
||||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# 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.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
# The available pcode tests are recorded here as instances of the 'name'
|
||||
# python class.
|
||||
|
||||
|
@ -1,3 +1,18 @@
|
||||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# 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.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
import os
|
||||
import glob
|
||||
import re
|
||||
|
@ -1,4 +1,19 @@
|
||||
#!/usr/bin/python
|
||||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# 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.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
|
||||
import re
|
||||
import os
|
||||
|
@ -1,3 +1,18 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/* This extension is different from the others. It produces a zip containing
|
||||
* directories of source bundles and jar bundles.
|
||||
* - Each source directory is added as a sourceset so that the eclipse plugin
|
||||
|
@ -1,6 +1,5 @@
|
||||
##VERSION: 2.0
|
||||
Module.manifest||GHIDRA||||END|
|
||||
build.gradle||GHIDRA||||END|
|
||||
extension.properties||GHIDRA||||END|
|
||||
scripts_jar1/META-INF/MANIFEST.MF||GHIDRA||||END|
|
||||
scripts_jar2/META-INF/MANIFEST.MF||GHIDRA||||END|
|
||||
|
@ -1,3 +1,18 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
apply from: "$rootProject.projectDir/gradle/distributableGhidraExtension.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/javaProject.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle"
|
||||
|
@ -2,7 +2,6 @@
|
||||
##MODULE IP: FAMFAMFAM Icons - CC 2.5
|
||||
##MODULE IP: Oxygen Icons - LGPL 3.0
|
||||
Module.manifest||GHIDRA||reviewed||END|
|
||||
build.gradle||GHIDRA||||END|
|
||||
data/README.txt||GHIDRA||||END|
|
||||
extension.properties||GHIDRA||||END|
|
||||
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
|
||||
|
@ -1,3 +1,18 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/javaProject.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/helpProject.gradle"
|
||||
|
@ -13,7 +13,6 @@
|
||||
.launch/Ghidra Code Coverage.launch||GHIDRA||||END|
|
||||
.launch/Ghidra.launch||GHIDRA||||END|
|
||||
Module.manifest||GHIDRA||||END|
|
||||
build.gradle||GHIDRA||||END|
|
||||
data/ElfFunctionsThatDoNotReturn||GHIDRA||||END|
|
||||
data/ExtensionPoint.manifest||GHIDRA||||END|
|
||||
data/MachOFunctionsThatDoNotReturn||GHIDRA||||END|
|
||||
@ -78,8 +77,6 @@ data/typeinfo/win32/windows_vs12_64.gdt||GHIDRA||||END|
|
||||
data/x64_linux_syscall_numbers||GHIDRA||||END|
|
||||
data/x86_linux_syscall_numbers||GHIDRA||||END|
|
||||
ghidra_scripts/AskScript.properties||GHIDRA||||END|
|
||||
ghidra_scripts/RecursiveStringFinder.py||GHIDRA||||END|
|
||||
ghidra_scripts/mark_in_out.py||GHIDRA||reviewed||END|
|
||||
ghidra_scripts/world.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
|
||||
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
|
||||
src/main/help/help/shared/arrow.gif||GHIDRA||||END|
|
||||
|
@ -1,7 +1,15 @@
|
||||
abort
|
||||
CxxThrowException
|
||||
CxxThrowException@8
|
||||
CxxFrameHandler3
|
||||
crtExitProcess
|
||||
ExitProcess
|
||||
ExitThread
|
||||
exit
|
||||
FreeLibraryAndExitThread
|
||||
invalid_parameter_noinfo_noreturn
|
||||
invoke_watson
|
||||
longjmp
|
||||
quick_exit
|
||||
RpcRaiseException
|
||||
terminate
|
||||
|
@ -17,10 +17,9 @@
|
||||
//@category Examples
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import generic.jar.ApplicationModule;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.util.GhidraJarBuilder;
|
||||
@ -32,8 +31,7 @@ public class BuildGhidraJarScript extends GhidraScript {
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
GhidraJarBuilder builder =
|
||||
new GhidraJarBuilder(toFiles(Application.getApplicationRootDirectories()));
|
||||
GhidraJarBuilder builder = new GhidraJarBuilder(Application.getApplicationLayout());
|
||||
|
||||
builder.setMainClass("ghidra.JarRun"); // default is ghidra.JarRun, only here if you want
|
||||
// to change it to something else.
|
||||
@ -69,12 +67,4 @@ public class BuildGhidraJarScript extends GhidraScript {
|
||||
// uncomment the following line to create a src zip for debugging.
|
||||
// builder.buildSrcZip(new File(installDir, "GhidraSrc.zip"), monitor);
|
||||
}
|
||||
|
||||
private List<File> toFiles(Collection<ResourceFile> resourceFiles) {
|
||||
List<File> fileList = new ArrayList<>();
|
||||
for (ResourceFile resourceFile : resourceFiles) {
|
||||
fileList.add(resourceFile.getFile(true));
|
||||
}
|
||||
return fileList;
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ public class DWARF_ExtractorScript extends GhidraScript {
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
if (!DWARFProgram.isDWARF(currentProgram, monitor)) {
|
||||
if (!DWARFProgram.isDWARF(currentProgram)) {
|
||||
popup("Unable to find DWARF information, aborting");
|
||||
return;
|
||||
}
|
||||
|
@ -1,3 +1,18 @@
|
||||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# 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.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
#Given a function, find all strings used within all called funtions.
|
||||
# @category: Strings
|
||||
|
||||
|
@ -1,3 +1,18 @@
|
||||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# 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.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
# Sets up IOPORT IN/OUT references for the Program
|
||||
#@category Instructions
|
||||
# Before running this script, you should have created an OVERLAY memory
|
||||
|
@ -285,6 +285,20 @@
|
||||
</UL>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A name="elf"></A>ELF</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>Writes an ELF program that was imported with the ELF loader back to its original file
|
||||
layout. Any file-backed bytes that were modified by the user in the program database will
|
||||
be reflected in the new file.</P>
|
||||
|
||||
<P><IMG alt="" border="0" src="../../shared/note.png"> <I>Writing back a modified Memory
|
||||
Map is not supported. </I></P>
|
||||
|
||||
<P><IMG alt="" border="0" src="../../shared/note.png"> <I>Relocation bytes are always
|
||||
restored to their original values, even if the user modifies them.</I></P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A name="gzf"></A>Ghidra Zip File (.gzf)</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
@ -320,7 +334,7 @@
|
||||
|
||||
<UL>
|
||||
<LI><B>Address Space</B> - Specifies which address space to export as Intel Hex format
|
||||
only supports one address space. This option will be intialized to the "default"
|
||||
only supports one address space. This option will be initialized to the "default"
|
||||
address space.</LI>
|
||||
<LI><B>Record Size</B> - Specifies the size (in bytes) of each record in the
|
||||
output file. The default 16.</LI>
|
||||
@ -331,6 +345,20 @@
|
||||
</UL>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A name="pe"></A>PE</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>Writes a PE program that was imported with the PE loader back to its original file
|
||||
layout. Any file-backed bytes that were modified by the user in the program database will
|
||||
be reflected in the new file.</P>
|
||||
|
||||
<P><IMG alt="" border="0" src="../../shared/note.png"> <I>Writing back a modified Memory
|
||||
Map is not supported. </I></P>
|
||||
|
||||
<P><IMG alt="" border="0" src="../../shared/note.png"> <I>Relocation bytes are always
|
||||
restored to their original values, even if the user modifies them.</I></P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A name="xml"/><A name="Options_XML"/>XML</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
@ -57,19 +57,38 @@ public abstract class AbstractDemanglerAnalyzer extends AbstractAnalyzer {
|
||||
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
|
||||
throws CancelledException {
|
||||
|
||||
try {
|
||||
monitor.setIndeterminate(true);
|
||||
return doAdded(program, set, monitor, log);
|
||||
}
|
||||
finally {
|
||||
monitor.setIndeterminate(false);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean doAdded(Program program, AddressSetView set, TaskMonitor monitor,
|
||||
MessageLog log)
|
||||
throws CancelledException {
|
||||
|
||||
DemanglerOptions options = getOptions();
|
||||
if (!validateOptions(options, log)) {
|
||||
log.appendMsg(getName(), "Invalid demangler options--cannot demangle");
|
||||
return false;
|
||||
}
|
||||
|
||||
monitor.initialize(100);
|
||||
int count = 0;
|
||||
|
||||
String defaultMessage = monitor.getMessage();
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
SymbolIterator it = symbolTable.getPrimarySymbolIterator(set, true);
|
||||
while (it.hasNext()) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
if (++count % 100 == 0) {
|
||||
monitor.setMessage(defaultMessage + " - " + count + " symbols");
|
||||
}
|
||||
|
||||
Symbol symbol = it.next();
|
||||
if (skipSymbol(symbol)) {
|
||||
continue;
|
||||
@ -81,12 +100,6 @@ public abstract class AbstractDemanglerAnalyzer extends AbstractAnalyzer {
|
||||
if (demangled != null) {
|
||||
apply(program, address, demangled, options, log, monitor);
|
||||
}
|
||||
|
||||
Address min = set.getMinAddress();
|
||||
Address max = set.getMaxAddress();
|
||||
int distance = (int) (address.getOffset() - min.getOffset());
|
||||
int percent = (int) ((distance / max.getOffset()) * 100);
|
||||
monitor.setProgress(percent);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -1110,7 +1110,14 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl
|
||||
int answer = OptionDialog.showYesNoDialog(tool.getToolFrame(), "Analyze",
|
||||
"<html>" + HTMLUtilities.escapeHTML(program.getDomainFile().getName()) +
|
||||
" has not been analyzed. Would you like to analyze it now?");
|
||||
return answer == OptionDialog.OPTION_ONE; // Analyze
|
||||
//Set to false for now. ANALYZED is a tri-valued variable:
|
||||
// null means not asked.
|
||||
// false means asked but could still turn true when analysis happens.
|
||||
// true means analysis has started.
|
||||
//Setting false here only works due to this code only being reachable
|
||||
// because of the behavior of GhidraProgramUtilities.shouldAskToAnalyze(program) above.
|
||||
GhidraProgramUtilities.setAnalyzedFlag(program, false);
|
||||
return answer == OptionDialog.OPTION_ONE; //Analyze
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -190,10 +190,12 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis
|
||||
if (!showOptionsDialog(program)) {
|
||||
return;
|
||||
}
|
||||
GhidraProgramUtilities.setAnalyzedFlag(program, true);
|
||||
|
||||
analysisMgr.initializeOptions(); // options may have changed
|
||||
|
||||
// At this point, any analysis that is done is consider to be true for analyzed.
|
||||
GhidraProgramUtilities.setAnalyzedFlag(program, true);
|
||||
|
||||
// start analysis to set the flag, but it probably won't do more. A bit goofy but better
|
||||
// than the way it was
|
||||
//TODO simplify all this after creating a taskManager per program instead of per tool.
|
||||
|
@ -34,6 +34,8 @@ import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class DWARFAnalyzer extends AbstractAnalyzer {
|
||||
private static final String DWARF_LOADED_OPTION_NAME = "DWARF Loaded";
|
||||
|
||||
private static final String OPTION_IMPORT_DATATYPES = "Import data types";
|
||||
private static final String OPTION_IMPORT_DATATYPES_DESC =
|
||||
"Import data types defined in the DWARF debug info.";
|
||||
@ -84,6 +86,7 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
|
||||
"Automatically extracts DWARF info from an ELF file.";
|
||||
|
||||
private DWARFImportOptions importOptions = new DWARFImportOptions();
|
||||
private long lastTxId = -1;
|
||||
|
||||
public DWARFAnalyzer() {
|
||||
super(DWARF_ANALYZER_NAME, DWARF_ANALYZER_DESCRIPTION, AnalyzerType.BYTE_ANALYZER);
|
||||
@ -105,22 +108,24 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
|
||||
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
|
||||
throws CancelledException {
|
||||
|
||||
if (!canAnalyze(program)) {
|
||||
// Check again because external DWARF data (ie. dSYM files) could have been moved
|
||||
// between the time canAnalyze() was called the first time and when this method
|
||||
// is called
|
||||
log.appendMsg("Unable to find DWARF information, skipping DWARF analysis");
|
||||
return false;
|
||||
long txId = program.getCurrentTransaction().getID();
|
||||
if (txId == lastTxId) {
|
||||
// Only run once per analysis session - as denoted by being in the same transaction
|
||||
return true;
|
||||
}
|
||||
lastTxId = txId;
|
||||
|
||||
if (DWARFProgram.alreadyDWARFImported(program)) {
|
||||
Msg.warn(this, "DWARF already imported, skipping. (Detected DWARF program module)");
|
||||
Options propList = program.getOptions(Program.PROGRAM_INFO);
|
||||
boolean alreadyLoaded = propList.getBoolean(DWARF_LOADED_OPTION_NAME, false) ||
|
||||
oldCheckIfDWARFImported(program);
|
||||
if (alreadyLoaded) {
|
||||
Msg.info(this, "DWARF already imported, skipping.");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWARFSectionProvider dsp = DWARFSectionProviderFactory.createSectionProviderFor(program);
|
||||
if (dsp == null) {
|
||||
// silently return, canAnalyze() was false positive
|
||||
log.appendMsg("Unable to find DWARF information, skipping DWARF analysis");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -139,6 +144,7 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
|
||||
DWARFImportSummary parseResults = dp.parse();
|
||||
parseResults.logSummaryResults();
|
||||
}
|
||||
propList.setBoolean(DWARF_LOADED_OPTION_NAME, true);
|
||||
return true;
|
||||
}
|
||||
catch (CancelledException ce) {
|
||||
@ -157,9 +163,16 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean oldCheckIfDWARFImported(Program prog) {
|
||||
// this was the old way of checking if the DWARF analyzer had already been run. Keep
|
||||
// it around for a little bit so existing programs that have already imported DWARF data
|
||||
// don't get re-run. Remove after a release or two.
|
||||
return DWARFFunctionImporter.hasDWARFProgModule(prog, DWARFProgram.DWARF_ROOT_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAnalyze(Program program) {
|
||||
return DWARFProgram.isDWARF(program, null);
|
||||
return DWARFProgram.isDWARF(program);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -24,6 +24,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.ComponentProvider;
|
||||
import docking.dnd.GenericDataFlavor;
|
||||
@ -303,12 +305,9 @@ public class CodeBrowserClipboardProvider extends ByteCopier
|
||||
private Transferable copyAddress() {
|
||||
|
||||
AddressSetView addressSet = getSelectedAddresses();
|
||||
StringBuilder buffy = new StringBuilder();
|
||||
AddressIterator it = addressSet.getAddresses(true);
|
||||
while (it.hasNext()) {
|
||||
buffy.append(it.next()).append('\n');
|
||||
}
|
||||
return createStringTransferable(buffy.toString());
|
||||
String joined = StringUtils.join((Iterator<Address>) it, "\n");
|
||||
return createStringTransferable(joined);
|
||||
}
|
||||
|
||||
protected Transferable copyCode(TaskMonitor monitor) {
|
||||
@ -377,8 +376,8 @@ public class CodeBrowserClipboardProvider extends ByteCopier
|
||||
private boolean pasteLabelsComments(Transferable pasteData, boolean pasteLabels,
|
||||
boolean pasteComments) {
|
||||
try {
|
||||
List<?> list = (List<?>) pasteData.getTransferData(
|
||||
CodeUnitInfoTransferable.localDataTypeFlavor);
|
||||
List<?> list =
|
||||
(List<?>) pasteData.getTransferData(CodeUnitInfoTransferable.localDataTypeFlavor);
|
||||
List<CodeUnitInfo> infos = CollectionUtils.asList(list, CodeUnitInfo.class);
|
||||
Command cmd = new CodeUnitInfoPasteCmd(currentLocation.getAddress(), infos, pasteLabels,
|
||||
pasteComments);
|
||||
@ -451,8 +450,8 @@ public class CodeBrowserClipboardProvider extends ByteCopier
|
||||
String oldName = symbol.getName();
|
||||
Namespace namespace = symbol.getParentNamespace();
|
||||
Address symbolAddress = symbol.getAddress();
|
||||
RenameLabelCmd cmd = new RenameLabelCmd(symbolAddress, oldName, labelName,
|
||||
namespace, SourceType.USER_DEFINED);
|
||||
RenameLabelCmd cmd = new RenameLabelCmd(symbolAddress, oldName, labelName, namespace,
|
||||
SourceType.USER_DEFINED);
|
||||
return tool.execute(cmd, currentProgram);
|
||||
}
|
||||
|
||||
|
@ -310,7 +310,9 @@ class EnumEditorPanel extends JPanel {
|
||||
EnumCellRenderer cellRenderer = new EnumCellRenderer();
|
||||
table.setRowHeight(table.getRowHeight() + 4);
|
||||
table.setDefaultEditor(String.class, new EnumStringCellEditor());
|
||||
table.getColumnModel().getColumn(EnumTableModel.VALUE_COL).setCellEditor(
|
||||
table.getColumnModel()
|
||||
.getColumn(EnumTableModel.VALUE_COL)
|
||||
.setCellEditor(
|
||||
new EnumLongCellEditor());
|
||||
table.setDefaultRenderer(String.class, cellRenderer);
|
||||
add(createInfoPanel(), BorderLayout.SOUTH);
|
||||
@ -335,13 +337,17 @@ class EnumEditorPanel extends JPanel {
|
||||
}
|
||||
|
||||
private void changed() {
|
||||
String name = nameField.getText();
|
||||
String name = nameField.getText().trim();
|
||||
if (name.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!name.equals(editedEnumDT.getName())) {
|
||||
try {
|
||||
editedEnumDT.setName(name);
|
||||
}
|
||||
catch (InvalidNameException e) {
|
||||
e.printStackTrace();
|
||||
setStatusMessage("'" + name + "' is not a valid name");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,16 +302,13 @@ public class DisassemblerPlugin extends Plugin {
|
||||
}
|
||||
|
||||
public void setDefaultContext(ListingActionContext context) {
|
||||
|
||||
Program contextProgram = context.getProgram();
|
||||
Register baseContextReg = contextProgram.getLanguage().getContextBaseRegister();
|
||||
if (baseContextReg != null && baseContextReg.hasChildren()) {
|
||||
return;
|
||||
}
|
||||
|
||||
tool.showDialog(new ProcessorStateDialog(contextProgram.getProgramContext()),
|
||||
context.getComponentProvider());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasContextRegisters(Program currentProgram) {
|
||||
Register baseContextReg = currentProgram.getLanguage().getContextBaseRegister();
|
||||
|
@ -20,7 +20,6 @@ import java.awt.Component;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
@ -53,8 +52,7 @@ import ghidra.util.filechooser.ExtensionFileFilter;
|
||||
import ghidra.util.filechooser.GhidraFileFilter;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
import ghidra.util.layout.VerticalLayout;
|
||||
import ghidra.util.task.TaskLauncher;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.util.task.*;
|
||||
|
||||
/**
|
||||
* Dialog for exporting a program from a Ghidra project to an external file in one of the
|
||||
@ -444,10 +442,65 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
|
||||
|
||||
private boolean doExport() {
|
||||
|
||||
AtomicBoolean success = new AtomicBoolean();
|
||||
TaskLauncher.launchModal("Exporting " + domainFile.getName(),
|
||||
monitor -> success.set(tryExport(monitor)));
|
||||
return success.get();
|
||||
ExportTask task = new ExportTask();
|
||||
TaskLauncher.launch(task);
|
||||
task.showResults();
|
||||
return task.getSuccess();
|
||||
}
|
||||
|
||||
private class ExportTask extends Task {
|
||||
|
||||
private boolean success;
|
||||
private boolean showResults;
|
||||
private Exporter exporter;
|
||||
private DomainObject exportedDomainObject;
|
||||
|
||||
public ExportTask() {
|
||||
super("Export " + domainFile.getName(), true, true, true, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
exporter = getSelectedExporter();
|
||||
|
||||
exporter.setExporterServiceProvider(tool);
|
||||
exportedDomainObject = getDomainObject(monitor);
|
||||
if (exportedDomainObject == null) {
|
||||
return;
|
||||
}
|
||||
ProgramSelection selection = getApplicableProgramSeletion();
|
||||
File outputFile = getSelectedOutputFile();
|
||||
|
||||
try {
|
||||
if (outputFile.exists() &&
|
||||
OptionDialog.showOptionDialog(getComponent(), "Overwrite Existing File?",
|
||||
"The file " + outputFile + " already exists.\nDo you want to overwrite it?",
|
||||
"Overwrite", OptionDialog.QUESTION_MESSAGE) != OptionDialog.OPTION_ONE) {
|
||||
return;
|
||||
}
|
||||
if (options != null) {
|
||||
exporter.setOptions(options);
|
||||
}
|
||||
success = exporter.export(outputFile, exportedDomainObject, selection, monitor);
|
||||
showResults = true;
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.error(this, "Exception exporting", e);
|
||||
SystemUtilities.runSwingLater(() -> setStatusText(
|
||||
"Exception exporting: " + e.getMessage() + ". If null, see log for details."));
|
||||
}
|
||||
}
|
||||
|
||||
void showResults() {
|
||||
if (showResults) {
|
||||
displaySummaryResults(exporter, exportedDomainObject);
|
||||
}
|
||||
}
|
||||
|
||||
boolean getSuccess() {
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean tryExport(TaskMonitor monitor) {
|
||||
|
@ -986,7 +986,7 @@ public class FunctionEditorModel {
|
||||
if (!paramInfo.isAutoParameter() && paramInfo.isNameModified()) {
|
||||
Parameter param = paramInfo.getOriginalParameter();
|
||||
if (param != null) {
|
||||
if (!param.getSymbol().checkIsValid()) {
|
||||
if (param.getSymbol().isDeleted()) {
|
||||
// concurrent removal of param - must do full update
|
||||
paramsOrReturnModified = true;
|
||||
break;
|
||||
|
@ -41,8 +41,10 @@ public class FunctionTagLoader {
|
||||
*
|
||||
* @param tagFile tag file
|
||||
* @return List list of function tags
|
||||
* @throws IOException if there is an exception reading the file
|
||||
* @throws SAXException if there is an exception parsing the file
|
||||
*/
|
||||
protected static Set<FunctionTag> loadTags(File tagFile) {
|
||||
protected static Set<FunctionTag> loadTags(File tagFile) throws SAXException, IOException {
|
||||
return loadTags(new ResourceFile(tagFile));
|
||||
}
|
||||
|
||||
@ -56,16 +58,17 @@ public class FunctionTagLoader {
|
||||
try {
|
||||
return loadTags(Application.getModuleDataFile(moduleDataFilePath));
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
catch (SAXException | IOException e) {
|
||||
Msg.error(FunctionTagLoader.class,
|
||||
"Error loading function tags file from " + moduleDataFilePath, e);
|
||||
}
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
protected static Set<FunctionTag> loadTags(final ResourceFile tagDataFile) {
|
||||
Set<FunctionTag> tags = new HashSet<>();
|
||||
protected static Set<FunctionTag> loadTags(final ResourceFile tagDataFile)
|
||||
throws SAXException, IOException {
|
||||
|
||||
Set<FunctionTag> tags = new HashSet<>();
|
||||
try {
|
||||
ErrorHandler errHandler = new ErrorHandler() {
|
||||
@Override
|
||||
@ -116,10 +119,6 @@ public class FunctionTagLoader {
|
||||
Msg.error(FunctionTagLoader.class, "Error parsing function tags from " + tagDataFile,
|
||||
e);
|
||||
}
|
||||
catch (SAXException | IOException e) {
|
||||
Msg.error(FunctionTagLoader.class, "Error loading function tags from " + tagDataFile,
|
||||
e);
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ public class AboutProgramPlugin extends Plugin implements FrontEndable {
|
||||
ProgramActionContext pac = (ProgramActionContext) context;
|
||||
Program program = pac.getProgram();
|
||||
if (program != null) {
|
||||
String menuName = "About " + program.getDomainFile().getName() + "...";
|
||||
String menuName = "About " + program.getDomainFile().getName();
|
||||
getMenuBarData().setMenuItemName(menuName);
|
||||
return true;
|
||||
}
|
||||
|
@ -139,6 +139,13 @@ public abstract class AbstractHoverProvider implements HoverProvider {
|
||||
return;
|
||||
}
|
||||
|
||||
Component component = event.getComponent();
|
||||
if (!component.isShowing()) {
|
||||
// This can happen since we are using a timer. When the timer fires, the source
|
||||
// component may have been hidden.
|
||||
return;
|
||||
}
|
||||
|
||||
ProgramLocation loc = getHoverLocation(fieldLocation, field, fieldBounds, event);
|
||||
for (HoverService hoverService : hoverServices) {
|
||||
JComponent comp = hoverService.getHoverComponent(program, loc, fieldLocation, field);
|
||||
|
@ -17,6 +17,7 @@ package ghidra.app.plugin.core.navigation;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.*;
|
||||
import docking.tool.ToolConstants;
|
||||
import ghidra.GhidraOptions;
|
||||
@ -29,7 +30,7 @@ import ghidra.app.util.HelpTopics;
|
||||
import ghidra.app.util.navigation.GoToAddressLabelDialog;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.bean.opteditor.OptionsVetoException;
|
||||
|
||||
@ -91,10 +92,20 @@ public class GoToAddressLabelPlugin extends Plugin implements OptionsChangeListe
|
||||
public void actionPerformed(NavigatableActionContext context) {
|
||||
goToDialog.show(context.getNavigatable(), context.getAddress(), tool);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnabledForContext(NavigatableActionContext context) {
|
||||
return context.getProgram() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
action.setHelpLocation(new HelpLocation(HelpTopics.NAVIGATION, action.getName()));
|
||||
action.setMenuBarData(new MenuData(
|
||||
new String[] { ToolConstants.MENU_NAVIGATION, "Go To..." }, null, "GoTo",
|
||||
action.setMenuBarData(
|
||||
new MenuData(new String[] { ToolConstants.MENU_NAVIGATION, "Go To..." }, null, "GoTo",
|
||||
MenuData.NO_MNEMONIC, "2")); // second item in the menu
|
||||
|
||||
action.setKeyBindingData(new KeyBindingData(KeyEvent.VK_G, 0));
|
||||
@ -144,7 +155,8 @@ public class GoToAddressLabelPlugin extends Plugin implements OptionsChangeListe
|
||||
* @param newValue new value of the option
|
||||
*/
|
||||
@Override
|
||||
public void optionsChanged(ToolOptions options, String opName, Object oldValue, Object newValue) {
|
||||
public void optionsChanged(ToolOptions options, String opName, Object oldValue,
|
||||
Object newValue) {
|
||||
if (opName.equals(GhidraOptions.OPTION_MAX_GO_TO_ENTRIES)) {
|
||||
maximumGotoEntries =
|
||||
options.getInt(GhidraOptions.OPTION_MAX_GO_TO_ENTRIES, DEFAULT_MAX_GOTO_ENTRIES);
|
||||
@ -182,16 +194,16 @@ public class GoToAddressLabelPlugin extends Plugin implements OptionsChangeListe
|
||||
ToolOptions opt = tool.getOptions(ToolConstants.TOOL_OPTIONS);
|
||||
// descriptions
|
||||
opt.registerOption(GhidraOptions.OPTION_NUMERIC_FORMATTING, DEFAULT_C_STYLE, null,
|
||||
"Interpret value entered in the Go To dialog as either hex, "
|
||||
+ "octal, or binary number.");
|
||||
"Interpret value entered in the Go To dialog as either hex, " +
|
||||
"octal, or binary number.");
|
||||
opt.registerOption(GhidraOptions.OPTION_MAX_GO_TO_ENTRIES, DEFAULT_MAX_GOTO_ENTRIES, null,
|
||||
"Max number of entries remembered in the go to list.");
|
||||
opt.registerOption(GO_TO_MEMORY, DEFAULT_MEMORY, null,
|
||||
"Remember the last successful go to input in the "
|
||||
+ "Go To dialog. If this option is enabled, then the "
|
||||
+ "Go To dialog will leave the last "
|
||||
+ "successful go to input in the combo box of the Go "
|
||||
+ "To dialog and will select the " + "value for easy paste replacement.");
|
||||
"Remember the last successful go to input in the " +
|
||||
"Go To dialog. If this option is enabled, then the " +
|
||||
"Go To dialog will leave the last " +
|
||||
"successful go to input in the combo box of the Go " +
|
||||
"To dialog and will select the " + "value for easy paste replacement.");
|
||||
|
||||
// options
|
||||
maximumGotoEntries =
|
||||
|
@ -267,8 +267,8 @@ class ProgramTreeActionManager implements ClipboardOwner {
|
||||
};
|
||||
goToViewAction.setEnabled(false);
|
||||
|
||||
goToViewAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Go To in View" }, null, "aview"));
|
||||
goToViewAction
|
||||
.setPopupMenuData(new MenuData(new String[] { "Go To in View" }, null, "aview"));
|
||||
|
||||
list.add(goToViewAction);
|
||||
|
||||
@ -282,8 +282,8 @@ class ProgramTreeActionManager implements ClipboardOwner {
|
||||
};
|
||||
removeViewAction.setEnabled(false);
|
||||
|
||||
removeViewAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Remove from View" }, null, "aview"));
|
||||
removeViewAction
|
||||
.setPopupMenuData(new MenuData(new String[] { "Remove from View" }, null, "aview"));
|
||||
|
||||
list.add(removeViewAction);
|
||||
|
||||
@ -297,8 +297,8 @@ class ProgramTreeActionManager implements ClipboardOwner {
|
||||
};
|
||||
replaceViewAction.setEnabled(false);
|
||||
|
||||
replaceViewAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Replace View" }, null, "aview"));
|
||||
replaceViewAction
|
||||
.setPopupMenuData(new MenuData(new String[] { "Replace View" }, null, "aview"));
|
||||
|
||||
list.add(replaceViewAction);
|
||||
|
||||
@ -446,8 +446,8 @@ class ProgramTreeActionManager implements ClipboardOwner {
|
||||
collapseAction.setEnabled(false);
|
||||
|
||||
// ACTIONS - auto generated
|
||||
collapseAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Collapse All" }, null, "expand"));
|
||||
collapseAction
|
||||
.setPopupMenuData(new MenuData(new String[] { "Collapse All" }, null, "expand"));
|
||||
|
||||
list.add(collapseAction);
|
||||
|
||||
@ -577,8 +577,7 @@ class ProgramTreeActionManager implements ClipboardOwner {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
ProgramNode node = list.get(i);
|
||||
for (ProgramNode node : list) {
|
||||
if (tree.getModel().getRoot() != node.getRoot()) {
|
||||
break;
|
||||
}
|
||||
@ -642,11 +641,12 @@ class ProgramTreeActionManager implements ClipboardOwner {
|
||||
|
||||
try {
|
||||
Clipboard systemClipboard = GClipboard.getSystemClipboard();
|
||||
Transferable t = systemClipboard.getContents(this);
|
||||
if (t == null) {
|
||||
if (!systemClipboard.isDataFlavorAvailable(TreeTransferable.localTreeNodeFlavor)) {
|
||||
return;
|
||||
}
|
||||
if (!t.isDataFlavorSupported(TreeTransferable.localTreeNodeFlavor)) {
|
||||
|
||||
Object data = systemClipboard.getData(TreeTransferable.localTreeNodeFlavor);
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -659,9 +659,8 @@ class ProgramTreeActionManager implements ClipboardOwner {
|
||||
}
|
||||
|
||||
private void doClearSystemClipboard(Clipboard systemClipboard) {
|
||||
// for some reason setting the contents to null for the
|
||||
// system clipboard causes a NullPointerException, so just
|
||||
// set it with an empty transferable.
|
||||
// for some reason setting the contents to null for the system clipboard causes a
|
||||
// NullPointerException, so just set it with an empty transferable.
|
||||
TreeTransferable dummyContents = new TreeTransferable(new ProgramNode[0]);
|
||||
systemClipboard.setContents(dummyContents, (clipboard, contents) -> {
|
||||
// a dummy implementation that will not prevent this plugin from being
|
||||
@ -979,8 +978,7 @@ class ProgramTreeActionManager implements ClipboardOwner {
|
||||
ArrayList<ProgramNode> list = tree.getSortedSelection();
|
||||
CompoundCmd compCmd = new CompoundCmd("Merge with Parent");
|
||||
String treeName = tree.getTreeName();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
ProgramNode node = list.get(i);
|
||||
for (ProgramNode node : list) {
|
||||
tree.removeSelectionPath(node.getTreePath());
|
||||
ProgramNode parentNode = (ProgramNode) node.getParent();
|
||||
if (node.isModule() && parentNode != null) {
|
||||
@ -1242,14 +1240,15 @@ class ProgramTreeActionManager implements ClipboardOwner {
|
||||
@SuppressWarnings("unchecked")
|
||||
// the cast is safe, since we checked the flavor
|
||||
private boolean isPasteOk(ProgramNode destNode) {
|
||||
Transferable t = null;
|
||||
boolean isCutOperation = false;
|
||||
|
||||
try {
|
||||
t = GClipboard.getSystemClipboard().getContents(this);
|
||||
if (t == null) {
|
||||
boolean isCutOperation = false;
|
||||
Clipboard systemClipboard = GClipboard.getSystemClipboard();
|
||||
if (!systemClipboard.isDataFlavorAvailable(TreeTransferable.localTreeNodeFlavor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// we will put items on the 'tempClipboard' when the cut action is executed
|
||||
Transferable temp = tempClipboard.getContents(this);
|
||||
isCutOperation = (temp != null);
|
||||
}
|
||||
@ -1258,26 +1257,16 @@ class ProgramTreeActionManager implements ClipboardOwner {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!t.isDataFlavorSupported(TreeTransferable.localTreeNodeFlavor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!t.isDataFlavorSupported(TreeTransferable.localTreeNodeFlavor)) {
|
||||
return false;
|
||||
}
|
||||
List<ProgramNode> list =
|
||||
(List<ProgramNode>) t.getTransferData(TreeTransferable.localTreeNodeFlavor);
|
||||
|
||||
(List<ProgramNode>) systemClipboard.getData(TreeTransferable.localTreeNodeFlavor);
|
||||
if (list == null) {
|
||||
// SCR 7990--something bad has happened to the copy buffer
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean pasteEnabled = false;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
ProgramNode pasteNode = list.get(i);
|
||||
|
||||
for (ProgramNode pasteNode : list) {
|
||||
boolean pasteAllowed = pasteMgr.isPasteAllowed(destNode, pasteNode, isCutOperation);
|
||||
if (isCutOperation && !pasteAllowed) {
|
||||
// for cut operation all nodes must be able to be pasted at destNode
|
||||
|
@ -38,7 +38,6 @@ import ghidra.app.plugin.core.compositeeditor.CompositeEditorModel;
|
||||
import ghidra.app.plugin.core.compositeeditor.DataTypeHelper;
|
||||
import ghidra.app.util.datatype.EmptyCompositeException;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.program.database.DatabaseObject;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
@ -1271,14 +1270,11 @@ class StackEditorModel extends CompositeEditorModel {
|
||||
for (int i = comps.length - 1; i >= 0; i--) {
|
||||
DataTypeComponent component = comps[i];
|
||||
DataType compDt = component.getDataType();
|
||||
if (compDt instanceof DatabaseObject) {
|
||||
DatabaseObject dbObj = (DatabaseObject) compDt;
|
||||
if (!dbObj.checkIsValid()) {
|
||||
if (compDt.isDeleted()) {
|
||||
clearComponent(component.getOrdinal());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
// The methods below were overridden to prevent data types with a length of
|
||||
|
@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,18 +15,16 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.stackeditor;
|
||||
|
||||
import ghidra.app.plugin.core.compositeeditor.CompositeEditorPanel;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.database.DatabaseObject;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.exception.UsrException;
|
||||
|
||||
import java.awt.event.*;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.app.plugin.core.compositeeditor.CompositeEditorPanel;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.util.exception.UsrException;
|
||||
|
||||
/**
|
||||
* Panel for editing a function stack.
|
||||
@ -274,15 +271,13 @@ public class StackEditorPanel extends CompositeEditorPanel {
|
||||
if (originalDt instanceof StackFrameDataType) {
|
||||
StackFrameDataType sfdt = (StackFrameDataType) originalDt;
|
||||
Function function = sfdt.getFunction();
|
||||
if (function instanceof DatabaseObject) {
|
||||
if (!((DatabaseObject) function).checkIsValid()) {
|
||||
if (function.isDeleted()) {
|
||||
// Cancel Editor.
|
||||
provider.dispose();
|
||||
PluginTool tool = ((StackEditorProvider) provider).getPlugin().getTool();
|
||||
tool.setStatusInfo("Stack Editor was closed for " + provider.getName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
StackFrame stack = function.getStackFrame();
|
||||
StackFrameDataType newSfdt = new StackFrameDataType(stack, dtm);
|
||||
if (!newSfdt.equals(((StackEditorModel) model).getViewComposite())) {
|
||||
@ -329,13 +324,10 @@ public class StackEditorPanel extends CompositeEditorPanel {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param localSizeField
|
||||
*/
|
||||
private void removeFocusListeners(JTextField textField) {
|
||||
FocusListener[] fl = textField.getFocusListeners();
|
||||
for (int i = 0; i < fl.length; i++) {
|
||||
textField.removeFocusListener(fl[i]);
|
||||
for (FocusListener element : fl) {
|
||||
textField.removeFocusListener(element);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -568,7 +568,7 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
||||
SymbolTreeRootNode rootNode = (SymbolTreeRootNode) tree.getModelRoot();
|
||||
|
||||
// the symbol may have been deleted while we are processing bulk changes
|
||||
if (symbol.checkIsValid()) {
|
||||
if (!symbol.isDeleted()) {
|
||||
GTreeNode newNode = rootNode.symbolAdded(symbol);
|
||||
tree.refilterLater(newNode);
|
||||
}
|
||||
@ -588,7 +588,7 @@ public class SymbolTreeProvider extends ComponentProviderAdapter {
|
||||
root.symbolRemoved(symbol, monitor);
|
||||
|
||||
// the symbol may have been deleted while we are processing bulk changes
|
||||
if (symbol.checkIsValid()) {
|
||||
if (!symbol.isDeleted()) {
|
||||
root.symbolAdded(symbol);
|
||||
}
|
||||
tree.refilterLater();
|
||||
|
@ -134,6 +134,10 @@ class ReferenceProvider extends ComponentProviderAdapter {
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
boolean isBusy() {
|
||||
return referenceKeyModel.isBusy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentHidden() {
|
||||
referenceKeyModel.setProgram(null);
|
||||
|
@ -151,8 +151,8 @@ class SymbolPanel extends JPanel {
|
||||
filterDialog.adjustFilter(symProvider, tableModel);
|
||||
}
|
||||
|
||||
NewSymbolFilter getFilter() {
|
||||
return filterDialog.getFilter();
|
||||
SymbolFilter getFilter() {
|
||||
return tableModel.getFilter();
|
||||
}
|
||||
|
||||
void readConfigState(SaveState saveState) {
|
||||
|
@ -149,10 +149,14 @@ class SymbolProvider extends ComponentProviderAdapter {
|
||||
return symbolPanel.getTable();
|
||||
}
|
||||
|
||||
NewSymbolFilter getFilter() {
|
||||
SymbolFilter getFilter() {
|
||||
return symbolPanel.getFilter();
|
||||
}
|
||||
|
||||
boolean isShowingDynamicSymbols() {
|
||||
return getFilter().acceptsDefaultLabelSymbols();
|
||||
}
|
||||
|
||||
private String generateSubTitle() {
|
||||
SymbolFilter filter = symbolKeyModel.getFilter();
|
||||
int rowCount = symbolKeyModel.getRowCount();
|
||||
@ -174,11 +178,15 @@ class SymbolProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
boolean isBusy() {
|
||||
return symbolKeyModel.isBusy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentHidden() {
|
||||
symbolKeyModel.reload(null);
|
||||
if (plugin != null) {
|
||||
plugin.closeReferenceProvider();
|
||||
plugin.symbolProviderClosed();
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,5 +207,4 @@ class SymbolProvider extends ComponentProviderAdapter {
|
||||
void writeConfigState(SaveState saveState) {
|
||||
symbolPanel.writeConfigState(saveState);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ public class SymbolReferenceModel extends AddressBasedTableModel<Reference> {
|
||||
|
||||
@Override
|
||||
protected TableColumnDescriptor<Reference> createTableColumnDescriptor() {
|
||||
TableColumnDescriptor<Reference> descriptor = new TableColumnDescriptor<Reference>();
|
||||
TableColumnDescriptor<Reference> descriptor = new TableColumnDescriptor<>();
|
||||
|
||||
descriptor.addVisibleColumn(
|
||||
DiscoverableTableUtils.adaptColumForModel(this, new ReferenceFromAddressTableColumn()),
|
||||
@ -139,7 +139,6 @@ public class SymbolReferenceModel extends AddressBasedTableModel<Reference> {
|
||||
|
||||
void symbolChanged(Symbol symbol) {
|
||||
if (currentSymbol != null && currentSymbol.equals(symbol)) {
|
||||
setCurrentSymbol(symbol);
|
||||
return;
|
||||
}
|
||||
checkRefs(symbol);
|
||||
|
@ -264,12 +264,13 @@ class SymbolTableModel extends AddressBasedTableModel<Symbol> {
|
||||
updateObject(Symbol);
|
||||
}
|
||||
else {
|
||||
// the symbol may be in the table, as it could have passed the filter before the change
|
||||
removeObject(Symbol);
|
||||
}
|
||||
}
|
||||
|
||||
void delete(List<Symbol> rowObjects) {
|
||||
if (rowObjects == null || rowObjects.size() == 0) {
|
||||
if (rowObjects == null || rowObjects.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -379,7 +380,7 @@ class SymbolTableModel extends AddressBasedTableModel<Symbol> {
|
||||
public Symbol getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
if (!symbol.checkIsValid()) {
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
return symbol;
|
||||
@ -400,7 +401,7 @@ class SymbolTableModel extends AddressBasedTableModel<Symbol> {
|
||||
public Boolean getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
if (!symbol.checkIsValid()) {
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
return symbol.isPinned();
|
||||
@ -435,7 +436,7 @@ class SymbolTableModel extends AddressBasedTableModel<Symbol> {
|
||||
public ProgramLocation getProgramLocation(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) {
|
||||
|
||||
if (!symbol.checkIsValid()) {
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
return symbol.getProgramLocation();
|
||||
@ -454,7 +455,7 @@ class SymbolTableModel extends AddressBasedTableModel<Symbol> {
|
||||
public String getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
if (!symbol.checkIsValid()) {
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -483,7 +484,7 @@ class SymbolTableModel extends AddressBasedTableModel<Symbol> {
|
||||
public String getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
if (!symbol.checkIsValid()) {
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -520,7 +521,7 @@ class SymbolTableModel extends AddressBasedTableModel<Symbol> {
|
||||
public String getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
if (!symbol.checkIsValid()) {
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
return symbol.getParentNamespace().getName(true);
|
||||
@ -530,7 +531,7 @@ class SymbolTableModel extends AddressBasedTableModel<Symbol> {
|
||||
private class SourceTableColumn
|
||||
extends AbstractProgramBasedDynamicTableColumn<Symbol, SourceType> {
|
||||
|
||||
private GColumnRenderer<SourceType> renderer = new AbstractGColumnRenderer<SourceType>() {
|
||||
private GColumnRenderer<SourceType> renderer = new AbstractGColumnRenderer<>() {
|
||||
@Override
|
||||
protected String getText(Object value) {
|
||||
if (value == null) {
|
||||
@ -580,7 +581,7 @@ class SymbolTableModel extends AddressBasedTableModel<Symbol> {
|
||||
@Override
|
||||
public Integer getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
if (!symbol.checkIsValid()) {
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
return Integer.valueOf(symbol.getReferenceCount());
|
||||
@ -612,7 +613,7 @@ class SymbolTableModel extends AddressBasedTableModel<Symbol> {
|
||||
@Override
|
||||
public Integer getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
if (!symbol.checkIsValid()) {
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -665,7 +666,7 @@ class SymbolTableModel extends AddressBasedTableModel<Symbol> {
|
||||
public String getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
if (!symbol.checkIsValid()) {
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -702,7 +703,7 @@ class SymbolTableModel extends AddressBasedTableModel<Symbol> {
|
||||
public String getValue(Symbol symbol, Settings settings, Program p,
|
||||
ServiceProvider svcProvider) throws IllegalArgumentException {
|
||||
|
||||
if (!symbol.checkIsValid()) {
|
||||
if (symbol.isDeleted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,9 @@ import ghidra.util.table.GhidraTable;
|
||||
import ghidra.util.table.SelectionNavigationAction;
|
||||
import ghidra.util.table.actions.MakeProgramSelectionAction;
|
||||
import ghidra.util.task.SwingUpdateManager;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.util.worker.Job;
|
||||
import ghidra.util.worker.Worker;
|
||||
import resources.Icons;
|
||||
import resources.ResourceManager;
|
||||
|
||||
@ -88,6 +91,13 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
||||
private BlockModelService blockModelService;
|
||||
private SwingUpdateManager swingMgr;
|
||||
|
||||
/**
|
||||
* A worker that will process domain object change event work off of the Swing thread. This
|
||||
* solves the issue of db lock contention that can happen during analysis while this class
|
||||
* attempts to get symbols from the db while processing a flurry of domain events.
|
||||
*/
|
||||
private Worker domainObjectWorker = Worker.createGuiWorker();
|
||||
|
||||
public SymbolTablePlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
|
||||
@ -124,6 +134,7 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
||||
deleteAction.dispose();
|
||||
makeSelectionAction.dispose();
|
||||
|
||||
domainObjectWorker.dispose();
|
||||
if (symProvider != null) {
|
||||
symProvider.dispose();
|
||||
symProvider = null;
|
||||
@ -165,17 +176,15 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
||||
if (oldProg != null) {
|
||||
inspector.setProgram(null);
|
||||
oldProg.removeListener(this);
|
||||
domainObjectWorker.clearAllJobs();
|
||||
symProvider.setProgram(null, inspector);
|
||||
refProvider.setProgram(null, inspector);
|
||||
tool.contextChanged(symProvider);
|
||||
}
|
||||
currentProgram = newProg;
|
||||
if (newProg != null) {
|
||||
|
||||
currentProgram.addListener(this);
|
||||
|
||||
inspector.setProgram(currentProgram);
|
||||
|
||||
symProvider.setProgram(currentProgram, inspector);
|
||||
refProvider.setProgram(currentProgram, inspector);
|
||||
}
|
||||
@ -184,17 +193,27 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
||||
}
|
||||
}
|
||||
|
||||
boolean isBusy() {
|
||||
return domainObjectWorker.isBusy() || symProvider.isBusy() || refProvider.isBusy();
|
||||
}
|
||||
|
||||
private void reload() {
|
||||
domainObjectWorker.clearAllJobs();
|
||||
symProvider.reload();
|
||||
refProvider.reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void domainObjectChanged(DomainObjectChangedEvent ev) {
|
||||
if (!symProvider.isVisible()) {
|
||||
if (!symProvider.isVisible() && !refProvider.isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev.containsEvent(DomainObject.DO_OBJECT_RESTORED) ||
|
||||
ev.containsEvent(ChangeManager.DOCR_MEMORY_BLOCK_ADDED) ||
|
||||
ev.containsEvent(ChangeManager.DOCR_MEMORY_BLOCK_REMOVED)) {
|
||||
|
||||
symProvider.reload();
|
||||
refProvider.reload();
|
||||
reload();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -208,103 +227,74 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
||||
}
|
||||
|
||||
ProgramChangeRecord rec = (ProgramChangeRecord) doRecord;
|
||||
Symbol symbol = null;
|
||||
SymbolTable symbolTable = currentProgram.getSymbolTable();
|
||||
switch (eventType) {
|
||||
case ChangeManager.DOCR_CODE_ADDED:
|
||||
case ChangeManager.DOCR_CODE_REMOVED:
|
||||
if (rec.getNewValue() instanceof Data) {
|
||||
symbol = symbolTable.getPrimarySymbol(rec.getStart());
|
||||
if (symbol != null && symbol.isDynamic()) {
|
||||
symProvider.symbolChanged(symbol);
|
||||
refProvider.symbolChanged(symbol);
|
||||
}
|
||||
domainObjectWorker.schedule(
|
||||
new CodeAddedRemoveJob(currentProgram, rec.getStart()));
|
||||
}
|
||||
break;
|
||||
|
||||
case ChangeManager.DOCR_SYMBOL_ADDED:
|
||||
|
||||
Address addAddr = rec.getStart();
|
||||
Symbol primaryAtAdd = symbolTable.getPrimarySymbol(addAddr);
|
||||
if (primaryAtAdd != null && primaryAtAdd.isDynamic()) {
|
||||
symProvider.symbolRemoved(primaryAtAdd);
|
||||
}
|
||||
symbol = (Symbol) rec.getNewValue();
|
||||
symProvider.symbolAdded(symbol);
|
||||
refProvider.symbolAdded(symbol);
|
||||
Symbol symbol = (Symbol) rec.getNewValue();
|
||||
domainObjectWorker.schedule(
|
||||
new SymbolAddedJob(currentProgram, symbol, addAddr));
|
||||
break;
|
||||
|
||||
case ChangeManager.DOCR_SYMBOL_REMOVED:
|
||||
|
||||
Address removeAddr = rec.getStart();
|
||||
Long symbolID = (Long) rec.getNewValue();
|
||||
Symbol removedSymbol =
|
||||
symbolTable.createSymbolPlaceholder(removeAddr, symbolID);
|
||||
symProvider.symbolRemoved(removedSymbol);
|
||||
refProvider.symbolRemoved(removedSymbol);
|
||||
Symbol primaryAtRemove = symbolTable.getPrimarySymbol(removeAddr);
|
||||
if (primaryAtRemove != null && primaryAtRemove.isDynamic()) {
|
||||
symProvider.symbolAdded(primaryAtRemove);
|
||||
}
|
||||
domainObjectWorker.schedule(
|
||||
new SymbolRemovedJob(currentProgram, removeAddr, symbolID));
|
||||
break;
|
||||
|
||||
case ChangeManager.DOCR_SYMBOL_RENAMED:
|
||||
case ChangeManager.DOCR_SYMBOL_SCOPE_CHANGED:
|
||||
case ChangeManager.DOCR_SYMBOL_DATA_CHANGED:
|
||||
|
||||
symbol = (Symbol) rec.getObject();
|
||||
if (symbol.checkIsValid()) { // symbol may have been removed (e.g., parameter)
|
||||
symProvider.symbolChanged(symbol);
|
||||
refProvider.symbolChanged(symbol);
|
||||
}
|
||||
domainObjectWorker.schedule(new SymbolChangedJob(currentProgram, symbol));
|
||||
break;
|
||||
|
||||
case ChangeManager.DOCR_SYMBOL_SOURCE_CHANGED:
|
||||
|
||||
symbol = (Symbol) rec.getObject();
|
||||
symProvider.symbolChanged(symbol);
|
||||
domainObjectWorker.schedule(new SymbolSourceChangedJob(currentProgram, symbol));
|
||||
break;
|
||||
|
||||
case ChangeManager.DOCR_SYMBOL_SET_AS_PRIMARY:
|
||||
|
||||
symbol = (Symbol) rec.getNewValue();
|
||||
symProvider.symbolChanged(symbol);
|
||||
Symbol oldSymbol = (Symbol) rec.getOldValue();
|
||||
if (oldSymbol != null) {
|
||||
symProvider.symbolChanged(oldSymbol);
|
||||
}
|
||||
Symbol oldPrimarySymbol = (Symbol) rec.getOldValue();
|
||||
domainObjectWorker.schedule(
|
||||
new SymbolSetAsPrimaryJob(currentProgram, symbol, oldPrimarySymbol));
|
||||
break;
|
||||
|
||||
case ChangeManager.DOCR_SYMBOL_ASSOCIATION_ADDED:
|
||||
case ChangeManager.DOCR_SYMBOL_ASSOCIATION_REMOVED:
|
||||
break;
|
||||
case ChangeManager.DOCR_MEM_REFERENCE_ADDED:
|
||||
Reference ref = (Reference) rec.getObject();
|
||||
symbol = symbolTable.getSymbol(ref);
|
||||
if (symbol != null) {
|
||||
symProvider.symbolChanged(symbol);
|
||||
refProvider.symbolChanged(symbol);
|
||||
}
|
||||
break;
|
||||
case ChangeManager.DOCR_MEM_REFERENCE_REMOVED:
|
||||
ref = (Reference) rec.getObject();
|
||||
Address toAddr = ref.getToAddress();
|
||||
if (toAddr.isMemoryAddress()) {
|
||||
symbol = symbolTable.getSymbol(ref);
|
||||
if (symbol == null) {
|
||||
|
||||
long id = symbolTable.getDynamicSymbolID(ref.getToAddress());
|
||||
removedSymbol = symbolTable.createSymbolPlaceholder(toAddr, id);
|
||||
symProvider.symbolRemoved(removedSymbol);
|
||||
}
|
||||
else {
|
||||
refProvider.symbolChanged(symbol);
|
||||
}
|
||||
}
|
||||
Reference ref = (Reference) rec.getObject();
|
||||
domainObjectWorker.schedule(new ReferenceAddedJob(currentProgram, ref));
|
||||
break;
|
||||
|
||||
case ChangeManager.DOCR_MEM_REFERENCE_REMOVED:
|
||||
|
||||
ref = (Reference) rec.getObject();
|
||||
domainObjectWorker.schedule(new ReferenceRemovedJob(currentProgram, ref));
|
||||
break;
|
||||
|
||||
case ChangeManager.DOCR_EXTERNAL_ENTRY_POINT_ADDED:
|
||||
case ChangeManager.DOCR_EXTERNAL_ENTRY_POINT_REMOVED:
|
||||
Symbol[] symbols = symbolTable.getSymbols(rec.getStart());
|
||||
for (Symbol element : symbols) {
|
||||
symProvider.symbolChanged(element);
|
||||
refProvider.symbolChanged(element);
|
||||
}
|
||||
|
||||
Address address = rec.getStart();
|
||||
domainObjectWorker.schedule(
|
||||
new ExternalEntryChangedJob(currentProgram, address));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -336,7 +326,8 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
||||
}
|
||||
}
|
||||
|
||||
void closeReferenceProvider() {
|
||||
void symbolProviderClosed() {
|
||||
domainObjectWorker.clearAllJobs();
|
||||
if (refProvider != null) {
|
||||
refProvider.closeComponent();
|
||||
}
|
||||
@ -514,4 +505,268 @@ public class SymbolTablePlugin extends Plugin implements DomainObjectListener {
|
||||
action.setSelected(false);
|
||||
action.setSelected(true);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Table Update Jobs
|
||||
//==================================================================================================
|
||||
|
||||
private abstract class AbstractSymbolUpdateJob extends Job {
|
||||
|
||||
protected Program program;
|
||||
|
||||
AbstractSymbolUpdateJob(Program program) {
|
||||
this.program = program;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void run(TaskMonitor taskMonitor) {
|
||||
if (program != currentProgram) {
|
||||
return;
|
||||
}
|
||||
doRun();
|
||||
}
|
||||
|
||||
protected abstract void doRun();
|
||||
}
|
||||
|
||||
private class CodeAddedRemoveJob extends AbstractSymbolUpdateJob {
|
||||
|
||||
private Address start;
|
||||
|
||||
CodeAddedRemoveJob(Program program, Address start) {
|
||||
super(program);
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
|
||||
if (!symProvider.isShowingDynamicSymbols()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: this code *should* be checking the entire address range to handle the case
|
||||
// where large address range was cleared. This implementation will handle the
|
||||
// case where individual code units are cleared. This feature has been this way
|
||||
// for many years. The assumption is that most users are not showing dynamic
|
||||
// symbols often, especially not when performing analysis or clearing large
|
||||
// address ranges. Checking each address of the changed range is very slow.
|
||||
// This code will need to be updated in the future if we decide updating the
|
||||
// dynamic symbols in the symbol table is worth the cost. For now, if the table
|
||||
// becomes out-of-date, then user can simply close and re-open the table to
|
||||
// trigger an update.
|
||||
SymbolTable symbolTable = currentProgram.getSymbolTable();
|
||||
Symbol symbol = symbolTable.getPrimarySymbol(start);
|
||||
if (symbol != null && symbol.isDynamic()) {
|
||||
symProvider.symbolChanged(symbol);
|
||||
refProvider.symbolChanged(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SymbolAddedJob extends AbstractSymbolUpdateJob {
|
||||
|
||||
private Symbol symbol;
|
||||
private Address address;
|
||||
|
||||
SymbolAddedJob(Program program, Symbol symbol, Address address) {
|
||||
super(program);
|
||||
this.symbol = symbol;
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
|
||||
symProvider.symbolAdded(symbol);
|
||||
refProvider.symbolAdded(symbol);
|
||||
|
||||
if (!symProvider.isShowingDynamicSymbols()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
Symbol primaryAtAdd = symbolTable.getPrimarySymbol(address);
|
||||
if (primaryAtAdd != null && primaryAtAdd.isDynamic()) {
|
||||
symProvider.symbolRemoved(primaryAtAdd);
|
||||
refProvider.symbolRemoved(primaryAtAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SymbolRemovedJob extends AbstractSymbolUpdateJob {
|
||||
|
||||
private long symbolId;
|
||||
private Address address;
|
||||
|
||||
SymbolRemovedJob(Program program, Address address, long symbolId) {
|
||||
super(program);
|
||||
this.address = address;
|
||||
this.symbolId = symbolId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
|
||||
SymbolTable symbolTable = currentProgram.getSymbolTable();
|
||||
Symbol removedSymbol =
|
||||
symbolTable.createSymbolPlaceholder(address, symbolId);
|
||||
symProvider.symbolRemoved(removedSymbol);
|
||||
refProvider.symbolRemoved(removedSymbol);
|
||||
|
||||
if (!symProvider.isShowingDynamicSymbols()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Symbol primaryAtRemove = symbolTable.getPrimarySymbol(address);
|
||||
if (primaryAtRemove != null && primaryAtRemove.isDynamic()) {
|
||||
symProvider.symbolAdded(primaryAtRemove);
|
||||
refProvider.symbolAdded(primaryAtRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SymbolChangedJob extends AbstractSymbolUpdateJob {
|
||||
|
||||
private Symbol symbol;
|
||||
|
||||
SymbolChangedJob(Program program, Symbol symbol) {
|
||||
super(program);
|
||||
this.symbol = symbol;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
|
||||
// Note: should not need this check--the provider should be built to handle this
|
||||
// if (symbol.checkIsValid())
|
||||
symProvider.symbolChanged(symbol);
|
||||
refProvider.symbolChanged(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
private class SymbolSourceChangedJob extends AbstractSymbolUpdateJob {
|
||||
|
||||
private Symbol symbol;
|
||||
|
||||
SymbolSourceChangedJob(Program program, Symbol symbol) {
|
||||
super(program);
|
||||
this.symbol = symbol;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
symProvider.symbolChanged(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
private class SymbolSetAsPrimaryJob extends AbstractSymbolUpdateJob {
|
||||
|
||||
private Symbol symbol;
|
||||
private Symbol oldPrimarySymbol;
|
||||
|
||||
SymbolSetAsPrimaryJob(Program program, Symbol symbol, Symbol oldPrimarySymbol) {
|
||||
super(program);
|
||||
this.symbol = symbol;
|
||||
this.oldPrimarySymbol = oldPrimarySymbol;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
|
||||
symProvider.symbolChanged(symbol);
|
||||
if (oldPrimarySymbol != null) {
|
||||
symProvider.symbolChanged(oldPrimarySymbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ReferenceAddedJob extends AbstractSymbolUpdateJob {
|
||||
|
||||
private Reference reference;
|
||||
|
||||
ReferenceAddedJob(Program program, Reference reference) {
|
||||
super(program);
|
||||
this.reference = reference;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
|
||||
Address toAddr = reference.getToAddress();
|
||||
boolean isValid = toAddr.isMemoryAddress() || toAddr.isExternalAddress();
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
Symbol symbol = symbolTable.getSymbol(reference);
|
||||
if (symbol == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!symProvider.isShowingDynamicSymbols() && symbol.isDynamic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
symProvider.symbolChanged(symbol);
|
||||
refProvider.symbolChanged(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
private class ReferenceRemovedJob extends AbstractSymbolUpdateJob {
|
||||
|
||||
private Reference reference;
|
||||
|
||||
ReferenceRemovedJob(Program program, Reference reference) {
|
||||
super(program);
|
||||
this.reference = reference;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
|
||||
Address toAddr = reference.getToAddress();
|
||||
boolean isValid = toAddr.isMemoryAddress() || toAddr.isExternalAddress();
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
Symbol symbol = symbolTable.getSymbol(reference);
|
||||
if (symbol != null) {
|
||||
symProvider.symbolChanged(symbol);
|
||||
refProvider.symbolChanged(symbol);
|
||||
}
|
||||
|
||||
if (symProvider.isShowingDynamicSymbols()) {
|
||||
long id = symbolTable.getDynamicSymbolID(reference.getToAddress());
|
||||
Symbol removedSymbol = symbolTable.createSymbolPlaceholder(toAddr, id);
|
||||
symProvider.symbolRemoved(removedSymbol);
|
||||
refProvider.symbolRemoved(removedSymbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ExternalEntryChangedJob extends AbstractSymbolUpdateJob {
|
||||
|
||||
private Address address;
|
||||
|
||||
ExternalEntryChangedJob(Program program, Address address) {
|
||||
super(program);
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
Symbol[] symbols = symbolTable.getSymbols(address);
|
||||
for (Symbol element : symbols) {
|
||||
symProvider.symbolChanged(element);
|
||||
refProvider.symbolChanged(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -148,4 +148,31 @@ public abstract class GhidraScriptProvider
|
||||
return scriptName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the start of certification header line if this file type is
|
||||
* subject to certification.
|
||||
* @return start of certification header or null if not supported
|
||||
*/
|
||||
protected String getCertifyHeaderStart() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the prefix for each certification header bofy line if
|
||||
* this file is subject to certification
|
||||
* @return certification heaber body prefix or null if not supported
|
||||
*/
|
||||
protected String getCertificationBodyPrefix() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the end of certification header line if this file type is
|
||||
* subject to certification.
|
||||
* @return end of certification header or null if not supported
|
||||
*/
|
||||
protected String getCertifyHeaderEnd() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -164,6 +164,16 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
||||
return "//";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCertifyHeaderStart() {
|
||||
return "/* ###";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCertifyHeaderEnd() {
|
||||
return "*/";
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Fix script name for search in script directories, such as Java package parts in the name and inner class names.
|
||||
|
@ -20,7 +20,6 @@ import static ghidra.util.HTMLUtilities.*;
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.KeyStroke;
|
||||
@ -47,9 +46,6 @@ public class ScriptInfo {
|
||||
static final String AT_MENUPATH = "@menupath";
|
||||
static final String AT_TOOLBAR = "@toolbar";
|
||||
|
||||
private static final Pattern DOCUMENTATION_START = Pattern.compile("/\\*");
|
||||
private static final Pattern DOCUMENTATION_END = Pattern.compile("\\*/");
|
||||
|
||||
// omit from METADATA to avoid pre-populating in new scripts
|
||||
private static final String AT_IMPORTPACKAGE = "@importpackage";
|
||||
|
||||
@ -186,6 +182,16 @@ public class ScriptInfo {
|
||||
|
||||
init();
|
||||
|
||||
String commentPrefix = provider.getCommentCharacter();
|
||||
|
||||
// Note that skipping certification header presumes that the header
|
||||
// is intact with an appropriate start and end
|
||||
String certifyHeaderStart = provider.getCertifyHeaderStart();
|
||||
String certifyHeaderEnd = provider.getCertifyHeaderEnd();
|
||||
String certifyHeaderBodyPrefix = provider.getCertificationBodyPrefix();
|
||||
boolean allowCertifyHeader = (certifyHeaderStart != null);
|
||||
boolean skipCertifyHeader = false;
|
||||
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
@ -197,16 +203,34 @@ public class ScriptInfo {
|
||||
break;
|
||||
}
|
||||
|
||||
if (DOCUMENTATION_START.matcher(line).find()) {
|
||||
while (line != null && !DOCUMENTATION_END.matcher(line).find()) {
|
||||
line = reader.readLine();
|
||||
}
|
||||
if (allowCertifyHeader) {
|
||||
// Skip past certification header if found
|
||||
if (skipCertifyHeader) {
|
||||
String trimLine = line.trim();
|
||||
if (trimLine.startsWith(certifyHeaderEnd)) {
|
||||
allowCertifyHeader = false;
|
||||
skipCertifyHeader = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
String commentPrefix = provider.getCommentCharacter();
|
||||
if (certifyHeaderBodyPrefix == null ||
|
||||
trimLine.startsWith(certifyHeaderBodyPrefix)) {
|
||||
continue; // skip certification header body
|
||||
}
|
||||
// broken certification header - unexpected line
|
||||
Msg.error(this,
|
||||
"Script contains invalid certification header: " + getName());
|
||||
allowCertifyHeader = false;
|
||||
skipCertifyHeader = false;
|
||||
}
|
||||
else if (line.startsWith(certifyHeaderStart)) {
|
||||
skipCertifyHeader = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (line.startsWith(commentPrefix)) {
|
||||
allowCertifyHeader = false;
|
||||
|
||||
line = line.substring(commentPrefix.length()).trim();
|
||||
|
||||
if (line.startsWith("@")) {
|
||||
|
@ -20,6 +20,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
@ -38,7 +39,7 @@ import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.datastruct.WeakDataStructureFactory;
|
||||
import ghidra.util.datastruct.WeakSet;
|
||||
import ghidra.util.table.*;
|
||||
@ -113,8 +114,7 @@ public class TableChooserDialog extends DialogComponentProvider
|
||||
table.installNavigation(goToService, navigatable);
|
||||
}
|
||||
table.getSelectionModel()
|
||||
.addListSelectionListener(
|
||||
e -> setOkEnabled(table.getSelectedRowCount() > 0));
|
||||
.addListSelectionListener(e -> setOkEnabled(table.getSelectedRowCount() > 0));
|
||||
|
||||
GhidraTableFilterPanel<AddressableRowObject> filterPanel =
|
||||
new GhidraTableFilterPanel<>(table, model);
|
||||
@ -128,7 +128,7 @@ public class TableChooserDialog extends DialogComponentProvider
|
||||
* @param callback the callback to notify
|
||||
*/
|
||||
public void setClosedListener(Callback callback) {
|
||||
this.closedCallback = Callback.dummyIfNull(callback);
|
||||
Swing.runNow(() -> closedCallback = Callback.dummyIfNull(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -153,8 +153,7 @@ public class TableChooserDialog extends DialogComponentProvider
|
||||
private void createTableModel() {
|
||||
|
||||
// note: the task monitor is installed later when this model is added to the threaded panel
|
||||
SystemUtilities.runSwingNow(
|
||||
() -> model = new TableChooserTableModel("Test", tool, program, null));
|
||||
Swing.runNow(() -> model = new TableChooserTableModel("Test", tool, program, null));
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
@ -175,8 +174,8 @@ public class TableChooserDialog extends DialogComponentProvider
|
||||
};
|
||||
|
||||
DockingAction selectionNavigationAction = new SelectionNavigationAction(owner, table);
|
||||
selectionNavigationAction.setHelpLocation(
|
||||
new HelpLocation(HelpTopics.SEARCH, "Selection_Navigation"));
|
||||
selectionNavigationAction
|
||||
.setHelpLocation(new HelpLocation(HelpTopics.SEARCH, "Selection_Navigation"));
|
||||
|
||||
addAction(selectAction);
|
||||
addAction(selectionNavigationAction);
|
||||
@ -296,7 +295,49 @@ public class TableChooserDialog extends DialogComponentProvider
|
||||
}
|
||||
|
||||
public void addCustomColumn(ColumnDisplay<?> columnDisplay) {
|
||||
model.addCustomColumn(columnDisplay);
|
||||
Swing.runNow(() -> model.addCustomColumn(columnDisplay));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default sorted column for this dialog.
|
||||
*
|
||||
* <P>This method should be called after all custom columns have been added via
|
||||
* {@link #addCustomColumn(ColumnDisplay)}.
|
||||
*
|
||||
* @param index the view's 0-based column index
|
||||
* @see #setSortState(TableSortState)
|
||||
* @throws IllegalArgumentException if an invalid column is requested for sorting
|
||||
*/
|
||||
public void setSortColumn(int index) {
|
||||
setSortState(TableSortState.createDefaultSortState(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the column sort state for this dialog. The {@link TableSortState} allows for
|
||||
* combinations of sorted columns in ascending or descending order.
|
||||
*
|
||||
* <P>This method should be called after all custom columns have been added via
|
||||
* {@link #addCustomColumn(ColumnDisplay)}.
|
||||
*
|
||||
* @param state the sort state
|
||||
* @see #setSortColumn(int)
|
||||
* @throws IllegalArgumentException if an invalid column is requested for sorting
|
||||
*/
|
||||
public void setSortState(TableSortState state) {
|
||||
AtomicReference<IllegalArgumentException> ref = new AtomicReference<>();
|
||||
Swing.runNow(() -> {
|
||||
try {
|
||||
model.setTableSortState(state);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
ref.set(e);
|
||||
}
|
||||
});
|
||||
IllegalArgumentException exception = ref.get();
|
||||
if (exception != null) {
|
||||
// use a new exception so the stack trace points to this class, not the runnable above
|
||||
throw new IllegalArgumentException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -313,15 +354,17 @@ public class TableChooserDialog extends DialogComponentProvider
|
||||
}
|
||||
|
||||
public void clearSelection() {
|
||||
table.clearSelection();
|
||||
Swing.runNow(() -> table.clearSelection());
|
||||
}
|
||||
|
||||
public void selectRows(int... rows) {
|
||||
|
||||
Swing.runNow(() -> {
|
||||
ListSelectionModel selectionModel = table.getSelectionModel();
|
||||
for (int row : rows) {
|
||||
selectionModel.addSelectionInterval(row, row);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public int[] getSelectedRows() {
|
||||
|
@ -36,7 +36,6 @@ import ghidra.program.util.*;
|
||||
*/
|
||||
public class DisplayableEol {
|
||||
|
||||
private static final String VAR_ARGS = "...";
|
||||
private static final String POINTER_ARROW = "-> ";
|
||||
|
||||
public static final int MY_EOLS = 0;
|
||||
@ -48,19 +47,16 @@ public class DisplayableEol {
|
||||
private boolean alwaysShowRepeatable = false;
|
||||
private boolean alwaysShowRefRepeats = false;
|
||||
private boolean alwaysShowAutomatic = false;
|
||||
private boolean showAutomaticFunctions;
|
||||
private boolean operandsFollowPointerRefs = false;
|
||||
private int maxDisplayLines;
|
||||
private int totalCommentsFound;
|
||||
|
||||
private boolean useAbbreviatedAutomatic;
|
||||
|
||||
/**
|
||||
* Construct a new DisplayableEol.
|
||||
* @param cu code unit that may have end of line or repeatable comments.
|
||||
*/
|
||||
public DisplayableEol(CodeUnit cu, boolean alwaysShowRepeatable, boolean alwaysShowRefRepeats,
|
||||
boolean alwaysShowAutomatic, boolean operandsFollowPointerRefs, int maxDisplayLines,
|
||||
boolean useAbbreviatedAutomatic) {
|
||||
boolean useAbbreviatedAutomatic, boolean showAutomaticFunctions) {
|
||||
this.codeUnit = cu;
|
||||
this.alwaysShowRepeatable = alwaysShowRepeatable;
|
||||
this.alwaysShowRefRepeats = alwaysShowRefRepeats;
|
||||
@ -68,6 +64,7 @@ public class DisplayableEol {
|
||||
this.operandsFollowPointerRefs = operandsFollowPointerRefs;
|
||||
this.maxDisplayLines = maxDisplayLines;
|
||||
this.useAbbreviatedAutomatic = useAbbreviatedAutomatic;
|
||||
this.showAutomaticFunctions = showAutomaticFunctions;
|
||||
|
||||
initComments();
|
||||
}
|
||||
@ -118,7 +115,8 @@ public class DisplayableEol {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the associated code unit has an end of line comment.
|
||||
* Return whether the associated code unit has an end of line comment
|
||||
* @return whether the associated code unit has an end of line comment
|
||||
*/
|
||||
public boolean hasEOL() {
|
||||
return (displayCommentArrays[MY_EOLS] != null) &&
|
||||
@ -126,7 +124,8 @@ public class DisplayableEol {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the associated code unit has a repeatable comment.
|
||||
* Return whether the associated code unit has a repeatable comment
|
||||
* @return whether the associated code unit has a repeatable comment
|
||||
*/
|
||||
public boolean hasRepeatable() {
|
||||
return (displayCommentArrays[MY_REPEATABLES] != null) &&
|
||||
@ -135,7 +134,9 @@ public class DisplayableEol {
|
||||
|
||||
/**
|
||||
* Return whether any memory reference from this code unit has a repeatable
|
||||
* comment at the reference's to address.
|
||||
* comment at the reference's to address
|
||||
* @return whether any memory reference from this code unit has a repeatable
|
||||
* comment at the reference's to address
|
||||
*/
|
||||
public boolean hasReferencedRepeatable() {
|
||||
return (displayCommentArrays[REF_REPEATABLES] != null) &&
|
||||
@ -143,10 +144,10 @@ public class DisplayableEol {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this code unit has an automatic comment.
|
||||
* (i.e. any memory reference from this code unit has a
|
||||
* function defined at the reference's to address, or if the to
|
||||
* address is a pointer.)
|
||||
* Return whether this code unit has an automatic comment. For example, a memory reference
|
||||
* from this code unit has a function defined at the reference's to address, or if the to
|
||||
* address is a pointer.
|
||||
* @return whether this code unit has an automatic comment
|
||||
*/
|
||||
public boolean hasAutomatic() {
|
||||
return (displayCommentArrays[MY_AUTOMATIC] != null) &&
|
||||
@ -367,6 +368,10 @@ public class DisplayableEol {
|
||||
private boolean handleDirectFlow(Set<String> set, Reference reference, Program program,
|
||||
Address toAddr) {
|
||||
|
||||
if (!showAutomaticFunctions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RefType type = reference.getReferenceType();
|
||||
if (!type.isFlow()) {
|
||||
return false;
|
||||
@ -537,7 +542,8 @@ public class DisplayableEol {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the comments (End of Line, Repeatable, Referenced Repeatables, and Referenced Data).
|
||||
* Return all the comments
|
||||
* @return the comments
|
||||
*/
|
||||
public String[] getComments() {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
|
@ -71,6 +71,10 @@ public interface StructConverter {
|
||||
* Reusable 32-bit image base offset datatype.
|
||||
*/
|
||||
public final static DataType IBO32 = new ImageBaseOffset32DataType();
|
||||
/**
|
||||
* Reusable 64-bit image base offset datatype.
|
||||
*/
|
||||
public final static DataType IBO64 = new ImageBaseOffset64DataType();
|
||||
|
||||
/**
|
||||
* Returns a structure datatype representing the
|
||||
|
@ -15,9 +15,10 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.dwarf4;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
@ -884,6 +885,30 @@ public class DIEAggregate {
|
||||
throw new IOException("Bad/unsupported DW_AT_high_pc attribute value or type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the raw lowPc and highPc values are the same.
|
||||
* <p>
|
||||
* This indicates an empty range, in which case the caller may want to take
|
||||
* special steps to avoid issues with Ghidra ranges.
|
||||
* <p>
|
||||
* Only seen in extremely old gcc versions. Typically the low & high
|
||||
* pc values are omitted if the CU is empty.
|
||||
*
|
||||
* @return boolean true if the LowPC and HighPC values are present and equal
|
||||
*/
|
||||
public boolean isLowPCEqualHighPC() {
|
||||
AttrInfo low = findAttribute(DWARFAttribute.DW_AT_low_pc);
|
||||
AttrInfo high = findAttribute(DWARFAttribute.DW_AT_high_pc);
|
||||
if (low != null && high != null && low.form == high.form &&
|
||||
low.attr instanceof DWARFNumericAttribute &&
|
||||
high.attr instanceof DWARFNumericAttribute) {
|
||||
DWARFNumericAttribute lowVal = (DWARFNumericAttribute) low.attr;
|
||||
DWARFNumericAttribute highVal = (DWARFNumericAttribute) high.attr;
|
||||
return lowVal.getValue() == highVal.getValue();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple class used by findAttribute() to return the found attribute, along with
|
||||
* the DIE it was found in, and the DWARFForm type of the raw attribute.
|
||||
|
@ -49,11 +49,14 @@ public class DWARFCompileUnit {
|
||||
String comp_dir = diea.getString(DWARFAttribute.DW_AT_comp_dir, null);
|
||||
|
||||
Number high_pc = null, low_pc = null, language = null, stmt_list = null;
|
||||
|
||||
if (diea.hasAttribute(DWARFAttribute.DW_AT_low_pc)) {
|
||||
low_pc = diea.getLowPC(0);
|
||||
}
|
||||
|
||||
if (diea.hasAttribute(DWARFAttribute.DW_AT_high_pc)) {
|
||||
// if lowPC and highPC values are the same, don't read the high value
|
||||
// because Ghidra can't express an empty range.
|
||||
if (diea.hasAttribute(DWARFAttribute.DW_AT_high_pc) && !diea.isLowPCEqualHighPC()) {
|
||||
high_pc = diea.getHighPC();
|
||||
}
|
||||
|
||||
|
@ -15,10 +15,11 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.dwarf4.next;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.cmd.comments.AppendCommentCmd;
|
||||
import ghidra.app.cmd.label.SetLabelPrimaryCmd;
|
||||
import ghidra.app.util.bin.format.dwarf4.*;
|
||||
@ -202,26 +203,26 @@ public class DWARFFunctionImporter {
|
||||
return;
|
||||
}
|
||||
|
||||
DWARFFunction function = new DWARFFunction(prog.getName(diea));
|
||||
function.namespace = function.dni.getParentNamespace(currentProgram);
|
||||
DWARFFunction dfunc = new DWARFFunction(prog.getName(diea));
|
||||
dfunc.namespace = dfunc.dni.getParentNamespace(currentProgram);
|
||||
|
||||
Number lowPC = diea.getLowPC(0);
|
||||
function.address = toAddr(lowPC);
|
||||
function.highAddress =
|
||||
dfunc.address = toAddr(lowPC);
|
||||
dfunc.highAddress =
|
||||
diea.hasAttribute(DWARFAttribute.DW_AT_high_pc) ? toAddr(diea.getHighPC()) : null;
|
||||
|
||||
String previousFunctionProcessed = functionsProcessed.get(function.address);
|
||||
String previousFunctionProcessed = functionsProcessed.get(dfunc.address);
|
||||
if (previousFunctionProcessed != null) {
|
||||
// Msg.info(this, "Duplicate function defintion found for " + dni.getCategoryPath() +
|
||||
// " at " + function.address + " in DIE " + diea.getHexOffset() + ", skipping");
|
||||
markAllChildrenAsProcessed(diea.getHeadFragment());
|
||||
return;
|
||||
}
|
||||
functionsProcessed.put(function.address,
|
||||
function.dni.getNamespacePath() + " DIE: " + diea.getHexOffset());
|
||||
functionsProcessed.put(dfunc.address,
|
||||
dfunc.dni.getNamespacePath() + " DIE: " + diea.getHexOffset());
|
||||
|
||||
// Check if the function is an external function
|
||||
function.isExternal = diea.getBool(DWARFAttribute.DW_AT_external, false);
|
||||
dfunc.isExternal = diea.getBool(DWARFAttribute.DW_AT_external, false);
|
||||
|
||||
// Retrieve the frame base if it exists
|
||||
DWARFLocation frameLoc = null;
|
||||
@ -229,9 +230,9 @@ public class DWARFFunctionImporter {
|
||||
List<DWARFLocation> frameBase = diea.getAsLocation(DWARFAttribute.DW_AT_frame_base);
|
||||
// get the framebase register, find where the frame is finally set
|
||||
// up.
|
||||
frameLoc = getTopLocation(frameBase, function.address.getOffset());
|
||||
frameLoc = getTopLocation(frameBase, dfunc.address.getOffset());
|
||||
if (frameLoc != null) {
|
||||
function.frameBase = (int) diea.evaluateLocation(frameLoc);
|
||||
dfunc.frameBase = (int) diea.evaluateLocation(frameLoc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,26 +246,134 @@ public class DWARFFunctionImporter {
|
||||
// function passing a pointer to the callee function where the object is
|
||||
// then operated on.
|
||||
DIEAggregate typeRef = diea.getTypeRef();
|
||||
if (typeRef != null) {
|
||||
function.retval = new DWARFVariable();
|
||||
function.retval.type = dwarfDTM.getDataType(typeRef, dwarfDTM.getVoidType());
|
||||
}
|
||||
DataType formalReturnType = (typeRef != null)
|
||||
? dwarfDTM.getDataType(typeRef, DataType.DEFAULT)
|
||||
: dwarfDTM.getVoidType();
|
||||
dfunc.retval = new DWARFVariable();
|
||||
dfunc.retval.type = formalReturnType;
|
||||
|
||||
boolean formalParamsOnly = false;
|
||||
boolean skipFuncSignature = false;
|
||||
List<Parameter> formalParams = new ArrayList<>();
|
||||
|
||||
for (DebugInfoEntry childEntry : diea.getHeadFragment().getChildren(
|
||||
DWARFTag.DW_TAG_formal_parameter)) {
|
||||
DIEAggregate childDIEA = prog.getAggregate(childEntry);
|
||||
|
||||
DWARFVariable var = processVariable(childDIEA, function, null, -1);
|
||||
if (var != null) {
|
||||
function.params.add(var);
|
||||
Parameter formalParam = createFormalParameter(childDIEA);
|
||||
if (formalParam == null) {
|
||||
skipFuncSignature = true;
|
||||
break;
|
||||
}
|
||||
formalParams.add(formalParam);
|
||||
|
||||
if (!formalParamsOnly) {
|
||||
DWARFVariable var = processVariable(childDIEA, dfunc, null, -1);
|
||||
if (var == null) {
|
||||
// we had an error, can't rely on detailed param data, fallback to
|
||||
// formal params
|
||||
formalParamsOnly = true;
|
||||
dfunc.params.clear();
|
||||
}
|
||||
else {
|
||||
dfunc.params.add(var);
|
||||
}
|
||||
}
|
||||
function.varArg =
|
||||
}
|
||||
dfunc.varArg =
|
||||
!diea.getHeadFragment().getChildren(DWARFTag.DW_TAG_unspecified_parameters).isEmpty();
|
||||
|
||||
processFuncChildren(diea, function);
|
||||
outputFunction(function, diea);
|
||||
processFuncChildren(diea, dfunc);
|
||||
|
||||
Function gfunc = createFunction(dfunc, diea);
|
||||
if (gfunc != null) {
|
||||
|
||||
if (formalParams.isEmpty() && dfunc.localVarErrors) {
|
||||
// if there were no defined parameters and we had problems decoding local variables,
|
||||
// don't force the method to have an empty param signature because there are other
|
||||
// issues afoot.
|
||||
skipFuncSignature = true;
|
||||
}
|
||||
|
||||
if (skipFuncSignature) {
|
||||
Msg.error(this,
|
||||
"Failed to get function signature information, leaving undefined: " +
|
||||
gfunc.getName() + "@" + gfunc.getEntryPoint());
|
||||
Msg.debug(this, "DIE info: " + diea.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (formalParamsOnly) {
|
||||
updateFunctionSignatureWithFormalParams(gfunc, formalParams,
|
||||
formalReturnType, dfunc.varArg, diea);
|
||||
}
|
||||
else {
|
||||
updateFunctionSignatureWithDetailParams(gfunc, dfunc, diea);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void updateFunctionSignatureWithFormalParams(Function gfunc, List<Parameter> params,
|
||||
DataType returnType, boolean varArgs, DIEAggregate diea) {
|
||||
try {
|
||||
ReturnParameterImpl returnVar = new ReturnParameterImpl(returnType, currentProgram);
|
||||
try {
|
||||
gfunc.setVarArgs(varArgs);
|
||||
gfunc.updateFunction(null, returnVar, params,
|
||||
FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.IMPORTED);
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
// try again after adjusting param names
|
||||
setUniqueParameterNames(gfunc, params);
|
||||
gfunc.updateFunction(null, returnVar, params,
|
||||
FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.IMPORTED);
|
||||
}
|
||||
}
|
||||
catch (InvalidInputException | DuplicateNameException e) {
|
||||
Msg.error(this,
|
||||
"Error updating function " + gfunc.getName() + " with formal params at " +
|
||||
gfunc.getEntryPoint().toString() + ": " + e.getMessage());
|
||||
Msg.error(this, "DIE info: " + diea.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFunctionSignatureWithDetailParams(Function gfunc, DWARFFunction dfunc,
|
||||
DIEAggregate diea) {
|
||||
try {
|
||||
CompilerSpec compilerSpec = currentProgram.getCompilerSpec();
|
||||
PrototypeModel convention = null;
|
||||
Variable returnVariable;
|
||||
List<Parameter> params = new ArrayList<>();
|
||||
|
||||
returnVariable = buildReturnVariable(dfunc.retval);
|
||||
for (int i = 0; i < dfunc.params.size(); ++i) {
|
||||
Parameter curparam = buildParameter(gfunc, i, dfunc.params.get(i), diea);
|
||||
params.add(curparam);
|
||||
if (i == 0 && checkThisParameter(dfunc.params.get(0), diea)) {
|
||||
convention = compilerSpec.matchConvention(GenericCallingConvention.thiscall);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < dfunc.local.size(); ++i) {
|
||||
commitLocal(gfunc, dfunc.local.get(i));
|
||||
}
|
||||
|
||||
if (dfunc.retval != null || params.size() > 0) {
|
||||
// Add the function signature definition into the data type manager
|
||||
// TODO: createFunctionDefinition(dfunc, infopath);
|
||||
|
||||
// NOTE: Storage is computed above for the purpose of identifying
|
||||
// a best fit calling convention. The commitPrototype method currently
|
||||
// always employs dynamic storage.
|
||||
commitPrototype(gfunc, returnVariable, params, convention);
|
||||
gfunc.setVarArgs(dfunc.varArg);
|
||||
}
|
||||
}
|
||||
catch (InvalidInputException | DuplicateNameException iie) {
|
||||
Msg.error(this, "Error updating function " + dfunc.dni.getName() + " at " +
|
||||
dfunc.address.toString() + ": " + iie.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void processFuncChildren(DIEAggregate diea, DWARFFunction dfunc)
|
||||
@ -305,6 +414,19 @@ public class DWARFFunctionImporter {
|
||||
}
|
||||
}
|
||||
|
||||
private Parameter createFormalParameter(DIEAggregate diea) {
|
||||
String name = diea.getString(DWARFAttribute.DW_AT_name, null);
|
||||
DataType dt = dwarfDTM.getDataType(diea.getTypeRef(), dwarfDTM.getVoidType());
|
||||
|
||||
try {
|
||||
return new ParameterImpl(name, dt, currentProgram);
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
Msg.debug(this, "Failed to create parameter for " + diea.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link DWARFVariable} from the specified {@link DIEAggregate DIEA} and
|
||||
* as a child of the specified function (if not null).
|
||||
@ -349,6 +471,9 @@ public class DWARFFunctionImporter {
|
||||
|
||||
DWARFLocation topLocation = getTopLocation(locList, funcAddr);
|
||||
if (topLocation == null) {
|
||||
if (dfunc != null) {
|
||||
dfunc.localVarErrors = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -368,12 +493,18 @@ public class DWARFFunctionImporter {
|
||||
catch (DWARFExpressionException | UnsupportedOperationException
|
||||
| IndexOutOfBoundsException ex) {
|
||||
importSummary.exprReadError++;
|
||||
if (dfunc != null) {
|
||||
dfunc.localVarErrors = true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (exprEvaluator.isDwarfStackValue()) {
|
||||
importSummary.varDWARFExpressionValue++;
|
||||
if (dfunc != null) {
|
||||
dfunc.localVarErrors = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else if (exprEvaluator.useUnknownRegister() && exprEvaluator.isRegisterLocation()) {
|
||||
@ -386,6 +517,9 @@ public class DWARFFunctionImporter {
|
||||
}
|
||||
else if (exprEvaluator.useUnknownRegister()) {
|
||||
importSummary.varDynamicRegisterError++;
|
||||
if (dfunc != null) {
|
||||
dfunc.localVarErrors = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else if (exprEvaluator.isStackRelative()) {
|
||||
@ -420,6 +554,9 @@ public class DWARFFunctionImporter {
|
||||
" can not fit into specified register " + dvar.reg.getName() +
|
||||
", size=" + dvar.reg.getMinimumByteSize() +
|
||||
", skipping. DWARF DIE: " + diea.getHexOffset());
|
||||
if (dfunc != null) {
|
||||
dfunc.localVarErrors = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -430,6 +567,9 @@ public class DWARFFunctionImporter {
|
||||
// The DWARF register did not have a mapping to a Ghidra register, so
|
||||
// log it to be displayed in an error summary at end of import phase.
|
||||
importSummary.unknownRegistersEncountered.add(exprEvaluator.getRawLastRegister());
|
||||
if (dfunc != null) {
|
||||
dfunc.localVarErrors = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -1030,75 +1170,7 @@ public class DWARFFunctionImporter {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void outputFunction(DWARFFunction dfunc, DIEAggregate diea) {
|
||||
try {
|
||||
Function function = createFunction(dfunc);
|
||||
if (function == null) {
|
||||
Msg.error(this, "DWARF DIE: " + diea.getHexOffset());
|
||||
return;
|
||||
}
|
||||
|
||||
DWARFSourceInfo sourceInfo = DWARFSourceInfo.create(diea);
|
||||
if (sourceInfo != null) {
|
||||
// Move the function into the program tree of the file
|
||||
moveIntoFragment(function.getName(), dfunc.address,
|
||||
dfunc.highAddress != null ? dfunc.highAddress : dfunc.address.add(1),
|
||||
sourceInfo.getFilename());
|
||||
|
||||
if (importOptions.isOutputSourceLocationInfo()) {
|
||||
appendComment(dfunc.address, CodeUnit.PLATE_COMMENT,
|
||||
sourceInfo.getDescriptionStr(), "\n");
|
||||
}
|
||||
}
|
||||
if (importOptions.isOutputDIEInfo()) {
|
||||
appendComment(dfunc.address, CodeUnit.PLATE_COMMENT,
|
||||
"DWARF DIE: " + diea.getHexOffset(), "\n");
|
||||
}
|
||||
|
||||
DWARFNameInfo dni = prog.getName(diea);
|
||||
if (dni.isNameModified()) {
|
||||
appendComment(dfunc.address, CodeUnit.PLATE_COMMENT,
|
||||
"Original name: " + dni.getOriginalName(), "\n");
|
||||
}
|
||||
|
||||
CompilerSpec compilerSpec = currentProgram.getCompilerSpec();
|
||||
PrototypeModel convention = null;
|
||||
Variable returnVariable;
|
||||
ArrayList<Parameter> params = new ArrayList<>();
|
||||
|
||||
// boolean specifyStorage = evaluateParameterStorage(dfunc);
|
||||
|
||||
returnVariable = buildReturnVariable(dfunc.retval);
|
||||
for (int i = 0; i < dfunc.params.size(); ++i) {
|
||||
Parameter curparam = buildParameter(function, i, dfunc.params.get(i), diea);
|
||||
params.add(curparam);
|
||||
if (i == 0 && checkThisParameter(dfunc.params.get(0), diea)) {
|
||||
convention = compilerSpec.matchConvention(GenericCallingConvention.thiscall);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < dfunc.local.size(); ++i) {
|
||||
commitLocal(function, dfunc.local.get(i));
|
||||
}
|
||||
|
||||
if (dfunc.retval != null || params.size() > 0) {
|
||||
// Add the function signature definition into the data type manager
|
||||
// TODO: createFunctionDefinition(dfunc, infopath);
|
||||
|
||||
// NOTE: Storage is computed above for the purpose of identifying
|
||||
// a best fit calling convention. The commitPrototype method currently
|
||||
// always employs dynamic storage.
|
||||
commitPrototype(function, returnVariable, params, convention);
|
||||
function.setVarArgs(dfunc.varArg);
|
||||
}
|
||||
}
|
||||
catch (InvalidInputException | DuplicateNameException iie) {
|
||||
Msg.error(this, "Error updating function " + dfunc.dni.getName() + " at " +
|
||||
dfunc.address.toString() + ": " + iie.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private Function createFunction(DWARFFunction dfunc) {
|
||||
private Function createFunction(DWARFFunction dfunc, DIEAggregate diea) {
|
||||
try {
|
||||
// create a new symbol if one does not exist (symbol table will figure this out)
|
||||
SymbolTable symbolTable = currentProgram.getSymbolTable();
|
||||
@ -1129,6 +1201,30 @@ public class DWARFFunctionImporter {
|
||||
function = currentProgram.getFunctionManager().createFunction(null, dfunc.address,
|
||||
new AddressSet(dfunc.address), SourceType.IMPORTED);
|
||||
}
|
||||
|
||||
DWARFSourceInfo sourceInfo = DWARFSourceInfo.create(diea);
|
||||
if (sourceInfo != null) {
|
||||
// Move the function into the program tree of the file
|
||||
moveIntoFragment(function.getName(), dfunc.address,
|
||||
dfunc.highAddress != null ? dfunc.highAddress : dfunc.address.add(1),
|
||||
sourceInfo.getFilename());
|
||||
|
||||
if (importOptions.isOutputSourceLocationInfo()) {
|
||||
appendComment(dfunc.address, CodeUnit.PLATE_COMMENT,
|
||||
sourceInfo.getDescriptionStr(), "\n");
|
||||
}
|
||||
}
|
||||
if (importOptions.isOutputDIEInfo()) {
|
||||
appendComment(dfunc.address, CodeUnit.PLATE_COMMENT,
|
||||
"DWARF DIE: " + diea.getHexOffset(), "\n");
|
||||
}
|
||||
|
||||
DWARFNameInfo dni = prog.getName(diea);
|
||||
if (dni.isNameModified()) {
|
||||
appendComment(dfunc.address, CodeUnit.PLATE_COMMENT,
|
||||
"Original name: " + dni.getOriginalName(), "\n");
|
||||
}
|
||||
|
||||
return function;
|
||||
}
|
||||
catch (OverlappingFunctionException e) {
|
||||
@ -1152,14 +1248,14 @@ public class DWARFFunctionImporter {
|
||||
* @throws InvalidInputException invalid parameter name
|
||||
* @throws DuplicateNameException (should not occur on non-DB parameter)
|
||||
*/
|
||||
private void setUniqueParameterNames(Function function, Parameter[] parameters)
|
||||
private void setUniqueParameterNames(Function function, List<Parameter> parameters)
|
||||
throws DuplicateNameException, InvalidInputException {
|
||||
SymbolTable symbolTable = currentProgram.getSymbolTable();
|
||||
// Create a set containing all the unique parameter names determined so far so they can
|
||||
// be avoided as additional parameter names are determined.
|
||||
Set<String> namesSoFar = new HashSet<>();
|
||||
for (int ordinal = 0; ordinal < parameters.length; ordinal++) {
|
||||
Parameter parameter = parameters[ordinal];
|
||||
for (int ordinal = 0; ordinal < parameters.size(); ordinal++) {
|
||||
Parameter parameter = parameters.get(ordinal);
|
||||
String baseName = parameter.getName();
|
||||
if (ordinal == 0 && Function.THIS_PARAM_NAME.equals(baseName)) {
|
||||
continue;
|
||||
@ -1233,14 +1329,13 @@ public class DWARFFunctionImporter {
|
||||
}
|
||||
|
||||
private void commitPrototype(Function function, Variable returnVariable,
|
||||
ArrayList<Parameter> params, PrototypeModel protoModel)
|
||||
List<Parameter> params, PrototypeModel protoModel)
|
||||
throws InvalidInputException, DuplicateNameException {
|
||||
|
||||
Parameter[] paramarray = new Parameter[params.size()];
|
||||
params.toArray(paramarray);
|
||||
CompilerSpec compilerSpec = currentProgram.getCompilerSpec();
|
||||
|
||||
if (protoModel == null) {
|
||||
Parameter[] paramarray = params.toArray(Parameter[]::new);
|
||||
protoModel = compilerSpec.findBestCallingConvention(paramarray);
|
||||
}
|
||||
|
||||
@ -1249,7 +1344,7 @@ public class DWARFFunctionImporter {
|
||||
FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.IMPORTED);
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
setUniqueParameterNames(function, paramarray);
|
||||
setUniqueParameterNames(function, params);
|
||||
function.updateFunction(protoModel.getName(), returnVariable, params,
|
||||
FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, true, SourceType.IMPORTED);
|
||||
}
|
||||
@ -1361,9 +1456,10 @@ public class DWARFFunctionImporter {
|
||||
public DWARFVariable retval;
|
||||
public boolean isExternal;
|
||||
public long frameBase;
|
||||
public ArrayList<DWARFVariable> params = new ArrayList<>();
|
||||
public ArrayList<DWARFVariable> local = new ArrayList<>();
|
||||
public List<DWARFVariable> params = new ArrayList<>();
|
||||
public List<DWARFVariable> local = new ArrayList<>();
|
||||
public boolean varArg;
|
||||
public boolean localVarErrors; // set to true if problem w/local var decoding
|
||||
|
||||
public DWARFFunction(DWARFNameInfo dni) {
|
||||
this.dni = dni;
|
||||
|
@ -54,12 +54,8 @@ public class DWARFProgram implements Closeable {
|
||||
private static final int NAME_HASH_REPLACEMENT_SIZE = 8 + 2 + 2;
|
||||
private static final String ELLIPSES_STR = "...";
|
||||
|
||||
public static boolean alreadyDWARFImported(Program prog) {
|
||||
return DWARFFunctionImporter.hasDWARFProgModule(prog, DWARF_ROOT_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the {@link Program program} probably DWARF information.
|
||||
* Returns true if the {@link Program program} probably has DWARF information.
|
||||
* <p>
|
||||
* If the program is an Elf binary, it must have (at least) ".debug_info" and ".debug_abbr" program sections.
|
||||
* <p>
|
||||
@ -67,17 +63,17 @@ public class DWARFProgram implements Closeable {
|
||||
* original binary file on the native filesystem. (ie. outside of Ghidra). See the DSymSectionProvider
|
||||
* for more info.
|
||||
* <p>
|
||||
* @param program
|
||||
* @param monitor
|
||||
* @return
|
||||
* @param program {@link Program} to test
|
||||
* @return boolean true if program has DWARF info, false if not
|
||||
*/
|
||||
public static boolean isDWARF(Program program, TaskMonitor monitor) {
|
||||
public static boolean isDWARF(Program program) {
|
||||
String format = program.getExecutableFormat();
|
||||
|
||||
if (ElfLoader.ELF_NAME.equals(format)) {
|
||||
if (ElfLoader.ELF_NAME.equals(format) &&
|
||||
DWARFSectionProviderFactory.createSectionProviderFor(program) != null) {
|
||||
return true;
|
||||
}
|
||||
else if (MachoLoader.MACH_O_NAME.equals(format) &&
|
||||
if (MachoLoader.MACH_O_NAME.equals(format) &&
|
||||
DSymSectionProvider.getDSYMForProgram(program) != null) {
|
||||
return true;
|
||||
}
|
||||
|
@ -15,14 +15,14 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.dwarf4.next.sectionprovider;
|
||||
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* Auto-detects which {@link DWARFSectionProvider} matches a Ghidra program.
|
||||
*/
|
||||
@ -44,7 +44,6 @@ public class DWARFSectionProviderFactory {
|
||||
static {
|
||||
sectionProviderFactoryFuncs.add(BaseSectionProvider::createSectionProviderFor);
|
||||
sectionProviderFactoryFuncs.add(DSymSectionProvider::createSectionProviderFor);
|
||||
sectionProviderFactoryFuncs.add(ElfSectionProvider::createSectionProviderFor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,95 +0,0 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.dwarf4.next.sectionprovider;
|
||||
|
||||
import ghidra.app.util.bin.*;
|
||||
import ghidra.app.util.bin.format.elf.*;
|
||||
import ghidra.app.util.opinion.ElfLoader;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import generic.continues.RethrowContinuesFactory;
|
||||
|
||||
/**
|
||||
* Fetches DWARF section data from ELF files, directly, without going through
|
||||
* the Ghidra memory block api. This section provider usually isn't needed as
|
||||
* ELF sections are normally provided as Ghidra memory blocks. In case of extra-
|
||||
* large binaries, Ghidra may not be able to map the debug sections into memory
|
||||
* and this section provider will allow the DWARF analyzer to still function.
|
||||
*/
|
||||
public class ElfSectionProvider implements DWARFSectionProvider {
|
||||
|
||||
private ElfHeader header;
|
||||
private RandomAccessByteProvider provider;
|
||||
|
||||
public static ElfSectionProvider createSectionProviderFor(Program program) {
|
||||
if (ElfLoader.ELF_NAME.equals(program.getExecutableFormat())) {
|
||||
try {
|
||||
File exePath = new File(program.getExecutablePath());
|
||||
return new ElfSectionProvider(exePath);
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ElfSectionProvider(File exeFile) throws IOException {
|
||||
provider = new RandomAccessByteProvider(exeFile);
|
||||
try {
|
||||
// Parse the ELF header to get the sections
|
||||
header = ElfHeader.createElfHeader(RethrowContinuesFactory.INSTANCE, provider);
|
||||
header.parse();
|
||||
}
|
||||
catch (ElfException e) {
|
||||
provider.close();
|
||||
throw new IOException("Error parsing ELF", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteProvider getSectionAsByteProvider(String sectionName) throws IOException {
|
||||
|
||||
ElfSectionHeader section = header.getSection("." + sectionName);
|
||||
|
||||
return (section != null) ? new ByteProviderWrapper(section.getReader().getByteProvider(),
|
||||
section.getOffset(), section.getSize()) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
provider.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSection(String... sectionNames) {
|
||||
for (String sectionName : sectionNames) {
|
||||
if (header.getSection("." + sectionName) == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -103,11 +103,24 @@ public class ElfSymbol implements ByteArrayConverter {
|
||||
|
||||
private String nameAsString;
|
||||
|
||||
/**
|
||||
* create an ElfSymbol()
|
||||
* Warning! the routine initSymbolName() must be called on the symbol later
|
||||
* to initialize the string name. This is a performance enhancement.
|
||||
*
|
||||
* @param reader to read symbol from
|
||||
* @param symbolIndex index of the symbol to read
|
||||
* @param symbolTable symbol table to associate the symbol to
|
||||
* @param stringTable string table to read symbols from
|
||||
* @param header else header
|
||||
* @return newly created ElfSymbol
|
||||
*
|
||||
* @throws IOException if an issue with reading occurs
|
||||
*/
|
||||
public static ElfSymbol createElfSymbol(FactoryBundledWithBinaryReader reader, int symbolIndex,
|
||||
ElfSymbolTable symbolTable, ElfStringTable stringTable, ElfHeader header)
|
||||
throws IOException {
|
||||
ElfSymbolTable symbolTable, ElfHeader header) throws IOException {
|
||||
ElfSymbol elfSymbol = (ElfSymbol) reader.getFactory().create(ElfSymbol.class);
|
||||
elfSymbol.initElfSymbol(reader, symbolIndex, symbolTable, stringTable, header);
|
||||
elfSymbol.initElfSymbol(reader, symbolIndex, symbolTable, header);
|
||||
return elfSymbol;
|
||||
}
|
||||
|
||||
@ -117,49 +130,6 @@ public class ElfSymbol implements ByteArrayConverter {
|
||||
public ElfSymbol() {
|
||||
}
|
||||
|
||||
private void initElfSymbol(FactoryBundledWithBinaryReader reader, int symbolIndex,
|
||||
ElfSymbolTable symbolTable, ElfStringTable stringTable, ElfHeader header)
|
||||
throws IOException {
|
||||
this.header = header;
|
||||
this.symbolTable = symbolTable;
|
||||
this.symbolTableIndex = symbolIndex;
|
||||
|
||||
if (header.is32Bit()) {
|
||||
st_name = reader.readNextInt();
|
||||
st_value = reader.readNextInt() & Conv.INT_MASK;
|
||||
st_size = reader.readNextInt() & Conv.INT_MASK;
|
||||
st_info = reader.readNextByte();
|
||||
st_other = reader.readNextByte();
|
||||
st_shndx = reader.readNextShort();
|
||||
}
|
||||
else {
|
||||
st_name = reader.readNextInt();
|
||||
st_info = reader.readNextByte();
|
||||
st_other = reader.readNextByte();
|
||||
st_shndx = reader.readNextShort();
|
||||
st_value = reader.readNextLong();
|
||||
st_size = reader.readNextLong();
|
||||
}
|
||||
|
||||
if (st_name == 0) {
|
||||
if (getType() == STT_SECTION) {
|
||||
ElfSectionHeader[] sections = header.getSections();
|
||||
if (st_shndx < 0 || st_shndx >= sections.length) {
|
||||
//invalid section reference...
|
||||
//this is a bug in objcopy, whereby sections are removed
|
||||
//but the corresponding section symbols are left behind.
|
||||
}
|
||||
else {
|
||||
ElfSectionHeader section = sections[st_shndx];
|
||||
nameAsString = section.getNameAsString();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
nameAsString = stringTable.readString(reader, st_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new section symbol.
|
||||
* @param header the corresponding ELF header
|
||||
@ -209,6 +179,69 @@ public class ElfSymbol implements ByteArrayConverter {
|
||||
this.symbolTableIndex = symbolIndex;
|
||||
}
|
||||
|
||||
private void initElfSymbol(FactoryBundledWithBinaryReader reader, int symbolIndex,
|
||||
ElfSymbolTable symbolTable, ElfHeader header) throws IOException {
|
||||
this.header = header;
|
||||
this.symbolTable = symbolTable;
|
||||
this.symbolTableIndex = symbolIndex;
|
||||
|
||||
if (header.is32Bit()) {
|
||||
st_name = reader.readNextInt();
|
||||
st_value = reader.readNextInt() & Conv.INT_MASK;
|
||||
st_size = reader.readNextInt() & Conv.INT_MASK;
|
||||
st_info = reader.readNextByte();
|
||||
st_other = reader.readNextByte();
|
||||
st_shndx = reader.readNextShort();
|
||||
}
|
||||
else {
|
||||
st_name = reader.readNextInt();
|
||||
st_info = reader.readNextByte();
|
||||
st_other = reader.readNextByte();
|
||||
st_shndx = reader.readNextShort();
|
||||
st_value = reader.readNextLong();
|
||||
st_size = reader.readNextLong();
|
||||
}
|
||||
|
||||
if (st_name == 0) {
|
||||
if (getType() == STT_SECTION) {
|
||||
ElfSectionHeader[] sections = header.getSections();
|
||||
if (st_shndx < 0 || st_shndx >= sections.length) {
|
||||
//invalid section reference...
|
||||
//this is a bug in objcopy, whereby sections are removed
|
||||
//but the corresponding section symbols are left behind.
|
||||
}
|
||||
else {
|
||||
ElfSectionHeader section = sections[st_shndx];
|
||||
nameAsString = section.getNameAsString();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// The string name will be initialized later
|
||||
// in a call to initSymbolName()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the string name of the symbol.
|
||||
*
|
||||
* NOTE: This routine MUST be called for each
|
||||
* ELFSymbol after the elf symbols have been created.
|
||||
*
|
||||
* This is done separately from the initial symbol entry read because
|
||||
* the string names are in a separate location. If they are read
|
||||
* at the same time the reading buffer will jump around and significantly
|
||||
* degrade reading performance.
|
||||
*
|
||||
* @param reader to read from
|
||||
* @param stringTable stringTable to initialize symbol name
|
||||
*/
|
||||
public void initSymbolName(FactoryBundledWithBinaryReader reader, ElfStringTable stringTable) {
|
||||
if (nameAsString == null) {
|
||||
nameAsString = stringTable.readString(reader, st_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the symbol table containing this symbol
|
||||
* @return symbol table
|
||||
@ -249,27 +282,37 @@ public class ElfSymbol implements ByteArrayConverter {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
if (this == obj) {
|
||||
return true;
|
||||
if (obj == null)
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ElfSymbol other = (ElfSymbol) obj;
|
||||
if (st_info != other.st_info)
|
||||
if (st_info != other.st_info) {
|
||||
return false;
|
||||
if (st_name != other.st_name)
|
||||
}
|
||||
if (st_name != other.st_name) {
|
||||
return false;
|
||||
if (st_other != other.st_other)
|
||||
}
|
||||
if (st_other != other.st_other) {
|
||||
return false;
|
||||
if (st_shndx != other.st_shndx)
|
||||
}
|
||||
if (st_shndx != other.st_shndx) {
|
||||
return false;
|
||||
if (st_size != other.st_size)
|
||||
}
|
||||
if (st_size != other.st_size) {
|
||||
return false;
|
||||
if (st_value != other.st_value)
|
||||
}
|
||||
if (st_value != other.st_value) {
|
||||
return false;
|
||||
if (symbolTableIndex != other.symbolTableIndex)
|
||||
}
|
||||
if (symbolTableIndex != other.symbolTableIndex) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ package ghidra.app.util.bin.format.elf;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ghidra.app.util.bin.ByteArrayConverter;
|
||||
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
||||
@ -96,15 +97,26 @@ public class ElfSymbolTable implements ElfFileSection, ByteArrayConverter {
|
||||
|
||||
long entryPos = reader.getPointerIndex();
|
||||
|
||||
// load the all the symbol entries first, don't initialize the string name
|
||||
// that will be done later to help localize memory access
|
||||
for (int i = 0; i < symbolCount; i++) {
|
||||
// Reposition reader to start of symbol element since ElfSymbol object
|
||||
// may not consume all symbol element data
|
||||
reader.setPointerIndex(entryPos);
|
||||
ElfSymbol sym = ElfSymbol.createElfSymbol(reader, i, this, stringTable, header);
|
||||
ElfSymbol sym = ElfSymbol.createElfSymbol(reader, i, this, header);
|
||||
symbolList.add(sym);
|
||||
entryPos += entrySize;
|
||||
}
|
||||
|
||||
// sort the entries by the index in the string table, so don't jump around reading
|
||||
List<ElfSymbol> sortedList = symbolList.stream().sorted(
|
||||
(o1, o2) -> Integer.compare(o1.getName(), o2.getName())).collect(Collectors.toList());
|
||||
|
||||
// initialize the Symbol string names from string table
|
||||
for (ElfSymbol sym : sortedList) {
|
||||
sym.initSymbolName(reader, stringTable);
|
||||
}
|
||||
|
||||
reader.setPointerIndex(ptr);
|
||||
|
||||
symbols = new ElfSymbol[symbolList.size()];
|
||||
@ -163,9 +175,9 @@ public class ElfSymbolTable implements ElfFileSection, ByteArrayConverter {
|
||||
* @return the symbol at the specified address
|
||||
*/
|
||||
public ElfSymbol getSymbolAt(long addr) {
|
||||
for (int i = 0; i < symbols.length; i++) {
|
||||
if (symbols[i].getValue() == addr) {
|
||||
return symbols[i];
|
||||
for (ElfSymbol symbol : symbols) {
|
||||
if (symbol.getValue() == addr) {
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@ -177,9 +189,9 @@ public class ElfSymbolTable implements ElfFileSection, ByteArrayConverter {
|
||||
*/
|
||||
public ElfSymbol[] getGlobalSymbols() {
|
||||
List<ElfSymbol> list = new ArrayList<>();
|
||||
for (int i = 0; i < symbols.length; i++) {
|
||||
if (symbols[i].getBind() == ElfSymbol.STB_GLOBAL) {
|
||||
list.add(symbols[i]);
|
||||
for (ElfSymbol symbol : symbols) {
|
||||
if (symbol.getBind() == ElfSymbol.STB_GLOBAL) {
|
||||
list.add(symbol);
|
||||
}
|
||||
}
|
||||
ElfSymbol[] array = new ElfSymbol[list.size()];
|
||||
@ -193,11 +205,11 @@ public class ElfSymbolTable implements ElfFileSection, ByteArrayConverter {
|
||||
*/
|
||||
public String[] getSourceFiles() {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (int j = 0; j < symbols.length; j++) {
|
||||
if (symbols[j].getType() == ElfSymbol.STT_FILE) {
|
||||
String name = symbols[j].getNameAsString();
|
||||
for (ElfSymbol symbol : symbols) {
|
||||
if (symbol.getType() == ElfSymbol.STT_FILE) {
|
||||
String name = symbol.getNameAsString();
|
||||
if (name != null) {
|
||||
list.add(symbols[j].getNameAsString());
|
||||
list.add(symbol.getNameAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,8 +80,10 @@ public class BuildVersionCommand extends LoadCommand {
|
||||
struct.add(DWORD, "minos", null);
|
||||
struct.add(DWORD, "sdk", null);
|
||||
struct.add(DWORD, "ntools", null);
|
||||
if (ntools > 0) {
|
||||
struct.add(new ArrayDataType(buildToolVersionDataType, ntools,
|
||||
buildToolVersionDataType.getLength()), "build_tool_version[]", null);
|
||||
}
|
||||
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
|
||||
return struct;
|
||||
}
|
||||
|
@ -136,13 +136,13 @@ public class DelayImportDataDirectory extends DataDirectory {
|
||||
createSymbol(program, tmpAddr, SymbolUtilities.getAddressAppendedName(
|
||||
DelayImportDescriptor.NAME + "_IAT", tmpAddr));
|
||||
markupThunk(program, isBinary, space, descriptor, descriptor.getAddressOfIAT(),
|
||||
descriptor.getThunksIAT(), monitor, log);
|
||||
descriptor.getThunksIAT(), true, monitor, log);
|
||||
|
||||
tmpAddr = addr(space, isBinary, descriptor, descriptor.getAddressOfINT());
|
||||
createSymbol(program, tmpAddr, SymbolUtilities.getAddressAppendedName(
|
||||
DelayImportDescriptor.NAME + "_INT", tmpAddr));
|
||||
markupThunk(program, isBinary, space, descriptor, descriptor.getAddressOfINT(),
|
||||
descriptor.getThunksINT(), monitor, log);
|
||||
descriptor.getThunksINT(), false, monitor, log);
|
||||
|
||||
// This table is optional
|
||||
if (descriptor.getAddressOfBoundIAT() != 0) {
|
||||
@ -150,7 +150,7 @@ public class DelayImportDataDirectory extends DataDirectory {
|
||||
createSymbol(program, tmpAddr, SymbolUtilities.getAddressAppendedName(
|
||||
DelayImportDescriptor.NAME + "_Bound_IAT", tmpAddr));
|
||||
markupThunk(program, isBinary, space, descriptor, descriptor.getAddressOfBoundIAT(),
|
||||
descriptor.getThunksBoundIAT(), monitor, log);
|
||||
descriptor.getThunksBoundIAT(), false, monitor, log);
|
||||
}
|
||||
|
||||
// This table is optional
|
||||
@ -159,8 +159,8 @@ public class DelayImportDataDirectory extends DataDirectory {
|
||||
createSymbol(program, tmpAddr, SymbolUtilities.getAddressAppendedName(
|
||||
DelayImportDescriptor.NAME + "_Unload_IAT", tmpAddr));
|
||||
markupThunk(program, isBinary, space, descriptor,
|
||||
descriptor.getAddressOfOriginalIAT(), descriptor.getThunksUnloadIAT(), monitor,
|
||||
log);
|
||||
descriptor.getAddressOfOriginalIAT(), descriptor.getThunksUnloadIAT(), false,
|
||||
monitor, log);
|
||||
}
|
||||
|
||||
|
||||
@ -224,9 +224,11 @@ public class DelayImportDataDirectory extends DataDirectory {
|
||||
DelayImportDescriptor descriptor,
|
||||
long ptr,
|
||||
List<ThunkData> thunks,
|
||||
boolean isIAT,
|
||||
TaskMonitor monitor,
|
||||
MessageLog log) {
|
||||
|
||||
boolean is64bit = ntHeader.getOptionalHeader().is64bit();
|
||||
long thunkPtr = va(ptr, isBinary);
|
||||
if (!descriptor.isUsingRVA()) {
|
||||
thunkPtr -= ntHeader.getOptionalHeader().getImageBase();
|
||||
@ -237,12 +239,14 @@ public class DelayImportDataDirectory extends DataDirectory {
|
||||
return;
|
||||
}
|
||||
DataType dt;
|
||||
if (thunk.getAddressOfData() == 0) {
|
||||
dt = ntHeader.getOptionalHeader().is64bit() ? QWORD : DWORD;
|
||||
if (thunk.isOrdinal() || thunk.getAddressOfData() == 0) {
|
||||
dt = is64bit ? QWORD : DWORD;
|
||||
}
|
||||
else if (isIAT) {
|
||||
dt = is64bit ? Pointer64DataType.dataType : Pointer32DataType.dataType;
|
||||
}
|
||||
else {
|
||||
dt = ntHeader.getOptionalHeader().is64bit() ? Pointer64DataType.dataType
|
||||
: Pointer32DataType.dataType;
|
||||
dt = is64bit ? IBO64 : IBO32;
|
||||
}
|
||||
|
||||
Address thunkAddress = space.getAddress(thunkPtr);
|
||||
|
@ -22,6 +22,7 @@ import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
||||
import ghidra.app.util.bin.format.pe.ImageRuntimeFunctionEntries._IMAGE_RUNTIME_FUNCTION_ENTRY;
|
||||
import ghidra.app.util.bin.format.pe.debug.DebugCOFFSymbol;
|
||||
import ghidra.app.util.bin.format.pe.debug.DebugCOFFSymbolAux;
|
||||
import ghidra.program.model.data.*;
|
||||
@ -110,7 +111,7 @@ public class FileHeader implements StructConverter {
|
||||
*/
|
||||
public final static int IMAGE_FILE_DLL = 0x2000;
|
||||
/**
|
||||
* File should only be run on a UP machine
|
||||
* File should only be run on a UP machine.
|
||||
*/
|
||||
public final static int IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000;
|
||||
/**
|
||||
@ -118,23 +119,24 @@ public class FileHeader implements StructConverter {
|
||||
*/
|
||||
public final static int IMAGE_FILE_BYTES_REVERSED_HI = 0x8000;
|
||||
|
||||
public final static String [] CHARACTERISTICS = {
|
||||
"Relocation info stripped from file",
|
||||
/**
|
||||
* Magic value in LordPE's Symbol Table pointer field.
|
||||
*/
|
||||
private final static int LORDPE_SYMBOL_TABLE = 0x726F4C5B;
|
||||
/**
|
||||
* Magic value in LordPE's Number of Symbols field.
|
||||
*/
|
||||
private final static int LORDPE_NUMBER_OF_SYMBOLS = 0x5D455064;
|
||||
|
||||
public final static String[] CHARACTERISTICS = { "Relocation info stripped from file",
|
||||
"File is executable (i.e. no unresolved externel references)",
|
||||
"Line nunbers stripped from file",
|
||||
"Local symbols stripped from file",
|
||||
"Agressively trim working set",
|
||||
"App can handle >2gb addresses",
|
||||
"Bytes of machine word are reversed",
|
||||
"32 bit word machine",
|
||||
"Line nunbers stripped from file", "Local symbols stripped from file",
|
||||
"Agressively trim working set", "App can handle >2gb addresses",
|
||||
"Bytes of machine word are reversed", "32 bit word machine",
|
||||
"Debugging info stripped from file in .DBG file",
|
||||
"If Image is on removable media, copy and run from the swap file",
|
||||
"If Image is on Net, copy and run from the swap file",
|
||||
"System file",
|
||||
"File is a DLL",
|
||||
"File should only be run on a UP machine",
|
||||
"Bytes of machine word are reversed"
|
||||
};
|
||||
"If Image is on Net, copy and run from the swap file", "System file", "File is a DLL",
|
||||
"File should only be run on a UP machine", "Bytes of machine word are reversed" };
|
||||
|
||||
private short machine;
|
||||
private short numberOfSections;
|
||||
@ -144,15 +146,15 @@ public class FileHeader implements StructConverter {
|
||||
private short sizeOfOptionalHeader; // delta between start of OptionalHeader and start of section table
|
||||
private short characteristics;
|
||||
|
||||
private SectionHeader [] sectionHeaders;
|
||||
private List<DebugCOFFSymbol>symbols = new ArrayList<>();
|
||||
private SectionHeader[] sectionHeaders;
|
||||
private List<DebugCOFFSymbol> symbols = new ArrayList<>();
|
||||
private List<_IMAGE_RUNTIME_FUNCTION_ENTRY> irfes = new ArrayList<>();
|
||||
|
||||
private FactoryBundledWithBinaryReader reader;
|
||||
private int startIndex;
|
||||
private NTHeader ntHeader;
|
||||
|
||||
static FileHeader createFileHeader(
|
||||
FactoryBundledWithBinaryReader reader, int startIndex,
|
||||
static FileHeader createFileHeader(FactoryBundledWithBinaryReader reader, int startIndex,
|
||||
NTHeader ntHeader) throws IOException {
|
||||
FileHeader fileHeader = (FileHeader) reader.getFactory().create(FileHeader.class);
|
||||
fileHeader.initFileHeader(reader, startIndex, ntHeader);
|
||||
@ -162,9 +164,11 @@ public class FileHeader implements StructConverter {
|
||||
/**
|
||||
* DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD.
|
||||
*/
|
||||
public FileHeader() {}
|
||||
public FileHeader() {
|
||||
}
|
||||
|
||||
private void initFileHeader(FactoryBundledWithBinaryReader reader, int startIndex, NTHeader ntHeader) throws IOException {
|
||||
private void initFileHeader(FactoryBundledWithBinaryReader reader, int startIndex,
|
||||
NTHeader ntHeader) throws IOException {
|
||||
this.reader = reader;
|
||||
this.startIndex = startIndex;
|
||||
this.ntHeader = ntHeader;
|
||||
@ -201,7 +205,7 @@ public class FileHeader implements StructConverter {
|
||||
* Returns the array of section headers.
|
||||
* @return the array of section headers
|
||||
*/
|
||||
public SectionHeader [] getSectionHeaders() {
|
||||
public SectionHeader[] getSectionHeaders() {
|
||||
if (sectionHeaders == null) {
|
||||
return new SectionHeader[0];
|
||||
}
|
||||
@ -216,6 +220,10 @@ public class FileHeader implements StructConverter {
|
||||
return symbols;
|
||||
}
|
||||
|
||||
public List<_IMAGE_RUNTIME_FUNCTION_ENTRY> getImageRuntimeFunctionEntries() {
|
||||
return irfes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the section header that contains the specified virtual address.
|
||||
* @param virtualAddr the virtual address
|
||||
@ -224,7 +232,7 @@ public class FileHeader implements StructConverter {
|
||||
public SectionHeader getSectionHeaderContaining(int virtualAddr) {
|
||||
for (SectionHeader sectionHeader : sectionHeaders) {
|
||||
int start = sectionHeader.getVirtualAddress();
|
||||
int end = sectionHeader.getVirtualAddress()+sectionHeader.getVirtualSize()-1;
|
||||
int end = sectionHeader.getVirtualAddress() + sectionHeader.getVirtualSize() - 1;
|
||||
if (virtualAddr >= start && virtualAddr <= end) {
|
||||
return sectionHeader;
|
||||
}
|
||||
@ -291,8 +299,8 @@ public class FileHeader implements StructConverter {
|
||||
public int getPointerToSections() {
|
||||
short sizeOptHdr = ntHeader.getFileHeader().sizeOfOptionalHeader;
|
||||
int ptrToSections = startIndex + IMAGE_SIZEOF_FILE_HEADER + sizeOptHdr;
|
||||
int testSize = ntHeader.getOptionalHeader().is64bit()
|
||||
? Constants.IMAGE_SIZEOF_NT_OPTIONAL64_HEADER
|
||||
int testSize =
|
||||
ntHeader.getOptionalHeader().is64bit() ? Constants.IMAGE_SIZEOF_NT_OPTIONAL64_HEADER
|
||||
: Constants.IMAGE_SIZEOF_NT_OPTIONAL32_HEADER;
|
||||
if (sizeOptHdr != testSize) {
|
||||
Msg.warn(this, "Non-standard optional header size: " + sizeOptHdr + " bytes");
|
||||
@ -305,10 +313,12 @@ public class FileHeader implements StructConverter {
|
||||
|
||||
int tmpIndex = getPointerToSections();
|
||||
if (numberOfSections < 0) {
|
||||
Msg.error(this, "Number of sections = "+numberOfSections);
|
||||
} else if (optHeader.getFileAlignment() == 0) {
|
||||
Msg.error(this, "Number of sections = " + numberOfSections);
|
||||
}
|
||||
else if (optHeader.getFileAlignment() == 0) {
|
||||
Msg.error(this, "File alignment == 0: section processing skipped");
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
sectionHeaders = new SectionHeader[numberOfSections];
|
||||
for (int i = 0; i < numberOfSections; ++i) {
|
||||
sectionHeaders[i] = SectionHeader.createSectionHeader(reader, tmpIndex);
|
||||
@ -330,8 +340,8 @@ public class FileHeader implements StructConverter {
|
||||
optHeader.getSectionAlignment());
|
||||
if (virtualAddress == alignedVirtualAddress) {
|
||||
if (sizeOfRawData > virtualSize) {
|
||||
sectionHeaders[i].setVirtualSize(
|
||||
Math.min(sizeOfRawData, alignedVirtualSize));
|
||||
sectionHeaders[i]
|
||||
.setVirtualSize(Math.min(sizeOfRawData, alignedVirtualSize));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -344,6 +354,36 @@ public class FileHeader implements StructConverter {
|
||||
reader.setPointerIndex(oldIndex);
|
||||
}
|
||||
|
||||
void processImageRuntimeFunctionEntries() throws IOException {
|
||||
FileHeader fh = ntHeader.getFileHeader();
|
||||
SectionHeader[] sections = fh.getSectionHeaders();
|
||||
|
||||
// Look for an exception handler section for an array of
|
||||
// RUNTIME_FUNCTION structures, bail if one isn't found
|
||||
SectionHeader irfeHeader = null;
|
||||
for (SectionHeader header : sections) {
|
||||
if (header.getName().equals(".pdata")) {
|
||||
irfeHeader = header;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (irfeHeader == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
long oldIndex = reader.getPointerIndex();
|
||||
|
||||
int start = irfeHeader.getPointerToRawData();
|
||||
reader.setPointerIndex(start);
|
||||
|
||||
ImageRuntimeFunctionEntries entries =
|
||||
ImageRuntimeFunctionEntries.createImageRuntimeFunctionEntries(reader, start, ntHeader);
|
||||
irfes = entries.getRuntimeFunctionEntries();
|
||||
|
||||
reader.setPointerIndex(oldIndex);
|
||||
}
|
||||
|
||||
void processSymbols() throws IOException {
|
||||
if (isLordPE()) {
|
||||
return;
|
||||
@ -353,12 +393,12 @@ public class FileHeader implements StructConverter {
|
||||
|
||||
int tmpIndex = getPointerToSymbolTable();
|
||||
if (!ntHeader.checkRVA(tmpIndex)) {
|
||||
Msg.error(this, "Invalid file index "+Integer.toHexString(tmpIndex));
|
||||
Msg.error(this, "Invalid file index " + Integer.toHexString(tmpIndex));
|
||||
return;
|
||||
}
|
||||
|
||||
if ( numberOfSymbols < 0 || numberOfSymbols > reader.length()) {
|
||||
Msg.error(this, "Invalid symbol count "+Integer.toHexString(numberOfSymbols));
|
||||
if (numberOfSymbols < 0 || numberOfSymbols > reader.length()) {
|
||||
Msg.error(this, "Invalid symbol count " + Integer.toHexString(numberOfSymbols));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -366,27 +406,30 @@ public class FileHeader implements StructConverter {
|
||||
|
||||
for (int i = 0; i < numberOfSymbols; ++i) {
|
||||
if (!ntHeader.checkRVA(tmpIndex)) {
|
||||
Msg.error(this, "Invalid file index "+Integer.toHexString(tmpIndex));
|
||||
Msg.error(this, "Invalid file index " + Integer.toHexString(tmpIndex));
|
||||
break;
|
||||
}
|
||||
|
||||
DebugCOFFSymbol symbol = DebugCOFFSymbol.createDebugCOFFSymbol(reader, tmpIndex, stringTableIndex);
|
||||
DebugCOFFSymbol symbol =
|
||||
DebugCOFFSymbol.createDebugCOFFSymbol(reader, tmpIndex, stringTableIndex);
|
||||
|
||||
tmpIndex += DebugCOFFSymbol.IMAGE_SIZEOF_SYMBOL;
|
||||
|
||||
tmpIndex += (DebugCOFFSymbolAux.IMAGE_SIZEOF_AUX_SYMBOL * symbol.getNumberOfAuxSymbols());
|
||||
tmpIndex +=
|
||||
(DebugCOFFSymbolAux.IMAGE_SIZEOF_AUX_SYMBOL * symbol.getNumberOfAuxSymbols());
|
||||
|
||||
int numberOfAuxSymbols = symbol.getNumberOfAuxSymbols();
|
||||
i += numberOfAuxSymbols > 0 ? numberOfAuxSymbols : 0;
|
||||
|
||||
symbols.add( symbol );
|
||||
symbols.add(symbol);
|
||||
}
|
||||
|
||||
reader.setPointerIndex(oldIndex);
|
||||
}
|
||||
|
||||
public boolean isLordPE() {
|
||||
if (getPointerToSymbolTable() == 0x726F4C5B && getNumberOfSymbols() == 0x5D455064) {
|
||||
if (getPointerToSymbolTable() == LORDPE_SYMBOL_TABLE &&
|
||||
getNumberOfSymbols() == LORDPE_NUMBER_OF_SYMBOLS) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -397,9 +440,9 @@ public class FileHeader implements StructConverter {
|
||||
|
||||
machine = reader.readNextShort();
|
||||
numberOfSections = reader.readNextShort();
|
||||
timeDateStamp = reader.readNextInt ();
|
||||
pointerToSymbolTable = reader.readNextInt ();
|
||||
numberOfSymbols = reader.readNextInt ();
|
||||
timeDateStamp = reader.readNextInt();
|
||||
pointerToSymbolTable = reader.readNextInt();
|
||||
numberOfSymbols = reader.readNextInt();
|
||||
sizeOfOptionalHeader = reader.readNextShort();
|
||||
characteristics = reader.readNextShort();
|
||||
}
|
||||
@ -411,22 +454,22 @@ public class FileHeader implements StructConverter {
|
||||
public DataType toDataType() throws DuplicateNameException {
|
||||
StructureDataType struct = new StructureDataType(NAME, 0);
|
||||
|
||||
struct.add(WORD,2,"Machine",getMachineName());
|
||||
struct.add(WORD,2,"NumberOfSections",null);
|
||||
struct.add(DWORD,4,"TimeDateStamp",null);
|
||||
struct.add(DWORD,4,"PointerToSymbolTable",null);
|
||||
struct.add(DWORD,4,"NumberOfSymbols",null);
|
||||
struct.add(WORD,2,"SizeOfOptionalHeader",null);
|
||||
struct.add(WORD,2,"Characteristics",null);
|
||||
struct.add(WORD, 2, "Machine", getMachineName());
|
||||
struct.add(WORD, 2, "NumberOfSections", null);
|
||||
struct.add(DWORD, 4, "TimeDateStamp", null);
|
||||
struct.add(DWORD, 4, "PointerToSymbolTable", null);
|
||||
struct.add(DWORD, 4, "NumberOfSymbols", null);
|
||||
struct.add(WORD, 2, "SizeOfOptionalHeader", null);
|
||||
struct.add(WORD, 2, "Characteristics", null);
|
||||
|
||||
struct.setCategoryPath(new CategoryPath("/PE"));
|
||||
|
||||
return struct;
|
||||
}
|
||||
|
||||
private void setSectionHeaders(SectionHeader [] sectionHeaders) {
|
||||
private void setSectionHeaders(SectionHeader[] sectionHeaders) {
|
||||
this.sectionHeaders = sectionHeaders;
|
||||
numberOfSections = (short)sectionHeaders.length;
|
||||
numberOfSections = (short) sectionHeaders.length;
|
||||
}
|
||||
|
||||
void writeHeader(RandomAccessFile raf, DataConverter dc) throws IOException {
|
||||
@ -449,61 +492,61 @@ public class FileHeader implements StructConverter {
|
||||
* @throws RuntimeException if the memory block is uninitialized
|
||||
*/
|
||||
public void addSection(MemoryBlock block, OptionalHeader optionalHeader) {
|
||||
DataDirectory [] directories = optionalHeader.getDataDirectories();
|
||||
|
||||
DataDirectory [] dataDirectories = optionalHeader.getDataDirectories();
|
||||
DataDirectory[] directories = optionalHeader.getDataDirectories();
|
||||
|
||||
DataDirectory[] dataDirectories = optionalHeader.getDataDirectories();
|
||||
|
||||
SecurityDataDirectory sdd = null;
|
||||
if (dataDirectories.length > OptionalHeader.IMAGE_DIRECTORY_ENTRY_SECURITY) {
|
||||
sdd = (SecurityDataDirectory)dataDirectories[OptionalHeader.IMAGE_DIRECTORY_ENTRY_SECURITY];
|
||||
sdd =
|
||||
(SecurityDataDirectory) dataDirectories[OptionalHeader.IMAGE_DIRECTORY_ENTRY_SECURITY];
|
||||
if (sdd != null && sdd.getSize() > 0) {
|
||||
sdd.updatePointers( PortableExecutable.computeAlignment( (int)block.getSize( ), optionalHeader.getFileAlignment( ) ) );
|
||||
sdd.updatePointers(PortableExecutable.computeAlignment((int) block.getSize(),
|
||||
optionalHeader.getFileAlignment()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int lastPos = computeAlignedNewPosition( optionalHeader, directories );
|
||||
int lastPos = computeAlignedNewPosition(optionalHeader, directories);
|
||||
|
||||
SectionHeader newSection = new SectionHeader(block, optionalHeader, lastPos);
|
||||
|
||||
SectionHeader [] newSectionHeaders = new SectionHeader[sectionHeaders.length + 1];
|
||||
SectionHeader[] newSectionHeaders = new SectionHeader[sectionHeaders.length + 1];
|
||||
System.arraycopy(sectionHeaders, 0, newSectionHeaders, 0, sectionHeaders.length);
|
||||
newSectionHeaders[sectionHeaders.length] = newSection;
|
||||
setSectionHeaders(newSectionHeaders);
|
||||
|
||||
int firstSectionStart = sectionHeaders[0].getPointerToRawData();
|
||||
int lastSectionEnd = sectionHeaders[sectionHeaders.length-1].getPointerToRawData()
|
||||
+sectionHeaders[sectionHeaders.length-1].getSizeOfRawData();
|
||||
int lastSectionEnd = sectionHeaders[sectionHeaders.length - 1].getPointerToRawData() +
|
||||
sectionHeaders[sectionHeaders.length - 1].getSizeOfRawData();
|
||||
|
||||
for (int i = 0 ; i < directories.length ; i++) {
|
||||
if (directories[i] == null ||
|
||||
directories[i].getSize() == 0 ||
|
||||
for (int i = 0; i < directories.length; i++) {
|
||||
if (directories[i] == null || directories[i].getSize() == 0 ||
|
||||
directories[i].isContainedInSection()) {
|
||||
continue;
|
||||
}
|
||||
if (directories[i].getVirtualAddress() < firstSectionStart) {
|
||||
if (i != OptionalHeader.IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT) {
|
||||
throw new RuntimeException("PE - Unexpected directory before sections: "+i);
|
||||
throw new RuntimeException("PE - Unexpected directory before sections: " + i);
|
||||
}
|
||||
}
|
||||
if (directories[i].getVirtualAddress() > lastSectionEnd) {
|
||||
if (i != OptionalHeader.IMAGE_DIRECTORY_ENTRY_SECURITY) {
|
||||
throw new RuntimeException("PE - Unexpected directory after sections: "+i);
|
||||
throw new RuntimeException("PE - Unexpected directory after sections: " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
|
||||
|
||||
if (dataDirectories.length > OptionalHeader.IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT) {
|
||||
BoundImportDataDirectory bidd = (BoundImportDataDirectory)dataDirectories[OptionalHeader.IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT];
|
||||
BoundImportDataDirectory bidd =
|
||||
(BoundImportDataDirectory) dataDirectories[OptionalHeader.IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT];
|
||||
if (bidd != null && bidd.getSize() > 0) {
|
||||
bidd.updatePointers(SectionHeader.IMAGE_SIZEOF_SECTION_HEADER);
|
||||
int endptr = bidd.getVirtualAddress() + bidd.getSize() - 1;
|
||||
if (endptr >= sectionHeaders[0].getPointerToRawData()) {
|
||||
int alignedPtr = PortableExecutable.computeAlignment(endptr, optionalHeader.getFileAlignment());
|
||||
int alignedPtr = PortableExecutable.computeAlignment(endptr,
|
||||
optionalHeader.getFileAlignment());
|
||||
offset = alignedPtr - sectionHeaders[0].getPointerToRawData();
|
||||
for (SectionHeader sectionHeader : sectionHeaders) {
|
||||
sectionHeader.updatePointers(offset);
|
||||
@ -514,9 +557,9 @@ public class FileHeader implements StructConverter {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (dataDirectories.length > OptionalHeader.IMAGE_DIRECTORY_ENTRY_DEBUG) {
|
||||
DebugDataDirectory ddd = (DebugDataDirectory)dataDirectories[OptionalHeader.IMAGE_DIRECTORY_ENTRY_DEBUG];
|
||||
DebugDataDirectory ddd =
|
||||
(DebugDataDirectory) dataDirectories[OptionalHeader.IMAGE_DIRECTORY_ENTRY_DEBUG];
|
||||
if (ddd != null && ddd.getSize() > 0) {
|
||||
if (ddd.getVirtualAddress() > newSection.getVirtualAddress()) {
|
||||
if (sdd != null && sdd.getSize() > 0) {
|
||||
@ -530,12 +573,12 @@ public class FileHeader implements StructConverter {
|
||||
}
|
||||
|
||||
if (block.isExecute()) {
|
||||
optionalHeader.setSizeOfCode(optionalHeader.getSizeOfCode() +
|
||||
newSection.getSizeOfRawData());
|
||||
optionalHeader
|
||||
.setSizeOfCode(optionalHeader.getSizeOfCode() + newSection.getSizeOfRawData());
|
||||
}
|
||||
else {
|
||||
optionalHeader.setSizeOfInitializedData(optionalHeader.getSizeOfInitializedData() +
|
||||
newSection.getSizeOfRawData());
|
||||
optionalHeader.setSizeOfInitializedData(
|
||||
optionalHeader.getSizeOfInitializedData() + newSection.getSizeOfRawData());
|
||||
}
|
||||
|
||||
int soi = newSection.getVirtualAddress() + newSection.getSizeOfRawData();
|
||||
@ -543,7 +586,8 @@ public class FileHeader implements StructConverter {
|
||||
optionalHeader.setSizeOfImage(soi);
|
||||
}
|
||||
|
||||
private int computeAlignedNewPosition( OptionalHeader optionalHeader, DataDirectory [] directories ) {
|
||||
private int computeAlignedNewPosition(OptionalHeader optionalHeader,
|
||||
DataDirectory[] directories) {
|
||||
int lastPos = 0;
|
||||
for (SectionHeader sectionHeader : sectionHeaders) {
|
||||
if (sectionHeader.getPointerToRawData() + sectionHeader.getSizeOfRawData() > lastPos) {
|
||||
@ -551,14 +595,13 @@ public class FileHeader implements StructConverter {
|
||||
}
|
||||
}
|
||||
for (DataDirectory directorie : directories) {
|
||||
if (directorie == null ||
|
||||
directorie.getSize() == 0) {
|
||||
if (directorie == null || directorie.getSize() == 0) {
|
||||
continue;
|
||||
}
|
||||
if (directorie.rvaToPointer() + directorie.getSize() > lastPos) {
|
||||
lastPos = directorie.rvaToPointer() + directorie.getSize();
|
||||
}
|
||||
}
|
||||
return PortableExecutable.computeAlignment( lastPos, optionalHeader.getFileAlignment( ) );
|
||||
return PortableExecutable.computeAlignment(lastPos, optionalHeader.getFileAlignment());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,486 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.pe;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
* typedef struct _IMAGE_RUNTIME_FUNCTION_ENTRY {
|
||||
* DWORD BeginAddress;
|
||||
* DWORD EndAddress;
|
||||
* union {
|
||||
* DWORD UnwindInfoAddress;
|
||||
* DWORD UnwindData;
|
||||
* } DUMMYUNIONNAME;
|
||||
* } RUNTIME_FUNCTION, *PRUNTIME_FUNCTION, _IMAGE_RUNTIME_FUNCTION_ENTRY, *_PIMAGE_RUNTIME_FUNCTION_ENTRY;
|
||||
*
|
||||
* #define UNW_FLAG_NHANDLER 0x0
|
||||
* #define UNW_FLAG_EHANDLER 0x1
|
||||
* #define UNW_FLAG_UHANDLER 0x2
|
||||
* #define UNW_FLAG_CHAININFO 0x4
|
||||
*
|
||||
* typedef struct _UNWIND_INFO {
|
||||
* UCHAR Version : 3;
|
||||
* UCHAR Flags : 5;
|
||||
* UCHAR SizeOfProlog;
|
||||
* UCHAR CountOfUnwindCodes;
|
||||
* UCHAR FrameRegister : 4;
|
||||
* UCHAR FrameOffset : 4;
|
||||
* UNWIND_CODE UnwindCode[1];
|
||||
*
|
||||
* //
|
||||
* // The unwind codes are followed by an optional DWORD aligned field that
|
||||
* // contains the exception handler address or the address of chained unwind
|
||||
* // information. If an exception handler address is specified, then it is
|
||||
* // followed by the language specified exception handler data.
|
||||
* //
|
||||
* // union {
|
||||
* // ULONG ExceptionHandler;
|
||||
* // ULONG FunctionEntry;
|
||||
* // };
|
||||
* //
|
||||
* // ULONG ExceptionData[];
|
||||
* //
|
||||
* } UNWIND_INFO, *PUNWIND_INFO;
|
||||
*/
|
||||
public class ImageRuntimeFunctionEntries {
|
||||
private final static int UNWIND_INFO_VERSION_BITMASK = 0x07;
|
||||
private final static int UNWIND_INFO_FLAGS_SHIFT = 0x03;
|
||||
private final static int UNWIND_INFO_FRAME_REGISTER_MASK = 0x0F;
|
||||
private final static int UNWIND_INFO_FRAME_OFFSET_SHIFT = 0x04;
|
||||
private final static int UNWIND_INFO_OPCODE_MASK = 0x0F;
|
||||
private final static int UNWIND_INFO_OPCODE_INFO_SHIFT = 0x04;
|
||||
private final static int UNWIND_INFO_SIZE = 0x0C;
|
||||
|
||||
List<_IMAGE_RUNTIME_FUNCTION_ENTRY> functionEntries = new ArrayList<>();
|
||||
|
||||
static ImageRuntimeFunctionEntries createImageRuntimeFunctionEntries(
|
||||
FactoryBundledWithBinaryReader reader, long index, NTHeader ntHeader)
|
||||
throws IOException {
|
||||
ImageRuntimeFunctionEntries imageRuntimeFunctionEntriesSection =
|
||||
(ImageRuntimeFunctionEntries) reader.getFactory()
|
||||
.create(ImageRuntimeFunctionEntries.class);
|
||||
imageRuntimeFunctionEntriesSection.initImageRuntimeFunctionEntries(reader, index, ntHeader);
|
||||
return imageRuntimeFunctionEntriesSection;
|
||||
}
|
||||
|
||||
/**
|
||||
* DO NOT USE THIS CONSTRUCTOR, USE create*(GenericFactory ...) FACTORY METHODS INSTEAD.
|
||||
*/
|
||||
public ImageRuntimeFunctionEntries() {
|
||||
}
|
||||
|
||||
private void initImageRuntimeFunctionEntries(FactoryBundledWithBinaryReader reader, long index,
|
||||
NTHeader ntHeader) throws IOException {
|
||||
|
||||
int entryCount = 0;
|
||||
|
||||
// Find the exception handler data section. This is an unbounded array of
|
||||
// RUNTIME_INFO structures one after another and there's no count field
|
||||
// to tell us how many there are, so get the maximum number there could be
|
||||
// based on the size of the section.
|
||||
FileHeader fh = ntHeader.getFileHeader();
|
||||
for (SectionHeader section : fh.getSectionHeaders()) {
|
||||
if (section.getName().contentEquals(".pdata")) {
|
||||
entryCount = section.getSizeOfRawData() / UNWIND_INFO_SIZE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (entryCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
long origIndex = reader.getPointerIndex();
|
||||
|
||||
reader.setPointerIndex(index);
|
||||
|
||||
for (int i = 0; i < entryCount; i++) {
|
||||
_IMAGE_RUNTIME_FUNCTION_ENTRY entry = new _IMAGE_RUNTIME_FUNCTION_ENTRY();
|
||||
entry.beginAddress = reader.readNextUnsignedInt();
|
||||
entry.endAddress = reader.readNextUnsignedInt();
|
||||
entry.unwindInfoAddressOrData = reader.readNextUnsignedInt();
|
||||
|
||||
// When the size of the section is bigger than the number of structures
|
||||
// the structure data fields will all be null, signaling the end of the
|
||||
// array of structures. Break out here.
|
||||
if (entry.beginAddress == 0 && entry.endAddress == 0 &&
|
||||
entry.unwindInfoAddressOrData == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Read and process the UNWIND_INFO structures the RUNTIME_INFO
|
||||
// structures point to
|
||||
entry.unwindInfo = readUnwindInfo(reader, entry.unwindInfoAddressOrData, ntHeader);
|
||||
|
||||
functionEntries.add(entry);
|
||||
}
|
||||
|
||||
reader.setPointerIndex(origIndex);
|
||||
}
|
||||
|
||||
private UNWIND_INFO readUnwindInfo(FactoryBundledWithBinaryReader reader, long offset,
|
||||
NTHeader ntHeader) throws IOException {
|
||||
long origIndex = reader.getPointerIndex();
|
||||
|
||||
long pointer = ntHeader.rvaToPointer(offset);
|
||||
UNWIND_INFO unwindInfo = new UNWIND_INFO(pointer);
|
||||
|
||||
if (pointer < 0) {
|
||||
return unwindInfo;
|
||||
}
|
||||
|
||||
reader.setPointerIndex(pointer);
|
||||
byte splitByte = reader.readNextByte();
|
||||
unwindInfo.version = (byte) (splitByte & UNWIND_INFO_VERSION_BITMASK);
|
||||
unwindInfo.flags = (byte) (splitByte >> UNWIND_INFO_FLAGS_SHIFT);
|
||||
|
||||
unwindInfo.sizeOfProlog = reader.readNextUnsignedByte();
|
||||
unwindInfo.countOfUnwindCodes = reader.readNextUnsignedByte();
|
||||
|
||||
splitByte = reader.readNextByte();
|
||||
unwindInfo.frameRegister = (byte) (splitByte & UNWIND_INFO_FRAME_REGISTER_MASK);
|
||||
unwindInfo.frameOffset = (byte) (splitByte >> UNWIND_INFO_FRAME_OFFSET_SHIFT);
|
||||
|
||||
unwindInfo.unwindCodes = new UNWIND_CODE[unwindInfo.countOfUnwindCodes];
|
||||
for (int i = 0; i < unwindInfo.countOfUnwindCodes; i++) {
|
||||
UNWIND_CODE code = new UNWIND_CODE();
|
||||
code.offsetInProlog = reader.readNextByte();
|
||||
|
||||
int opCodeData = reader.readNextUnsignedByte();
|
||||
code.opCode = UNWIND_CODE_OPCODE.fromInt((opCodeData & UNWIND_INFO_OPCODE_MASK));
|
||||
code.opInfoRegister =
|
||||
UNWIND_CODE_OPINFO_REGISTER.fromInt(opCodeData >> UNWIND_INFO_OPCODE_INFO_SHIFT);
|
||||
|
||||
unwindInfo.unwindCodes[i] = code;
|
||||
}
|
||||
|
||||
// You can have an exception handler and/or an unwind handler, or you
|
||||
// can have chained exception handling info only.
|
||||
if (unwindInfo.hasExceptionHandler() || unwindInfo.hasUnwindHandler()) {
|
||||
if (unwindInfo.hasExceptionHandler()) {
|
||||
unwindInfo.exceptionHandlerFunction = reader.readNextInt();
|
||||
}
|
||||
if (unwindInfo.hasUnwindHandler()) {
|
||||
unwindInfo.unwindHandlerFunction = reader.readNextInt();
|
||||
}
|
||||
}
|
||||
else if (unwindInfo.hasChainedUnwindInfo()) {
|
||||
unwindInfo.unwindHandlerChainInfo = new _IMAGE_RUNTIME_FUNCTION_ENTRY();
|
||||
unwindInfo.unwindHandlerChainInfo.beginAddress = reader.readNextInt();
|
||||
unwindInfo.unwindHandlerChainInfo.endAddress = reader.readNextInt();
|
||||
unwindInfo.unwindHandlerChainInfo.unwindInfoAddressOrData = reader.readNextInt();
|
||||
|
||||
// Follow the chain to the referenced UNWIND_INFO structure until we
|
||||
// get to the end
|
||||
unwindInfo.unwindHandlerChainInfo.unwindInfo = readUnwindInfo(reader,
|
||||
unwindInfo.unwindHandlerChainInfo.unwindInfoAddressOrData, ntHeader);
|
||||
}
|
||||
|
||||
reader.setPointerIndex(origIndex);
|
||||
|
||||
return unwindInfo;
|
||||
}
|
||||
|
||||
public List<_IMAGE_RUNTIME_FUNCTION_ENTRY> getRuntimeFunctionEntries() {
|
||||
return functionEntries;
|
||||
}
|
||||
|
||||
public class _IMAGE_RUNTIME_FUNCTION_ENTRY {
|
||||
public long beginAddress;
|
||||
public long endAddress;
|
||||
public long unwindInfoAddressOrData;
|
||||
public UNWIND_INFO unwindInfo;
|
||||
}
|
||||
|
||||
public enum UNWIND_CODE_OPCODE {
|
||||
UWOP_PUSH_NONVOL(0x00),
|
||||
UWOP_ALLOC_LARGE(0x01),
|
||||
UWOP_ALLOC_SMALL(0x02),
|
||||
UWOP_SET_FPREG(0x03),
|
||||
UWOP_SAVE_NONVOL(0x04),
|
||||
UWOP_SAVE_NONVOL_FAR(0x05),
|
||||
UWOP_SAVE_XMM(0x06),
|
||||
UWOP_SAVE_XMM_FAR(0x07),
|
||||
UWOP_SAVE_XMM128(0x08),
|
||||
UWOP_SAVE_XMM128_FAR(0x09),
|
||||
UWOP_PUSH_MACHFRAME(0x0A);
|
||||
|
||||
private final int id;
|
||||
|
||||
UNWIND_CODE_OPCODE(int value) {
|
||||
id = value;
|
||||
}
|
||||
|
||||
public int id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static UNWIND_CODE_OPCODE fromInt(int id) {
|
||||
UNWIND_CODE_OPCODE[] values = UNWIND_CODE_OPCODE.values();
|
||||
for (UNWIND_CODE_OPCODE value : values) {
|
||||
if (value.id == id) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum UNWIND_CODE_OPINFO_REGISTER {
|
||||
UNWIND_OPINFO_REGISTER_RAX(0x00),
|
||||
UNWIND_OPINFO_REGISTER_RCX(0x01),
|
||||
UNWIND_OPINFO_REGISTER_RDX(0x02),
|
||||
UNWIND_OPINFO_REGISTER_RBX(0x03),
|
||||
UNWIND_OPINFO_REGISTER_RSP(0x04),
|
||||
UNWIND_OPINFO_REGISTER_RBP(0x05),
|
||||
UNWIND_OPINFO_REGISTER_RSI(0x06),
|
||||
UNWIND_OPINFO_REGISTER_RDI(0x07),
|
||||
UNWIND_OPINFO_REGISTER_R8(0x08),
|
||||
UNWIND_OPINFO_REGISTER_R9(0x09),
|
||||
UNWIND_OPINFO_REGISTER_R10(0x0A),
|
||||
UNWIND_OPINFO_REGISTER_R11(0x0B),
|
||||
UNWIND_OPINFO_REGISTER_R12(0x0C),
|
||||
UNWIND_OPINFO_REGISTER_R13(0x0D),
|
||||
UNWIND_OPINFO_REGISTER_R14(0x0E),
|
||||
UNWIND_OPINFO_REGISTER_R15(0x0F);
|
||||
|
||||
private final int id;
|
||||
|
||||
UNWIND_CODE_OPINFO_REGISTER(int value) {
|
||||
id = value;
|
||||
}
|
||||
|
||||
public int id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static UNWIND_CODE_OPINFO_REGISTER fromInt(int id) {
|
||||
UNWIND_CODE_OPINFO_REGISTER[] values = UNWIND_CODE_OPINFO_REGISTER.values();
|
||||
for (UNWIND_CODE_OPINFO_REGISTER value : values) {
|
||||
if (value.id == id) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class UNWIND_CODE {
|
||||
public byte offsetInProlog;
|
||||
public UNWIND_CODE_OPCODE opCode;
|
||||
public UNWIND_CODE_OPINFO_REGISTER opInfoRegister;
|
||||
}
|
||||
|
||||
public class UNWIND_INFO implements StructConverter {
|
||||
private static final String NAME = "UNWIND_INFO";
|
||||
|
||||
private final static int UNW_FLAG_NHANDLER = 0x0;
|
||||
private final static int UNW_FLAG_EHANDLER = 0x1;
|
||||
private final static int UNW_FLAG_UHANDLER = 0x2;
|
||||
private final static int UNW_FLAG_CHAININFO = 0x4;
|
||||
|
||||
private final static int UNWIND_VERSION_FIELD_LENGTH = 0x03;
|
||||
private final static int UNWIND_FLAGS_FIELD_LENGTH = 0x05;
|
||||
private final static int UNWIND_FRAME_REGISTER_LENGTH = 0x04;
|
||||
private final static int UNWIND_OP_FIELD_LENGTH = 0x04;
|
||||
|
||||
byte version;
|
||||
byte flags;
|
||||
int sizeOfProlog;
|
||||
int countOfUnwindCodes;
|
||||
byte frameRegister;
|
||||
byte frameOffset;
|
||||
UNWIND_CODE[] unwindCodes;
|
||||
int exceptionHandlerFunction;
|
||||
int unwindHandlerFunction;
|
||||
_IMAGE_RUNTIME_FUNCTION_ENTRY unwindHandlerChainInfo;
|
||||
|
||||
long startOffset;
|
||||
|
||||
public UNWIND_INFO(long offset) {
|
||||
startOffset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
StructureDataType struct = new StructureDataType(NAME + "_" + startOffset, 0);
|
||||
try {
|
||||
StructureDataType vf = new StructureDataType("VersionFlags", 0);
|
||||
vf.insertBitField(0, 1, 0, BYTE, UNWIND_VERSION_FIELD_LENGTH, "Version", null);
|
||||
vf.insertBitField(0, 1, UNWIND_VERSION_FIELD_LENGTH, defineFlagsField(),
|
||||
UNWIND_FLAGS_FIELD_LENGTH, "Flags", null);
|
||||
|
||||
struct.add(vf, "Version + Flags", null);
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
struct.add(BYTE, "Version + Flags", null);
|
||||
}
|
||||
|
||||
struct.add(BYTE, "SizeOfProlog", null);
|
||||
struct.add(BYTE, "CountOfUnwindCodes", null);
|
||||
|
||||
try {
|
||||
StructureDataType fr = new StructureDataType("FrameRegisterAndOffset", 0);
|
||||
fr.insertBitField(0, 1, 0, BYTE, UNWIND_FRAME_REGISTER_LENGTH, "FrameRegister",
|
||||
null);
|
||||
fr.insertBitField(0, 1, UNWIND_FRAME_REGISTER_LENGTH, BYTE,
|
||||
UNWIND_FRAME_REGISTER_LENGTH, "FrameOffset", null);
|
||||
struct.add(fr, "FrameRegister + FrameOffset", null);
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
struct.add(BYTE, "FrameRegister + FrameOffset", null);
|
||||
}
|
||||
|
||||
for (int i = 0; i < countOfUnwindCodes; i++) {
|
||||
StructureDataType unwindCode = new StructureDataType("UnwindCode", 0);
|
||||
unwindCode.add(BYTE, "OffsetInProlog", null);
|
||||
|
||||
StructureDataType unwindCodeInfo = new StructureDataType("UnwindCodeInfo", 0);
|
||||
try {
|
||||
if (unwindCodes[i].opCode != null) {
|
||||
unwindCodeInfo.insertBitField(0, 1, 0, defineUnwindOpCodeField(),
|
||||
UNWIND_OP_FIELD_LENGTH, "UnwindOpCode", null);
|
||||
}
|
||||
else {
|
||||
unwindCodeInfo.insertBitField(0, 1, 0, BYTE, UNWIND_OP_FIELD_LENGTH,
|
||||
"UnwindOpCode", null);
|
||||
}
|
||||
|
||||
if (unwindCodes[i].opInfoRegister != null) {
|
||||
unwindCodeInfo.insertBitField(0, 1, UNWIND_OP_FIELD_LENGTH,
|
||||
defineUnwindCodeRegisterField(), UNWIND_OP_FIELD_LENGTH, "OpInfo",
|
||||
null);
|
||||
}
|
||||
else {
|
||||
unwindCodeInfo.insertBitField(0, 1, UNWIND_OP_FIELD_LENGTH, BYTE,
|
||||
UNWIND_OP_FIELD_LENGTH, "OpInfo", null);
|
||||
}
|
||||
}
|
||||
catch (InvalidDataTypeException e) {
|
||||
}
|
||||
unwindCode.add(unwindCodeInfo, "UnwindCodeInfo", null);
|
||||
|
||||
struct.add(unwindCode, "UnwindCode", null);
|
||||
}
|
||||
|
||||
if (hasExceptionHandler() || hasUnwindHandler()) {
|
||||
if (hasExceptionHandler()) {
|
||||
struct.add(IBO32, "ExceptionHandler", null);
|
||||
}
|
||||
if (hasUnwindHandler()) {
|
||||
struct.add(IBO32, "UnwindHandler", null);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (hasChainedUnwindInfo()) {
|
||||
struct.add(IBO32, "FunctionStartAddress", null);
|
||||
struct.add(IBO32, "FunctionEndAddress", null);
|
||||
struct.add(IBO32, "FunctionUnwindInfoAddress", null);
|
||||
}
|
||||
}
|
||||
|
||||
return struct;
|
||||
}
|
||||
|
||||
public boolean hasExceptionHandler() {
|
||||
return (flags & UNW_FLAG_EHANDLER) == UNW_FLAG_EHANDLER;
|
||||
}
|
||||
|
||||
public boolean hasUnwindHandler() {
|
||||
return (flags & UNW_FLAG_UHANDLER) == UNW_FLAG_UHANDLER;
|
||||
}
|
||||
|
||||
public boolean hasChainedUnwindInfo() {
|
||||
return (flags & UNW_FLAG_CHAININFO) == UNW_FLAG_CHAININFO;
|
||||
}
|
||||
|
||||
private EnumDataType defineFlagsField() {
|
||||
EnumDataType flagsField = new EnumDataType("Flags", 5);
|
||||
flagsField.add("UNW_FLAG_NHANDLER", UNW_FLAG_NHANDLER);
|
||||
flagsField.add("UNW_FLAG_EHANDLER", UNW_FLAG_EHANDLER);
|
||||
flagsField.add("UNW_FLAG_UHANDLER", UNW_FLAG_UHANDLER);
|
||||
flagsField.add("UNW_FLAG_CHAININFO", UNW_FLAG_CHAININFO);
|
||||
|
||||
return flagsField;
|
||||
}
|
||||
|
||||
private EnumDataType defineUnwindOpCodeField() {
|
||||
EnumDataType unwindOpCodeField = new EnumDataType("UNWIND_CODE_OPCODE", 4);
|
||||
unwindOpCodeField.add("UWOP_PUSH_NONVOL", UNWIND_CODE_OPCODE.UWOP_PUSH_NONVOL.id);
|
||||
unwindOpCodeField.add("UWOP_ALLOC_LARGE", UNWIND_CODE_OPCODE.UWOP_ALLOC_LARGE.id);
|
||||
unwindOpCodeField.add("UWOP_ALLOC_SMALL", UNWIND_CODE_OPCODE.UWOP_ALLOC_SMALL.id);
|
||||
unwindOpCodeField.add("UWOP_SET_FPREG", UNWIND_CODE_OPCODE.UWOP_SET_FPREG.id);
|
||||
unwindOpCodeField.add("UWOP_SAVE_NONVOL", UNWIND_CODE_OPCODE.UWOP_SAVE_NONVOL.id);
|
||||
unwindOpCodeField.add("UWOP_SAVE_NONVOL_FAR",
|
||||
UNWIND_CODE_OPCODE.UWOP_SAVE_NONVOL_FAR.id);
|
||||
unwindOpCodeField.add("UWOP_SAVE_XMM", UNWIND_CODE_OPCODE.UWOP_SAVE_XMM.id);
|
||||
unwindOpCodeField.add("UWOP_SAVE_XMM_FAR", UNWIND_CODE_OPCODE.UWOP_SAVE_XMM_FAR.id);
|
||||
unwindOpCodeField.add("UWOP_SAVE_XMM128", UNWIND_CODE_OPCODE.UWOP_SAVE_XMM128.id);
|
||||
unwindOpCodeField.add("UWOP_SAVE_XMM128_FAR",
|
||||
UNWIND_CODE_OPCODE.UWOP_SAVE_XMM128_FAR.id);
|
||||
unwindOpCodeField.add("UWOP_PUSH_MACHFRAME", UNWIND_CODE_OPCODE.UWOP_PUSH_MACHFRAME.id);
|
||||
|
||||
return unwindOpCodeField;
|
||||
}
|
||||
|
||||
private EnumDataType defineUnwindCodeRegisterField() {
|
||||
EnumDataType unwindCodeRegisterField =
|
||||
new EnumDataType("UNWIND_CODE_OPINFO_REGISTER", 4);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RAX",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RAX.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RCX",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RCX.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RDX",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RDX.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RBX",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RBX.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RSP",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RSP.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RBP",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RBP.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RSI",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RSI.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_RDI",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_RDI.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R8",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R8.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R9",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R9.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R10",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R10.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R11",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R11.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R12",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R12.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R13",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R13.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R14",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R14.id);
|
||||
unwindCodeRegisterField.add("UNWIND_OPINFO_REGISTER_R15",
|
||||
UNWIND_CODE_OPINFO_REGISTER.UNWIND_OPINFO_REGISTER_R15.id);
|
||||
|
||||
return unwindCodeRegisterField;
|
||||
}
|
||||
}
|
||||
}
|
@ -82,8 +82,8 @@ public class NTHeader implements StructConverter, OffsetValidator {
|
||||
public NTHeader() {
|
||||
}
|
||||
|
||||
private void initNTHeader(FactoryBundledWithBinaryReader reader, int index, SectionLayout layout,
|
||||
boolean advancedProcess, boolean parseCliHeaders)
|
||||
private void initNTHeader(FactoryBundledWithBinaryReader reader, int index,
|
||||
SectionLayout layout, boolean advancedProcess, boolean parseCliHeaders)
|
||||
throws InvalidNTHeaderException, IOException {
|
||||
this.reader = reader;
|
||||
this.index = index;
|
||||
@ -172,9 +172,9 @@ public class NTHeader implements StructConverter, OffsetValidator {
|
||||
//low alignment mode?
|
||||
//
|
||||
if (optionalHeader != null) {
|
||||
if (optionalHeader.getFileAlignment() == optionalHeader.getSectionAlignment()
|
||||
&& optionalHeader.getSectionAlignment() < 800
|
||||
&& optionalHeader.getFileAlignment() > 1) {
|
||||
if (optionalHeader.getFileAlignment() == optionalHeader.getSectionAlignment() &&
|
||||
optionalHeader.getSectionAlignment() < 800 &&
|
||||
optionalHeader.getFileAlignment() > 1) {
|
||||
return rva;
|
||||
}
|
||||
}
|
||||
@ -270,6 +270,7 @@ public class NTHeader implements StructConverter, OffsetValidator {
|
||||
|
||||
fileHeader.processSections(optionalHeader);
|
||||
fileHeader.processSymbols();
|
||||
fileHeader.processImageRuntimeFunctionEntries();
|
||||
|
||||
if (advancedProcess) {
|
||||
optionalHeader.processDataDirectories(TaskMonitorAdapter.DUMMY_MONITOR);
|
||||
@ -278,7 +279,7 @@ public class NTHeader implements StructConverter, OffsetValidator {
|
||||
|
||||
void writeHeader(RandomAccessFile raf, DataConverter dc) throws IOException {
|
||||
|
||||
raf.seek( index );
|
||||
raf.seek(index);
|
||||
|
||||
raf.write(dc.getBytes(signature));
|
||||
|
||||
|
@ -431,32 +431,95 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable
|
||||
}
|
||||
|
||||
public class CliTypeFnPtr extends CliSigType {
|
||||
private CliAbstractSig sig;
|
||||
private boolean isDefSig; // true => MethodDef, false => MethodRef
|
||||
private long dataOffset;
|
||||
|
||||
private CliRetType retType;
|
||||
private CliParam params[];
|
||||
private int sizeOfCount;
|
||||
private byte flags;
|
||||
private int genericParamCount;
|
||||
private int sizeOfGenericCount;
|
||||
|
||||
private int sentinelIndex; // SENTINEL is before the parameter index in this field
|
||||
|
||||
private final int FNPTR_FLAGS_DEFAULT = 0x00;
|
||||
private final int FNPTR_FLAGS_VARARG = 0x05;
|
||||
private final int FNPTR_FLAGS_GENERIC = 0x10;
|
||||
private final int FNPTR_FLAGS_HASTHIS = 0x20;
|
||||
private final int FNPTR_FLAGS_EXPLICITTHIS = 0x40;
|
||||
|
||||
public CliTypeFnPtr(BinaryReader reader, CliElementType typeCode) throws IOException {
|
||||
super(typeCode);
|
||||
// TODO: MethodDef and MethodRef sig need to have static isX(reader) methods so I can tell the difference
|
||||
//sig = new CliSigMethodRef(blob); // MethodRef is just Def plus possible sentinel and minus potential XORed args in the first byte
|
||||
|
||||
sentinelIndex = -1;
|
||||
|
||||
dataOffset = reader.getPointerIndex();
|
||||
|
||||
// Flags is similar to a MethodDef unless vararg is used.
|
||||
flags = reader.readNextByte();
|
||||
|
||||
if ((flags & FNPTR_FLAGS_GENERIC) == FNPTR_FLAGS_GENERIC) {
|
||||
long origIndex = reader.getPointerIndex();
|
||||
genericParamCount = decodeCompressedUnsignedInt(reader);
|
||||
sizeOfGenericCount = (int) (reader.getPointerIndex() - origIndex);
|
||||
}
|
||||
|
||||
long origIndex = reader.getPointerIndex();
|
||||
int paramCount = decodeCompressedUnsignedInt(reader);
|
||||
this.sizeOfCount = (int) (reader.getPointerIndex() - origIndex);
|
||||
|
||||
try {
|
||||
retType = new CliRetType(reader);
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
retType = null;
|
||||
}
|
||||
params = new CliParam[paramCount];
|
||||
|
||||
for (int i = 0; i < paramCount; i++) {
|
||||
if (reader.peekNextByte() == CliElementType.ELEMENT_TYPE_SENTINEL.id()) {
|
||||
reader.readNextByte();
|
||||
sentinelIndex = i;
|
||||
}
|
||||
try {
|
||||
params[i] = new CliParam(reader);
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
params[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepresentation() {
|
||||
return "FnPtr " + sig.getRepresentation();
|
||||
return "FnPtr " /*+ sig.getRepresentation()*/;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortRepresentation() {
|
||||
return "FnPtr " + sig.getShortRepresentation();
|
||||
return "FnPtr " /*+ sig.getShortRepresentation()*/;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getDefinitionDataType() {
|
||||
StructureDataType struct = new StructureDataType(new CategoryPath(PATH), "FnPtr", 0);
|
||||
struct.add(CliTypeCodeDataType.dataType, "FnPtr", "FnPtr");
|
||||
struct.add(DWORD, "MethodDefOrRef", "index into blob heap");
|
||||
StructureDataType struct =
|
||||
new StructureDataType(new CategoryPath(PATH), "FnPtr_" + dataOffset, 0);
|
||||
struct.add(CliTypeCodeDataType.dataType, CliElementType.ELEMENT_TYPE_FNPTR.toString(),
|
||||
"");
|
||||
struct.add(BYTE, "Flags", "ORed VARARG/GENERIC/HASTHIS/EXPLICITTHIS");
|
||||
|
||||
if (genericParamCount > 0) {
|
||||
struct.add(getDataTypeForBytes(sizeOfGenericCount), "GenParamCount",
|
||||
"Number of generic paramameters for the method");
|
||||
}
|
||||
struct.add(getDataTypeForBytes(sizeOfCount), "ParamCount",
|
||||
"Number of parameter types to follow RetType");
|
||||
struct.add(retType.getDefinitionDataType(), "RetType", null);
|
||||
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
struct.add(params[i].getDefinitionDataType(), "Param" + i, null);
|
||||
}
|
||||
return struct;
|
||||
// TODO: Return the correct size of a signature reference (always 4B in this context perchance?)
|
||||
}
|
||||
}
|
||||
|
||||
@ -552,17 +615,18 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable
|
||||
}
|
||||
|
||||
public class CliTypeVarOrMvar extends CliSigType {
|
||||
private long dataOffset;
|
||||
private int number;
|
||||
private int numberBytes;
|
||||
|
||||
public CliTypeVarOrMvar(BinaryReader reader, CliElementType typeCode) throws IOException {
|
||||
super(typeCode);
|
||||
|
||||
long origIndex = reader.getPointerIndex();
|
||||
dataOffset = reader.getPointerIndex();
|
||||
number = decodeCompressedUnsignedInt(reader);
|
||||
|
||||
long endIndex = reader.getPointerIndex();
|
||||
numberBytes = (int) (endIndex - origIndex);
|
||||
numberBytes = (int) (endIndex - dataOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -572,9 +636,9 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable
|
||||
|
||||
@Override
|
||||
public DataType getDefinitionDataType() {
|
||||
StructureDataType struct =
|
||||
new StructureDataType(new CategoryPath(PATH), "VarOrMvar", 0);
|
||||
struct.add(BYTE, "Type", "Var or Mvar");
|
||||
StructureDataType struct = new StructureDataType(new CategoryPath(PATH),
|
||||
baseTypeCode.toString() + "_" + dataOffset, 0);
|
||||
struct.add(CliTypeCodeDataType.dataType, baseTypeCode.toString(), "");
|
||||
struct.add(getDataTypeForBytes(numberBytes), "number", null);
|
||||
return struct;
|
||||
}
|
||||
@ -582,16 +646,24 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable
|
||||
|
||||
public class CliTypePtr extends CliSigType {
|
||||
private List<CliCustomMod> customMods = new ArrayList<>();
|
||||
private CliElementType typeCode;
|
||||
private CliSigType type;
|
||||
private long dataOffset;
|
||||
|
||||
public CliTypePtr(BinaryReader reader, CliElementType typeCode) throws IOException {
|
||||
public CliTypePtr(BinaryReader reader, CliElementType typeCode)
|
||||
throws IOException, InvalidInputException {
|
||||
super(typeCode);
|
||||
|
||||
dataOffset = reader.getPointerIndex();
|
||||
|
||||
while (CliCustomMod.isCustomMod(reader)) {
|
||||
customMods.add(new CliCustomMod(reader));
|
||||
}
|
||||
|
||||
typeCode = CliElementType.fromInt(reader.readNextByte());
|
||||
type = readCliType(reader);
|
||||
}
|
||||
|
||||
public CliSigType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -601,17 +673,22 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable
|
||||
modsRep += mod.toString() + ", ";
|
||||
}
|
||||
modsRep = modsRep.substring(0, modsRep.length() - 2);
|
||||
return String.format("Ptr %s %s", modsRep, typeCode.toString());
|
||||
return String.format("Ptr %s %s", modsRep, type.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getDefinitionDataType() {
|
||||
StructureDataType struct = new StructureDataType(new CategoryPath(PATH), "Ptr", 0);
|
||||
struct.add(CliTypeCodeDataType.dataType, "TypeCode", "Ptr");
|
||||
StructureDataType struct = new StructureDataType(new CategoryPath(PATH),
|
||||
CliElementType.ELEMENT_TYPE_PTR.toString() + "_" + dataOffset, 0);
|
||||
struct.add(CliTypeCodeDataType.dataType, CliElementType.ELEMENT_TYPE_PTR.toString(),
|
||||
"");
|
||||
|
||||
for (CliCustomMod mod : customMods) {
|
||||
struct.add(mod.getDefinitionDataType());
|
||||
}
|
||||
struct.add(CliTypeCodeDataType.dataType, "Type", "type or void");
|
||||
|
||||
struct.add(type.getDefinitionDataType(), type.baseTypeCode.toString(), "");
|
||||
|
||||
return struct;
|
||||
}
|
||||
}
|
||||
@ -682,6 +759,19 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable
|
||||
typeBytes = (int) (endIndex - origIndex);
|
||||
}
|
||||
|
||||
public CliTypeTable getTable() {
|
||||
try {
|
||||
return CliIndexTypeDefOrRef.getTableName(encodedType);
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public int getRowIndex() {
|
||||
return CliIndexTypeDefOrRef.getRowIndex(encodedType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepresentation() {
|
||||
return "ValueType " + Integer.toHexString(encodedType);
|
||||
@ -714,12 +804,17 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable
|
||||
|
||||
@Override
|
||||
public DataType getDefinitionDataType() {
|
||||
String tableName = getTable().name();
|
||||
|
||||
StructureDataType struct =
|
||||
new StructureDataType(new CategoryPath(PATH), "ValueType", 0);
|
||||
struct.add(CliTypeCodeDataType.dataType, "ValueType", "ValueType");
|
||||
struct.add(getDataTypeForBytes(typeBytes), "Type", "TypeDefOrRefOrSpecEncoded");
|
||||
struct.add(getDataTypeForBytes(typeBytes), "TypeDefOrRefEncoded",
|
||||
tableName + ": Row 0x" + Integer.toHexString(getRowIndex()));
|
||||
|
||||
return struct;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public CliSigType readCliType(BinaryReader reader) throws IOException, InvalidInputException {
|
||||
@ -761,10 +856,11 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable
|
||||
}
|
||||
}
|
||||
|
||||
// The CustomMod signature part contains a required CMOD option (CMOD_OPT or CMOD_REQD) then a compressed TypeDefOrRefOrSpecEncoded
|
||||
public static class CliCustomMod {
|
||||
// The CustomMod signature part contains a required CMOD
|
||||
// option (CMOD_OPT or CMOD_REQD) then a compressed TypeDefOrRefOrSpecEncoded
|
||||
public static class CliCustomMod implements CliRepresentable {
|
||||
private CliElementType cmod;
|
||||
private int typeEncoded;
|
||||
private int encodedType;
|
||||
private int sizeOfCount;
|
||||
|
||||
public static boolean isCustomMod(BinaryReader reader) throws IOException {
|
||||
@ -776,7 +872,7 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable
|
||||
cmod = CliElementType.fromInt(reader.readNextByte());
|
||||
|
||||
long origIndex = reader.getPointerIndex();
|
||||
typeEncoded = decodeCompressedUnsignedInt(reader);
|
||||
encodedType = decodeCompressedUnsignedInt(reader);
|
||||
|
||||
long endIndex = reader.getPointerIndex();
|
||||
sizeOfCount = (int) (endIndex - origIndex);
|
||||
@ -787,12 +883,12 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable
|
||||
}
|
||||
|
||||
public int getTypeEncoded() {
|
||||
return typeEncoded;
|
||||
return encodedType;
|
||||
}
|
||||
|
||||
public CliTypeTable getTable() {
|
||||
try {
|
||||
return CliIndexTypeDefOrRef.getTableName(typeEncoded);
|
||||
return CliIndexTypeDefOrRef.getTableName(encodedType);
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
return null;
|
||||
@ -800,7 +896,7 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable
|
||||
}
|
||||
|
||||
public int getRowIndex() {
|
||||
return CliIndexTypeDefOrRef.getRowIndex(typeEncoded);
|
||||
return CliIndexTypeDefOrRef.getRowIndex(encodedType);
|
||||
}
|
||||
|
||||
public CliAbstractTableRow getRow(CliStreamMetadata stream) {
|
||||
@ -808,25 +904,40 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable
|
||||
}
|
||||
|
||||
public DataType getDefinitionDataType() {
|
||||
StructureDataType struct = new StructureDataType(new CategoryPath(PATH),
|
||||
CliCustomMod.class.getSimpleName(), 0);
|
||||
struct.add(BYTE, "CMOD", "CMOD_OPT or CMOD_REQD");
|
||||
struct.add(getDataTypeForBytes(this.sizeOfCount), "Type",
|
||||
"TypeDefOrRefOrSpec encoded type");
|
||||
StructureDataType struct =
|
||||
new StructureDataType(new CategoryPath(PATH), "CustomMod", 0);
|
||||
|
||||
String tableName = getTable().name();
|
||||
|
||||
struct.add(CliTypeCodeDataType.dataType, cmod.toString(), null);
|
||||
struct.add(getDataTypeForBytes(this.sizeOfCount), "TypeDefOrRefEncoded",
|
||||
tableName + ": Row 0x" + Integer.toHexString(getRowIndex()));
|
||||
return struct;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepresentation(CliStreamMetadata stream) {
|
||||
return String.format("%s %s", cmod.toString(), getRow(stream));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepresentation() {
|
||||
return String.format("%s %x", cmod.toString(), typeEncoded);
|
||||
return String.format("%s %x", cmod.toString(), encodedType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortRepresentation() {
|
||||
return getRepresentation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortRepresentation(CliStreamMetadata stream) {
|
||||
return getRepresentation(stream);
|
||||
}
|
||||
}
|
||||
|
||||
// The only possible constraint is ELEMENT_TYPE_PINNED (CliTypeCode.Pinned)
|
||||
public static class CliConstraint {
|
||||
public static class CliConstraint implements CliRepresentable {
|
||||
private CliElementType constraint;
|
||||
|
||||
public static boolean isConstraint(BinaryReader reader) throws IOException {
|
||||
@ -841,6 +952,7 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable
|
||||
return constraint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepresentation() {
|
||||
if (constraint == CliElementType.ELEMENT_TYPE_PINNED) {
|
||||
return constraint.toString();
|
||||
@ -848,36 +960,96 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable
|
||||
return String.format("Invalid Constraint (%s - %x)", constraint.toString(),
|
||||
constraint.id());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortRepresentation() {
|
||||
return getRepresentation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepresentation(CliStreamMetadata stream) {
|
||||
return getRepresentation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortRepresentation(CliStreamMetadata stream) {
|
||||
return getRepresentation();
|
||||
}
|
||||
}
|
||||
|
||||
// The only possible result is ELEMENT_TYPE_BYREF
|
||||
public static class CliByRef implements CliRepresentable {
|
||||
private CliElementType byRef;
|
||||
|
||||
public static boolean isByRef(BinaryReader reader) throws IOException {
|
||||
return (reader.peekNextByte() == CliElementType.ELEMENT_TYPE_BYREF.id());
|
||||
}
|
||||
|
||||
public CliByRef(BinaryReader reader) throws IOException {
|
||||
byRef = CliElementType.fromInt(reader.readNextByte());
|
||||
}
|
||||
|
||||
public CliElementType getByRef() {
|
||||
return byRef;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepresentation() {
|
||||
if (byRef == CliElementType.ELEMENT_TYPE_BYREF) {
|
||||
return byRef.toString();
|
||||
}
|
||||
return String.format("Invalid ByRef (%s - %x)", byRef.toString(), byRef.id());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortRepresentation() {
|
||||
return getRepresentation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepresentation(CliStreamMetadata stream) {
|
||||
return getRepresentation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortRepresentation(CliStreamMetadata stream) {
|
||||
return getRepresentation();
|
||||
}
|
||||
}
|
||||
|
||||
public class CliTypeBase implements CliRepresentable {
|
||||
private List<CliCustomMod> customMods = new ArrayList<>();
|
||||
private boolean constraint = false;
|
||||
private boolean byRef = false;
|
||||
private List<CliRepresentable> modifiers = new ArrayList<>();
|
||||
|
||||
private CliSigType type;
|
||||
|
||||
private long dataOffset = 0;
|
||||
private boolean isVoidAllowed = false;
|
||||
|
||||
public CliTypeBase(BinaryReader reader, boolean isRetType)
|
||||
throws IOException, InvalidInputException {
|
||||
dataOffset = reader.getPointerIndex();
|
||||
|
||||
this.isVoidAllowed = isRetType;
|
||||
|
||||
// Check for any of the modifiers, which can repeat out of order
|
||||
// before encountering the base type
|
||||
while (CliCustomMod.isCustomMod(reader) || CliConstraint.isConstraint(reader) ||
|
||||
CliByRef.isByRef(reader)) {
|
||||
|
||||
// Get any custom modifiers
|
||||
while (CliCustomMod.isCustomMod(reader)) {
|
||||
customMods.add(new CliCustomMod(reader));
|
||||
modifiers.add(new CliCustomMod(reader));
|
||||
}
|
||||
|
||||
// Check to see if it's a constrained variable
|
||||
if (CliConstraint.isConstraint(reader)) {
|
||||
constraint = true;
|
||||
reader.readNextByte();
|
||||
modifiers.add(new CliConstraint(reader));
|
||||
}
|
||||
|
||||
// Check to see if it's a ByRef
|
||||
byte byRefCheck = reader.peekNextByte();
|
||||
if (byRefCheck == CliElementType.ELEMENT_TYPE_BYREF.id()) {
|
||||
byRef = true;
|
||||
reader.readNextByte();
|
||||
if (CliByRef.isByRef(reader)) {
|
||||
modifiers.add(new CliByRef(reader));
|
||||
}
|
||||
}
|
||||
|
||||
type = readCliType(reader);
|
||||
@ -888,35 +1060,49 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable
|
||||
}
|
||||
|
||||
public List<CliCustomMod> getCustomMods() {
|
||||
List<CliCustomMod> customMods = new ArrayList<CliCustomMod>();
|
||||
for (CliRepresentable mod : modifiers) {
|
||||
if (mod instanceof CliCustomMod) {
|
||||
customMods.add((CliCustomMod) mod);
|
||||
}
|
||||
}
|
||||
|
||||
return customMods;
|
||||
}
|
||||
|
||||
public boolean isByRef() {
|
||||
return byRef;
|
||||
for (CliRepresentable mod : modifiers) {
|
||||
if (mod instanceof CliByRef) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isConstrained() {
|
||||
return constraint;
|
||||
for (CliRepresentable mod : modifiers) {
|
||||
if (mod instanceof CliConstraint) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String getRepresentationCommon(CliStreamMetadata stream, boolean shortRep) {
|
||||
String rep = "";
|
||||
|
||||
for (CliCustomMod mod : customMods) {
|
||||
rep += mod.getRepresentation() + "; ";
|
||||
for (CliRepresentable mod : modifiers) {
|
||||
if (mod instanceof CliCustomMod) {
|
||||
CliCustomMod customMod = (CliCustomMod) mod;
|
||||
rep += customMod.getRepresentation() + "; ";
|
||||
}
|
||||
|
||||
if (customMods.size() > 0) {
|
||||
rep = rep.substring(0, rep.length() - 2) + " ";
|
||||
}
|
||||
|
||||
if (constraint) {
|
||||
else if (mod instanceof CliConstraint) {
|
||||
rep += "constrained ";
|
||||
}
|
||||
|
||||
if (byRef) {
|
||||
else if (mod instanceof CliByRef) {
|
||||
rep += "byref ";
|
||||
}
|
||||
}
|
||||
|
||||
// The one special case value we have is the SENTINEL, which
|
||||
// we represent as "..." to denote that VarArgs might be passed
|
||||
@ -952,20 +1138,25 @@ public abstract class CliAbstractSig extends CliBlob implements CliRepresentable
|
||||
}
|
||||
|
||||
public DataType getDefinitionDataType() {
|
||||
StructureDataType struct = new StructureDataType(new CategoryPath(PATH), "Type", 0);
|
||||
StructureDataType struct =
|
||||
new StructureDataType(new CategoryPath(PATH), "Type_" + dataOffset, 0);
|
||||
|
||||
for (CliCustomMod mod : customMods) {
|
||||
struct.add(mod.getDefinitionDataType(), "CustomMod", null);
|
||||
for (CliRepresentable mod : modifiers) {
|
||||
if (mod instanceof CliCustomMod) {
|
||||
CliCustomMod customMod = (CliCustomMod) mod;
|
||||
struct.add(customMod.getDefinitionDataType(), "CustomMod", null);
|
||||
}
|
||||
else if (mod instanceof CliConstraint) {
|
||||
struct.add(CliTypeCodeDataType.dataType,
|
||||
CliElementType.ELEMENT_TYPE_PINNED.toString(), "Constrained");
|
||||
}
|
||||
else if (mod instanceof CliByRef) {
|
||||
struct.add(CliTypeCodeDataType.dataType,
|
||||
CliElementType.ELEMENT_TYPE_BYREF.toString(), "By Reference");
|
||||
}
|
||||
}
|
||||
|
||||
if (constraint) {
|
||||
struct.add(BYTE, "CONSTRAINT", "Constrained");
|
||||
}
|
||||
|
||||
if (byRef) {
|
||||
struct.add(BYTE, "BYREF", "By reference");
|
||||
}
|
||||
struct.add(type.getDefinitionDataType(), "Type", null);
|
||||
struct.add(type.getDefinitionDataType(), type.baseTypeCode.toString(), null);
|
||||
return struct;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,475 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.pe.cli.blobs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.format.pe.cli.blobs.CliAbstractSig.CliElementType;
|
||||
import ghidra.app.util.bin.format.pe.cli.blobs.CliAbstractSig.CliParam;
|
||||
import ghidra.app.util.bin.format.pe.cli.streams.CliStreamMetadata;
|
||||
import ghidra.app.util.bin.format.pe.cli.tables.CliAbstractTableRow;
|
||||
import ghidra.app.util.bin.format.pe.cli.tables.CliTableCustomAttribute.CliCustomAttributeRow;
|
||||
import ghidra.app.util.bin.format.pe.cli.tables.CliTableMemberRef.CliMemberRefRow;
|
||||
import ghidra.app.util.bin.format.pe.cli.tables.CliTableMethodDef.CliMethodDefRow;
|
||||
import ghidra.app.util.bin.format.pe.cli.tables.CliTypeTable;
|
||||
import ghidra.app.util.bin.format.pe.cli.tables.indexes.CliIndexCustomAttributeType;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
|
||||
public class CliBlobCustomAttrib extends CliBlob {
|
||||
|
||||
private CliFixedArg[] fixedArgs;
|
||||
private CliNamedArg[] namedArgs;
|
||||
private short numNamed;
|
||||
|
||||
// Fixed constants for validating the structure
|
||||
private static final short CLIBLOBCUSTOMATTRIB_PROLOG = 0x0001;
|
||||
private static final byte CLIBLOBCUSTOMATTRIB_TYPE_FIELD = 0x53;
|
||||
private static final byte CLIBLOBCUSTOMATTRIB_TYPE_PROPERTY = 0x54;
|
||||
|
||||
private static final int CLIBLOBCUSTOMATTRIB_STRING_BOUNDARY_128 = 0x80;
|
||||
private static final int CLIBLOBCUSTOMATTRIB_STRING_BOUNDARY_192 = 0xC0;
|
||||
|
||||
private static final int CLIBLOBCUSTOMATTRIB_STRING_SIZE_ONE = 0x01;
|
||||
private static final int CLIBLOBCUSTOMATTRIB_STRING_SIZE_TWO = 0x02;
|
||||
private static final int CLIBLOBCUSTOMATTRIB_STRING_SIZE_FOUR = 0x03;
|
||||
private static final int CLIBLOBCUSTOMATTRIB_STRING_SIZE_BITMASK = 0x3F;
|
||||
|
||||
private static final int CLIBLOBCUSTOMATTRIB_STRING_INDICATOR_SHIFT = 0x06;
|
||||
private static final int CLIBLOBCUSTOMATTRIB_STRING_INDICATOR_BITMASK = 0x03;
|
||||
|
||||
// UTF-8 boundaries that help detect the end of a string where
|
||||
// lengths aren't specified in FixedArg
|
||||
private static final int CLIBLOBCUSTOMATTRIB_UTF8_LOW = 0x1F;
|
||||
private static final int CLIBLOBCUSTOMATTRIB_UTF8_HIGH = 0x7F;
|
||||
|
||||
private class CliFixedArg {
|
||||
private CliElementType elem;
|
||||
private Object value;
|
||||
|
||||
public CliFixedArg(CliElementType elem, Object value) {
|
||||
this.elem = elem;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public CliElementType getElem() {
|
||||
return elem;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private class CliNamedArg {
|
||||
private int fieldOrProp;
|
||||
private CliElementType fieldOrPropType;
|
||||
private String fieldOrPropName;
|
||||
|
||||
public CliNamedArg(int fieldOrProp, CliElementType fieldOrPropType,
|
||||
String fieldOrPropName) {
|
||||
this.fieldOrProp = fieldOrProp;
|
||||
this.fieldOrPropType = fieldOrPropType;
|
||||
this.fieldOrPropName = fieldOrPropName;
|
||||
}
|
||||
|
||||
public int getFieldOrProp() {
|
||||
return fieldOrProp;
|
||||
}
|
||||
|
||||
public CliElementType getFieldOrPropType() {
|
||||
return fieldOrPropType;
|
||||
}
|
||||
|
||||
public String getFieldOrPropName() {
|
||||
return fieldOrPropName;
|
||||
}
|
||||
}
|
||||
|
||||
public CliBlobCustomAttrib(CliBlob blob, CliCustomAttributeRow row,
|
||||
CliStreamMetadata metadataStream) throws IOException {
|
||||
super(blob);
|
||||
|
||||
BinaryReader reader = blob.getContentsReader();
|
||||
|
||||
// Validate the blob prolog
|
||||
short prolog = reader.readNextShort();
|
||||
if (prolog != CLIBLOBCUSTOMATTRIB_PROLOG) {
|
||||
Msg.warn(this,
|
||||
getName() + " had unexpected prolog (0x" + Integer.toHexString(prolog) + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
// The location in the blob table for the actual CustomAttrib blob
|
||||
int valueIndex = row.valueIndex;
|
||||
|
||||
// The entry in the MethodRef or MethodDef table that corresponds to the method
|
||||
// This is a CustomAttributeType coded index
|
||||
int typeIndex = row.typeIndex;
|
||||
|
||||
// The entry of the parent table index, also a CustomAttributeType coded index
|
||||
int parentIndex = row.parentIndex;
|
||||
|
||||
// The FixedArg parameters in the CustomAttrib blob are stored concatenated
|
||||
// against each other without delimeters or type indicators, so you have to look
|
||||
// back to the originating method signature to figure out what's what.
|
||||
|
||||
CliParam[] params = null;
|
||||
try {
|
||||
// Get the table type and row for the attribute and depending on the type
|
||||
// get the parameters
|
||||
CliTypeTable tableType = CliIndexCustomAttributeType.getTableName(typeIndex);
|
||||
int tableRowIndex = CliIndexCustomAttributeType.getRowIndex(typeIndex);
|
||||
CliAbstractTableRow tableRow = metadataStream.getTable(tableType).getRow(tableRowIndex);
|
||||
|
||||
if (tableType == CliTypeTable.MemberRef) {
|
||||
CliMemberRefRow memberRefRow = (CliMemberRefRow) tableRow;
|
||||
CliBlob memberRefBlob =
|
||||
metadataStream.getBlobStream().getBlob(memberRefRow.signatureIndex);
|
||||
CliSigMethodRef methodRefSig = new CliSigMethodRef(memberRefBlob);
|
||||
params = methodRefSig.getParams();
|
||||
}
|
||||
else if (tableType == CliTypeTable.MethodDef) {
|
||||
CliMethodDefRow methodDefRow = (CliMethodDefRow) tableRow;
|
||||
CliBlob methodDefBlob =
|
||||
metadataStream.getBlobStream().getBlob(methodDefRow.sigIndex);
|
||||
CliSigMethodDef methodDefSig = new CliSigMethodDef(methodDefBlob);
|
||||
params = methodDefSig.getParamTypes();
|
||||
}
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
Msg.warn(this, "Unable to process the parameters in " + getName());
|
||||
return;
|
||||
}
|
||||
|
||||
// Process zero to multiple FixedArgs
|
||||
fixedArgs = processFixedArgs(reader, params).toArray(CliFixedArg[]::new);
|
||||
|
||||
// Process zero to multiple NamedArgs here
|
||||
namedArgs = processNamedArgs(reader).toArray(CliNamedArg[]::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getContentsDataType() {
|
||||
StructureDataType struct = new StructureDataType(new CategoryPath(PATH), getName(), 0);
|
||||
struct.add(WORD, "PROLOG", "Magic (0x0001)");
|
||||
|
||||
// Display the FixedArgs
|
||||
if (fixedArgs != null) {
|
||||
for (int i = 0; i < fixedArgs.length; i++) {
|
||||
CliElementType elem = fixedArgs[i].elem;
|
||||
|
||||
switch (elem) {
|
||||
case ELEMENT_TYPE_CHAR:
|
||||
struct.add(UTF16, "FixedArg_" + i, "Elem (" + fixedArgs[i].getElem() + ")");
|
||||
break;
|
||||
|
||||
case ELEMENT_TYPE_I1:
|
||||
case ELEMENT_TYPE_U1:
|
||||
case ELEMENT_TYPE_BOOLEAN:
|
||||
struct.add(BYTE, "FixedArg_" + i, "Elem (" + fixedArgs[i].getElem() + ")");
|
||||
break;
|
||||
|
||||
case ELEMENT_TYPE_I2:
|
||||
case ELEMENT_TYPE_U2:
|
||||
struct.add(WORD, "FixedArg_" + i, "Elem (" + fixedArgs[i].getElem() + ")");
|
||||
break;
|
||||
|
||||
case ELEMENT_TYPE_I4:
|
||||
case ELEMENT_TYPE_U4:
|
||||
case ELEMENT_TYPE_R4:
|
||||
case ELEMENT_TYPE_VALUETYPE:
|
||||
struct.add(DWORD, "FixedArg_" + i, "Elem (" + fixedArgs[i].getElem() + ")");
|
||||
break;
|
||||
|
||||
case ELEMENT_TYPE_I8:
|
||||
case ELEMENT_TYPE_U8:
|
||||
case ELEMENT_TYPE_R8:
|
||||
struct.add(QWORD, "FixedArg_" + i, "Elem (" + fixedArgs[i].getElem() + ")");
|
||||
|
||||
case ELEMENT_TYPE_STRING:
|
||||
String s = (String) fixedArgs[i].value;
|
||||
int l = s.length();
|
||||
if (l < CLIBLOBCUSTOMATTRIB_STRING_BOUNDARY_128) {
|
||||
struct.add(BYTE, "PackedLen", "");
|
||||
}
|
||||
else if (l < CLIBLOBCUSTOMATTRIB_STRING_BOUNDARY_192) {
|
||||
struct.add(WORD, "PackedLen", "");
|
||||
}
|
||||
else {
|
||||
struct.add(DWORD, "PackedLen", "");
|
||||
|
||||
}
|
||||
struct.add(UTF8, ((String) fixedArgs[i].value).length(), "FixedArg_" + i,
|
||||
"");
|
||||
break;
|
||||
|
||||
case ELEMENT_TYPE_I:
|
||||
struct.add(BYTE, "ELEMENT_TYPE_I", "");
|
||||
struct.add(UTF8, ((String) fixedArgs[i].value).length(), "FixedArg_" + i,
|
||||
"");
|
||||
break;
|
||||
|
||||
default:
|
||||
Msg.warn(this, "Unprocessed FixedArg element type in CustomAttr #" +
|
||||
(i + 1) + ": " + fixedArgs[i].getElem().name());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct.add(WORD, "NumNamed", "Number of NamedArgs to follow");
|
||||
|
||||
// Display the NamedArgs
|
||||
if (namedArgs != null) {
|
||||
for (CliNamedArg cliNamedArg : namedArgs) {
|
||||
int fieldOrProp = cliNamedArg.getFieldOrProp();
|
||||
if (fieldOrProp == CLIBLOBCUSTOMATTRIB_TYPE_FIELD) {
|
||||
struct.add(BYTE, "FieldOrProp", "FIELD");
|
||||
}
|
||||
else if (fieldOrProp == CLIBLOBCUSTOMATTRIB_TYPE_PROPERTY) {
|
||||
struct.add(BYTE, "FieldOrProp", "PROPERTY");
|
||||
}
|
||||
else {
|
||||
struct.add(BYTE, "FieldOrProp", "Unknown value");
|
||||
}
|
||||
|
||||
struct.add(BYTE, "FieldOrPropType", cliNamedArg.getFieldOrPropType().name());
|
||||
|
||||
int nameLen = cliNamedArg.getFieldOrPropName().length();
|
||||
if (nameLen < CLIBLOBCUSTOMATTRIB_STRING_BOUNDARY_128) {
|
||||
struct.add(BYTE, "PackedLen", "");
|
||||
}
|
||||
else if (nameLen < CLIBLOBCUSTOMATTRIB_STRING_BOUNDARY_192) {
|
||||
struct.add(WORD, "PackedLen", "");
|
||||
}
|
||||
else {
|
||||
struct.add(DWORD, "PackedLen", "");
|
||||
|
||||
}
|
||||
|
||||
struct.add(UTF8, nameLen, "FieldOrPropName", "");
|
||||
}
|
||||
}
|
||||
|
||||
return struct;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentsName() {
|
||||
return "CustomAttrib";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentsComment() {
|
||||
return "A CustomAttrib blob stores values of fixed or named parameters supplied when " +
|
||||
"instantiating a custom attribute";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepresentation() {
|
||||
return "Blob (" + getContentsDataType().getDisplayName() + ")";
|
||||
}
|
||||
|
||||
// SerStrings ("serialized strings") have a length field that varies in size
|
||||
// based on the length of the string. This measures and decodes the Byte, Word,
|
||||
// or DWord length field and returns it.
|
||||
private int readSerStringLength(BinaryReader reader) throws IOException {
|
||||
byte[] lengthBytes;
|
||||
int length = 0;
|
||||
ByteBuffer buf;
|
||||
|
||||
// The first byte is either the size or an indicator that we have more
|
||||
// size bytes ahead. Values contained in more than one bytes are stored
|
||||
// big-endian.
|
||||
byte firstByte = reader.readNextByte();
|
||||
|
||||
// Shift the highest two bits to the bottom and mask off to detect the
|
||||
// size of the field holding the size of the string (1, 2, or 4 bytes),
|
||||
// then cut the indicator bits off the first byte of the length.
|
||||
byte stringSizeIndicator = (byte) (firstByte >> CLIBLOBCUSTOMATTRIB_STRING_INDICATOR_SHIFT &
|
||||
CLIBLOBCUSTOMATTRIB_STRING_INDICATOR_BITMASK);
|
||||
firstByte = (byte) (firstByte & CLIBLOBCUSTOMATTRIB_STRING_SIZE_BITMASK);
|
||||
|
||||
if (stringSizeIndicator <= CLIBLOBCUSTOMATTRIB_STRING_SIZE_ONE) {
|
||||
length = firstByte;
|
||||
}
|
||||
else if (stringSizeIndicator == CLIBLOBCUSTOMATTRIB_STRING_SIZE_TWO) {
|
||||
lengthBytes = new byte[] { firstByte, reader.readNextByte() };
|
||||
|
||||
// Convert from big-endian
|
||||
buf = ByteBuffer.wrap(lengthBytes);
|
||||
buf.order(ByteOrder.BIG_ENDIAN);
|
||||
length = buf.getShort();
|
||||
}
|
||||
else if (stringSizeIndicator == CLIBLOBCUSTOMATTRIB_STRING_SIZE_FOUR) {
|
||||
lengthBytes = new byte[] { firstByte, reader.readNextByte(), reader.readNextByte(),
|
||||
reader.readNextByte() };
|
||||
|
||||
// Convert from big-endian
|
||||
buf = ByteBuffer.wrap(lengthBytes);
|
||||
buf.order(ByteOrder.BIG_ENDIAN);
|
||||
length = buf.getInt();
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
private ArrayList<CliFixedArg> processFixedArgs(BinaryReader reader, CliParam[] params)
|
||||
throws IOException {
|
||||
ArrayList<CliFixedArg> processFixedArgs = new ArrayList<>();
|
||||
if (params == null) {
|
||||
return processFixedArgs;
|
||||
}
|
||||
|
||||
for (CliParam param : params) {
|
||||
byte elemByte = reader.peekNextByte();
|
||||
if (elemByte == CliElementType.ELEMENT_TYPE_I.id()) {
|
||||
reader.readNextByte();
|
||||
|
||||
// IntPtr followed by a string of the name of the element, the
|
||||
// length of which is not specified and must be read until a
|
||||
// non-printable UTF-8 character is encountered to signal the
|
||||
// end of the name
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (((reader.peekNextByte() &
|
||||
CLIBLOBCUSTOMATTRIB_UTF8_HIGH) > CLIBLOBCUSTOMATTRIB_UTF8_LOW) &&
|
||||
((reader.peekNextByte() &
|
||||
CLIBLOBCUSTOMATTRIB_UTF8_HIGH) < CLIBLOBCUSTOMATTRIB_UTF8_HIGH)) {
|
||||
sb.append((char) reader.readNextByte());
|
||||
}
|
||||
|
||||
processFixedArgs.add(new CliFixedArg(CliElementType.ELEMENT_TYPE_I, sb.toString()));
|
||||
}
|
||||
else {
|
||||
// Process Elem types
|
||||
CliElementType baseTypeCode = param.getType().baseTypeCode;
|
||||
switch (baseTypeCode) {
|
||||
case ELEMENT_TYPE_BOOLEAN:
|
||||
addFixedArg(processFixedArgs, baseTypeCode, reader.readNextByte());
|
||||
break;
|
||||
|
||||
case ELEMENT_TYPE_CHAR:
|
||||
addFixedArg(processFixedArgs, baseTypeCode, reader.readNextShort());
|
||||
break;
|
||||
|
||||
case ELEMENT_TYPE_I1:
|
||||
addFixedArg(processFixedArgs, baseTypeCode, reader.readNextByte());
|
||||
break;
|
||||
|
||||
case ELEMENT_TYPE_U1:
|
||||
addFixedArg(processFixedArgs, baseTypeCode, reader.readNextUnsignedByte());
|
||||
break;
|
||||
|
||||
case ELEMENT_TYPE_I2:
|
||||
addFixedArg(processFixedArgs, baseTypeCode, reader.readNextShort());
|
||||
break;
|
||||
|
||||
case ELEMENT_TYPE_U2:
|
||||
addFixedArg(processFixedArgs, baseTypeCode, reader.readNextUnsignedShort());
|
||||
break;
|
||||
|
||||
case ELEMENT_TYPE_I4:
|
||||
addFixedArg(processFixedArgs, baseTypeCode, reader.readNextInt());
|
||||
break;
|
||||
|
||||
case ELEMENT_TYPE_U4:
|
||||
addFixedArg(processFixedArgs, baseTypeCode, reader.readNextUnsignedInt());
|
||||
break;
|
||||
|
||||
case ELEMENT_TYPE_I8:
|
||||
addFixedArg(processFixedArgs, baseTypeCode, reader.readNextByte());
|
||||
processFixedArgs.add(
|
||||
new CliFixedArg(param.getType().baseTypeCode, reader.readNextLong()));
|
||||
break;
|
||||
|
||||
case ELEMENT_TYPE_U8:
|
||||
addFixedArg(processFixedArgs, baseTypeCode,
|
||||
reader.readNextByteArray(LongLongDataType.dataType.getLength()));
|
||||
break;
|
||||
|
||||
case ELEMENT_TYPE_R4:
|
||||
addFixedArg(processFixedArgs, baseTypeCode,
|
||||
reader.readNextByteArray(Float4DataType.dataType.getLength()));
|
||||
break;
|
||||
|
||||
case ELEMENT_TYPE_R8:
|
||||
addFixedArg(processFixedArgs, baseTypeCode,
|
||||
reader.readNextByteArray(Float8DataType.dataType.getLength()));
|
||||
break;
|
||||
|
||||
case ELEMENT_TYPE_STRING:
|
||||
int length = readSerStringLength(reader);
|
||||
if (length > 0) {
|
||||
addFixedArg(processFixedArgs, baseTypeCode, new String(
|
||||
reader.readNextByteArray(length), StandardCharsets.UTF_8));
|
||||
}
|
||||
break;
|
||||
|
||||
case ELEMENT_TYPE_VALUETYPE:
|
||||
addFixedArg(processFixedArgs, baseTypeCode, reader.readNextInt());
|
||||
break;
|
||||
|
||||
default:
|
||||
Msg.info(this,
|
||||
"A CustomAttrib with an unprocessed element type was deteceted: " +
|
||||
param.getRepresentation());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return processFixedArgs;
|
||||
}
|
||||
|
||||
private void addFixedArg(ArrayList<CliFixedArg> fixedArgs, CliElementType baseTypeCode,
|
||||
Object value) {
|
||||
fixedArgs.add(new CliFixedArg(baseTypeCode, value));
|
||||
}
|
||||
|
||||
private ArrayList<CliNamedArg> processNamedArgs(BinaryReader reader) throws IOException {
|
||||
numNamed = reader.readNextShort();
|
||||
|
||||
// Process zero to multiple NamedArgs here
|
||||
ArrayList<CliNamedArg> processNamedArgs = new ArrayList<>();
|
||||
for (int i = 0; i < numNamed; i++) {
|
||||
byte fieldOrProp = reader.readNextByte();
|
||||
if ((fieldOrProp != CLIBLOBCUSTOMATTRIB_TYPE_FIELD) &&
|
||||
fieldOrProp != CLIBLOBCUSTOMATTRIB_TYPE_PROPERTY) {
|
||||
Msg.warn(this, "Invalid FieldOrProp value in NamedArg #" + (i + 1) + ": 0x" +
|
||||
Integer.toHexString(fieldOrProp));
|
||||
continue;
|
||||
}
|
||||
|
||||
CliElementType fieldOrPropType = CliElementType.fromInt(reader.readNextByte());
|
||||
|
||||
// +1 to account for the null terminator
|
||||
int nameLen = readSerStringLength(reader) + 1;
|
||||
String fieldOrPropName =
|
||||
new String(reader.readNextByteArray(nameLen), StandardCharsets.UTF_8);
|
||||
|
||||
processNamedArgs.add(new CliNamedArg(fieldOrProp, fieldOrPropType, fieldOrPropName));
|
||||
}
|
||||
|
||||
return processNamedArgs;
|
||||
}
|
||||
}
|
@ -26,12 +26,14 @@ import ghidra.util.exception.InvalidInputException;
|
||||
public class CliSigField extends CliAbstractSig {
|
||||
private CliParam type;
|
||||
|
||||
private long dataOffset;
|
||||
private static final byte CLISIGFIELD_PROLOG = 0x06;
|
||||
|
||||
public CliSigField(CliBlob blob) throws IOException {
|
||||
super(blob);
|
||||
|
||||
BinaryReader reader = getContentsReader();
|
||||
dataOffset = reader.getPointerIndex();
|
||||
|
||||
byte prolog = reader.readNextByte();
|
||||
if (prolog != CLISIGFIELD_PROLOG) {
|
||||
@ -67,7 +69,7 @@ public class CliSigField extends CliAbstractSig {
|
||||
public DataType getContentsDataType() {
|
||||
StructureDataType struct = new StructureDataType(new CategoryPath(PATH), getName(), 0);
|
||||
struct.add(BYTE, "FIELD", "Magic (0x06)");
|
||||
struct.add(type.getDefinitionDataType(), "Type", null);
|
||||
struct.add(type.getDefinitionDataType(), type.getType().baseTypeCode.toString(), null);
|
||||
return struct;
|
||||
}
|
||||
|
||||
|
@ -85,17 +85,19 @@ public class CliSigMethodDef extends CliAbstractSig {
|
||||
@Override
|
||||
public DataType getContentsDataType() {
|
||||
StructureDataType struct = new StructureDataType(new CategoryPath(PATH), getName(), 0);
|
||||
struct.add(BYTE, "flags", "ORed calling convention and THIS presence"); // TODO: enum
|
||||
struct.add(BYTE, "Flags", "ORed VARARG/GENERIC/HASTHIS/EXPLICITTHIS"); // TODO: enum
|
||||
if (genericParamCount > 0) {
|
||||
struct.add(getDataTypeForBytes(sizeOfGenericCount), "GenParamCount",
|
||||
"Number of generic paramameters for the method");
|
||||
}
|
||||
struct.add(getDataTypeForBytes(sizeOfCount), "Count",
|
||||
"Number of param types to follow RetType");
|
||||
"Number of parameter types to follow RetType");
|
||||
struct.add(retType.getDefinitionDataType(), "RetType", null);
|
||||
for (CliParam param : params) {
|
||||
struct.add(param.getDefinitionDataType(), null, null);
|
||||
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
struct.add(params[i].getDefinitionDataType(), "Param" + i, null);
|
||||
}
|
||||
|
||||
return struct;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
|
||||
public class CliSigMethodRef extends CliAbstractSig {
|
||||
|
||||
private long dataOffset;
|
||||
private CliRetType retType;
|
||||
private CliParam params[];
|
||||
private int sizeOfCount;
|
||||
@ -45,6 +45,8 @@ public class CliSigMethodRef extends CliAbstractSig {
|
||||
|
||||
// Flags is similar to a MethodDef unless vararg is used.
|
||||
BinaryReader reader = getContentsReader();
|
||||
dataOffset = reader.getPointerIndex();
|
||||
|
||||
flags = reader.readNextByte();
|
||||
|
||||
if ((flags & METHODREFSIG_FLAGS_GENERIC) == METHODREFSIG_FLAGS_GENERIC) {
|
||||
@ -90,16 +92,21 @@ public class CliSigMethodRef extends CliAbstractSig {
|
||||
@Override
|
||||
public DataType getContentsDataType() {
|
||||
StructureDataType struct = new StructureDataType(new CategoryPath(PATH), getName(), 0);
|
||||
struct.add(BYTE, "FirstByte", "ORed VARARG and HASTHIS/EXPLICITTHIS");
|
||||
struct.add(BYTE, "Flags", "ORed VARARG/GENERIC/HASTHIS/EXPLICITTHIS");
|
||||
if (genericParamCount > 0) {
|
||||
struct.add(getDataTypeForBytes(sizeOfGenericCount), "GenParamCount",
|
||||
"Number of generic paramameters for the method");
|
||||
}
|
||||
struct.add(getDataTypeForBytes(sizeOfCount), "ParamCount",
|
||||
"Number of param types to follow RetType");
|
||||
"Number of parameter types to follow RetType");
|
||||
struct.add(retType.getDefinitionDataType(), "RetType", null);
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
struct.add(params[i].getDefinitionDataType(), "Type" + i, null);
|
||||
if (sentinelIndex == i) {
|
||||
struct.add(CliTypeCodeDataType.dataType,
|
||||
CliElementType.ELEMENT_TYPE_SENTINEL.toString(), "SENTINEL");
|
||||
}
|
||||
|
||||
struct.add(params[i].getDefinitionDataType(), "Param" + i, null);
|
||||
}
|
||||
return struct;
|
||||
}
|
||||
|
@ -29,13 +29,22 @@ public class CliMethodDef implements StructConverter {
|
||||
|
||||
private Address addr;
|
||||
|
||||
private boolean isFatHeader;
|
||||
private boolean hasMoreSections;
|
||||
private boolean initLocals;
|
||||
public enum HeaderFormat {
|
||||
Fat, Tiny
|
||||
}
|
||||
|
||||
private int headerFlags;
|
||||
private HeaderFormat headerFormat;
|
||||
private int headerSize; // Size of this header
|
||||
private int maxStack; // Max number of items on operand stack
|
||||
private int methodSize; // Size of method body (code)
|
||||
private int localVarSigTok;
|
||||
|
||||
private static final int CLIMETHODDEF_HEADER_FLAGS_SHIFT = 0x08;
|
||||
private static final int CLIMETHODDEF_HEADER_FLAGS_MASK = 0x0FFF;
|
||||
private static final int CLIMETHODDEF_HEADER_SIZE_SHIFT = 0x0C;
|
||||
private static final int CLIMETHODDEF_HEADER_SIZE_FAT_MULTIPLIER = 0x04;
|
||||
|
||||
private static final byte CorILMethod_TinyFormat = 0x2;
|
||||
private static final byte CorILMethod_FatFormat = 0x3;
|
||||
private static final byte CorILMethod_MoreSects = 0x8;
|
||||
@ -45,48 +54,51 @@ public class CliMethodDef implements StructConverter {
|
||||
this.addr = addr;
|
||||
|
||||
// Read first byte, see if tiny or fat.
|
||||
byte one = reader.readNextByte();
|
||||
if ((one & CorILMethod_FatFormat) == CorILMethod_FatFormat) {
|
||||
isFatHeader = true;
|
||||
if ((one & CorILMethod_MoreSects) == CorILMethod_MoreSects)
|
||||
hasMoreSections = true;
|
||||
if ((one & CorILMethod_InitLocals) == CorILMethod_InitLocals)
|
||||
initLocals = true;
|
||||
byte two = reader.readNextByte(); // TODO: need to read byte two? Seems to only have header length (in the wrong order?? >_<)
|
||||
int firstByte = reader.readNextUnsignedByte();
|
||||
if ((firstByte & CorILMethod_FatFormat) == CorILMethod_FatFormat) {
|
||||
headerFormat = HeaderFormat.Fat;
|
||||
|
||||
// The header flags are stored across 12 bits, the top 4 bits
|
||||
// indicate the size of the header
|
||||
headerFlags =
|
||||
(firstByte << CLIMETHODDEF_HEADER_FLAGS_SHIFT) + reader.readNextUnsignedByte();
|
||||
headerSize = headerFlags >> CLIMETHODDEF_HEADER_SIZE_SHIFT;
|
||||
headerFlags = (headerFlags & CLIMETHODDEF_HEADER_FLAGS_MASK);
|
||||
|
||||
// The raw header size bits indicate: "Size of this header
|
||||
// expressed as the count of 4-bytes integers occupied."
|
||||
headerSize = headerSize * CLIMETHODDEF_HEADER_SIZE_FAT_MULTIPLIER;
|
||||
|
||||
maxStack = reader.readNextShort();
|
||||
methodSize = reader.readNextInt();
|
||||
localVarSigTok = reader.readNextInt();
|
||||
}
|
||||
else if ((one & CorILMethod_TinyFormat) == CorILMethod_TinyFormat) {
|
||||
isFatHeader = false;
|
||||
hasMoreSections = false;
|
||||
initLocals = false;
|
||||
else if ((firstByte & CorILMethod_TinyFormat) == CorILMethod_TinyFormat) {
|
||||
headerFormat = HeaderFormat.Tiny;
|
||||
headerSize = 1;
|
||||
headerFlags = 0;
|
||||
maxStack = 8;
|
||||
methodSize = (((one & ~0x3) & 0xff) >> 2); // Mask off first 2 bits, right shift to get 6 length bits. 0xff mask to convert to right sign.
|
||||
methodSize = (((firstByte & ~0x3) & 0xff) >> 2); // Mask off first 2 bits, right shift to get 6 length bits. 0xff mask to convert to right sign.
|
||||
}
|
||||
}
|
||||
|
||||
private void fillTinyHeaderType(Structure struct) {
|
||||
struct.add(BYTE, "Size+Flags", "L.S. Bits 0:1 Flags, Bits 2:7 Size of method in Bytes");
|
||||
}
|
||||
|
||||
private void fillFatHeaderType(Structure struct) {
|
||||
struct.add( WORD, "Size+Flags", "L.S. Bits 0:3 Size of hdr in B, Bits 4:15 Flags");
|
||||
struct.add( WORD, "MaxStack", "Maximum number of items on the operand stack");
|
||||
struct.add(DWORD, "CodeSize", "Size of actual method body in B");
|
||||
struct.add(DWORD, "LocalVarSigTok", "Signature for the local variables of the method. 0 means no locals. References standalone signature in Metadata tables, which references #Blob heap.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
StructureDataType struct =
|
||||
new StructureDataType(new CategoryPath(PATH), "MethodDefHdr_" + addr, 0);
|
||||
if (isFatHeader) {
|
||||
fillFatHeaderType(struct);
|
||||
StructureDataType struct;
|
||||
|
||||
if (headerFormat == HeaderFormat.Fat) {
|
||||
struct = new StructureDataType(new CategoryPath(PATH), "MethodDefHdr_Fat", 0);
|
||||
struct.add(WORD, "Size+Flags", "L.S. Bits 0:3 Size of hdr in bytes, Bits 4:15 Flags");
|
||||
struct.add(WORD, "MaxStack", "Maximum number of items on the operand stack");
|
||||
struct.add(DWORD, "CodeSize", "Size of actual method body in bytes");
|
||||
struct.add(DWORD, "LocalVarSigTok",
|
||||
"Signature for the local variables of the method. 0 means no locals. References standalone signature in Metadata tables, which references #Blob heap.");
|
||||
}
|
||||
else {
|
||||
fillTinyHeaderType(struct);
|
||||
struct = new StructureDataType(new CategoryPath(PATH), "MethodDefHdr_Tiny", 0);
|
||||
struct.add(BYTE, "Size+Flags", "L.S. Bits 0:1 Flags, Bits 2:7 Size of method in Bytes");
|
||||
}
|
||||
|
||||
return struct;
|
||||
}
|
||||
|
||||
@ -95,7 +107,14 @@ public class CliMethodDef implements StructConverter {
|
||||
}
|
||||
|
||||
public boolean hasMoreSections() {
|
||||
return hasMoreSections;
|
||||
return (headerFlags & CorILMethod_MoreSects) == CorILMethod_MoreSects;
|
||||
}
|
||||
|
||||
public boolean hasLocals() {
|
||||
return (headerFlags & CorILMethod_InitLocals) == CorILMethod_InitLocals;
|
||||
}
|
||||
|
||||
public HeaderFormat getHeaderFormat() {
|
||||
return headerFormat;
|
||||
}
|
||||
}
|
||||
|
@ -435,7 +435,7 @@ public class CliStreamMetadata extends CliAbstractStream {
|
||||
table.markup(program, isBinary, monitor, log, ntHeader);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.error(this, "Failed to markup " + table);
|
||||
Msg.error(this, "Failed to markup " + table + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,12 +18,20 @@ package ghidra.app.util.bin.format.pe.cli.tables;
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.format.pe.cli.streams.CliStreamMetadata;
|
||||
import ghidra.app.util.bin.format.pe.NTHeader;
|
||||
import ghidra.app.util.bin.format.pe.cli.blobs.CliBlobCustomAttrib;
|
||||
import ghidra.app.util.bin.format.pe.cli.streams.*;
|
||||
import ghidra.app.util.bin.format.pe.cli.tables.indexes.CliIndexCustomAttributeType;
|
||||
import ghidra.app.util.bin.format.pe.cli.tables.indexes.CliIndexHasCustomAttribute;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.CategoryPath;
|
||||
import ghidra.program.model.data.StructureDataType;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Describes the CustomAttribute table.
|
||||
@ -45,13 +53,17 @@ public class CliTableCustomAttribute extends CliAbstractTable {
|
||||
public String getRepresentation() {
|
||||
String parentRep, typeRep;
|
||||
try {
|
||||
parentRep = getRowRepresentationSafe(CliIndexHasCustomAttribute.getTableName(parentIndex), CliIndexHasCustomAttribute.getRowIndex(parentIndex));
|
||||
parentRep =
|
||||
getRowRepresentationSafe(CliIndexHasCustomAttribute.getTableName(parentIndex),
|
||||
CliIndexHasCustomAttribute.getRowIndex(parentIndex));
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
parentRep = Integer.toHexString(parentIndex);
|
||||
}
|
||||
try {
|
||||
typeRep = getRowRepresentationSafe(CliIndexCustomAttributeType.getTableName(parentIndex), CliIndexCustomAttributeType.getRowIndex(parentIndex));
|
||||
typeRep =
|
||||
getRowRepresentationSafe(CliIndexCustomAttributeType.getTableName(parentIndex),
|
||||
CliIndexCustomAttributeType.getRowIndex(parentIndex));
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
typeRep = Integer.toHexString(typeIndex);
|
||||
@ -59,10 +71,13 @@ public class CliTableCustomAttribute extends CliAbstractTable {
|
||||
return String.format("Parent %s Type %s Value %x", parentRep, typeRep, valueIndex);
|
||||
}
|
||||
}
|
||||
public CliTableCustomAttribute(BinaryReader reader, CliStreamMetadata stream, CliTypeTable tableId) throws IOException {
|
||||
|
||||
public CliTableCustomAttribute(BinaryReader reader, CliStreamMetadata stream,
|
||||
CliTypeTable tableId) throws IOException {
|
||||
super(reader, stream, tableId);
|
||||
for (int i = 0; i < this.numRows; i++) {
|
||||
CliCustomAttributeRow row = new CliCustomAttributeRow(CliIndexHasCustomAttribute.readCodedIndex(reader, stream),
|
||||
CliCustomAttributeRow row = new CliCustomAttributeRow(
|
||||
CliIndexHasCustomAttribute.readCodedIndex(reader, stream),
|
||||
CliIndexCustomAttributeType.readCodedIndex(reader, stream), readBlobIndex(reader));
|
||||
rows.add(row);
|
||||
blobs.add(row.valueIndex);
|
||||
@ -72,10 +87,31 @@ public class CliTableCustomAttribute extends CliAbstractTable {
|
||||
|
||||
@Override
|
||||
public StructureDataType getRowDataType() {
|
||||
StructureDataType rowDt = new StructureDataType(new CategoryPath(PATH), "CustomAttribute Row", 0);
|
||||
StructureDataType rowDt =
|
||||
new StructureDataType(new CategoryPath(PATH), "CustomAttribute Row", 0);
|
||||
rowDt.add(CliIndexHasCustomAttribute.toDataType(metadataStream), "Parent", null);
|
||||
rowDt.add(CliIndexCustomAttributeType.toDataType(metadataStream), "Type", null);
|
||||
rowDt.add(metadataStream.getBlobIndexDataType(), "Value", null);
|
||||
return rowDt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markup(Program program, boolean isBinary, TaskMonitor monitor, MessageLog log,
|
||||
NTHeader ntHeader)
|
||||
throws DuplicateNameException, CodeUnitInsertionException, IOException {
|
||||
CliStreamBlob blobStream = metadataStream.getBlobStream();
|
||||
|
||||
for (CliAbstractTableRow row : rows) {
|
||||
CliCustomAttributeRow customRow = (CliCustomAttributeRow) row;
|
||||
int valueIndex = customRow.valueIndex;
|
||||
|
||||
Address addr = CliAbstractStream.getStreamMarkupAddress(program, isBinary, monitor, log,
|
||||
ntHeader, blobStream, valueIndex);
|
||||
|
||||
// Create CustomAttrib Blob object
|
||||
CliBlobCustomAttrib blob =
|
||||
new CliBlobCustomAttrib(blobStream.getBlob(valueIndex), customRow, metadataStream);
|
||||
blobStream.updateBlob(blob, addr, program);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import ghidra.program.model.data.CategoryPath;
|
||||
import ghidra.program.model.data.StructureDataType;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
@ -100,16 +101,24 @@ public class CliTableField extends CliAbstractTable {
|
||||
public void markup(Program program, boolean isBinary, TaskMonitor monitor, MessageLog log,
|
||||
NTHeader ntHeader)
|
||||
throws DuplicateNameException, CodeUnitInsertionException, IOException {
|
||||
int fieldRowIndex = 0;
|
||||
for (CliAbstractTableRow row : rows) {
|
||||
CliFieldRow fieldRow = (CliFieldRow) row;
|
||||
fieldRowIndex++;
|
||||
|
||||
// Create FieldSig object and bookmark it
|
||||
// Create FieldSig object
|
||||
Address sigAddr = CliAbstractStream.getStreamMarkupAddress(program, isBinary, monitor,
|
||||
log, ntHeader, metadataStream.getBlobStream(), fieldRow.sigIndex);
|
||||
|
||||
CliSigField fieldSig =
|
||||
new CliSigField(metadataStream.getBlobStream().getBlob(fieldRow.sigIndex));
|
||||
metadataStream.getBlobStream().updateBlob(fieldSig, sigAddr, program);
|
||||
|
||||
if (!metadataStream.getBlobStream().updateBlob(fieldSig, sigAddr, program)) {
|
||||
Msg.warn(CliTableField.class,
|
||||
"Couldn't update FieldSig blob " +
|
||||
metadataStream.getStringsStream().getString(fieldRow.nameIndex) +
|
||||
" at Field table index " + fieldRowIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,14 +16,13 @@
|
||||
package ghidra.app.util.bin.format.pe.cli.tables;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.MemoryByteProvider;
|
||||
import ghidra.app.util.bin.format.pe.NTHeader;
|
||||
import ghidra.app.util.bin.format.pe.PeUtils;
|
||||
import ghidra.app.util.bin.format.pe.cli.blobs.CliAbstractSig.CliParam;
|
||||
import ghidra.app.util.bin.format.pe.cli.blobs.CliAbstractSig.*;
|
||||
import ghidra.app.util.bin.format.pe.cli.blobs.CliBlob;
|
||||
import ghidra.app.util.bin.format.pe.cli.blobs.CliSigMethodDef;
|
||||
import ghidra.app.util.bin.format.pe.cli.methods.CliMethodDef;
|
||||
@ -31,6 +30,8 @@ import ghidra.app.util.bin.format.pe.cli.methods.CliMethodExtraSections;
|
||||
import ghidra.app.util.bin.format.pe.cli.streams.CliAbstractStream;
|
||||
import ghidra.app.util.bin.format.pe.cli.streams.CliStreamMetadata;
|
||||
import ghidra.app.util.bin.format.pe.cli.tables.CliTableParam.CliParamRow;
|
||||
import ghidra.app.util.bin.format.pe.cli.tables.CliTableTypeDef.CliTypeDefRow;
|
||||
import ghidra.app.util.bin.format.pe.cli.tables.CliTableTypeRef.CliTypeRefRow;
|
||||
import ghidra.app.util.bin.format.pe.cli.tables.flags.CliFlags.CliEnumMethodAttributes;
|
||||
import ghidra.app.util.bin.format.pe.cli.tables.flags.CliFlags.CliEnumMethodImplAttributes;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
@ -40,6 +41,7 @@ import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.listing.Function.FunctionUpdateType;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.program.model.symbol.SymbolUtilities;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
@ -52,6 +54,8 @@ import ghidra.util.task.TaskMonitor;
|
||||
*/
|
||||
public class CliTableMethodDef extends CliAbstractTable {
|
||||
|
||||
private static final int CLITABLEMETHODDEF_PINVOKE_JUMP_LENGTH = 0x06;
|
||||
|
||||
public class CliMethodDefRow extends CliAbstractTableRow {
|
||||
public int RVA;
|
||||
public short ImplFlags; // MethodImplAttributes
|
||||
@ -63,6 +67,42 @@ public class CliTableMethodDef extends CliAbstractTable {
|
||||
private static final int NEXT_ROW_PARAM_INIT_VALUE = -1;
|
||||
private int nextRowParamIndex = NEXT_ROW_PARAM_INIT_VALUE;
|
||||
|
||||
private static final int METHODIMPLATTRIBUTES_CODETYPE_IL = 0x00;
|
||||
private static final int METHODIMPLATTRIBUTES_CODETYPE_NATIVE = 0x01;
|
||||
private static final int METHODIMPLATTRIBUTES_CODETYPE_OPTIL = 0x02;
|
||||
private static final int METHODIMPLATTRIBUTES_CODETYPE_RUNTIME = 0x03;
|
||||
private static final int METHODIMPLATTRIBUTES_MANAGED_MANAGED = 0x00;
|
||||
private static final int METHODIMPLATTRIBUTES_MANAGED_UNMANAGED = 0x04;
|
||||
private static final int METHODIMPLATTRIBUTES_FORWARDREF = 0x10;
|
||||
private static final int METHODIMPLATTRIBUTES_PRESERVESIG = 0x80;
|
||||
private static final int METHODIMPLATTRIBUTES_INTERNALCALL = 0x1000;
|
||||
private static final int METHODIMPLATTRIBUTES_SYNCHRONIZED = 0x20;
|
||||
private static final int METHODIMPLATTRIBUTES_NOINLINING = 0x08;
|
||||
private static final int METHODIMPLATTRIBUTES_AGGRESSIVEINLINING = 0x1000;
|
||||
private static final int METHODIMPLATTRIBUTES_MAXMETHODIMPLVAL = 0xffff;
|
||||
|
||||
private static final int METHODATTRIBUTES_MEMBERACCESS_COMPILERCONTROLLED = 0x00;
|
||||
private static final int METHODATTRIBUTES_MEMBERACCESS_PRIVATE = 0x01;
|
||||
private static final int METHODATTRIBUTES_MEMBERACCESS_FAMANDASSEM = 0x02;
|
||||
private static final int METHODATTRIBUTES_MEMBERACCESS_ASSEM = 0x03;
|
||||
private static final int METHODATTRIBUTES_MEMBERACCESS_FAMILY = 0x04;
|
||||
private static final int METHODATTRIBUTES_MEMBERACCESS_FAMORASSEM = 0x05;
|
||||
private static final int METHODATTRIBUTES_MEMBERACCESS_PUBLIC = 0x06;
|
||||
private static final int METHODATTRIBUTES_STATIC = 0x10;
|
||||
private static final int METHODATTRIBUTES_FINAL = 0x20;
|
||||
private static final int METHODATTRIBUTES_VIRTUAL = 0x40;
|
||||
private static final int METHODATTRIBUTES_HIDEBYSIG = 0x80;
|
||||
private static final int METHODATTRIBUTES_VTABLELAYOUT_REUSESLOT = 0x0000;
|
||||
private static final int METHODATTRIBUTES_VTABLELAYOUT_NEWSLOT = 0x0100;
|
||||
private static final int METHODATTRIBUTES_STRICT = 0x0200;
|
||||
private static final int METHODATTRIBUTES_ABSTRACT = 0x0400;
|
||||
private static final int METHODATTRIBUTES_SPECIALNAME = 0x0800;
|
||||
private static final int METHODATTRIBUTES_PINVOKEIMPL = 0x2000;
|
||||
private static final int METHODATTRIBUTES_UNMANAGEDEXPORT = 0x08;
|
||||
private static final int METHODATTRIBUTES_RTSPECIALNAME = 0x1000;
|
||||
private static final int METHODATTRIBUTES_HASSECURITY = 0x4000;
|
||||
private static final int METHODATTRIBUTES_REQUIRESECOBJECT = 0x8000;
|
||||
|
||||
public CliMethodDefRow(int rva, short implFlags, short flags, int nameIndex, int sigIndex,
|
||||
int paramIndex) {
|
||||
this.RVA = rva;
|
||||
@ -141,6 +181,28 @@ public class CliTableMethodDef extends CliAbstractTable {
|
||||
CliEnumMethodImplAttributes.dataType.getName(ImplFlags & 0xffff),
|
||||
CliEnumMethodAttributes.dataType.getName(Flags & 0xffff));
|
||||
}
|
||||
|
||||
// Static functions have four parameters but the first is an implied
|
||||
// pointer to the object they're associated with. It's not in the
|
||||
// ParameterTable and looking for the extra parameter will walk
|
||||
// you into the parameter of another function.
|
||||
boolean isStatic() {
|
||||
return (Flags & METHODATTRIBUTES_STATIC) == METHODATTRIBUTES_STATIC;
|
||||
}
|
||||
|
||||
boolean isPInvokeImpl() {
|
||||
return (Flags & METHODATTRIBUTES_PINVOKEIMPL) == METHODATTRIBUTES_PINVOKEIMPL;
|
||||
}
|
||||
|
||||
boolean isNative() {
|
||||
return (ImplFlags &
|
||||
METHODIMPLATTRIBUTES_CODETYPE_NATIVE) == METHODIMPLATTRIBUTES_CODETYPE_NATIVE;
|
||||
}
|
||||
|
||||
boolean isManaged() {
|
||||
return (ImplFlags &
|
||||
METHODIMPLATTRIBUTES_CODETYPE_IL) == METHODIMPLATTRIBUTES_CODETYPE_IL;
|
||||
}
|
||||
}
|
||||
|
||||
public CliTableMethodDef(BinaryReader reader, CliStreamMetadata stream, CliTypeTable tableId)
|
||||
@ -159,7 +221,7 @@ public class CliTableMethodDef extends CliAbstractTable {
|
||||
}
|
||||
lastRow = row;
|
||||
}
|
||||
reader.setPointerIndex(this.readerOffset); // TODO: why do this, also elsewhere
|
||||
reader.setPointerIndex(this.readerOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -167,46 +229,45 @@ public class CliTableMethodDef extends CliAbstractTable {
|
||||
NTHeader ntHeader)
|
||||
throws DuplicateNameException, CodeUnitInsertionException, IOException {
|
||||
|
||||
int rvaZero = 0;
|
||||
|
||||
int methodRowIndex = 0;
|
||||
for (CliAbstractTableRow method : rows) {
|
||||
methodRowIndex++;
|
||||
|
||||
CliMethodDefRow methodRow = (CliMethodDefRow) method;
|
||||
|
||||
// This indicates the method is abstract, runtime, or PInvokeImpl
|
||||
if (methodRow.RVA == 0) {
|
||||
rvaZero++;
|
||||
continue;
|
||||
}
|
||||
|
||||
Address addr = PeUtils.getMarkupAddress(program, isBinary, ntHeader, methodRow.RVA);
|
||||
Address startAddr = addr;
|
||||
Address endAddr = addr;
|
||||
|
||||
if (methodRow.isPInvokeImpl() && methodRow.isNative()) {
|
||||
endAddr = startAddr.add(CLITABLEMETHODDEF_PINVOKE_JUMP_LENGTH - 1);
|
||||
}
|
||||
else {
|
||||
// Create MethodDef at this RVA
|
||||
BinaryReader reader =
|
||||
new BinaryReader(new MemoryByteProvider(program.getMemory(), addr),
|
||||
!program.getMemory().isBigEndian());
|
||||
CliMethodDef methodDef = new CliMethodDef(addr, reader);
|
||||
|
||||
PeUtils.createData(program, addr, methodDef.toDataType(), log);
|
||||
DataType methodDefDataType = methodDef.toDataType();
|
||||
PeUtils.createData(program, addr, methodDefDataType, log);
|
||||
|
||||
// Get the function's address space, default to zero-length just in case
|
||||
Address startAddr = addr.add(methodDef.toDataType().getLength());
|
||||
Address endAddr = startAddr;
|
||||
startAddr = addr.add(methodDefDataType.getLength());
|
||||
endAddr = startAddr;
|
||||
if (methodDef.getMethodSize() > 0) {
|
||||
endAddr = startAddr.add(methodDef.getMethodSize() - 1);
|
||||
}
|
||||
AddressSetView funcAddrSet = new AddressSet(startAddr, endAddr);
|
||||
|
||||
// Let Ghidra assign a default function name and then try to decode the
|
||||
// real one if it exists
|
||||
String funcName = null;
|
||||
if (methodRow.nameIndex > 0) {
|
||||
funcName = metadataStream.getStringsStream().getString(methodRow.nameIndex);
|
||||
}
|
||||
|
||||
// Do extra data sections in MethodDef
|
||||
if (methodDef.hasMoreSections()) {
|
||||
int extraSectionOffset =
|
||||
methodDef.toDataType().getLength() + methodDef.getMethodSize();
|
||||
methodDefDataType.getLength() + methodDef.getMethodSize();
|
||||
|
||||
// Round up to the next offset divisible by 4
|
||||
extraSectionOffset = ((extraSectionOffset + 3) / 4) * 4;
|
||||
@ -216,6 +277,17 @@ public class CliTableMethodDef extends CliAbstractTable {
|
||||
Address extraSectionAddr = addr.add(extraSectionOffset);
|
||||
PeUtils.createData(program, extraSectionAddr, extraSections.toDataType(), log);
|
||||
}
|
||||
}
|
||||
|
||||
AddressSetView funcAddrSet = new AddressSet(startAddr, endAddr);
|
||||
|
||||
// Let Ghidra assign a default function name and then try to decode the
|
||||
// real one if it exists
|
||||
String funcName = null;
|
||||
if (methodRow.nameIndex > 0) {
|
||||
funcName = SymbolUtilities.replaceInvalidChars(
|
||||
metadataStream.getStringsStream().getString(methodRow.nameIndex), true);
|
||||
}
|
||||
|
||||
// Get the function signature blob
|
||||
CliBlob blob = metadataStream.getBlobStream().getBlob(methodRow.sigIndex);
|
||||
@ -227,20 +299,58 @@ public class CliTableMethodDef extends CliAbstractTable {
|
||||
metadataStream.getBlobStream().updateBlob(methodSig, sigAddr, program);
|
||||
DataType returnType = methodSig.getReturnType().getExecutionDataType();
|
||||
|
||||
// Pull apart the function parameter names and types
|
||||
int maxSequence = 0;
|
||||
int stackOffset = 0;
|
||||
CliParam paramTypes[] = methodSig.getParamTypes();
|
||||
int paramCount = paramTypes.length;
|
||||
CliTableParam paramTable = (CliTableParam) metadataStream.getTable(CliTypeTable.Param);
|
||||
ParameterImpl parameters[] = new ParameterImpl[paramTypes.length];
|
||||
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
CliParamRow paramRow = (CliParamRow) paramTable.getRow(methodRow.paramIndex + i);
|
||||
// Store the parameters in a Hashtable because by the time processing
|
||||
// finishes the number of actual parameters might change
|
||||
HashMap<Integer, ParameterImpl> parameterList = new HashMap<Integer, ParameterImpl>();
|
||||
|
||||
String paramName = metadataStream.getStringsStream().getString(paramRow.nameIndex);
|
||||
DataType dataType = paramTypes[i].getExecutionDataType();
|
||||
// Some Static function first parameters being pointers to a ValueType
|
||||
// have the same number of parameters specified, but one or more are implied
|
||||
// pointers to the object they're associated with. It's not in the Parameter
|
||||
// Table and looking for the extra parameter in the table will walk you
|
||||
// into the parameter list of another function.
|
||||
ParameterImpl staticParameter = null;
|
||||
if (methodRow.isStatic() && paramCount > 0) {
|
||||
CliParam staticParam = paramTypes[0];
|
||||
String paramName = "";
|
||||
|
||||
// Walk the path from the ELEMENT_TYPE_PTR to the ELEMENT_TYPE_VALUETYPE
|
||||
if (staticParam.getType() instanceof CliTypePtr) {
|
||||
CliTypePtr ptrToValueType = (CliTypePtr) staticParam.getType();
|
||||
if (ptrToValueType.getType() instanceof CliTypeValueType) {
|
||||
CliTypeValueType valueType = (CliTypeValueType) ptrToValueType.getType();
|
||||
|
||||
// Get the table and row specifying the type name
|
||||
CliTypeTable tableType = valueType.getTable();
|
||||
int rowIndex = valueType.getRowIndex();
|
||||
|
||||
int paramNameStringIndex = 0;
|
||||
CliAbstractTable table = metadataStream.getTable(tableType);
|
||||
CliAbstractTableRow row = table.getRow(rowIndex);
|
||||
if (tableType.id() == tableType.TypeDef.id()) {
|
||||
CliTypeDefRow typeDefRow = (CliTypeDefRow) row;
|
||||
paramNameStringIndex = typeDefRow.typeNameIndex;
|
||||
}
|
||||
else if (tableType.id() == tableType.TypeRef.id()) {
|
||||
CliTypeRefRow typeRefRow = (CliTypeRefRow) row;
|
||||
paramNameStringIndex = typeRefRow.typeNameIndex;
|
||||
}
|
||||
|
||||
if (paramNameStringIndex > 0) {
|
||||
paramName =
|
||||
metadataStream.getStringsStream().getString(paramNameStringIndex);
|
||||
paramName = SymbolUtilities.replaceInvalidChars(paramName, true);
|
||||
|
||||
DataType dataType = staticParam.getExecutionDataType();
|
||||
|
||||
try {
|
||||
parameters[i] = new ParameterImpl(paramName, dataType, stackOffset, program);
|
||||
staticParameter =
|
||||
new ParameterImpl(paramName, dataType, stackOffset, program);
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
Msg.warn(this, "Error processing parameter \"" + paramName +
|
||||
@ -248,6 +358,77 @@ public class CliTableMethodDef extends CliAbstractTable {
|
||||
}
|
||||
|
||||
stackOffset += dataType.getLength();
|
||||
|
||||
paramCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pull apart the function's Param table entries
|
||||
for (int i = 0; i < paramCount; i++) {
|
||||
CliParamRow paramRow = (CliParamRow) paramTable.getRow(methodRow.paramIndex + i);
|
||||
|
||||
if (paramRow.sequence > maxSequence) {
|
||||
maxSequence = paramRow.sequence;
|
||||
}
|
||||
|
||||
String paramName = SymbolUtilities.replaceInvalidChars(
|
||||
metadataStream.getStringsStream().getString(paramRow.nameIndex), true);
|
||||
|
||||
DataType dataType = paramTypes[i].getExecutionDataType();
|
||||
|
||||
if (paramRow.sequence == 0) {
|
||||
// Parameters with a 0 sequence number are the return type,
|
||||
// reduce the size of the array and put any previously discovered
|
||||
// parameters into it
|
||||
returnType = dataType;
|
||||
}
|
||||
else {
|
||||
// Parameters are placed in the proper order based on the sequence
|
||||
// field (1-based) to compensate for some static methods having an implied
|
||||
// first parameter that won't be represented in the Parameter Table
|
||||
// and some return types that are represented as parameters.
|
||||
try {
|
||||
parameterList.put((int) paramRow.sequence,
|
||||
new ParameterImpl(paramName, dataType, stackOffset, program));
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
Msg.warn(this, "Error processing parameter \"" + paramName +
|
||||
"\" in function \"" + funcName + "\": " + e.getMessage());
|
||||
}
|
||||
|
||||
stackOffset += dataType.getLength();
|
||||
}
|
||||
}
|
||||
|
||||
ParameterImpl[] parameters = new ParameterImpl[maxSequence];
|
||||
parameterList.forEach((key, value) ->
|
||||
// Sequences are 1-based
|
||||
parameters[key - 1] = value);
|
||||
|
||||
// For static functions, fill in the pointer to ValueType
|
||||
// parameters that are implied before the actual parameters
|
||||
if (methodRow.isStatic()) {
|
||||
if (staticParameter != null) {
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
if (parameters[i] == null) {
|
||||
ParameterImpl param = null;
|
||||
try {
|
||||
param = new ParameterImpl(staticParameter.getName() + i,
|
||||
staticParameter.getDataType(), staticParameter.getStackOffset(),
|
||||
staticParameter.getProgram());
|
||||
}
|
||||
catch (InvalidInputException e1) {
|
||||
Msg.warn(this,
|
||||
"Couldn't clone " + staticParameter.getName() +
|
||||
" implied static function parameter in function : " +
|
||||
funcName + "in position " + i);
|
||||
}
|
||||
parameters[i] = param;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
@ -257,15 +438,44 @@ public class CliTableMethodDef extends CliAbstractTable {
|
||||
newFunc.updateFunction(null, null, FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS,
|
||||
true, SourceType.ANALYSIS, parameters);
|
||||
}
|
||||
catch (NullPointerException e) {
|
||||
Msg.warn(this, "Error processing function \"" + funcName + "\" (" + methodRowIndex +
|
||||
"): Bad parameters provided");
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
Msg.warn(this, "Error processing function \"" + funcName + "\"");
|
||||
Msg.warn(this, "Error processing function \" (\" + methodRowIndex + \")" +
|
||||
funcName + "\": Invalid function");
|
||||
}
|
||||
catch (OverlappingFunctionException e) {
|
||||
Msg.warn(this, "Error processing function \"" + funcName + "\"");
|
||||
String err = "Error processing function \" (\" + methodRowIndex + \")" + funcName +
|
||||
"\": Overlapping function (" + startAddr + ", " + endAddr + ": ";
|
||||
|
||||
Function existingFuncA = program.getFunctionManager().getFunctionAt(startAddr);
|
||||
Function existingFuncB = program.getFunctionManager().getFunctionAt(endAddr);
|
||||
|
||||
if (existingFuncA != null && existingFuncB == null) {
|
||||
err = err + existingFuncA.getName();
|
||||
}
|
||||
else if (existingFuncA == null && existingFuncB != null) {
|
||||
err = err + existingFuncB.getName();
|
||||
}
|
||||
else if (existingFuncA != null && existingFuncA == existingFuncB) {
|
||||
err = err + existingFuncA.getName();
|
||||
}
|
||||
|
||||
err = err + ")";
|
||||
|
||||
Msg.warn(this, err);
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
String paramNames = "";
|
||||
for (int i = 0; i < parameters.length - 1; i++) {
|
||||
paramNames += parameters[i].getName() + ", ";
|
||||
}
|
||||
paramNames += parameters[parameters.length - 1].getName();
|
||||
Msg.warn(this, "Error processing function \"" + funcName + "\" (" + methodRowIndex +
|
||||
"): Duplicate parameter name (" + paramNames + ")");
|
||||
}
|
||||
if (rvaZero > 0) {
|
||||
Msg.warn(this, rvaZero + " methods with RVA 0");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,13 @@ public class CliTableParam extends CliAbstractTable {
|
||||
public short sequence;
|
||||
public int nameIndex;
|
||||
|
||||
private static final int PARAMATTRIBUTES_IN = 0x1;
|
||||
private static final int PARAMATTRIBUTES_OUT = 0x2;
|
||||
private static final int PARAMATTRIBUTES_OPTIONAL = 0x10;
|
||||
private static final int PARAMATTRIBUTES_HASDEFAULT = 0x1000;
|
||||
private static final int PARAMATTRIBUTES_HASFIELDMARSHAL = 0x2000;
|
||||
private static final int PARAMATTRIBUTES_UNUSED = 0xCFE0;
|
||||
|
||||
public CliParamRow(short flags, short sequence, int nameIndex) {
|
||||
super();
|
||||
this.flags = flags;
|
||||
@ -47,10 +54,12 @@ public class CliTableParam extends CliAbstractTable {
|
||||
}
|
||||
}
|
||||
|
||||
public CliTableParam(BinaryReader reader, CliStreamMetadata stream, CliTypeTable tableId) throws IOException {
|
||||
public CliTableParam(BinaryReader reader, CliStreamMetadata stream, CliTypeTable tableId)
|
||||
throws IOException {
|
||||
super(reader, stream, tableId);
|
||||
for (int i = 0; i < this.numRows; i++) {
|
||||
CliParamRow row = new CliParamRow(reader.readNextShort(), reader.readNextShort(), readStringIndex(reader));
|
||||
CliParamRow row = new CliParamRow(reader.readNextShort(), reader.readNextShort(),
|
||||
readStringIndex(reader));
|
||||
rows.add(row);
|
||||
strings.add(row.nameIndex);
|
||||
}
|
||||
@ -59,9 +68,9 @@ public class CliTableParam extends CliAbstractTable {
|
||||
|
||||
@Override
|
||||
public StructureDataType getRowDataType() {
|
||||
StructureDataType rowDt = new StructureDataType(new CategoryPath(PATH), "ParamRow",0);
|
||||
StructureDataType rowDt = new StructureDataType(new CategoryPath(PATH), "ParamRow", 0);
|
||||
rowDt.add(CliEnumParamAttributes.dataType, "Flags", "bitmask of type ParamAttributes");
|
||||
rowDt.add( WORD, "Sequence", "constant");
|
||||
rowDt.add(WORD, "Sequence", "constant");
|
||||
rowDt.add(metadataStream.getStringIndexDataType(), "Name", "index into String heap");
|
||||
return rowDt;
|
||||
}
|
||||
|
@ -90,6 +90,7 @@ public class CliTableStandAloneSig extends CliAbstractTable {
|
||||
throws DuplicateNameException, CodeUnitInsertionException, IOException {
|
||||
for (CliAbstractTableRow row : rows) {
|
||||
Integer sigIndex = ((CliStandAloneSigRow) row).signatureIndex;
|
||||
|
||||
CliBlob blob =
|
||||
metadataStream.getBlobStream().getBlob(((CliStandAloneSigRow) row).signatureIndex);
|
||||
Address sigAddr = CliAbstractStream.getStreamMarkupAddress(program, isBinary, monitor,
|
||||
|
@ -461,8 +461,12 @@ public class DemangledFunction extends DemangledObject {
|
||||
// If returnType is null check for constructor or destructor names
|
||||
if (THIS_CALL.equals(function.getCallingConventionName())) {
|
||||
String n = getName();
|
||||
if (n.equals("~" + namespace.getName()) || n.equals(namespace.getName())) {
|
||||
// constructor && destructor
|
||||
if (n.equals(namespace.getName())) {
|
||||
// constructor
|
||||
return DataType.DEFAULT;
|
||||
}
|
||||
if (n.equals("~" + namespace.getName())) {
|
||||
// destructor
|
||||
return VoidDataType.dataType;
|
||||
}
|
||||
}
|
||||
|
@ -441,6 +441,7 @@ public abstract class DemangledObject implements Demangled {
|
||||
namespace = program.getGlobalNamespace();
|
||||
}
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
for (String namespaceName : getNamespaceList(typeNamespace)) {
|
||||
|
||||
// TODO - This is compensating for too long templates. We should probably genericize
|
||||
@ -448,16 +449,9 @@ public abstract class DemangledObject implements Demangled {
|
||||
// same name is the same class--would that reflect reality?
|
||||
namespaceName = ensureNameLength(namespaceName);
|
||||
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
|
||||
List<Symbol> symbols = symbolTable.getSymbols(namespaceName, namespace);
|
||||
Symbol namespaceSymbol =
|
||||
symbols.stream().filter(s -> (s.getSymbolType() == SymbolType.NAMESPACE ||
|
||||
s.getSymbolType() == SymbolType.CLASS)).findFirst().orElse(null);
|
||||
if (namespaceSymbol == null) {
|
||||
try {
|
||||
namespace =
|
||||
symbolTable.createNameSpace(namespace, namespaceName, SourceType.IMPORTED);
|
||||
symbolTable.getOrCreateNameSpace(namespace, namespaceName, SourceType.IMPORTED);
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
Msg.error(DemangledObject.class,
|
||||
@ -471,22 +465,27 @@ public abstract class DemangledObject implements Demangled {
|
||||
"Failed to create namespace: " + e.getMessage());
|
||||
break;
|
||||
}
|
||||
|
||||
Symbol nsSymbol = namespace.getSymbol();
|
||||
if (!isPermittedNamespaceType(nsSymbol.getSymbolType(), functionPermitted)) {
|
||||
|
||||
String allowedTypes = "SymbolType.CLASS, SymbolType.NAMESPACE";
|
||||
if (functionPermitted) {
|
||||
allowedTypes += ", SymbolType.FUNCTION";
|
||||
}
|
||||
else if (isPermittedNamespaceSymbol(namespaceSymbol, functionPermitted)) {
|
||||
namespace = (Namespace) namespaceSymbol.getObject();
|
||||
}
|
||||
else {
|
||||
|
||||
Msg.error(DemangledObject.class,
|
||||
"Failed to create namespace due to name conflict: " +
|
||||
"Bad namespace type - must be one of: " + allowedTypes +
|
||||
NamespaceUtils.getNamespaceQualifiedName(namespace, namespaceName, false));
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return namespace;
|
||||
}
|
||||
|
||||
private static boolean isPermittedNamespaceSymbol(Symbol symbol, boolean functionPermitted) {
|
||||
SymbolType symbolType = symbol.getSymbolType();
|
||||
private static boolean isPermittedNamespaceType(SymbolType symbolType,
|
||||
boolean functionPermitted) {
|
||||
if (symbolType == SymbolType.CLASS || symbolType == SymbolType.NAMESPACE) {
|
||||
return true;
|
||||
}
|
||||
|
@ -0,0 +1,167 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.exporter;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.DomainObjectService;
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.opinion.Loader;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.program.database.mem.AddressSourceInfo;
|
||||
import ghidra.program.database.mem.FileBytes;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.reloc.Relocation;
|
||||
import ghidra.util.Conv;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import utilities.util.FileUtilities;
|
||||
|
||||
/**
|
||||
* An {@link Exporter} that can export programs imported with a particular {@link Loader}
|
||||
*/
|
||||
public abstract class AbstractLoaderExporter extends Exporter {
|
||||
|
||||
/**
|
||||
* Creates a new {@link AbstractLoaderExporter}
|
||||
*
|
||||
* @param name The display name of this exporter
|
||||
* @param extension The default extension for this exporter
|
||||
* @param help The {@link HelpLocation} for this exporter
|
||||
*/
|
||||
protected AbstractLoaderExporter(String name, String extension, HelpLocation help) {
|
||||
super(name, extension, help);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the given file format is supported by this exporter
|
||||
*
|
||||
* @param fileFormat The file format (loader name) of the program to export
|
||||
* @return True if the given file format is supported by this exporter; otherwise, false
|
||||
*/
|
||||
protected abstract boolean supportsFileFormat(String fileFormat);
|
||||
|
||||
@Override
|
||||
public boolean export(File file, DomainObject domainObj, AddressSetView addrSet,
|
||||
TaskMonitor monitor) throws IOException, ExporterException {
|
||||
|
||||
if (!(domainObj instanceof Program)) {
|
||||
log.appendMsg("Unsupported type: " + domainObj.getClass().getSimpleName());
|
||||
return false;
|
||||
}
|
||||
|
||||
Program program = (Program) domainObj;
|
||||
Memory memory = program.getMemory();
|
||||
|
||||
String fileFormat = program.getExecutableFormat();
|
||||
if (!supportsFileFormat(fileFormat)) {
|
||||
log.appendMsg("Unsupported file format: " + fileFormat);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write source program's file bytes to a temp file
|
||||
File tempFile = File.createTempFile("ghidra_export_", null);
|
||||
try (OutputStream out = new FileOutputStream(tempFile, false)) {
|
||||
FileBytes[] fileBytes = memory.getAllFileBytes()
|
||||
.stream()
|
||||
.filter(fb -> program.getExecutablePath().endsWith(fb.getFilename()))
|
||||
.toArray(FileBytes[]::new);
|
||||
for (FileBytes bytes : fileBytes) {
|
||||
FileUtilities.copyStreamToStream(new FileBytesInputStream(bytes), out, monitor);
|
||||
}
|
||||
}
|
||||
|
||||
// Undo relocations in the temp file
|
||||
String error = null;
|
||||
try (RandomAccessFile fout = new RandomAccessFile(tempFile, "rw")) {
|
||||
Iterable<Relocation> relocs = () -> program.getRelocationTable().getRelocations();
|
||||
for (Relocation reloc : relocs) {
|
||||
AddressSourceInfo info = memory.getAddressSourceInfo(reloc.getAddress());
|
||||
if (info == null) {
|
||||
error = "Failed to get relocation address source";
|
||||
break;
|
||||
}
|
||||
if (info.getFileOffset() < 0) {
|
||||
error = "Failed to get relocation file offset";
|
||||
break;
|
||||
}
|
||||
if (info.getFileOffset() >= fout.length()) {
|
||||
error = "Relocation file offset exceeds file length";
|
||||
break;
|
||||
}
|
||||
fout.seek(info.getFileOffset());
|
||||
fout.write(reloc.getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
// If errors occurred, log them and delete the malformed temp file
|
||||
if (error != null) {
|
||||
log.appendMsg(error);
|
||||
if (!tempFile.delete()) {
|
||||
log.appendMsg("Failed to delete malformed file: " + tempFile);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move temp file to desired output file
|
||||
Path from = Paths.get(tempFile.toURI());
|
||||
Path to = Paths.get(file.toURI());
|
||||
Files.move(from, to, StandardCopyOption.REPLACE_EXISTING);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Option> getOptions(DomainObjectService domainObjectService) {
|
||||
return EMPTY_OPTIONS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOptions(List<Option> options) {
|
||||
// No options
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link InputStream} that reads a {@link FileBytes} modified bytes
|
||||
*/
|
||||
private static class FileBytesInputStream extends InputStream {
|
||||
|
||||
private final FileBytes fileBytes;
|
||||
private final long size;
|
||||
private long pos;
|
||||
|
||||
/**
|
||||
* Creates a new {@link InputStream} that can read over the modified bytes of the given
|
||||
* {@link FileBytes} object
|
||||
*
|
||||
* @param fileBytes The {@link FileBytes} to use for the {@link InputStream}
|
||||
*/
|
||||
FileBytesInputStream(FileBytes fileBytes) {
|
||||
this.fileBytes = fileBytes;
|
||||
this.size = fileBytes.getSize();
|
||||
this.pos = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return pos < size ? Conv.byteToInt(fileBytes.getModifiedByte(pos++)) : -1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user