mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
GP-141 handle non-standard RTTI type_info
This commit is contained in:
parent
4300bec382
commit
0f6d3cbc35
9 changed files with 332 additions and 122 deletions
|
@ -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