/* ### * 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. */ // Script to fix up Windows RTTI vtables and structures //@category C++ import java.util.*; import ghidra.app.script.GhidraScript; import ghidra.program.model.address.*; import ghidra.program.model.data.*; import ghidra.program.model.listing.CircularDependencyException; import ghidra.program.model.listing.Data; import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.symbol.*; import ghidra.util.exception.*; public class FixUpRttiAnalysisScript extends GhidraScript { private static final String RTTI_BASE_CLASS_ARRAY_LABEL = "RTTI_Base_Class_Array"; private static final String RTTI_CLASS_HIERARCHY_DESCRIPTOR_LABEL = "RTTI_Class_Hierarchy_Descriptor"; private static final String RTTI_BASE_CLASS_DESCRIPTOR_LABEL = "RTTI_Base_Class_Descriptor"; private static final String RTTI_BASE_COMPLETE_OBJECT_LOADER_LABEL = "RTTI_Complete_Object_Locator"; private static final String RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME = "RTTIBaseClassDescriptor"; private static final String RTTI_BASE_COMPLETE_OBJECT_LOADER_DATA_NAME = "RTTICompleteObjectLocator"; private static final String RTTI_CLASS_HIERARCHY_DESCRIPTOR_DATA_NAME = "RTTIClassHierarchyDescriptor"; private static final String VFTABLE_META_PTR_LABEL = "vftable_meta_ptr"; private static final String VFTABLE_LABEL = "vftable"; SymbolTable symbolTable = null; DataTypeManager dataTypeManager = null; GlobalNamespace globalNamespace = null; int defaultPointerSize = 0; boolean isWindows = false; @Override public void run() throws Exception { if (currentProgram == null) { println("There is no open program"); return; } setIsWindows(); if (!isWindows) { println("This script only handles Windows programs"); return; } // TODO: check version and only run if before 9.3? symbolTable = currentProgram.getSymbolTable(); dataTypeManager = currentProgram.getDataTypeManager(); globalNamespace = (GlobalNamespace) currentProgram.getGlobalNamespace(); defaultPointerSize = currentProgram.getDefaultPointerSize(); if (defaultPointerSize != 4 && defaultPointerSize != 8) { println("This script only works on 32 or 64 bit programs"); return; } applyMissingRTTIStructures(); } /** * Method to find and apply missing RTTI structures * @throws CancelledException if cancelled * @throws Exception if error applying label or data */ private List applyMissingRTTIStructures() throws CancelledException, Exception { List completeObjectLocatorSymbols = createMissingRTTICompleteObjectLocator(); List baseClassDescriptorSymbols = createMissingBaseClassDescriptors(); List
classHierarchyDescriptors = createMissingClassHierarchyDescriptors( baseClassDescriptorSymbols, completeObjectLocatorSymbols); createMissingBaseClassArrays(classHierarchyDescriptors); List vftableSymbols = createMissingVftableSymbols(completeObjectLocatorSymbols); return vftableSymbols; } /** * Method to set the global variable isWindows */ private void setIsWindows() { String compilerID = currentProgram.getCompilerSpec().getCompilerSpecID().getIdAsString().toLowerCase(); isWindows = compilerID.contains("windows"); } /** * Method to iterate over all symbols with Base Class Descriptor symbol and if * the correct data type has not already been created, do so. * @return List of all symbols with valid (even previously) BaseClassDescriptor structure applied * @throws CancelledException when cancelled * @throws Exception when data cannot be created */ private List createMissingRTTICompleteObjectLocator() throws CancelledException, Exception { List completeObjectLocatorSymbols = new ArrayList(); SymbolIterator dataSymbols = symbolTable.getSymbols(getInitializedMemory(), SymbolType.LABEL, true); while (dataSymbols.hasNext()) { monitor.checkCanceled(); Symbol symbol = dataSymbols.next(); if (!symbol.getName().contains(RTTI_BASE_COMPLETE_OBJECT_LOADER_LABEL)) { continue; } Data data = getDataAt(symbol.getAddress()); if (data != null && data.getDataType().getName().contains(RTTI_BASE_COMPLETE_OBJECT_LOADER_DATA_NAME)) { completeObjectLocatorSymbols.add(symbol); continue; } // for some reason it was named but not created so create it data = createCompleteObjectLocator(symbol.getAddress()); if (data != null && data.getDataType().getName().contains(RTTI_BASE_COMPLETE_OBJECT_LOADER_DATA_NAME)) { completeObjectLocatorSymbols.add(symbol); continue; } println("Cannot create RTTI_CompleteObjectLocator at " + symbol.getAddress()); } return completeObjectLocatorSymbols; } /** * Method to create a CompleteObjectLocator structure at the given address * @param address the address where the structure will be created * @return the created CompleteObjectLocator data or null if it couldn't be created * @throws CancelledException if cancelled * @throws Exception if error creating data */ private Data createCompleteObjectLocator(Address address) throws CancelledException, Exception { DataType completeObjLocatorDataType = dataTypeManager.getDataType(CategoryPath.ROOT, RTTI_BASE_COMPLETE_OBJECT_LOADER_DATA_NAME); if (completeObjLocatorDataType == null) { return null; } int sizeOfDt = completeObjLocatorDataType.getLength(); clearListing(address, address.add(sizeOfDt)); Data completeObjectLocator = createData(address, completeObjLocatorDataType); if (completeObjectLocator == null) { return null; } return completeObjectLocator; } /** * Method to iterate over all symbols with Base Class Descriptor symbol and if * the correct data type has not already been created, do so. * @return List of all symbols with valid (even previously) BaseClassDescriptor structure applied * @throws Exception when cancelled */ private List createMissingBaseClassDescriptors() throws Exception { List baseClassDescriptorSymbols = new ArrayList(); SymbolIterator dataSymbols = symbolTable.getSymbols(getInitializedMemory(), SymbolType.LABEL, true); while (dataSymbols.hasNext()) { monitor.checkCanceled(); Symbol symbol = dataSymbols.next(); if (!symbol.getName().contains(RTTI_BASE_CLASS_DESCRIPTOR_LABEL)) { continue; } Data data = getDataAt(symbol.getAddress()); if (data != null && data.getDataType().getName().contains(RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME)) { baseClassDescriptorSymbols.add(symbol); continue; } // for some reason it was named but not created so create it data = createBaseClassDescriptor(symbol.getAddress()); if (data != null && data.getDataType().getName().contains(RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME)) { baseClassDescriptorSymbols.add(symbol); continue; } println("Cannot create RTTI_Base_Class_Descriptor at " + symbol.getAddress()); } return baseClassDescriptorSymbols; } /** * Method to create a BaseClassDescriptor structure at the given address * @param baseClassDescriptorAddress the address where the structure will be created * @return the created BaseClassDescriptor data or null if it couldn't be created * @throws CancelledException if cancelled * @throws Exception if error creating data */ private Data createBaseClassDescriptor(Address baseClassDescriptorAddress) throws CancelledException, Exception { DataType baseClassDescriptor = dataTypeManager.getDataType(CategoryPath.ROOT, RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME); int sizeOfDt = baseClassDescriptor.getLength(); clearListing(baseClassDescriptorAddress, baseClassDescriptorAddress.add(sizeOfDt)); Data baseClassDescArray = createData(baseClassDescriptorAddress, baseClassDescriptor); if (baseClassDescArray == null) { return null; } return baseClassDescArray; } /** * Method to apply missing RTTI Base Class Descriptor structures and symbols * @param address address to apply the missing structure and symbol * @param numBaseClasses number of base classes in the array pointing to BaseClassDescriptors * @param classNamespace name of the class * @throws AddressOutOfBoundsException if try clear listing at address out of bounds * @throws MemoryAccessException if cannot access memory * @throws CancelledException if cancelled * @throws Exception if issue making data */ private void createBaseClassDescriptors(Address address, int numBaseClasses, Namespace classNamespace) throws CancelledException, MemoryAccessException, AddressOutOfBoundsException, Exception { for (int i = 0; i < numBaseClasses; i++) { monitor.checkCanceled(); Address baseClassDescriptorAddress = getReferencedAddress(address.add(i * 4)); Data baseClassDescriptor = getDataAt(baseClassDescriptorAddress); if (baseClassDescriptor == null || !baseClassDescriptor.getDataType() .getName() .equals( RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME)) { int num1 = getInt(baseClassDescriptorAddress.add(8)); int num2 = getInt(baseClassDescriptorAddress.add(12)); int num3 = getInt(baseClassDescriptorAddress.add(16)); int num4 = getInt(baseClassDescriptorAddress.add(20)); baseClassDescriptor = createBaseClassDescriptor(baseClassDescriptorAddress); if (baseClassDescriptor != null) { symbolTable.createLabel( baseClassDescriptorAddress, RTTI_BASE_CLASS_DESCRIPTOR_LABEL + "_at_(" + num1 + "," + num2 + "," + num3 + "," + num4 + ")", classNamespace, SourceType.ANALYSIS); } else { println( "Failed to create a baseClassDescArray structure at " + address.toString()); } } } } /** * * @param baseClassDescriptors the given list of BaseClassDescriptor symbols * @param completeObjectLocators the given list of CompleteObjectLocator symbols * @return list of ClassHierarchyDescriptor addresses * @throws CancelledException if cancelled * @throws MemoryAccessException if memory cannot be read * @throws InvalidInputException if issue setting return type * @throws AddressOutOfBoundsException if try clear listing at address out of bounds * @throws Exception if there is an issue creating a label */ private List
createMissingClassHierarchyDescriptors(List baseClassDescriptors, List completeObjectLocators) throws CancelledException, MemoryAccessException, InvalidInputException, AddressOutOfBoundsException, Exception { List
classHierarchyDescriptorAddresses = new ArrayList
(); Iterator baseClassDescriptorIterator = baseClassDescriptors.iterator(); while (baseClassDescriptorIterator.hasNext()) { monitor.checkCanceled(); Symbol symbol = baseClassDescriptorIterator.next(); Address classHierarchyDescriptorAddress = createClassHierarchyDescriptor( symbol.getAddress().add(24), symbol.getParentNamespace()); if (classHierarchyDescriptorAddress != null && !classHierarchyDescriptorAddresses.contains(classHierarchyDescriptorAddress)) { classHierarchyDescriptorAddresses.add(classHierarchyDescriptorAddress); } } Iterator completeObjectLocatorIterator = completeObjectLocators.iterator(); while (completeObjectLocatorIterator.hasNext()) { monitor.checkCanceled(); Symbol symbol = completeObjectLocatorIterator.next(); Address classHierarchyDescriptorAddress = createClassHierarchyDescriptor( symbol.getAddress().add(16), symbol.getParentNamespace()); if (classHierarchyDescriptorAddress != null && !classHierarchyDescriptorAddresses.contains(classHierarchyDescriptorAddress)) { classHierarchyDescriptorAddresses.add(classHierarchyDescriptorAddress); } } return classHierarchyDescriptorAddresses; } /** * * @param address the address where the ClassHierarchyDescriptor is to be created * @param classNamespace the namespace of the class * @return the given class's ClassHierarchyDescriptor address * @throws CancelledException if cancelled * @throws MemoryAccessException if memory cannot be read * @throws InvalidInputException if issue setting return type * @throws Exception if issue creating label */ private Address createClassHierarchyDescriptor(Address address, Namespace classNamespace) throws CancelledException, MemoryAccessException, InvalidInputException, Exception { Address classHierarchyDescriptorAddress = getReferencedAddress(address); Data classHierarchyStructure = getDataAt(classHierarchyDescriptorAddress); if (classHierarchyStructure != null && classHierarchyStructure.getDataType() .getName() .equals( RTTI_CLASS_HIERARCHY_DESCRIPTOR_DATA_NAME)) { return classHierarchyDescriptorAddress; } Symbol classHierarchySymbol; classHierarchySymbol = symbolTable.createLabel(classHierarchyDescriptorAddress, RTTI_CLASS_HIERARCHY_DESCRIPTOR_LABEL, classNamespace, SourceType.ANALYSIS); classHierarchyStructure = createClassHierarchyStructure(classHierarchyDescriptorAddress); if (classHierarchyStructure == null) { println("Failed to create a classHierarchyDescriptor structure at " + classHierarchyDescriptorAddress.toString()); symbolTable.removeSymbolSpecial(classHierarchySymbol); return null; } return classHierarchyDescriptorAddress; } /** * Method to create a ClassHierarchyDescriptor structure at the given address * @param classHierarchyDescriptorAddress the address where the structure will be created * @return the created ClassHierarchyDescriptor data or null if it couldn't be created * @throws CancelledException if cancelled * @throws AddressOutOfBoundsException if try clear listing at address out of bounds * @throws Exception if issue creating data */ private Data createClassHierarchyStructure(Address classHierarchyDescriptorAddress) throws CancelledException, AddressOutOfBoundsException, Exception { DataType classHDatatype = dataTypeManager.getDataType(CategoryPath.ROOT, RTTI_CLASS_HIERARCHY_DESCRIPTOR_DATA_NAME); int sizeOfDt = classHDatatype.getLength(); clearListing(classHierarchyDescriptorAddress, classHierarchyDescriptorAddress.add(sizeOfDt)); Data classHierarchyStructure = createData(classHierarchyDescriptorAddress, classHDatatype); if (classHierarchyStructure == null) { return null; } return classHierarchyStructure; } /** * * @param classHierarchyDescriptors the given list of applied ClassHierarchyDescriptor structures * @return a list of base class array addresses * @throws CancelledException if cancelled * @throws MemoryAccessException if memory cannot be read * @throws AddressOutOfBoundsException if try clear listing at address out of bounds * @throws Exception if there is an issue creating a label */ private List
createMissingBaseClassArrays(List
classHierarchyDescriptors) throws CancelledException, MemoryAccessException, AddressOutOfBoundsException, Exception { List
baseClassArrayAddresses = new ArrayList
(); Iterator
classHierarchyDescriptorIterator = classHierarchyDescriptors.iterator(); while (classHierarchyDescriptorIterator.hasNext()) { monitor.checkCanceled(); Address classHierarchyDescriptorAddress = classHierarchyDescriptorIterator.next(); Symbol classHierarchyDescriptorSymbol = symbolTable.getPrimarySymbol(classHierarchyDescriptorAddress); Namespace classNamespace = classHierarchyDescriptorSymbol.getParentNamespace(); int numBaseClasses = getInt(classHierarchyDescriptorAddress.add(8)); Address baseClassArrayAddress = getReferencedAddress(classHierarchyDescriptorAddress.add(12)); Data baseClassDescArray = getDataAt(baseClassArrayAddress); if (baseClassDescArray != null && baseClassDescArray.isArray()) { baseClassArrayAddresses.add(baseClassArrayAddress); continue; } baseClassDescArray = createBaseClassArray(baseClassArrayAddress, numBaseClasses); if (baseClassDescArray != null && baseClassDescArray.isArray()) { Symbol primarySymbol = symbolTable.getPrimarySymbol(baseClassArrayAddress); if (primarySymbol == null || !primarySymbol.getName().contains(RTTI_BASE_CLASS_ARRAY_LABEL)) { symbolTable.createLabel(baseClassArrayAddress, RTTI_BASE_CLASS_ARRAY_LABEL, classNamespace, SourceType.ANALYSIS); } baseClassArrayAddresses.add(baseClassArrayAddress); createBaseClassDescriptors(baseClassArrayAddress, numBaseClasses, classNamespace); continue; } println("Failed to create a baseClassDescArray structure at " + baseClassArrayAddress.toString()); } return baseClassArrayAddresses; } /** * Method to create a base class array at the given address with the given number of base class's in the array * @param baseClassArrayAddress the address where the array will be created * @param numBaseClasses the number of BaseClass's in the array * @return the created BaseClassArray data or null if cannot retrieve it * @throws CancelledException if cancelled * @throws Exception if error creating data */ private Data createBaseClassArray(Address baseClassArrayAddress, int numBaseClasses) throws CancelledException, Exception { int sizeOfDt; ArrayDataType baseClassDescArrayDT; int addressSize = baseClassArrayAddress.getSize(); if (addressSize == 32) { DataType baseClassDescriptor = dataTypeManager.getDataType(CategoryPath.ROOT, RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME); PointerDataType baseClassDescriptorPtr = new PointerDataType(baseClassDescriptor); sizeOfDt = baseClassDescriptorPtr.getLength(); baseClassDescArrayDT = new ArrayDataType(baseClassDescriptorPtr, numBaseClasses, sizeOfDt); } else if (addressSize == 64) { DataType imageBaseOffset = dataTypeManager.getDataType(CategoryPath.ROOT, "ImageBaseOffset32"); sizeOfDt = imageBaseOffset.getLength(); baseClassDescArrayDT = new ArrayDataType(imageBaseOffset, numBaseClasses, sizeOfDt); } else { return null; } clearListing(baseClassArrayAddress, baseClassArrayAddress.add(numBaseClasses * sizeOfDt)); Data baseClassDescArray = createData(baseClassArrayAddress, baseClassDescArrayDT); if (baseClassDescArray == null) { return null; } return baseClassDescArray; } /** * Method to create missing vftables and return a list of them * @param completeObjectLocatorSymbols the list of completeObjectLocatorSymbols * @return list of vftable symbols * @throws CancelledException if cancelled * @throws InvalidInputException if invalid input * @throws CircularDependencyException if namespace has circular dependency * @throws DuplicateNameException if try to create label with duplicate name in namespace */ private List createMissingVftableSymbols(List completeObjectLocatorSymbols) throws CancelledException, InvalidInputException, DuplicateNameException, CircularDependencyException { List vftables = new ArrayList(); Iterator iterator = completeObjectLocatorSymbols.iterator(); while (iterator.hasNext()) { monitor.checkCanceled(); Symbol completeObjectLocatorSymbol = iterator.next(); Address completeObjectLocatorAddress = completeObjectLocatorSymbol.getAddress(); Namespace classNamespace = completeObjectLocatorSymbol.getParentNamespace(); if (classNamespace.equals(globalNamespace)) { println("no class namespace for " + completeObjectLocatorAddress.toString()); continue; } Reference[] referencesTo = getReferencesTo(completeObjectLocatorAddress); if (referencesTo.length == 0) { println("no refs to " + completeObjectLocatorAddress.toString()); continue; } for (Reference refTo : referencesTo) { Address vftableMetaPointer = refTo.getFromAddress(); if (vftableMetaPointer == null) { println("can't retrieve meta address"); continue; } Address vftableAddress = vftableMetaPointer.add(defaultPointerSize); if (vftableAddress == null) { println("can't retrieve vftable address"); continue; } // if not created, create vftable meta pointer label if (getGivenSymbol(vftableAddress, VFTABLE_META_PTR_LABEL, classNamespace) == null) { symbolTable.createLabel(vftableMetaPointer, VFTABLE_META_PTR_LABEL, classNamespace, SourceType.ANALYSIS); } // if not created, create vftable label Symbol vftableSymbol = getGivenSymbol(vftableAddress, VFTABLE_LABEL, classNamespace); if (vftableSymbol == null) { vftableSymbol = symbolTable.createLabel(vftableAddress, VFTABLE_LABEL, classNamespace, SourceType.ANALYSIS); if (vftableSymbol == null) { continue; } } if (!vftables.contains(vftableSymbol)) { vftables.add(vftableSymbol); } } } return vftables; } /** * Method to retrieve the symbol with the given address, containing name (containing to account * for pdb case where sometimes has extra chars) and namespace * @param address the given address * @param name the given name * @param namespace the given namespace * @return the symbol with the given address, containing name, with given namespace * @throws CancelledException if cancelled */ private Symbol getGivenSymbol(Address address, String name, Namespace namespace) throws CancelledException { SymbolIterator symbols = symbolTable.getSymbolsAsIterator(address); for (Symbol sym : symbols) { monitor.checkCanceled(); if (sym.getName().contains(name) && sym.getParentNamespace().equals(namespace)) { return sym; } } return null; } /** * Method to return referenced address at the given address * @param address the address to look for a referenced address at * @return the first referenced address from the given address * @throws MemoryAccessException if memory cannot be read */ private Address getReferencedAddress(Address address) throws MemoryAccessException { //TODO: switch to this then test then just rewrite the call and get rid of this method // MSDataTypeUtils.getReferencedAddress(currentProgram, address); // this will work whether there is a created reference or not int addressSize = address.getSize(); if (addressSize == 32) { long offset = getInt(address); return address.getNewAddress(offset); } // this currently will workn only if there is a created reference // TODO: get ibo bytes and figure out what the ibo ref address would be if (addressSize == 64) { Reference refs[] = getReferencesFrom(address); if (refs.length == 0) { return null; } return refs[0].getToAddress(); } return null; } /** * Method to retrieve the AddressSet of the current program's initialized memory * @return the AddressSet of the current program's initialized memory * @throws CancelledException if cancelled */ private AddressSet getInitializedMemory() throws CancelledException { AddressSet dataAddresses = new AddressSet(); MemoryBlock[] blocks = currentProgram.getMemory().getBlocks(); for (MemoryBlock block : blocks) { monitor.checkCanceled(); if (block.isInitialized()) { dataAddresses.add(block.getStart(), block.getEnd()); } } return dataAddresses; } }