diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/TypeDescriptorModel.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/TypeDescriptorModel.java index f9d4dc26ec..d451bb5423 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/TypeDescriptorModel.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/TypeDescriptorModel.java @@ -15,9 +15,7 @@ */ package ghidra.app.cmd.data; -import java.util.List; - -import ghidra.app.plugin.prototype.MicrosoftCodeAnalyzerPlugin.RttiAnalyzer; +import ghidra.app.cmd.data.rtti.RttiUtil; import ghidra.app.util.datatype.microsoft.DataValidationOptions; import ghidra.app.util.datatype.microsoft.MSDataTypeUtils; import ghidra.app.util.demangler.DemangledObject; @@ -31,9 +29,6 @@ import ghidra.program.model.mem.*; import ghidra.program.model.scalar.Scalar; import ghidra.program.model.symbol.Namespace; import ghidra.program.model.symbol.Symbol; -import ghidra.program.util.ProgramMemoryUtil; -import ghidra.util.exception.CancelledException; -import ghidra.util.task.TaskMonitor; import mdemangler.*; import mdemangler.datatype.MDDataType; import mdemangler.datatype.complex.MDComplexType; @@ -257,23 +252,11 @@ public class TypeDescriptorModel extends AbstractCreateDataTypeModel { if (MSDataTypeUtils.is64Bit(program)) { return true; } - Memory memory = program.getMemory(); - try { - List dataBlocks = ProgramMemoryUtil.getMemoryBlocksStartingWithName( - program, program.getMemory(), ".data", TaskMonitor.DUMMY); - for (MemoryBlock memoryBlock : dataBlocks) { - Address typeInfoAddress = - memory.findBytes(memoryBlock.getStart(), memoryBlock.getEnd(), - RttiAnalyzer.TYPE_INFO_STRING.getBytes(), null, true, TaskMonitor.DUMMY); - if (typeInfoAddress != null) { - return true; // RTTI has type info string in the data section. - } - } + Address address = RttiUtil.getTypeInfoTypeDescriptorAddress(program); + if (address == null) { + return false; } - catch (CancelledException e) { - // Shouldn't happen since using dummy monitor. Do nothing. - } - return false; + return RttiUtil.isTypeInfoTypeDescriptorAddress(program, address); } /** diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/rtti/RttiUtil.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/rtti/RttiUtil.java index a74e58bbc9..ac9edf913b 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/rtti/RttiUtil.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/cmd/data/rtti/RttiUtil.java @@ -17,9 +17,14 @@ package ghidra.app.cmd.data.rtti; import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAbsoluteAddress; +import java.io.IOException; +import java.util.List; + import ghidra.app.cmd.data.TypeDescriptorModel; import ghidra.app.util.NamespaceUtils; import ghidra.app.util.PseudoDisassembler; +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.MemoryByteProvider; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressSetView; import ghidra.program.model.listing.GhidraClass; @@ -27,15 +32,22 @@ import ghidra.program.model.listing.Program; import ghidra.program.model.mem.Memory; import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.symbol.*; +import ghidra.program.util.ProgramMemoryUtil; import ghidra.util.Msg; +import ghidra.util.exception.AssertException; +import ghidra.util.exception.CancelledException; import ghidra.util.exception.InvalidInputException; +import ghidra.util.task.TaskMonitor; /** * RttiUtil provides constants and static methods for processing RTTI information. */ -class RttiUtil { +public class RttiUtil { 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"; + private static final String MANGLED_TYPE_INFO_SYMBOL = "??_R0?AVtype_info@@@8"; private RttiUtil() { // utility class; can't create @@ -111,7 +123,7 @@ class RttiUtil { * @param vfTableBaseAddress the base address in the program for the vf table * @return the number of virtual function addresses in the vf table */ - static int getVfTableCount(Program program, Address vfTableBaseAddress) { + public static int getVfTableCount(Program program, Address vfTableBaseAddress) { Memory memory = program.getMemory(); MemoryBlock textBlock = memory.getBlock(".text"); @@ -157,7 +169,7 @@ class RttiUtil { * @param rtti0Model the model for the type descriptor whose namespace is to be returned. * @return the namespace or the empty string. */ - static String getDescriptorTypeNamespace(TypeDescriptorModel rtti0Model) { + public static String getDescriptorTypeNamespace(TypeDescriptorModel rtti0Model) { String descriptorTypeNamespace = rtti0Model.getDescriptorTypeNamespace(); // Can be null. if (descriptorTypeNamespace == null) { descriptorTypeNamespace = ""; // Couldn't get namespace so leave it off. @@ -165,4 +177,64 @@ class RttiUtil { return descriptorTypeNamespace; } + /** + * Gets the address of the base type_info structure in the provided program. + * 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) { + SymbolTable table = program.getSymbolTable(); + List symbols = table.getGlobalSymbols(TYPE_INFO_LABEL); + if (symbols.isEmpty()) { + symbols = table.getGlobalSymbols(MANGLED_TYPE_INFO_SYMBOL); + } + if (!symbols.isEmpty()) { + for (Symbol symbol : symbols) { + if (isTypeInfoTypeDescriptorAddress(program, symbol.getAddress())) { + return symbol.getAddress(); + } + } + } + return locateTypeInfoAddress(program); + } + + /** + * Checks if the provided address is a TypeDescriptor containing the + * {@value TYPE_INFO_STRING} component + * @param program the program + * @param address the descriptor address + * @return true if {@value TYPE_INFO_STRING} is present in the descriptor at the address + */ + public static boolean isTypeInfoTypeDescriptorAddress(Program program, Address address) { + MemoryByteProvider provider = new MemoryByteProvider(program.getMemory(), address); + try { + BinaryReader reader = new BinaryReader(provider, !program.getLanguage().isBigEndian()); + String value = reader.readAsciiString(program.getDefaultPointerSize() * 2); + return TYPE_INFO_STRING.equals(value); + } catch (IOException e) { + return false; + } + } + + private static Address locateTypeInfoAddress(Program program) { + Memory memory = program.getMemory(); + try { + List dataBlocks = ProgramMemoryUtil.getMemoryBlocksStartingWithName( + program, program.getMemory(), ".data", TaskMonitor.DUMMY); + for (MemoryBlock memoryBlock : dataBlocks) { + Address typeInfoAddress = + memory.findBytes(memoryBlock.getStart(), memoryBlock.getEnd(), + TYPE_INFO_STRING.getBytes(), null, true, TaskMonitor.DUMMY); + if (typeInfoAddress != null) { + return TypeDescriptorModel.getBaseAddress(program, typeInfoAddress); + } + } + } catch (CancelledException e) { + // impossible + throw new AssertException(e); + } + return null; + } + } diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/plugin/prototype/MicrosoftCodeAnalyzerPlugin/RttiAnalyzer.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/plugin/prototype/MicrosoftCodeAnalyzerPlugin/RttiAnalyzer.java index 793fd2463a..6bbc56f112 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/plugin/prototype/MicrosoftCodeAnalyzerPlugin/RttiAnalyzer.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/plugin/prototype/MicrosoftCodeAnalyzerPlugin/RttiAnalyzer.java @@ -21,6 +21,7 @@ import ghidra.app.cmd.data.CreateTypeDescriptorBackgroundCmd; import ghidra.app.cmd.data.TypeDescriptorModel; import ghidra.app.cmd.data.rtti.CreateRtti4BackgroundCmd; import ghidra.app.cmd.data.rtti.Rtti4Model; +import ghidra.app.cmd.data.rtti.RttiUtil; import ghidra.app.services.*; import ghidra.app.util.datatype.microsoft.*; import ghidra.app.util.importer.MessageLog; @@ -29,9 +30,10 @@ import ghidra.program.model.data.InvalidDataTypeException; import ghidra.program.model.lang.UndefinedValueException; import ghidra.program.model.listing.Program; import ghidra.program.model.mem.MemoryBlock; +import ghidra.program.model.symbol.SourceType; import ghidra.program.util.ProgramMemoryUtil; import ghidra.util.bytesearch.*; -import ghidra.util.exception.CancelledException; +import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; /** @@ -48,7 +50,6 @@ public class RttiAnalyzer extends AbstractAnalyzer { // they are used for RTTI, then change the CLASS_PREFIX_CHARS to ".". Need to be // careful that changing to this doesn't cause problems to RTTI analysis. private static final String CLASS_PREFIX_CHARS = ".?A"; - public static final String TYPE_INFO_STRING = ".?AVtype_info@@"; private DataValidationOptions validationOptions; private DataApplyOptions applyOptions; @@ -76,30 +77,20 @@ public class RttiAnalyzer extends AbstractAnalyzer { public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) throws CancelledException { - List dataBlocks = - ProgramMemoryUtil.getMemoryBlocksStartingWithName(program, set, ".data", monitor); - List
typeInfoAddresses = - ProgramMemoryUtil.findString(TYPE_INFO_STRING, program, dataBlocks, set, monitor); - - int typeInfoCount = typeInfoAddresses.size(); - if (typeInfoCount != 1) { - if (typeInfoCount == 0) { - log.appendMsg(this.getName(), "Couldn't find type info structure."); - return true; - } - log.appendMsg(this.getName(), - "Found " + typeInfoCount + " type info structures when expecting only 1."); - return false; - } - - // Found exactly 1 type info string, so use it to find RTTI structures. - Address typeInfoStringAddress = typeInfoAddresses.get(0); - Address typeInfoRtti0Address = - TypeDescriptorModel.getBaseAddress(program, typeInfoStringAddress); + Address typeInfoRtti0Address = RttiUtil.getTypeInfoTypeDescriptorAddress(program); if (typeInfoRtti0Address == null) { log.appendMsg(this.getName(), "Couldn't find RTTI type info structure."); return true; } + + // ensure the label is present + try { + program.getSymbolTable().createLabel( + typeInfoRtti0Address, RttiUtil.TYPE_INFO_LABEL, SourceType.ANALYSIS); + } 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 = @@ -113,6 +104,8 @@ public class RttiAnalyzer extends AbstractAnalyzer { } int alignment = program.getDefaultPointerSize(); + List dataBlocks = ProgramMemoryUtil.getMemoryBlocksStartingWithName( + program, program.getMemory(), ".data", TaskMonitor.DUMMY); Set
possibleTypeAddresses = ProgramMemoryUtil.findDirectReferences(program, dataBlocks, alignment, commonVfTableAddress, monitor); diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/test/java/ghidra/app/cmd/data/AbstractCreateDataTypeModelTest.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/test/java/ghidra/app/cmd/data/AbstractCreateDataTypeModelTest.java index 5a7fb5d37d..6ecbfe6b28 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/test/java/ghidra/app/cmd/data/AbstractCreateDataTypeModelTest.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/test/java/ghidra/app/cmd/data/AbstractCreateDataTypeModelTest.java @@ -20,8 +20,9 @@ import static org.junit.Assert.*; import org.junit.Assert; import generic.test.AbstractGenericTest; + +import ghidra.app.cmd.data.rtti.RttiUtil; import ghidra.app.plugin.core.analysis.AutoAnalysisManager; -import ghidra.app.plugin.prototype.MicrosoftCodeAnalyzerPlugin.RttiAnalyzer; import ghidra.app.services.DataTypeManagerService; import ghidra.app.util.datatype.microsoft.DataApplyOptions; import ghidra.app.util.datatype.microsoft.DataValidationOptions; @@ -139,7 +140,7 @@ public class AbstractCreateDataTypeModelTest extends AbstractGenericTest { builder.createMemory(".rdata", "0x01003000", 0x2000); builder.createMemory(".data", "0x01005000", 0x2000); setupDTMService(builder.getProgram()); - builder.setBytes("0x01005008", RttiAnalyzer.TYPE_INFO_STRING.getBytes()); + builder.setBytes("0x01005008", RttiUtil.TYPE_INFO_STRING.getBytes()); return builder; } @@ -157,7 +158,7 @@ public class AbstractCreateDataTypeModelTest extends AbstractGenericTest { builder.createMemory(".rdata", "0x101003000", 0x2000); builder.createMemory(".data", "0x101005000", 0x2000); setupDTMService(builder.getProgram()); - builder.setBytes("0x101005010", RttiAnalyzer.TYPE_INFO_STRING.getBytes()); + builder.setBytes("0x101005010", RttiUtil.TYPE_INFO_STRING.getBytes()); return builder; } @@ -174,7 +175,7 @@ public class AbstractCreateDataTypeModelTest extends AbstractGenericTest { builder.createMemory(".text", "0x101001000", 0x2000); builder.createMemory(".rdata", "0x101003000", 0x2000); builder.createMemory(".data", "0x101005000", 0x2000); - builder.setBytes("0x101005010", RttiAnalyzer.TYPE_INFO_STRING.getBytes()); + builder.setBytes("0x101005010", RttiUtil.TYPE_INFO_STRING.getBytes()); return builder; } diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/test/java/ghidra/app/cmd/data/exceptionhandling/AbstractEHTest.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/test/java/ghidra/app/cmd/data/exceptionhandling/AbstractEHTest.java index f6715a6f1a..ee606062c5 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/test/java/ghidra/app/cmd/data/exceptionhandling/AbstractEHTest.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/test/java/ghidra/app/cmd/data/exceptionhandling/AbstractEHTest.java @@ -16,7 +16,7 @@ package ghidra.app.cmd.data.exceptionhandling; import ghidra.app.cmd.data.AbstractCreateDataTypeModelTest; -import ghidra.app.plugin.prototype.MicrosoftCodeAnalyzerPlugin.RttiAnalyzer; +import ghidra.app.cmd.data.rtti.RttiUtil; import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramDB; import ghidra.program.model.address.Address; @@ -46,7 +46,7 @@ public class AbstractEHTest extends AbstractCreateDataTypeModelTest { getHexAddress32AsByteString(tryAddress, bigEndian)); builder.setBytes(ipCountCompAddr.toString(), getIntAsByteString(ipCount, bigEndian)); builder.setBytes(ipCompAddr.toString(), getHexAddress32AsByteString(ipAddress, bigEndian)); - builder.setBytes("0x01005008", RttiAnalyzer.TYPE_INFO_STRING.getBytes()); + builder.setBytes("0x01005008", RttiUtil.TYPE_INFO_STRING.getBytes()); } protected void setupV2FuncInfo32(ProgramBuilder builder, long address, int magicNum, @@ -98,7 +98,7 @@ public class AbstractEHTest extends AbstractCreateDataTypeModelTest { getHexAddressAsIbo32ByteString(builder, ipAddress, bigEndian)); builder.setBytes(unwindHelpCompAddr.toString(), getIntAsByteString(unwindHelpDisplacement, bigEndian)); - builder.setBytes("0x101005010", RttiAnalyzer.TYPE_INFO_STRING.getBytes()); + builder.setBytes("0x101005010", RttiUtil.TYPE_INFO_STRING.getBytes()); } protected void setupV2FuncInfo64(ProgramBuilder builder, long address, int magicNum, @@ -301,7 +301,7 @@ public class AbstractEHTest extends AbstractCreateDataTypeModelTest { // FuncInfo setupV1FuncInfo32(builder, 0x01003340, EHFunctionInfoModel.EH_MAGIC_NUMBER_V1, 3, "0x01003368", 2, "0x01003380", 4, "0x010033d0"); // 28 bytes - builder.setBytes("0x01005008", RttiAnalyzer.TYPE_INFO_STRING.getBytes()); + builder.setBytes("0x01005008", RttiUtil.TYPE_INFO_STRING.getBytes()); setupCompleteFlow32NoESTypeList(builder); } @@ -309,7 +309,7 @@ public class AbstractEHTest extends AbstractCreateDataTypeModelTest { // FuncInfo setupV2FuncInfo32(builder, 0x01003340, EHFunctionInfoModel.EH_MAGIC_NUMBER_V2, 3, "0x01003368", 2, "0x01003380", 4, "0x010033d0", "0x010033f0"); // 32 bytes - builder.setBytes("0x01005008", RttiAnalyzer.TYPE_INFO_STRING.getBytes()); + builder.setBytes("0x01005008", RttiUtil.TYPE_INFO_STRING.getBytes()); setupCompleteFlow32(builder); } @@ -317,7 +317,7 @@ public class AbstractEHTest extends AbstractCreateDataTypeModelTest { // FuncInfo setupV3FuncInfo32(builder, 0x01003340, EHFunctionInfoModel.EH_MAGIC_NUMBER_V3, 3, "0x01003368", 2, "0x01003380", 4, "0x010033d0", "0x010033f0", 0x1); // 36 bytes - builder.setBytes("0x01005008", RttiAnalyzer.TYPE_INFO_STRING.getBytes()); + builder.setBytes("0x01005008", RttiUtil.TYPE_INFO_STRING.getBytes()); setupCompleteFlow32(builder); } @@ -421,7 +421,7 @@ public class AbstractEHTest extends AbstractCreateDataTypeModelTest { // FuncInfo setupV1FuncInfo64(builder, 0x101003340L, EHFunctionInfoModel.EH_MAGIC_NUMBER_V1, 3, "0x101003368", 2, "0x101003380", 4, "0x1010033d0", 0x00000200); - builder.setBytes("0x101005010", RttiAnalyzer.TYPE_INFO_STRING.getBytes()); + builder.setBytes("0x101005010", RttiUtil.TYPE_INFO_STRING.getBytes()); setupCompleteFlow64NoESTypeList(builder); } @@ -429,7 +429,7 @@ public class AbstractEHTest extends AbstractCreateDataTypeModelTest { // FuncInfo setupV2FuncInfo64(builder, 0x101003340L, EHFunctionInfoModel.EH_MAGIC_NUMBER_V2, 3, "0x101003368", 2, "0x101003380", 4, "0x1010033d0", 0x00000200, "0x1010033f0"); - builder.setBytes("0x101005010", RttiAnalyzer.TYPE_INFO_STRING.getBytes()); + builder.setBytes("0x101005010", RttiUtil.TYPE_INFO_STRING.getBytes()); setupCompleteFlow64(builder); } @@ -437,7 +437,7 @@ public class AbstractEHTest extends AbstractCreateDataTypeModelTest { // FuncInfo setupV3FuncInfo64(builder, 0x101003340L, EHFunctionInfoModel.EH_MAGIC_NUMBER_V3, 3, "0x101003368", 2, "0x101003380", 4, "0x1010033d0", 0x00000200, "0x1010033f0", 0x1); - builder.setBytes("0x101005010", RttiAnalyzer.TYPE_INFO_STRING.getBytes()); + builder.setBytes("0x101005010", RttiUtil.TYPE_INFO_STRING.getBytes()); setupCompleteFlow64(builder); }