def annotateVirtualCall(calledFunc, startingFunc, callAddr, thisOverride=None): print calledFunc funcDef = FunctionDefinitionDataType(calledFunc.signature) if thisOverride is not None: originalThis = funcDef.arguments[0] funcDef.replaceArgument(0, originalThis.name, thisOverride, originalThis.comment, SourceType.DEFAULT) HighFunctionDBUtil.writeOverride(startingFunc, callAddr, funcDef) currentProgram.listing.setComment(callAddr, CodeUnit.PRE_COMMENT, "{@symbol %s}"%calledFunc.symbol.getName(True))
def createFunction(self, element): """ Convert CastXML XML element into a Ghidra FunctionDefinitionDataType. Args: element (ElementTree): XML element Returns (FunctionDefinitionDataType): Ghidra FunctionDefinitionDataType """ functionName = "" if 'name' in element.attrib: functionName = element.attrib['name'] else: functionName = "anon_func" + element.attrib['id'] print("Function: {0}".format(functionName)) functionType = None if 'file' in element.attrib: filePath = self.getFileFromId(element.attrib['file']) categoryPath = self.getCategoryPathFromFile(filePath) functionType = FunctionDefinitionDataType(categoryPath, functionName) else: functionType = FunctionDefinitionDataType(functionName) returnTypeElement = self.getTypeInfoElementById( element.attrib['returns']) returnType = self.getDataType(returnTypeElement) functionType.setReturnType(returnType) parms = [] argumentElements = element.getchildren() for i, argumentElement in enumerate(argumentElements): if argumentElement.tag == "Argument": argumentName = "" if 'name' in argumentElement.attrib: argumentName = argumentElement.attrib['name'] else: argumentName = "a" + str(i) argumentTypeElement = self.getTypeInfoElementById( argumentElement.attrib['type']) parmDataType = self.getDataType(argumentTypeElement) parms.append( ParameterDefinitionImpl(argumentName, parmDataType, "")) elif argumentElement.tag == "Elipsis": functionType.setVarArgs(True) functionType.setArguments(parms) self.recordTypeForId(element.attrib['id'], functionType) return functionType
def create_types(self): """ Create custom data types if the don't exist yet (why would they?). """ self.function_type = dtm.getDataType("/ScatterLoadSubFunction") if not self.function_type: self.function_type = FunctionDefinitionDataType("ScatterLoadSubFunction") self.function_type.setReturnType(VoidDataType.dataType) self.function_type.setArguments([ ParameterDefinitionImpl("source", PointerDataType(VoidDataType.dataType), None), ParameterDefinitionImpl("destination", PointerDataType(VoidDataType.dataType), None), ParameterDefinitionImpl("dest_size", UnsignedIntegerDataType.dataType, None) ]) self.table_entry_type = dtm.getDataType("/ScatterLoadTableEntry") if not self.table_entry_type: self.table_entry_type = StructureDataType("ScatterLoadTableEntry", 0) self.table_entry_type.add(PointerDataType(VoidDataType.dataType), 0, "source", "Source data for RAM initialization") self.table_entry_type.add(PointerDataType(VoidDataType.dataType), 0, "destination", "Start of target area in RAM") self.table_entry_type.add(UnsignedIntegerDataType.dataType, 0, "dest_size", "Length of target area in RAM, in bytes") self.table_entry_type.add(PointerDataType(self.function_type), 0, "func", "Function to be called")
def defineIOExternalTrap(): dtm = currentProgram.getDataTypeManager() dt = dtm.findDataType(currentProgram.getName() + "/" + "IOExternalTrap") uint = dtm.getDataType("/uint") IOService = dtm.getDataType("/IOService") IOTrap_def = "IOService::IOTrap(void * p1, void * p2, void * p3, void * p4, void * p5, void * p6)" fdef = FunctionDefinitionDataType(IOTrap_def) fdef.setReturnType(uint) fdef.setGenericCallingConvention(GenericCallingConvention.thiscall) st = StructureDataType("IOExternalTrap", 0) st.setToMachineAlignment() st.add(PointerDataType(IOService), "object", "") st.add(PointerDataType(fdef), "func", "") dtm.addDataType(PointerDataType(st), None)
typeManager = currentProgram.getDataTypeManager() func_manager = currentProgram.getFunctionManager() d = code_manager.getDataAt(vftableAddress) assert(d.isArray()) assert(d.getPrimarySymbol().getParentNamespace().getSymbol().getSymbolType()==SymbolType.CLASS) class_name = d.getPrimarySymbol().getParentNamespace().getName() vftable_classname = naming_prefix+class_name+"_vftable" l = [] typeManager.findDataTypes(vftable_classname,l) if l!=[]: print("Class "+vftable_classname+" already exists. Nothing is changed.") else: category = typeManager.createCategory(CategoryPath(categoryPathString)) vftable_type = StructureDataType(vftable_classname, 0) for i in range(d.getNumComponents()): comp = d.getComponent(i) assert(comp.isPointer()) func_addr = comp.getValue() func = func_manager.getFunctionAt(func_addr) assert(func!=None) func_name = func.getName() func_dt = FunctionDefinitionDataType("TGN_func") func_dt.setGenericCallingConvention(GenericCallingConvention.thiscall) func_dt.setReturnType(DWordDataType()) datatype = PointerDataType(func_dt) vftable_type.insert(i, datatype, datatype.getLength(), func_name, "TGN: Automatically created field") category.addDataType(vftable_type, DataTypeConflictHandler.REPLACE_HANDLER)
def generateVtableStruct(vtableAddr): mem = currentProgram.getMemory() vtableClassName = structName vtableName = "vtable" + vtableClassName keepgoing = True cAddr = vtableAddr #.add(8) structData = StructureDataType(vtableName, 0) antiFreeze = 0 while True: #print("Checking " + cAddr.toString()) fnToCheck = CUManager.getCodeUnitContaining(cAddr) #print(fnToCheck.getMnemonicString()) if fnToCheck != None and fnToCheck.getMnemonicString() == "addr": #print("Found start of vtable at") cAddr = cAddr.add(4) break if antiFreeze >= 100: print("Something has to have gone wrong...") return cAddr = cAddr.add(4) antiFreeze += 1 while True: fs = getAddress(mem.getInt(cAddr)) valpart = fs.toString() fntoadd = functionManager.getFunctionContaining(getAddress(valpart)) if fntoadd != None: #print("YES, this is an pointer") #print(fntoadd) if fntoadd != None: dt = FunctionDefinitionDataType( fntoadd, False ) #Second parameter is "Formality", I think this strips the "this" parameter, so lets not set this True #dt.setCategoryPath(CategoryPath("/" + vtableName)) fnClass = getClassName(fntoadd.toString()) dt.setCategoryPath(CategoryPath("/vtable" + fnClass)) dtAdded = dataManager.addDataType( dt, DataTypeConflictHandler.REPLACE_HANDLER) ptr = PointerDataType(dtAdded) #ptr.setCategoryPath(CategoryPath("/" + vtableName)) ptr.setCategoryPath(CategoryPath("/vtable" + fnClass)) ptrAdded = dataManager.addDataType( ptr, DataTypeConflictHandler.REPLACE_HANDLER) structData.add(ptrAdded, ptrAdded.getLength(), fntoadd.toString(), "") else: print("Vtable reached the end.") break cAddr = cAddr.add(4) if structData != None: vtableCDataType = dataManager.addDataType( structData, DataTypeConflictHandler.REPLACE_HANDLER) vtableCDataTypePtr = PointerDataType(vtableCDataType) vtableDTtoAdd = dataManager.addDataType( vtableCDataTypePtr, DataTypeConflictHandler.REPLACE_HANDLER) print("Created " + vtableName) else: print("Skipped " + vtableName)
if data.isPointer(): # it will (hopefully) be a function ptr func_ptr = getFunctionAt(data.value) if not func_ptr: # weird stuff, just skip cur_addr = cur_addr.add(ptr_size) continue print( "[+] {} reference @ 0x{} ( -> 0x{}), adding to the vtable struct" .format(func_ptr.toString(), cur_addr, data.value)) func_name = func_ptr.toString() # include namespace # create a function definition func_definition = FunctionDefinitionDataType( CategoryPath.ROOT, func_name, func_ptr.getSignature()) dtm.addDataType(func_definition, conflict_handler) # add a function pointer to the structure vtable_type.add(func_definition, ptr_size, func_name, "") cur_addr = cur_addr.add(ptr_size) cutoff_ctr += 1 dtm.addDataType(vtable_type, conflict_handler) cmd = CreateStructureCmd(vtable_type, vtable_addr) success = cmd.applyTo(currentProgram) # pointer to the create data type vtable_ptr = dtm.getPointer(vtable_type)
class KeilRAMInit(object): """ The main flow of the script, put into a class so we can break out logical sections of the flow into methods and store intermediate data in the class instance. """ def run(self): """ The high-level flow """ self.ram_block = memory.getBlock("ram") if not self.ram_block or self.ram_block.isInitialized(): yikes("No uninitialized memory block named 'ram' found") if currentSelection is None or currentSelection.isEmpty(): self.scatterload_function = listing.getFunctionContaining(currentAddress) if not self.scatterload_function: yikes("Please launch the script from within a function") self.find_scatterload_table() else: self.scatterload_table = currentSelection.getFirstRange() print("Scatterload table at", self.scatterload_table) self.create_types() self.parse_scatterload_table() if not (self.data_init_start and self.ram_clear_start): yikes("Data init or RAM clear entry are missing from the scatterload table") print("Initialization data at %s (%d bytes)" % (self.data_init_start, self.data_size_packed)) print("Uncompressed initialized data size: %d bytes" % self.data_size_unpacked) print("Zeroed data at %s (%d bytes)" % (self.ram_clear_start, self.ram_clear_size)) self.uncompress_init_data() self.create_ram_blocks() print(r"All done \o/") def create_types(self): """ Create custom data types if the don't exist yet (why would they?). """ self.function_type = dtm.getDataType("/ScatterLoadSubFunction") if not self.function_type: self.function_type = FunctionDefinitionDataType("ScatterLoadSubFunction") self.function_type.setReturnType(VoidDataType.dataType) self.function_type.setArguments([ ParameterDefinitionImpl("source", PointerDataType(VoidDataType.dataType), None), ParameterDefinitionImpl("destination", PointerDataType(VoidDataType.dataType), None), ParameterDefinitionImpl("dest_size", UnsignedIntegerDataType.dataType, None) ]) self.table_entry_type = dtm.getDataType("/ScatterLoadTableEntry") if not self.table_entry_type: self.table_entry_type = StructureDataType("ScatterLoadTableEntry", 0) self.table_entry_type.add(PointerDataType(VoidDataType.dataType), 0, "source", "Source data for RAM initialization") self.table_entry_type.add(PointerDataType(VoidDataType.dataType), 0, "destination", "Start of target area in RAM") self.table_entry_type.add(UnsignedIntegerDataType.dataType, 0, "dest_size", "Length of target area in RAM, in bytes") self.table_entry_type.add(PointerDataType(self.function_type), 0, "func", "Function to be called") def extract_scatterload_A(self): """ Extract the scatterload table pointers from a function that loads the pointers individually """ load_values = [] for i, insn in enumerate(listing.getInstructions(self.scatterload_function.getEntryPoint(), True)): pair = get_load_data(insn) if pair: load_values.append(pair) if len(load_values) == 2: break if i == 8: return None print("Type A scatterload function identified") retval = [] for pointed, pointer in sorted(load_values): listing.clearCodeUnits(pointer, pointer.add(4), False) listing.createData(pointer, PointerDataType.dataType) retval.append(pointed) return retval def extract_scatterload_B(self): """ Extract the pointers from a function that uses a ldmia instruction to load both pointers at once """ insn_adr = listing.getInstructionAt(self.scatterload_function.getEntryPoint()) insn_ldmia = insn_adr.getNext() ldmia_op1 = insn_ldmia.getOpObjects(1) # We're looking for an adr/ldmia combination that loads two consecutive values into two regs if not (insn_adr.mnemonicString == "adr" and insn_ldmia.mnemonicString == "ldmia" and insn_adr.getRegister(0) == insn_ldmia.getRegister(0) and ldmia_op1 and len(ldmia_op1) == 2): return None print("Type B scatterload function identified") # The stored pointers to the scatterload table are offsets relative to the location of the first pointer offsets = currentAddress.getNewAddress(insn_adr.getOpObjects(1)[0].getValue()) listing.clearCodeUnits(offsets, offsets.add(8), False) listing.createData(offsets, UnsignedIntegerDataType.dataType) listing.createData(offsets.add(4), UnsignedIntegerDataType.dataType) return [offsets.add(offset) for offset in read_uints(offsets, 2)] def find_scatterload_table(self): """ Assuming we're really looking at the __scatterload function, extract the scatterload table location and size. """ table_pointers = self.extract_scatterload_A() or self.extract_scatterload_B() if not table_pointers: print("Cannot identify the current function as a scatterload function :(") print("If you're certain this is it please help the script along:") print(" 1. Find the scatterload table yourself - N*16 bytes large and referenced by this function") print(" 2. Select the entire table and re-run the script") exit(1) scatterload_table_start, scatterload_table_end = table_pointers symbols.createLabel(scatterload_table_start, "Region$$Table$$Base", SourceType.USER_DEFINED) symbols.createLabel(scatterload_table_end, "Region$$Table$$Limit", SourceType.USER_DEFINED) self.scatterload_function.setName("__scatterload", SourceType.USER_DEFINED) self.scatterload_table = AddressRangeImpl(scatterload_table_start, scatterload_table_end.subtract(1)) def parse_scatterload_table(self): """ Go through the scatterload table and identify the calls to unpack the init data and clear the bss section. """ self.data_init_start = None self.data_size_packed = None self.data_size_unpacked = None self.ram_clear_start = None self.ram_clear_size = None if self.scatterload_table.getLength() % 0x10: yikes("Scatterload table size not a multiple of 0x10") scatterload_nentries = self.scatterload_table.getLength() / 0x10 if scatterload_nentries < 2: yikes("Scatterload table too small to contain a decompress and clear entry") # Actually iterating might be unnecessary because the table layout is identical # in all targets I've seen so far, but what do I know. # The one assumption in here is that the unpack call comes _before_ the clear call. for entry in range(scatterload_nentries): addr = self.scatterload_table.getMinAddress().add(entry * 0x10) listing.clearCodeUnits(addr, addr.add(0x10), False) listing.createData(addr, self.table_entry_type) entry = read_uints(addr, 4) function_name = None is_rwdata_init_function = False if entry[1] == self.ram_block.getStart().getOffset(): print("Init data call found:", entry) is_rwdata_init_function = True self.data_init_start = addr.getNewAddress(entry[0]) self.data_size_unpacked = entry[2] elif self.data_init_start and entry[1] == self.ram_block.getStart().getOffset() + self.data_size_unpacked: print("Clear memory call found:", entry) function_name = "__scatterload_zeroinit" self.data_size_packed = entry[0] - self.data_init_start.getOffset() self.ram_clear_start = addr.getNewAddress(entry[1]) self.ram_clear_size = entry[2] function_addr = addr.getNewAddress(entry[3]) function = createFunction(function_addr, function_name) # Using commands for high-level operations is the "Ghidra Way" it seems: # https://github.com/NationalSecurityAgency/ghidra/issues/1126 ApplyFunctionSignatureCmd(function_addr, self.function_type, SourceType.USER_DEFINED, True, False).applyTo(currentProgram) if is_rwdata_init_function: self.rwdata_init_function = function or getFunctionAt(function_addr) def uncompress_init_data(self): """ Uncompress the init data by trying all available decompressors. """ symbols.createLabel(self.data_init_start, "rwdata_init", SourceType.USER_DEFINED).setPrimary() listing.clearCodeUnits(self.data_init_start, self.data_init_start.add(self.data_size_packed), False) listing.createData(self.data_init_start, ArrayDataType(ByteDataType.dataType, self.data_size_packed, 1)) packed_data = read_bytes(self.data_init_start, self.data_size_packed) if self.data_size_packed == self.data_size_unpacked: print("Initialization data packed size equals unpacked size, assuming no compression") self.unpacked_data = packed_data self.rwdata_init_function.setName("__scatterload_copy", SourceType.USER_DEFINED) else: print("Uncompressing initialization data") for core in unpack_cores: self.unpacked_data = unpack(packed_data, self.data_size_unpacked, core) if self.unpacked_data is not None: self.rwdata_init_function.setName(core[1], SourceType.USER_DEFINED) break else: yikes("Failed to uncompress initialization data") def create_ram_blocks(self): """ Split the ram block into parts, and initialize them with our unpacked data and zeros respectively. """ print("Creating and initializing RAM blocks") undef_start = self.ram_clear_start.add(self.ram_clear_size) ram_init = self.ram_block ram_init.setName("ram.init") memory.split(ram_init, self.ram_clear_start) ram_clear = memory.getBlock(self.ram_clear_start) ram_clear.setName("ram.clear") memory.split(ram_clear, undef_start) ram_undef = memory.getBlock(undef_start) ram_undef.setName("ram.undef") ram_clear = memory.convertToInitialized(ram_clear, 0) ram_init = memory.convertToInitialized(ram_init, 0) ram_init.putBytes(ram_init.getStart(), array("b", array("B", self.unpacked_data).tostring()))
def generateVtableStruct(vtableSymbol): vtableAddr = vtableSymbol.getAddress() nameStartsAt = 5 while True: if vtableSymbol.getName()[nameStartsAt].isdigit(): nameStartsAt += 1 else: break vtableClassName = vtableSymbol.getName()[nameStartsAt:] vtableName = "" structData = None keepgoing = True #cAddr = vtableAddr.add(8) cAddr = vtableAddr #tmp = next(codeUnits) #tmp = next(codeUnits) antiFreeze = 0 while True: #print("Checking " + cAddr.toString()) fnToCheck = CUManager.getCodeUnitContaining(cAddr) #print(fnToCheck.getMnemonicString()) if fnToCheck != None and fnToCheck.getMnemonicString() == "addr": #print("Found start of vtable at") cAddr = cAddr.add(4) break if antiFreeze >= 50: print("Something has to have gone wrong...") return cAddr = cAddr.add(4) antiFreeze += 1 if "google" in vtableClassName and not doGoogle: print("Skipped vtable" + vtableClassName) return if "CryptoPP" in vtableClassName and not doFOSS: print("Skipped vtable" + vtableClassName) return while True: monitor.checkCanceled() fs = getAddress(mem.getInt(cAddr)) valpart = fs.toString() fntoadd = functionManager.getFunctionContaining(getAddress(valpart)) if fntoadd != None: #print("YES, this is an pointer") if vtableName == "": #vtableClassName = getClassName(fntoadd.toString()) vtableName = "vtable" + vtableClassName structData = StructureDataType(vtableName, 0) #print("Making vtable for " + vtableClassName) monitor.setMessage("Observe: Making vtable for " + vtableClassName) #print(fntoadd) if fntoadd != None: dt = FunctionDefinitionDataType( fntoadd, False ) #Second parameter is "Formality", I think this strips the "this" parameter, so lets not set this True #dt.setCategoryPath(CategoryPath("/" + vtableName)) fnClass = getClassName(fntoadd.toString()) dt.setCategoryPath(CategoryPath("/vtable" + fnClass)) dtAdded = dataManager.addDataType( dt, DataTypeConflictHandler.REPLACE_HANDLER) ptr = PointerDataType(dtAdded) #ptr.setCategoryPath(CategoryPath("/" + vtableName)) ptr.setCategoryPath(CategoryPath("/vtable" + fnClass)) ptrAdded = dataManager.addDataType( ptr, DataTypeConflictHandler.REPLACE_HANDLER) structData.add(ptrAdded, ptrAdded.getLength(), fntoadd.toString(), "") else: break cAddr = cAddr.add(4) if structData != None: vtableCDataType = dataManager.addDataType( structData, DataTypeConflictHandler.REPLACE_HANDLER) vtableCDataTypePtr = PointerDataType(vtableCDataType) vtableDTtoAdd = dataManager.addDataType( vtableCDataTypePtr, DataTypeConflictHandler.REPLACE_HANDLER) print("Created " + vtableName) else: print("Skipped " + vtableName)
d = code_manager.getDataAt(vftableAddress) assert (d.isArray()) assert (d.getPrimarySymbol().getParentNamespace().getSymbol().getSymbolType() == SymbolType.CLASS) class_name = d.getPrimarySymbol().getParentNamespace().getName() vftable_classname = naming_prefix + class_name + "_vftable" l = [] typeManager.findDataTypes(vftable_classname, l) if l != []: print("Class " + vftable_classname + " already exists. Nothing is changed.") else: category = typeManager.createCategory(CategoryPath(categoryPathString)) vftable_type = StructureDataType(vftable_classname, 0) for i in range(d.getNumComponents()): comp = d.getComponent(i) assert (comp.isPointer()) func_addr = comp.getValue() func = func_manager.getFunctionAt(func_addr) assert (func != None) func_name = func.getName() func_dt = FunctionDefinitionDataType("TGN_func") func_dt.setReturnType(DWordDataType()) datatype = PointerDataType(func_dt) vftable_type.insert(i, datatype, datatype.getLength(), func_name, "TGN: Automatically created field") category.addDataType(vftable_type, DataTypeConflictHandler.REPLACE_HANDLER)
def fixLabel(data): name = getSymbolAt(data).getName() labelAddress = getSymbolAt(data).getAddress() #print labelAddress, name # ghidra refers to some functions as data, I've seen only one case if ("LAB_" not in name): currentProgram.getListing().clearCodeUnits(data, data.add(8), False) name = name.split("_")[1] ret = createFunction(labelAddress, "LAB_" + name) labelName = name.replace("LAB_", "FUN_") #print disassemble(labelAddress) if disassemble(labelAddress) == False: popup("This Label " + labelAddress + "cannot be disassembled !") return -1 #print "labelName:",labelName ret = createFunction(labelAddress, labelName) func = getFunctionAt(labelAddress) if func == None: # Calling CreateFunction twice will force function creation # Don't ask me,ask NSA ret = createFunction(labelAddress, labelName) # why it sometimes returns None ? last chance func = getFunctionAt(labelAddress) if (func == None): listing = currentProgram.getListing() blockModelService = state.getTool().getService(BlockModelService) cbm = blockModelService.getActiveSubroutineModel() cbIter = cbm.getCodeBlocksContaining(labelAddress, monitor) l = labelAddress currentProgram.getListing().clearCodeUnits(l, l.add(8), True) createFunction(labelAddress, "FUN_" + name) #x = getFunctionAt(labelAddress) #print cbIter #x = CreateFunctionCmd(labelAddress,False) #print type(x) #funcBody = x.getFunctionBody(currentProgram,labelAddress) #print funcBody #mgr = currentProgram.getFunctionManager() #mgr.createFunction("test",labelAddress,funcBody,SourceType.USER_DEFINED) #raise Exception("Unable to create a function 0x%s" %(labelAddress.toString())) #x = ApplyFunctionSignatureCmd(labelName) #print x #return func = getFunctionAt(labelAddress) assert (func != None) func.setCustomVariableStorage(False) #params = func.getParameters() df = FunctionDefinitionDataType(func, False) # TODO : remove the below line , no need to change calling convention #df.setGenericCallingConvention(GenericCallingConvention.thiscall) df.setReturnType(func.getReturnType()) #df = FunctionDefinitionDataType(func,False) """ func.replaceParameters(FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS,#CUSTOM_STORAGE, True, SourceType.USER_DEFINED, params) """ x = ApplyFunctionSignatureCmd(func.getEntryPoint(), df, SourceType.USER_DEFINED) x.applyTo(func.getProgram())
dword = DWordDataType() #parameter definitions #WARNING, a NEW definition will need to be created for each of the same instance in a signature #to give the parameter a default name change the first argument pointer32Def = ParameterDefinitionImpl("p32", pointer32, "32 bit pointer") dwordDef = ParameterDefinitionImpl(None, dword, "double word") # list of functions to change signatures functionsToChange = [ "foo", "bar" #... ] #function definitions fooDef = FunctionDefinitionDataType("foo") fooDef.setArguments([pointer32Def, dwordDef]) barDef = FunctionDefinitionDataType("bar") barDef.setArguments([pointer32Def]) functionDefs = [fooDef, barDef] func = getFirstFunction() #loop though functions looking names that match our list while func is not None: func_name = func.getName() if func_name in functionsToChange: for defi in functionDefs: if func_name == defi.getName(): cmd = ApplyFunctionSignatureCmd(func.getEntryPoint(), defi, SourceType.USER_DEFINED)