mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
Merge remote-tracking branch 'origin/patch'
Conflicts: Ghidra/application.properties
This commit is contained in:
commit
f47a35266c
10 changed files with 362 additions and 122 deletions
|
@ -6,6 +6,36 @@
|
||||||
</HEAD>
|
</HEAD>
|
||||||
|
|
||||||
<BODY>
|
<BODY>
|
||||||
|
<H1 align="center">Ghidra 9.2.1 Change History (December 2020)</H1>
|
||||||
|
<blockquote><p><u>Improvements</u></p>
|
||||||
|
<ul>
|
||||||
|
<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>. 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>
|
||||||
|
<li><I>Scripting</I>. Removed unnecessary 1-second delay when launching a script. (GP-443)</li>
|
||||||
|
</ul>
|
||||||
|
</blockquote>
|
||||||
|
<blockquote><p><u>Bugs</u></p>
|
||||||
|
<ul>
|
||||||
|
<li><I>Analysis</I>. Fixed the processing of CIL metadata that express arrays of non-primitive types. (GP-331)</li>
|
||||||
|
<li><I>API</I>. WrappedMemBuffer methods <code>getInt</code>, <code>getShort</code>, <code>getLong</code>, and <code>getBigInteger</code> have been fixed when allocated at a non-zero offset, wrapping another MemBuffer such as DumbMemBufferImpl. (GP-486)</li>
|
||||||
|
<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>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>
|
||||||
|
<li><I>Graphing</I>. Fixed issue where graph satellite view did not reflect main graph when graph vertices are hidden using hide actions or filters. (GP-514)</li>
|
||||||
|
<li><I>GUI</I>. Fixed stack overflow in TableChooserDialogs. (GP-460, Issue #2536)</li>
|
||||||
|
<li><I>PDB</I>. Corrected PDB parser selection bug affecting PDB load/download on Windows. (GP-390)</li>
|
||||||
|
<li><I>Processors</I>. Fixed handling of certain ARM/THUMB switch calculation functions. (GP-389)</li>
|
||||||
|
</ul>
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
<H1 align="center">Ghidra 9.2 Change History (November 2020)</H1>
|
<H1 align="center">Ghidra 9.2 Change History (November 2020)</H1>
|
||||||
<blockquote><p><u>New Features</u></p>
|
<blockquote><p><u>New Features</u></p>
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
@ -30,6 +30,7 @@ import ghidra.util.datastruct.ListAccumulator;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import ghidra.util.task.TaskMonitorAdapter;
|
import ghidra.util.task.TaskMonitorAdapter;
|
||||||
|
import utility.function.TerminatingConsumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <CODE>ProgramMemoryUtil</CODE> contains some static methods for
|
* <CODE>ProgramMemoryUtil</CODE> contains some static methods for
|
||||||
|
@ -725,7 +726,7 @@ public class ProgramMemoryUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the string in memory indicated by the searchString limited to the indicated
|
* Finds the string in memory indicated by the searchString limited to the indicated
|
||||||
* memory blocks and address set.
|
* memory blocks and address set.
|
||||||
|
@ -740,9 +741,36 @@ public class ProgramMemoryUtil {
|
||||||
public static List<Address> findString(String searchString, Program program,
|
public static List<Address> findString(String searchString, Program program,
|
||||||
List<MemoryBlock> blocks, AddressSetView set, TaskMonitor monitor)
|
List<MemoryBlock> blocks, AddressSetView set, TaskMonitor monitor)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
|
|
||||||
|
List<Address> addresses = new ArrayList<>();
|
||||||
|
|
||||||
|
// just add each found location to the list, no termination of search
|
||||||
|
TerminatingConsumer<Address> collector = (i) -> addresses.add(i);
|
||||||
|
|
||||||
|
locateString(searchString, collector, program, blocks, set, monitor);
|
||||||
|
|
||||||
|
return addresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the string in memory indicated by the searchString limited to the indicated
|
||||||
|
* memory blocks and address set. Each found location calls the foundLocationConsumer.consume(addr)
|
||||||
|
* method. If the search should terminate, (ie. enough results found), then terminateRequested() should
|
||||||
|
* return true. Requesting termination is different than a cancellation from the task monitor.
|
||||||
|
*
|
||||||
|
* @param searchString the string to find
|
||||||
|
* @param foundLocationConsumer location consumer with consumer.accept(Address addr) routine defined
|
||||||
|
* @param program the program to search
|
||||||
|
* @param blocks the only blocks to search
|
||||||
|
* @param set a set of the addresses to limit the results
|
||||||
|
* @param monitor a task monitor to allow
|
||||||
|
* @throws CancelledException if the user cancels
|
||||||
|
*/
|
||||||
|
public static void locateString(String searchString, TerminatingConsumer<Address> foundLocationConsumer, Program program,
|
||||||
|
List<MemoryBlock> blocks, AddressSetView set, TaskMonitor monitor)
|
||||||
|
throws CancelledException {
|
||||||
|
|
||||||
monitor.setMessage("Finding \"" + searchString + "\".");
|
monitor.setMessage("Finding \"" + searchString + "\".");
|
||||||
List<Address> addresses = new ArrayList<>();
|
|
||||||
int length = searchString.length();
|
int length = searchString.length();
|
||||||
byte[] bytes = searchString.getBytes();
|
byte[] bytes = searchString.getBytes();
|
||||||
Memory memory = program.getMemory();
|
Memory memory = program.getMemory();
|
||||||
|
@ -759,7 +787,10 @@ public class ProgramMemoryUtil {
|
||||||
break; // no more found in block.
|
break; // no more found in block.
|
||||||
}
|
}
|
||||||
if (set.contains(foundAddress)) {
|
if (set.contains(foundAddress)) {
|
||||||
addresses.add(foundAddress);
|
foundLocationConsumer.accept(foundAddress);
|
||||||
|
if (foundLocationConsumer.terminationRequested()) {
|
||||||
|
return; // termination of search requested
|
||||||
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
startAddress = foundAddress.add(length);
|
startAddress = foundAddress.add(length);
|
||||||
|
@ -770,7 +801,6 @@ public class ProgramMemoryUtil {
|
||||||
}
|
}
|
||||||
while (startAddress.compareTo(endAddress) <= 0);
|
while (startAddress.compareTo(endAddress) <= 0);
|
||||||
}
|
}
|
||||||
return addresses;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -25,10 +25,15 @@ import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.lang.UndefinedValueException;
|
import ghidra.program.model.lang.UndefinedValueException;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.mem.*;
|
import ghidra.program.model.mem.DumbMemBufferImpl;
|
||||||
|
import ghidra.program.model.mem.Memory;
|
||||||
import ghidra.program.model.scalar.Scalar;
|
import ghidra.program.model.scalar.Scalar;
|
||||||
import ghidra.program.model.symbol.Namespace;
|
import ghidra.program.model.symbol.Namespace;
|
||||||
import ghidra.program.model.symbol.Symbol;
|
import ghidra.program.model.symbol.Symbol;
|
||||||
|
import ghidra.program.model.util.AddressSetPropertyMap;
|
||||||
|
import ghidra.util.exception.AssertException;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
import mdemangler.*;
|
import mdemangler.*;
|
||||||
import mdemangler.datatype.MDDataType;
|
import mdemangler.datatype.MDDataType;
|
||||||
import mdemangler.datatype.complex.MDComplexType;
|
import mdemangler.datatype.complex.MDComplexType;
|
||||||
|
@ -248,15 +253,18 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel {
|
||||||
* @return true if the data type has a vf table pointer. Otherwise, it has a hash value.
|
* @return true if the data type has a vf table pointer. Otherwise, it has a hash value.
|
||||||
*/
|
*/
|
||||||
private static boolean hasVFPointer(Program program) {
|
private static boolean hasVFPointer(Program program) {
|
||||||
// Should be true when 64 bit or RTTI.
|
|
||||||
if (MSDataTypeUtils.is64Bit(program)) {
|
Address typeInfoVftableAddress = null;
|
||||||
|
try {
|
||||||
|
typeInfoVftableAddress = RttiUtil.findTypeInfoVftableAddress(program, TaskMonitor.DUMMY);
|
||||||
|
}
|
||||||
|
catch (CancelledException e) {
|
||||||
|
throw new AssertException(e);
|
||||||
|
}
|
||||||
|
if (typeInfoVftableAddress != null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Address address = RttiUtil.getTypeInfoTypeDescriptorAddress(program);
|
return false;
|
||||||
if (address == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return RttiUtil.isTypeInfoTypeDescriptorAddress(program, address);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -350,9 +358,11 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel {
|
||||||
throw new UndefinedValueException(
|
throw new UndefinedValueException(
|
||||||
"No vf table pointer is defined for this TypeDescriptor model.");
|
"No vf table pointer is defined for this TypeDescriptor model.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Address vfTableAddress;
|
||||||
// component 0 is either vf table pointer or hash value.
|
// component 0 is either vf table pointer or hash value.
|
||||||
Address vfTableAddress =
|
vfTableAddress = EHDataTypeUtilities.getAddress(getDataType(), VF_TABLE_OR_HASH_ORDINAL, getMemBuffer());
|
||||||
EHDataTypeUtilities.getAddress(getDataType(), VF_TABLE_OR_HASH_ORDINAL, getMemBuffer());
|
|
||||||
return vfTableAddress.getOffset() != 0 ? vfTableAddress : null;
|
return vfTableAddress.getOffset() != 0 ? vfTableAddress : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -187,7 +187,7 @@ public class CreateVfTableBackgroundCmd extends AbstractCreateDataBackgroundCmd<
|
||||||
|
|
||||||
// Create functions that are referred to by the vf table.
|
// Create functions that are referred to by the vf table.
|
||||||
if (applyOptions.shouldCreateFunction()) {
|
if (applyOptions.shouldCreateFunction()) {
|
||||||
int elementCount = model.getCount();
|
int elementCount = model.getElementCount();
|
||||||
for (int tableElementIndex = 0; tableElementIndex < elementCount; tableElementIndex++) {
|
for (int tableElementIndex = 0; tableElementIndex < elementCount; tableElementIndex++) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
Address vfPointer = model.getVirtualFunctionPointer(tableElementIndex);
|
Address vfPointer = model.getVirtualFunctionPointer(tableElementIndex);
|
||||||
|
|
|
@ -15,16 +15,14 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.cmd.data.rtti;
|
package ghidra.app.cmd.data.rtti;
|
||||||
|
|
||||||
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAbsoluteAddress;
|
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import ghidra.app.cmd.data.TypeDescriptorModel;
|
import ghidra.app.cmd.data.TypeDescriptorModel;
|
||||||
import ghidra.app.util.NamespaceUtils;
|
import ghidra.app.util.NamespaceUtils;
|
||||||
import ghidra.app.util.PseudoDisassembler;
|
import ghidra.app.util.PseudoDisassembler;
|
||||||
import ghidra.app.util.bin.BinaryReader;
|
import ghidra.app.util.datatype.microsoft.MSDataTypeUtils;
|
||||||
import ghidra.app.util.bin.MemoryByteProvider;
|
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressSetView;
|
import ghidra.program.model.address.AddressSetView;
|
||||||
import ghidra.program.model.listing.GhidraClass;
|
import ghidra.program.model.listing.GhidraClass;
|
||||||
|
@ -34,20 +32,23 @@ import ghidra.program.model.mem.MemoryBlock;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
import ghidra.program.util.ProgramMemoryUtil;
|
import ghidra.program.util.ProgramMemoryUtil;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.*;
|
||||||
import ghidra.util.exception.CancelledException;
|
|
||||||
import ghidra.util.exception.InvalidInputException;
|
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
import utility.function.TerminatingConsumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RttiUtil provides constants and static methods for processing RTTI information.
|
* RttiUtil provides constants and static methods for processing RTTI information.
|
||||||
*/
|
*/
|
||||||
public class RttiUtil {
|
public class RttiUtil {
|
||||||
|
|
||||||
|
private static final String TYPE_INFO_NAMESPACE = "type_info";
|
||||||
|
private static final int MIN_MATCHING_VFTABLE_PTRS = 5;
|
||||||
static final String CONST_PREFIX = "const ";
|
static final String CONST_PREFIX = "const ";
|
||||||
public static final String TYPE_INFO_STRING = ".?AVtype_info@@";
|
|
||||||
public static final String TYPE_INFO_LABEL = "class_type_info_RTTI_Type_Descriptor";
|
public static final String TYPE_INFO_LABEL = "class_type_info_RTTI_Type_Descriptor";
|
||||||
private static final String MANGLED_TYPE_INFO_SYMBOL = "??_R0?AVtype_info@@@8";
|
public static final String TYPE_INFO_STRING = ".?AVtype_info@@";
|
||||||
|
private static final String CLASS_PREFIX_CHARS = ".?A";
|
||||||
|
private static Map<Program, Address> vftableMap = new WeakHashMap<>();
|
||||||
|
|
||||||
private RttiUtil() {
|
private RttiUtil() {
|
||||||
// utility class; can't create
|
// utility class; can't create
|
||||||
|
@ -151,6 +152,12 @@ public class RttiUtil {
|
||||||
: false) {
|
: false) {
|
||||||
break; // Not pointing to text section.
|
break; // Not pointing to text section.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// any references after the first one ends the table
|
||||||
|
if (tableSize > 0 && program.getReferenceManager().hasReferencesTo(currentVfPointerAddress)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (!pseudoDisassembler.isValidSubroutine(referencedAddress, true)) {
|
if (!pseudoDisassembler.isValidSubroutine(referencedAddress, true)) {
|
||||||
break; // Not pointing to possible function.
|
break; // Not pointing to possible function.
|
||||||
}
|
}
|
||||||
|
@ -178,63 +185,185 @@ public class RttiUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the address of the base type_info structure in the provided program.
|
* Identify common TypeInfo address through examination of discovered VtTables
|
||||||
* The descriptor will only be manually located if {@value TYPE_INFO_STRING} is present.
|
|
||||||
* @param program the program
|
|
||||||
* @return the address of the type_info structure or null if not found
|
|
||||||
*/
|
*/
|
||||||
public static Address getTypeInfoTypeDescriptorAddress(Program program) {
|
private static class CommonRTTIMatchCounter implements TerminatingConsumer<Address> {
|
||||||
SymbolTable table = program.getSymbolTable();
|
int matchingAddrCount = 0;
|
||||||
List<Symbol> symbols = table.getGlobalSymbols(TYPE_INFO_LABEL);
|
int defaultPointerSize = 4;
|
||||||
if (symbols.isEmpty()) {
|
boolean terminationRequest = false;
|
||||||
symbols = table.getGlobalSymbols(MANGLED_TYPE_INFO_SYMBOL);
|
Address commonVftableAddress = null;
|
||||||
|
Program program;
|
||||||
|
|
||||||
|
public CommonRTTIMatchCounter(Program program) {
|
||||||
|
this.program = program;
|
||||||
|
defaultPointerSize = program.getDefaultPointerSize();
|
||||||
}
|
}
|
||||||
if (!symbols.isEmpty()) {
|
|
||||||
for (Symbol symbol : symbols) {
|
public Address getinfoVfTable() {
|
||||||
if (isTypeInfoTypeDescriptorAddress(program, symbol.getAddress())) {
|
return commonVftableAddress;
|
||||||
return symbol.getAddress();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return locateTypeInfoAddress(program);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Checks if the provided address is a TypeDescriptor containing the
|
public boolean terminationRequested() {
|
||||||
* {@value TYPE_INFO_STRING} component
|
return terminationRequest;
|
||||||
* @param program the program
|
}
|
||||||
* @param address the descriptor address
|
|
||||||
* @return true if {@value TYPE_INFO_STRING} is present in the descriptor at the address
|
@Override
|
||||||
*/
|
public void accept(Address foundAddress) {
|
||||||
public static boolean isTypeInfoTypeDescriptorAddress(Program program, Address address) {
|
Address mangledClassNameAddress = foundAddress;
|
||||||
MemoryByteProvider provider = new MemoryByteProvider(program.getMemory(), address);
|
|
||||||
try {
|
Address pointerToTypeInfoVftable =
|
||||||
BinaryReader reader = new BinaryReader(provider, !program.getLanguage().isBigEndian());
|
mangledClassNameAddress.subtract(2 * defaultPointerSize);
|
||||||
String value = reader.readAsciiString(program.getDefaultPointerSize() * 2);
|
|
||||||
return TYPE_INFO_STRING.equals(value);
|
Address possibleVftableAddress =
|
||||||
} catch (IOException e) {
|
MSDataTypeUtils.getAbsoluteAddress(program, pointerToTypeInfoVftable);
|
||||||
return false;
|
if (possibleVftableAddress == null) {
|
||||||
|
return; // valid address not found
|
||||||
|
}
|
||||||
|
if (possibleVftableAddress.getOffset() == 0) {
|
||||||
|
return; // don't want zero_address to count
|
||||||
|
}
|
||||||
|
// if ever we find one that doesn't match, start count over
|
||||||
|
if (!possibleVftableAddress.equals(commonVftableAddress)) {
|
||||||
|
if (matchingAddrCount > 2) {
|
||||||
|
return; // already have more than one match, assume this one was outlier, ignore
|
||||||
|
}
|
||||||
|
matchingAddrCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
commonVftableAddress = possibleVftableAddress;
|
||||||
|
matchingAddrCount++;
|
||||||
|
|
||||||
|
if (matchingAddrCount > MIN_MATCHING_VFTABLE_PTRS) {
|
||||||
|
// done finding good addresses have at Minimum matching number
|
||||||
|
terminationRequest = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Address locateTypeInfoAddress(Program program) {
|
/**
|
||||||
Memory memory = program.getMemory();
|
* Method to figure out the type_info vftable address using pointed to value by all RTTI classes
|
||||||
try {
|
* @param program the current program
|
||||||
List<MemoryBlock> dataBlocks = ProgramMemoryUtil.getMemoryBlocksStartingWithName(
|
* @param monitor the TaskMonitor
|
||||||
program, program.getMemory(), ".data", TaskMonitor.DUMMY);
|
* @return the type_info address or null if it cannot be determined
|
||||||
for (MemoryBlock memoryBlock : dataBlocks) {
|
* @throws CancelledException if cancelled
|
||||||
Address typeInfoAddress =
|
*/
|
||||||
memory.findBytes(memoryBlock.getStart(), memoryBlock.getEnd(),
|
public static Address findTypeInfoVftableAddress(Program program, TaskMonitor monitor)
|
||||||
TYPE_INFO_STRING.getBytes(), null, true, TaskMonitor.DUMMY);
|
throws CancelledException {
|
||||||
if (typeInfoAddress != null) {
|
|
||||||
return TypeDescriptorModel.getBaseAddress(program, typeInfoAddress);
|
// Checked for cached value
|
||||||
}
|
if (vftableMap.containsKey(program)) {
|
||||||
}
|
return vftableMap.get(program);
|
||||||
} catch (CancelledException e) {
|
|
||||||
// impossible
|
|
||||||
throw new AssertException(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if type info vftable already a symbol, just use the address of the symbol
|
||||||
|
Address infoVftableAddress = findTypeInfoVftableLabel(program);
|
||||||
|
if (infoVftableAddress == null) {
|
||||||
|
|
||||||
|
// search for mangled class prefix names, and locate the vftable pointer relative to some
|
||||||
|
// minimum number that all point to the same location which should be the vftable
|
||||||
|
AddressSetView set = program.getMemory().getLoadedAndInitializedAddressSet();
|
||||||
|
List<MemoryBlock> dataBlocks =
|
||||||
|
ProgramMemoryUtil.getMemoryBlocksStartingWithName(program, set, ".data", monitor);
|
||||||
|
|
||||||
|
CommonRTTIMatchCounter vfTableAddrChecker = new CommonRTTIMatchCounter(program);
|
||||||
|
|
||||||
|
ProgramMemoryUtil.locateString(CLASS_PREFIX_CHARS, vfTableAddrChecker, program,
|
||||||
|
dataBlocks, set, monitor);
|
||||||
|
infoVftableAddress = vfTableAddrChecker.getinfoVfTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// cache result of search
|
||||||
|
vftableMap.put(program, infoVftableAddress);
|
||||||
|
|
||||||
|
return infoVftableAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* find type info vftable by existing type_info::vftable symbol
|
||||||
|
* @param program program to check
|
||||||
|
* @return return vftable addr if symbol exists
|
||||||
|
*/
|
||||||
|
private static Address findTypeInfoVftableLabel(Program program) {
|
||||||
|
SymbolTable symbolTable = program.getSymbolTable();
|
||||||
|
Namespace typeinfoNamespace =
|
||||||
|
symbolTable.getNamespace(TYPE_INFO_NAMESPACE, program.getGlobalNamespace());
|
||||||
|
Symbol vftableSymbol =
|
||||||
|
symbolTable.getLocalVariableSymbol("vftable", typeinfoNamespace);
|
||||||
|
if (vftableSymbol != null) {
|
||||||
|
return vftableSymbol.getAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
vftableSymbol = symbolTable.getLocalVariableSymbol("`vftable'", typeinfoNamespace);
|
||||||
|
if (vftableSymbol != null) {
|
||||||
|
return vftableSymbol.getAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
vftableSymbol = symbolTable.getLocalVariableSymbol("type_info", typeinfoNamespace);
|
||||||
|
if (vftableSymbol != null) {
|
||||||
|
return vftableSymbol.getAddress();
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to create type_info vftable label (and namespace if needed) at the given address
|
||||||
|
* @param program the current program
|
||||||
|
* @param address the given address
|
||||||
|
*/
|
||||||
|
public static void createTypeInfoVftableSymbol(Program program, Address address) {
|
||||||
|
|
||||||
|
SymbolTable symbolTable = program.getSymbolTable();
|
||||||
|
|
||||||
|
Namespace typeinfoNamespace =
|
||||||
|
symbolTable.getNamespace(TYPE_INFO_NAMESPACE, program.getGlobalNamespace());
|
||||||
|
|
||||||
|
if (typeinfoNamespace == null) {
|
||||||
|
try {
|
||||||
|
typeinfoNamespace = symbolTable.createClass(program.getGlobalNamespace(),
|
||||||
|
TYPE_INFO_NAMESPACE, SourceType.IMPORTED);
|
||||||
|
}
|
||||||
|
catch (DuplicateNameException e) {
|
||||||
|
Msg.error(RttiUtil.class, "Duplicate type_info class namespace at " +
|
||||||
|
program.getName() + " " + address + ". " + e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (InvalidInputException e) {
|
||||||
|
Msg.error(RttiUtil.class, "Invalid input creating type_info class namespace " +
|
||||||
|
program.getName() + " " + address + ". " + e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check to see if symbol already exists both non-pdb and pdb versions
|
||||||
|
Symbol vftableSymbol = symbolTable.getSymbol(TYPE_INFO_NAMESPACE, address, typeinfoNamespace);
|
||||||
|
if (vftableSymbol != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vftableSymbol = symbolTable.getSymbol("`vftable'", address, typeinfoNamespace);
|
||||||
|
if (vftableSymbol != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
vftableSymbol =
|
||||||
|
symbolTable.createLabel(address, "vftable", typeinfoNamespace, SourceType.IMPORTED);
|
||||||
|
if (vftableSymbol == null) {
|
||||||
|
Msg.error(RttiUtil.class,
|
||||||
|
program.getName() + " Couldn't create type_info vftable symbol. ");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (InvalidInputException e) {
|
||||||
|
Msg.error(RttiUtil.class,
|
||||||
|
program.getName() + " Couldn't create type_info vftable symbol. " + e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ public class VfTableModel extends AbstractCreateDataTypeModel {
|
||||||
private Program lastProgram;
|
private Program lastProgram;
|
||||||
private DataType lastDataType;
|
private DataType lastDataType;
|
||||||
private int lastElementCount = -1;
|
private int lastElementCount = -1;
|
||||||
|
private int elementCount = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the model for the vf table data.
|
* Creates the model for the vf table data.
|
||||||
|
@ -54,8 +55,18 @@ public class VfTableModel extends AbstractCreateDataTypeModel {
|
||||||
*/
|
*/
|
||||||
public VfTableModel(Program program, Address vfTableAddress,
|
public VfTableModel(Program program, Address vfTableAddress,
|
||||||
DataValidationOptions validationOptions) {
|
DataValidationOptions validationOptions) {
|
||||||
super(program, RttiUtil.getVfTableCount(program, vfTableAddress), vfTableAddress,
|
// use one for the data type element count, because there is only one array of some element size
|
||||||
|
super(program, 1, vfTableAddress,
|
||||||
validationOptions);
|
validationOptions);
|
||||||
|
elementCount= RttiUtil.getVfTableCount(program, vfTableAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of vftable elements in this vftable
|
||||||
|
* @return number of elements
|
||||||
|
*/
|
||||||
|
public int getElementCount() {
|
||||||
|
return elementCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -79,7 +90,7 @@ public class VfTableModel extends AbstractCreateDataTypeModel {
|
||||||
long entrySize = individualEntryDataType.getLength();
|
long entrySize = individualEntryDataType.getLength();
|
||||||
|
|
||||||
// Each entry is a pointer to where a function can possibly be created.
|
// Each entry is a pointer to where a function can possibly be created.
|
||||||
long numEntries = getCount();
|
long numEntries = elementCount;
|
||||||
if (numEntries == 0) {
|
if (numEntries == 0) {
|
||||||
throw new InvalidDataTypeException(
|
throw new InvalidDataTypeException(
|
||||||
getName() + " data type at " + getAddress() + " doesn't have a valid vf table.");
|
getName() + " data type at " + getAddress() + " doesn't have a valid vf table.");
|
||||||
|
@ -120,9 +131,8 @@ public class VfTableModel extends AbstractCreateDataTypeModel {
|
||||||
|
|
||||||
lastProgram = program;
|
lastProgram = program;
|
||||||
lastDataType = null;
|
lastDataType = null;
|
||||||
lastElementCount = -1;
|
lastElementCount = elementCount;
|
||||||
|
|
||||||
lastElementCount = getCount();
|
|
||||||
if (lastElementCount > 0) {
|
if (lastElementCount > 0) {
|
||||||
DataTypeManager dataTypeManager = program.getDataTypeManager();
|
DataTypeManager dataTypeManager = program.getDataTypeManager();
|
||||||
PointerDataType pointerDt = new PointerDataType(dataTypeManager);
|
PointerDataType pointerDt = new PointerDataType(dataTypeManager);
|
||||||
|
|
|
@ -19,18 +19,14 @@ import java.util.*;
|
||||||
|
|
||||||
import ghidra.app.cmd.data.CreateTypeDescriptorBackgroundCmd;
|
import ghidra.app.cmd.data.CreateTypeDescriptorBackgroundCmd;
|
||||||
import ghidra.app.cmd.data.TypeDescriptorModel;
|
import ghidra.app.cmd.data.TypeDescriptorModel;
|
||||||
import ghidra.app.cmd.data.rtti.CreateRtti4BackgroundCmd;
|
import ghidra.app.cmd.data.rtti.*;
|
||||||
import ghidra.app.cmd.data.rtti.Rtti4Model;
|
|
||||||
import ghidra.app.cmd.data.rtti.RttiUtil;
|
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
import ghidra.app.util.datatype.microsoft.*;
|
import ghidra.app.util.datatype.microsoft.*;
|
||||||
import ghidra.app.util.importer.MessageLog;
|
import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.InvalidDataTypeException;
|
import ghidra.program.model.data.InvalidDataTypeException;
|
||||||
import ghidra.program.model.lang.UndefinedValueException;
|
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.mem.MemoryBlock;
|
import ghidra.program.model.mem.MemoryBlock;
|
||||||
import ghidra.program.model.symbol.SourceType;
|
|
||||||
import ghidra.program.util.ProgramMemoryUtil;
|
import ghidra.program.util.ProgramMemoryUtil;
|
||||||
import ghidra.util.bytesearch.*;
|
import ghidra.util.bytesearch.*;
|
||||||
import ghidra.util.exception.*;
|
import ghidra.util.exception.*;
|
||||||
|
@ -77,48 +73,47 @@ public class RttiAnalyzer extends AbstractAnalyzer {
|
||||||
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
|
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
|
|
||||||
Address typeInfoRtti0Address = RttiUtil.getTypeInfoTypeDescriptorAddress(program);
|
Address commonVfTableAddress = RttiUtil.findTypeInfoVftableAddress(program, monitor);
|
||||||
if (typeInfoRtti0Address == null) {
|
|
||||||
log.appendMsg(this.getName(), "Couldn't find RTTI type info structure.");
|
if (commonVfTableAddress == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure the label is present
|
RttiUtil.createTypeInfoVftableSymbol(program,commonVfTableAddress);
|
||||||
try {
|
|
||||||
program.getSymbolTable().createLabel(
|
Set<Address> possibleTypeAddresses = locatePotentialRTTI0Entries(program, set, monitor);
|
||||||
typeInfoRtti0Address, RttiUtil.TYPE_INFO_LABEL, SourceType.ANALYSIS);
|
if (possibleTypeAddresses == null) {
|
||||||
} catch (InvalidInputException e) {
|
|
||||||
// not invalid
|
|
||||||
throw new AssertException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the address of the vf table data in common for all RTTI 0.
|
|
||||||
TypeDescriptorModel typeDescriptorModel =
|
|
||||||
new TypeDescriptorModel(program, typeInfoRtti0Address, validationOptions);
|
|
||||||
try {
|
|
||||||
Address commonVfTableAddress = typeDescriptorModel.getVFTableAddress();
|
|
||||||
if (commonVfTableAddress == null) {
|
|
||||||
log.appendMsg(this.getName(),
|
|
||||||
"Couldn't get vf table address for RTTI 0 @ " + typeInfoRtti0Address + ". ");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int alignment = program.getDefaultPointerSize();
|
|
||||||
List<MemoryBlock> dataBlocks = ProgramMemoryUtil.getMemoryBlocksStartingWithName(
|
|
||||||
program, program.getMemory(), ".data", TaskMonitor.DUMMY);
|
|
||||||
Set<Address> possibleTypeAddresses = ProgramMemoryUtil.findDirectReferences(program,
|
|
||||||
dataBlocks, alignment, commonVfTableAddress, monitor);
|
|
||||||
|
|
||||||
// We now have a list of potential rtti0 addresses.
|
|
||||||
processRtti0(possibleTypeAddresses, program, monitor);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (InvalidDataTypeException | UndefinedValueException e) {
|
|
||||||
log.appendMsg(this.getName(), "Couldn't get vf table address for RTTI 0 @ " +
|
// We now have a list of potential rtti0 addresses.
|
||||||
typeInfoRtti0Address + ". " + e.getMessage());
|
processRtti0(possibleTypeAddresses, program, monitor);
|
||||||
return false;
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* locate any potential RTTI0 based on pointers to the type_info vftable
|
||||||
|
* @param program proram to locate within
|
||||||
|
* @param set restricted set to locate within
|
||||||
|
* @param monitor monitor for canceling
|
||||||
|
* @return set of potential RTTI0 entries
|
||||||
|
* @throws CancelledException
|
||||||
|
*/
|
||||||
|
private Set<Address> locatePotentialRTTI0Entries(Program program, AddressSetView set,
|
||||||
|
TaskMonitor monitor) throws CancelledException {
|
||||||
|
Address commonVfTableAddress = RttiUtil.findTypeInfoVftableAddress(program, monitor);
|
||||||
|
if (commonVfTableAddress == null) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// use the type_info vftable address to find a list of potential RTTI0 addresses
|
||||||
|
int alignment = program.getDefaultPointerSize();
|
||||||
|
List<MemoryBlock> dataBlocks = ProgramMemoryUtil.getMemoryBlocksStartingWithName(
|
||||||
|
program, program.getMemory(), ".data", TaskMonitor.DUMMY);
|
||||||
|
Set<Address> possibleTypeAddresses = ProgramMemoryUtil.findDirectReferences(program,
|
||||||
|
dataBlocks, alignment, commonVfTableAddress, monitor);
|
||||||
|
return possibleTypeAddresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processRtti0(Collection<Address> possibleRtti0Addresses, Program program,
|
private void processRtti0(Collection<Address> possibleRtti0Addresses, Program program,
|
||||||
|
@ -168,6 +163,9 @@ public class RttiAnalyzer extends AbstractAnalyzer {
|
||||||
dataBlocks.addAll(ProgramMemoryUtil.getMemoryBlocksStartingWithName(program,
|
dataBlocks.addAll(ProgramMemoryUtil.getMemoryBlocksStartingWithName(program,
|
||||||
program.getMemory(), ".data", monitor));
|
program.getMemory(), ".data", monitor));
|
||||||
|
|
||||||
|
dataBlocks.addAll(ProgramMemoryUtil.getMemoryBlocksStartingWithName(program,
|
||||||
|
program.getMemory(), ".text", monitor));
|
||||||
|
|
||||||
List<Address> rtti4Addresses =
|
List<Address> rtti4Addresses =
|
||||||
getRtti4Addresses(program, dataBlocks, rtti0Locations, validationOptions, monitor);
|
getRtti4Addresses(program, dataBlocks, rtti0Locations, validationOptions, monitor);
|
||||||
|
|
||||||
|
|
|
@ -269,7 +269,7 @@ class AbstractRttiTest extends AbstractCreateDataTypeModelTest {
|
||||||
vfTableModel.validate();
|
vfTableModel.validate();
|
||||||
assertEquals(addressVfTable, vfTableModel.getAddress());
|
assertEquals(addressVfTable, vfTableModel.getAddress());
|
||||||
int numVfTableEntries = vfAddresses.length;
|
int numVfTableEntries = vfAddresses.length;
|
||||||
assertEquals(numVfTableEntries, vfTableModel.getCount());
|
assertEquals(numVfTableEntries, vfTableModel.getElementCount());
|
||||||
for (int i = 0; i < numVfTableEntries; i++) {
|
for (int i = 0; i < numVfTableEntries; i++) {
|
||||||
assertEquals(addr(program, vfAddresses[i]), vfTableModel.getVirtualFunctionPointer(i));
|
assertEquals(addr(program, vfAddresses[i]), vfTableModel.getVirtualFunctionPointer(i));
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ public class RttiModelTest extends AbstractRttiTest {
|
||||||
setupRtti4_32(builder, 0x01001340L, 0, 0, 0, "0x01005364", "0x0100137c");
|
setupRtti4_32(builder, 0x01001340L, 0, 0, 0, "0x01005364", "0x0100137c");
|
||||||
Address address = builder.addr(0x01001340L);
|
Address address = builder.addr(0x01001340L);
|
||||||
checkInvalidModel(new Rtti4Model(program, address, defaultValidationOptions),
|
checkInvalidModel(new Rtti4Model(program, address, defaultValidationOptions),
|
||||||
"TypeDescriptor data type at 01005364 doesn't point to a vfTable address in a loaded and initialized memory block.");
|
"No vf table pointer is defined for this TypeDescriptor model.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -62,7 +62,7 @@ public class RttiModelTest extends AbstractRttiTest {
|
||||||
setupRtti0_32(builder, 0x01001364, "0x01007700", "0x0", "stuff");
|
setupRtti0_32(builder, 0x01001364, "0x01007700", "0x0", "stuff");
|
||||||
Address address = builder.addr(0x01001340L);
|
Address address = builder.addr(0x01001340L);
|
||||||
checkInvalidModel(new Rtti4Model(program, address, defaultValidationOptions),
|
checkInvalidModel(new Rtti4Model(program, address, defaultValidationOptions),
|
||||||
"TypeDescriptor data type at 01001364 doesn't point to a vfTable address in a loaded and initialized memory block.");
|
"No vf table pointer is defined for this TypeDescriptor model.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
/* ###
|
||||||
|
* 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 utility.function;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TerminatingConsumer is a Consumer {@link Consumer} that can request termination
|
||||||
|
* of the supplier once some condition is reached, for example some number of consumed results
|
||||||
|
* accepted. If termination is required override the terminationRequested()
|
||||||
|
* method to return true when termination state is reached.
|
||||||
|
*
|
||||||
|
* @param <T> the type of the input to the operation
|
||||||
|
*/
|
||||||
|
public interface TerminatingConsumer<T> extends Consumer<T> {
|
||||||
|
|
||||||
|
default boolean terminationRequested() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue