def nextimmref(self, ea, ui=True): """ Finds the next occurrance of an immediate value being a reference, like ldr r2, [r2,#(dword_809EEF4+0x1F8 - 0x809f0e4)] :param ea: ea to start searching from :param ui: if True, jump to address automatically :return: hex formatted ea of next name """ # don't count this item ea = Data.Data(ea).ea + Data.Data(ea).getSize() output = idaapi.BADADDR while ea < self.end_ea: d = Data.Data(ea) if d.isCode() and '#' in d.getOrigDisasm(): disasm = d.getOrigDisasm() # check out the xrefs from the data, see if it references to them xrefs = d.getXRefsFrom() for xref in xrefs[0]: if Data.Data(xref).getName() in disasm: output = ea break for xref in xrefs[1]: if Data.Data(xref).getName() in disasm: output = ea break if output != idaapi.BADADDR: break ea += d.getSize() if ui: idaapi.jumpto(ea) return '%07X' % output
def rngExterns(start_ea, end_ea, toStr=True): """ creates .equs for all external symbols used in the range :param start_ea: start ea of the range, inclusive :param end_ea: end ea of the range, exclusive :return: a string containing all the external symbol .equs, or just the refs if not disp """ ea = start_ea xrefs = [] # if there's a function at end_ea, include all of its refs if Function.isFunction(end_ea): f = Function.Function(end_ea) end_ea = f.func_ea + f.getSize(withPool=True) # obtain xrefs of every data item, filtering out internal ones and duplicates while ea < end_ea: d = Data.Data(ea) # append crefs ands xrefs for xref in d.getXRefsFrom()[0]: # all code refs shouldn't have a +1 in them. The thumb switch isn't involved with the symbol itself if (idc.isCode(idc.GetFlags(xref)) or idc.isCode( idc.GetFlags(xref - 1))) and xref & 1 == 1: xref = xref - 1 if ((xref < start_ea or xref >= end_ea ) # filter internal (not external; within range) and xref not in xrefs): # filter duplicate xrefs.append(xref) for xref in d.getXRefsFrom()[1]: # all code refs shouldn't have a +1 in them. The thumb switch isn't involved with the symbol itself if (idc.isCode(idc.GetFlags(xref)) or idc.isCode( idc.GetFlags(xref - 1))) and xref & 1 == 1: xref = xref - 1 if ((xref < start_ea or xref >= end_ea ) # filter internal (not external; within range) and xref not in xrefs # filter duplicate and d.isPointer(xref) ): # filter non-pointer symbols, like byte_50 xrefs.append(xref) # advance to next item ea = ea + d.getSize() xrefs.sort() if not toStr: return xrefs output = '' # output file formats to include symbols into linking process for xref in xrefs: d = Data.Data(xref) name = d.getName() xref = d.ea output += '.equ %s, 0x%07X\n' % (name, xref) return output
def unkTillName(ea): d = Data.Data(ea) while True: size = d.getSize() idc.del_items(d.ea, d.getSize()) d = Data.Data(d.ea + size) if d.getName(): break return d.ea
def tillName(ea, f): d = Data.Data(ea) while True: size = d.getSize() f(d.ea) d = Data.Data(d.ea + size) if d.getName(): break return d.ea
def arrTillRef(ea): # TODO [BUG]: sometimes swallows names? if not Data.Data(ea).isPointer(ea): return False ref_ea = next.byDataElement( ea, lambda ea: idc.Name(ea) or Data.Data(ea).hasPointer(), ui=False) delRange(ea, ref_ea) idc.make_array(ea, ref_ea - ea) return True
def actionP(): """ Profiling Action. Time Profiling and other analyses go here. """ # tp.runTimeTests() n = 10 x = lambda ea: Data.Data(ea).__str__() t, output = tp.avgTime_us(n, x, here()) print('[%03d us] %s' % (t, Data.Data(here()).getDisasm()))
def runTimeTests(n=10): """ Performs time profiling tests for optimization purposes :param n: number of times to sample the time. Result is the average of all samples """ x = lambda: Data.Data(idc.here())._lowerCode(idc.GetDisasm(idc.here())) y = lambda: Data.Data(idc.here())._lowerCodeOLD(idc.GetDisasm(idc.here())) print(x()) runTimeTest(n, 'new _lowerCode', x) print(y()) runTimeTest(n, 'old _lowerCode', y)
def unk2ArrRng(start_ea, end_ea): """ converts all completely unknowns to byte arrays """ d = Data.Data(start_ea) while d.ea < end_ea: if d.getName() and idc.isUnknown(idc.GetFlags(d.ea)): name = d.getName() if unk2Arr(d.ea): print('%s -> %s' % (name, d.getName())) d = Data.Data(d.ea) d = Data.Data(d.ea + d.getSize())
def delShiftedContent(ea): d = Data.Data(ea) content = d.getContent() if type(content) == list or not d.getXRefsFrom( )[1] or content < 0x8000000 or not d.isPointer(content): return False dContent = Data.Data(content) if content == dContent.ea or dContent.isCode(): return False if not idc.del_items(dContent.ea, dContent.getSize()): for i in range(dContent.ea, dContent.ea + dContent.getSize()): idc.del_items(i, 1) return True
def nextbin(self, ea, ui=True): """ Finds the next big blob of data. The heuristic is it has to be at least sizeLimitHeuristic in size UI jumps to start_ea automatically. :param ea: ea to search from :param ui: if True, jump to address automatically :return: tuple hex format of the bin range and the size: (%07X, %07X, 0x$X) """ sizeLimitHeuristic = 0x1000 # don't count this item ea = Data.Data(ea).ea + Data.Data(ea).getSize() # range params start_ea = idaapi.BADADDR end_ea = idaapi.BADADDR size = 0 # state machine of finding range st_start = 0 st_traverse = 1 st_end = 2 state = st_start while ea < self.end_ea: d = Data.Data(ea) if not d.isCode(): if state == st_start: start_ea = ea size = 0 state = st_traverse if state == st_traverse: size += d.getSize() if state == st_end: raise(Exception('entered invalid state')) if d.isCode(): # only end if valid size if state == st_traverse: if size >= sizeLimitHeuristic: state = st_end else: state = st_start if state == st_end: end_ea = ea break ea += d.getSize() idaapi.jumpto(start_ea) return '0x%07X, 0x%07X, 0x%X' % (start_ea, end_ea, size)
def nextOneWordArr(): d = Data.Data(ea) while (d.ea < pointerRange[1]): content = d.getContent() # case: byte array that's 4 elements. Likely a word if type(content) == list and len(content) == 4 and (d.getSize() / len(content) == 1): break d = Data.Data(d.ea + d.getSize()) if d.ea >= pointerRange[1]: print(False) else: print('%07X' % d.ea) idc.jumpto(d.ea)
def findMostUsedLabels(start_ea, end_ea, count, notModified=False, disp=True): # type: (int, int, int, bool, bool) -> list[int] """ Scans through all labels in the given range and counts the ones with the highest amount of references :param start_ea: start of the range :param end_ea: end of the range :param count: :param notModified: :param disp: :return: """ xrefs = [] if count <= 0: count = 1 for i in range(count): xrefs.append((0, 0)) ea = start_ea while ea < end_ea: if not idc.get_name(ea): ea += idc.get_item_size(ea) continue if notModified: name = Data.Data(ea).getName() if not ('_' in name and name[name.rindex('_'):] == ('_%X' % ea)): continue currXrefs = Data.Data(ea).getXRefsTo() numXrefs = len(currXrefs[0]) + len(currXrefs[1]) # add if more than least in the list and sort if numXrefs > xrefs[0][1]: xrefs[0] = (ea, numXrefs) xrefs = sorted(xrefs, key=lambda tup: tup[1]) ea += idc.get_item_size(ea) # reverse to display most common first xrefs = sorted(xrefs, key=lambda tup: tup[1], reverse=True) if disp: for ea, xrefCount in xrefs: print('%07x <%s>: %d' % (ea, Data.Data(ea).getName(), xrefCount)) output = [] for ea, xrefCount in xrefs: output.append(ea) return output
def checkExtractedCode(self): """ Checks if any gameFile that is not disassembled has any code in it All code must be disassembled, and data should be extracted If code is contained within extracted binaries, they are reported back :return: [] if no code in extracted ranged. list[gameFile] otherwise. """ # grab necessary variables from the environment and assert that they were given gameFiles = self.get('gameFiles') if not gameFiles: print('ERROR: environmental variables for gameFiles' + ' must be provided.') return markedFiles = [] keys = gameFiles.keys() keys.sort() for file in keys: if not '.s' in file: # traverse the range, make sure it has no code ea = gameFiles[file][0] while ea < gameFiles[file][1]: d = Data.Data(ea) if (d.isCode()): markedFiles.append(file) break ea += d.getSize() return markedFiles
def markRedundantInsts(start_ea, end_ea): """ Some instructions, like add r0, r0, #0 can be optimized to add r0, #0 by assemblers. This gets in the way of disassembly. This attempts to fix that by replacing all such occurrances with purely their data format, and it also adds a comment on that line specifying the original inst. To specify that a data item has to be forced to data, this puts <mkdata> in its comment. :param start_ea: start address of the marking :param end_ea: end address of the marking """ ea = start_ea while ea < end_ea: d = Data.Data(ea) if d.isCode() and '<mkdata>' not in d.getComment(): redundant = True # MOVS R3, R3 content = d.getContent() if d.getContent() in srchTools.nextTools.next._getFakeInstructions( ): print("%07X: <mkdata>" % (ea)) else: redundant = False if redundant: cmt = d.getComment() if cmt: cmt = '<mkdata> ' + cmt else: cmt = '<mkdata>' d.setComment(cmt) ea += d.getSize()
def sync_identified_units_names(source_units, addr_space): def test_matching_name(unit, ea, verbose=False): if idc.get_name(ea) != unit['name']: err_msg = '0x{:X}: found differing unit name: unit {} != ida {}'.format(ea, unit['name'], idc.get_name(ea)) if verbose: return err_msg return False return True print(os.getcwd()) log_file = open('../a.log.ign', 'w') synced_function_addrs, synced_data_addrs, synced_unk_addrs = cache_find_synced_units(source_units, addr_space, recache=True) for ea in synced_function_addrs: unit = source_unit.get_physical_unit(source_units, ea) func = Function.Function(ea) out = test_matching_name(unit, func.func_ea, verbose=True) if type(out) is str: print(out) log_file.write(out + '\n') for ea in synced_data_addrs: unit = source_unit.get_physical_unit(source_units, ea) data = Data.Data(ea) out = test_matching_name(unit, data.ea, verbose=True) if type(out) is str: print(out) log_file.write(out + '\n') log_file.close()
def testContent(self): """ ensures that the content of the data object is identical to that in the database """ for key in self.testData.keys(): td = self.testData[key] d = Data.Data(td['ea']) Test.assertEquals(d.getContent(), td['content'], "%s: Invalid Content. Expected='%s', Actual='%s'" % (key, td['content'], d.getContent())) if key == "array": print("IDA") print(self.hexArr(d.getContent())) romData = self.readROM(td['ea'], td['size']) print("NO LOADER") print(self.hexArr([4, 255, 3, 42, 0, 255, 34, 9, 3, 3, 81, 78, 1, 3, 4, 54, 131, 4, 20, 32, 3, 4, 12, 38, 4, 4, 11, 1, 4, 4, 14, 0, 4, 4, 27, 80, 4, 4, 17, 36, 0, 4, 33, 8, 4, 4, 24, 3, 4, 4, 82, 22, 1, 4, 1, 11, 1, 4, 19, 37, 1, 4, 32, 62, 1, 4, 31, 19, 1, 4, 13, 20, 1, 4, 0, 10, 1, 4, 80, 67, 1, 4, 16, 72, 1, 4, 10, 75, 1, 4, 25, 35, 1, 4, 28, 18, 2, 4, 69, 79, 2, 4, 45, 26, 2, 4, 43, 21, 2, 4, 47, 29, 2, 4, 61, 64, 2, 4, 49, 31, 2, 4, 65, 70, 2, 4, 64, 69, 2, 4, 40, 14])) print("ROM") print(self.hexArr(Data.Data._combineBytes(romData, 4))) Test.assertEquals(d.getContent(bin=True), self.readROM(td['ea'], td['size']), "%s: Invalid Bin Content. Expected='%s', Actual='%s'" % (key, self.readROM(td['ea'], td['size']), d.getContent(bin=True)))
def find_synced_units(source_units, addr_space): """ :returns: units which are synced in type, size, and starting address. This only applies to Function or Data. All else is treated as Unknown it returns a list of synced function addresses, synced data addresses, and synced unknown addresses """ synced_functions = [] synced_data = [] synced_unk = [] for i, ea in enumerate(addr_space): unit = source_unit.get_physical_unit(source_units, ea) unit_size = source_unit.compute_unit_size_from_index(addr_space, i) if unit['unit']['id'] is AsmFile.UNIT_IDS.FUNCTION: if Function.isFunction(ea): func = Function.Function(ea) if func.func_ea == ea and func.getSize(withPool=True) == unit_size: synced_functions.append(ea) else: data = Data.Data(ea) if data.ea == ea: if data.getSize() == unit_size: if unit['unit']['id'] == AsmFile.UNIT_IDS.DATA: synced_data.append(ea) else: synced_unk.append(ea) else: # get size until another label is defined till_label_size = _next.name(ea, ui=False, hexOut=False) - ea if till_label_size == unit_size: if unit['unit']['id'] == AsmFile.UNIT_IDS.DATA: synced_data.append(ea) else: synced_unk.append(ea) return synced_functions, synced_data, synced_unk
def rng(start_ea, end_ea, debug=False): # type: (int, int) -> str """ disassembles all data elements within a range if a function is detected within the range, the function itself is disassembled as a whole item. (meaning it could surpass end_ea, but it would be the last item) :param start_ea: the start ea of the range :param end_ea: the end ea, not included :return: the disassembly of the range, in optimal format """ ea = start_ea disasm = '' # disassemble the range ea = start_ea while ea < end_ea: if Function.isFunction(ea): f = Function.Function(ea) if debug: print("%07X: disass function %s @ %07X" % (ea, f.getName(), f.func_ea)) disasm += f.getFormattedDisasm(start_ea, end_ea) + "\n\n" ea = ea + f.getSize(withPool=True) else: d = Data.Data(ea) if debug: print("%07X: disass data %s @ %07X" % (ea, d.getName(), d.ea)) disasm += d.getFormattedDisasm(start_ea, end_ea) + "\n" ea = ea + d.getSize() # add comment for debugging purposes # disasm += "/*For debugging purposes, connect comment at any range!*/\n" return disasm
def read(self, accesses_path): # type: (str) -> None """ Reads in the memory accesses defined by the MemAccessScanner protocol :param accesses_path: path to a file containing the accesses :return: """ f = open(accesses_path, "r") s = f.read() items = s.split(' ') self.accesses = [] for i in range(len(items)): if '::' in items[i]: access_ea = int(items[i][items[i].index('::') + 2:], 16) access = items[i + 1][:-1] self.accesses.append((access_ea, access)) self.accesses.sort(key=lambda tup: tup[0]) for access_ea, access in self.accesses: if Function.isFunction(access_ea): func_ea = Function.Function(access_ea).func_ea if func_ea not in self.funcs: self.funcs.append(func_ea) else: data_ea = Data.Data(access_ea).ea if data_ea not in self.data: self.data.append(data_ea) self.funcs.sort() self.data.sort() f.close()
def registerUncompFile(ea, force=True): # type: (int) -> bool d = Data.Data(ea) compPtr = d.getContent() if not idc.is_dword(d.getFlags()) or type(compPtr) == list: if not force: return False print('[%07X] -> dword' % (ea)) forceItemOp(ea, idc.create_dword, ea) d = Data.Data(ea) compPtr = d.getContent() # compressed pointers have the 31th bit set if not compPtr & (1 << 31): return False compPtr = compPtr - (1 << 31) # make its content an array, and set a name for it, and a size compData = Data.Data(compPtr) if compData.ea != compPtr: idc.del_items(compData.ea) compData = Data.Data(compPtr) compSize = getLZ77CompressedSize(compPtr) # size must have been identified if compSize == -1: return False if compSize % 4 != 0: compSize += 4 - (compSize % 4) # must be word aligned if compData.getSize() != compSize: if not idc.del_items(compPtr, compSize): for i in range(compPtr, compPtr + compSize): idc.del_items(i, 1) idc.make_array(compPtr, compSize) if not compData.getName(): compData.setName('comp_%07X' % compData.ea) idc.op_man(ea, 0, '%s + 1<<31' % compData.getName()) # now register the compressed data as its own file filename = 'data/compressed/%s.lz77' % compData.getName() print('[%07X] addFile %s' % (ea, filename)) dis = Terminal.DisTerminal() dis.addFile(filename, compPtr, compPtr + compSize) return True
def testBasic(self): # type: () -> None """ Tests that InvalidDataException is raised if instantiated with invalid EA. And tests that valid data give valid behavior: ea, name, size, and comments :testParams: encapsulated object showing manually computed function parameters """ for key in self.testData.keys(): td = self.testData[key] d = Data.Data(td['ea']) # valid EA Test.assertEquals(d.ea, td['ea'], "%s: Data EA mistmatch: Expected=0x%08X, Actual=0x%08X" % (key, td['ea'], d.ea)) # getName Test.assertEquals(d.getName(), td['name'], "%s: Data name mismatch: Expected='%s', Actual='%s'" % (key, td['name'], d.getName())) # setName d.setName("t__" + td['name']) Test.assertEquals(d.getName(), "t__" + td['name'], "%s: setName() not working" % key) d.setName(td['name']) Test.assertEquals(d.getName(), td['name'], "%s: could not set name back to normal" % key) # getSize Test.assertEquals(d.getSize(), td['size'], "%s: invalid size. Expected=%d, Actual=%d" % (key, td['size'], d.getSize())) # getComment Test.assertEquals(d.getComment(), td['cmt'], "%s: Comment mismatch: Expected='%s', Actual='%s'" % (key, td['cmt'], d.getComment())) # setComment d.setComment("t__" + td['cmt']) Test.assertEquals(d.getComment(), "t__" + td['cmt'], "%s: setComment() not working" % key) d.setComment(td['cmt']) Test.assertEquals(d.getComment(), td['cmt'], "%s: could not set comment back to normal" % key) # xrefs Test.assertEquals(d.getXRefsTo(), td['xrefsTo'], "%s: Invalid XrefsTo. Expected=%s, Actual=%s" % (key, self.xrefs2str(td['xrefsTo']), self.xrefs2str(d.getXRefsTo()))) Test.assertEquals(d.getXRefsFrom(), td['xrefsFrom'], "%s: Invalid XrefsFrom. Expected=%s, Actual=%s" % (key, self.xrefs2str(td['xrefsFrom']), self.xrefs2str(d.getXRefsFrom()))) # test that when addressing in the middle of an array, the EA returned is its beginning td = self.testData['array'] d = Data.Data(td['ea'] + td['size']/2) Test.assertEquals(td['ea'], d.ea, "array: EA not fixed to beginning of array. Expected=0x%08X, Actual=0x%08X" % (td['ea'], d.ea))
def rngInc(start_ea, end_ea): """ Reports back the exposed (or public) symbols of the range The symbols are .global forwarded, and represent the symbols defined within the range :param start_ea: linear address of the start of the range :param end_ea: linear address of the end of the range, exclusive :return: a series of .equ's representing the public (and private) interface of the range """ ea = start_ea pubrefs = [] # fwdrefs = [] while ea < end_ea: d = Data.Data(ea) if d.getName(): # check xrefs to the item, if any is outside the range, it's a public reference isPublic = False xrefsTo = d.getXRefsTo() for cref in xrefsTo[0]: if cref < start_ea or cref >= end_ea: isPublic = True for dref in xrefsTo[1]: if dref < start_ea or dref >= end_ea: isPublic = True if isPublic: pubrefs.append((d.getName(), d.ea)) # For debugging purposes # else: # fwdrefs.append((d.getName(), d.ea)) ea = ea + d.getSize() # string build includes inc = '/* Public Symbols */\n' for name, ea in pubrefs: d = Data.Data(ea) if d.isFunctionStart(): cmt = idc.get_func_cmt(ea, repeatable=1) if cmt: cmt = ' // ' + cmt.replace('\n', '\n// ') else: cmt = '' inc += '.global %s%s\n' % (name, cmt) # For debugging purposes, defining forward references could be useful in include files # inc += "\n// Forward Reference\n" # for name, ea in fwdrefs: # inc += ".equ %s, 0x%07X\n" % (name, ea) inc += "\n" return inc
def getFormattedDisasm(self, start_ea=False, end_ea=False): # type: () -> str """ Gets the disassembly of the function by creating data elements of all its items, including its pool. :param start_ea: file range start, enables formatting to use a global/local macro :param end_ea: file range end, enables formatting to use a global/local macro :return: """ ea = self.func_ea disasm = '' # spefiy function comment, if available # put // for function comment in each line comment = '' if self.getComment(repeatable=True): comment += '// ' + self.getComment(repeatable=True).replace( '\n', '\n// ') + '\n' if self.getComment(): comment += '// ' + self.getComment().replace('\n', '\n// ') + '\n' disasm += comment # specify start of function # file range supplied, inform if thumb or arm function, local/global thumb via macros isThumb = self.isThumb() if end_ea: if isThumb: if self.isGlobal(start_ea, end_ea): disasm += '\tthumb_func_start %s\n' % (self.getName()) else: disasm += '\tthumb_local_start\n' else: disasm += "\tarm_func_start %s\n" % (self.getName()) # no macros approach, give sufficient type to symbols else: disasm = '.func\n' # specify whether this is an arm or thumb function if isThumb: disasm += ".thumb_func\n" else: disasm += ".arm\n" # disassemble all items within the function while ea < self.func_ea + self.getSize(withPool=True): d = Data.Data(ea) disasm += d.getFormattedDisasm(start_ea, end_ea) + "\n" # advance to next item ea = ea + d.getSize() if end_ea: if isThumb: disasm += "\tthumb_func_end %s" % self.getName() else: disasm += "\tarm_func_end %s" % self.getName() else: disasm += ".endfunc // %s" % self.getName() return disasm
def deadfunc(ea, end_ea=None, ui=True, hexOut=True): """ This finds the next occurrance of a dead function not recognized as a function (ie, red code or data) This can only find functions ranges it can guarantee, ie, only PUSH {..., LR} POP {..., PC} patterns. :param ea: ea to start searching from :param end_ea: the last address of the search range :param ui: if True, jump to address automatically :param hexOut: output hex formatted ea range instead :return: range of ea of next dead function """ # don't count this item ea = Data.Data(ea).ea + Data.Data(ea).getSize() foundPush = False push_ea = idaapi.BADADDR pop_ea = idaapi.BADADDR push_regs = None if not end_ea: end_ea = end_ea while ea < end_ea: # the current item must not belong to a function, or have any data xrefs if not Function.isFunction(ea) and not Data.Data(ea).getXRefsTo()[1]: try: inst = InstDecoder.Inst(ea).fields except ValueError: ea += 2 continue # if PUSH {..., LR} if inst and inst['magic'] == InstDecoder.INST_PUSHPOP and not inst[ 'pop'] and inst['lr']: foundPush = True push_ea = ea push_regs = inst['Rlist'] # detected a POP {..., PC} after the PUSH {..., LR}, and the registers match if (foundPush and inst and inst['magic'] == InstDecoder.INST_PUSHPOP and inst['pop'] and inst['lr'] and inst['Rlist'] == push_regs): pop_ea = ea break else: foundPush = False ea += 2 if ui: idc.jumpto(push_ea) if hexOut: return '(%07X, %07X)' % (push_ea, pop_ea) return (push_ea, pop_ea)
def getStackVarDisasm(self): """ if the function uses stack variables with SP, their symbols should be defined :return: """ disasm = '' id = idc.GetFrame(self.func_ea) firstMember = idc.GetFirstMember(id) if hasStackVars(self.func_ea): # first, obtain the base by finding an instruction that uses one of the stack variables stackVars = getStackVars(self.func_ea) ea = self.func_ea base = -1 # TODO: maybe use get_min_spd_ea(func_ea) to get the base pointer? this stands for stack pointer delta! # search function instructions to find base (TODO: hacky, but i dunno how else to find base yet) while ea < self.func_ea + self.getSize(): d = Data.Data(ea) origDisasm = d.getOrigDisasm() # case where the stack frame is referenced for var, offset in stackVars: if var in origDisasm and '#' in origDisasm: # cases like LDR SP, [base+var_xx] if '[' in origDisasm: # grab the base if '+' in origDisasm: base = int( origDisasm[origDisasm.index('#') + 1:origDisasm.index('+')], 16) else: base = 0 # obtained base! no need to continue looping break # some cases like ADD SP, base+var_xx don't have '[' elif '+' in origDisasm: base = int( origDisasm[origDisasm.index('#') + 1:origDisasm.index('+')], 16) # obtained base! no need to continue looping break if base != -1: break ea += d.getSize() # if base couldn't be found still, it's likely no SP access is done with variables if base == -1: base = 0 # build up disasm based on stack vars using base-relative offsets for name, off in stackVars: relOff = base - off if relOff > 0: disasm += ".equ %s, -0x%X\n" % (name, abs(relOff)) else: disasm += ".equ %s, 0x%X\n" % (name, abs(relOff)) return disasm
def nextred(self, ea, ui=True): """ Looks for code items outside function items. The first detected is returned :param ea: ea to start searching from :param ui: if True, jump to address automatically :return: hex formatted ea of next name """ # don't count this item ea = Data.Data(ea).ea + Data.Data(ea).getSize() output = idaapi.BADADDR while ea < self.end_ea: d = Data.Data(ea) if d.isCode() and not Function.isFunction(d.ea): output = ea break ea += d.getSize() if ui: idaapi.jumpto(ea) return '%07X' % output
def nextknown(self, ea, ui=True): """ Finds the next ea with which a name exists :param ea: ea to start searching from :param ui: if True, jump to address automatically :return: hex formatted ea of next name """ # don't count this item ea = Data.Data(ea).ea + Data.Data(ea).getSize() output = idaapi.BADADDR while ea < self.end_ea: d = Data.Data(ea) if not idc.isUnknown(d._getFlags()): output = ea break ea += d.getSize() if ui: idaapi.jumpto(ea) return '%07X' % output
def nextarm(self, ea, ui=True): # type: (int) -> str """ Finds the next ARM item, which has a Segment register value 'T' of 0 :param ea: address to start searching from :param ui: if True, jump to address automatically :return: the address (in str) of the next ARM instruction """ # don't count this item ea += Data.Data(ea).getSize() output = idaapi.BADADDR while ea < self.end_ea: d = Data.Data(ea) # detect next code32 if idc.GetReg(ea, 'T') == 0: output = ea break ea += d.getSize() return '%07X' % output
def removeRedCode(start_ea, end_ea): """ unconditionally removes all red code within a specified region :param start_ea: start of the region :param end_ea: end of the region :return: """ srchNext = next redStart_ea = redEnd_ea = srchNext.red(start_ea, end_ea, ui=False) while redEnd_ea < end_ea: d = Data.Data(redEnd_ea) while d.isCode() and not Function.isFunction(d.ea): redEnd_ea += 2 d = Data.Data(redEnd_ea) # change to bytes print("%07X: del red code (%07X, %07X)" % (redStart_ea, redStart_ea, redEnd_ea)) idc.del_items(redStart_ea, 0, redEnd_ea - redStart_ea) redStart_ea = redEnd_ea = srchNext.red(redEnd_ea, end_ea, ui=False)
def nextascii(self, ea, ui=True): # type: (int) -> str """ returns the next data item containing ascii characters (seems valid for utf too) :param ea: the address to start searching from :param ui: if True, jump to address automatically :return: hex formatted str of the address of the next ascii item """ # don't count this item ea = Data.Data(ea).ea + Data.Data(ea).getSize() output = idaapi.BADADDR while ea < self.end_ea: d = Data.Data(ea) # ARM, unless it's a branch if idc.isASCII(d._getFlags()): output = ea break ea += d.getSize() if ui: idaapi.jumpto(output) return '%07X' % output