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 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 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 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 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 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)