def downloadFuncs(): funcs = list() func = FunctionInfo() i = 0 j = 0 k = 0 line = "" startEA, endEA = 0, 0 for segEA in idautils.Segments(): print "[%d] Segment EA : %016x" % (i, segEA) i += 1 j = 0 for fnEA in idautils.Functions(segEA, SegEnd(segEA)): print "[%d] Function EA : %016x" % (j, fnEA) j += 1 fnName = idc.GetFunctionName(fnEA) func = FunctionInfo() func.m_startAddr = fnEA func.m_funcName = fnName k = 0 for (startEA, endEA) in idautils.Chunks(fnEA): print "[%d] Chunks" % (k) k += 1 for head in idautils.Heads(startEA, endEA): # s = "%s : %x : %s" % (fnName, head, GetDisasm(head)) # print s line = "%016x|%s" % (head, GetDisasm(head)) func.m_ins.append(line) func.m_endAddr = endEA funcs.append(func) return funcs
def jumps_to(f, t): f_ea = idc.LocByName(f) t_ea = idc.LocByName(t) for start, end in idautils.Chunks(f_ea): ea = start while (ea < end): i = idautils.DecodeInstruction(ea) #Functions have data chunks in them, these are offsets and dwords. skipping 4 ahead seems like a safe choice. if type(i) == type(None): ea += 4 continue #detect if instruction is a jump to t_ea m = idc.GetMnem(ea) if idc.GetMnem(ea) == "LDR" and idc.GetOpnd( ea, 0) == "PC" and t in idc.GetOpnd(ea, 1): return True elif idc.GetMnem(ea) == "BX" and idc.GetOpnd(ea, 0) == t: return True try: ea += i.size except: print "0x%08x" % ea print "DecodeInstruction failed!" print i.size return False
def parse_strings(): strings_added = 0 retry = [] text_seg = common.get_text_seg() if text_seg is None: common._debug('Failed to get text segment') return strings_added # This may be inherently flawed as it will only search for defined functions # and as of IDA Pro 6.95 it fails to autoanalyze many GO functions, currently # this works well since we redefine/find (almost) all the functions prior to # this being used. Could be worth a strategy rethink later one or on diff archs for addr in idautils.Functions(text_seg.startEA, text_seg.endEA): name = idc.GetFunctionName(addr) end_addr = idautils.Chunks(addr).next()[1] if (end_addr < addr): common._error('Unable to find good end for the function %s' % name) pass common._debug('Found function %s starting/ending @ 0x%x 0x%x' % (name, addr, end_addr)) while addr <= end_addr: if parse_str_ptr(addr): strings_added += 1 addr = idc.FindCode(addr, idaapi.SEARCH_DOWN) elif is_string_patt(addr): if 'rodata' not in idc.get_segm_name( addr) and 'text' not in idc.get_segm_name(addr): common._debug('Should a string be in the %s section?' % idc.get_segm_name(addr)) string_addr = idc.GetOperandValue(addr, 1) addr_3 = idc.FindCode(idc.FindCode(addr, idaapi.SEARCH_DOWN), idaapi.SEARCH_DOWN) string_len = idc.GetOperandValue(addr_3, 1) if string_len > 1: if create_string(string_addr, string_len): if create_offset(addr): strings_added += 1 else: # There appears to be something odd that goes on with IDA making some strings, always works # the second time, so lets just force a retry... retry.append((addr, string_addr, string_len)) # Skip the extra mov lines since we know it won't be a load on any of them addr = idc.FindCode(addr_3, idaapi.SEARCH_DOWN) else: addr = idc.FindCode(addr, idaapi.SEARCH_DOWN) for instr_addr, string_addr, string_len in retry: if create_string(string_addr, string_len): if create_offset(instr_addr): strings_added += 1 else: common._error( 'Unable to make a string @ 0x%x with length of %d for usage in function @ 0x%x' % (string_addr, string_len, instr_addr)) return strings_added
def GetDataXrefString(ea): name = idc.GetFunctionName(ea) ea = idc.LocByName(name) f_start = ea f_end = idc.GetFunctionAttr(ea, idc.FUNCATTR_END) ret = [] for chunk in idautils.Chunks(ea): astart = chunk[0] aend = chunk[1] for head in idautils.Heads(astart, aend): # If the element is an instruction if idc.isCode(idc.GetFlags(head)): refs = list(idautils.DataRefsFrom(head)) for ref in refs: s = idc.GetString(ref, -1, idc.ASCSTR_C) if not s or len(s) <= 4: s = idc.GetString(ref, -1, idc.ASCSTR_UNICODE) if s: if len(s) > 4: ret.append(repr(s)) if len(ret) > 0: return "\n\n" + "\n".join(ret) else: return ""
def parse_func(pfn): try: hf = idaapi.hexrays_failure_t() cfunc = idaapi.decompile(pfn.start_ea, hf) mbr = idaapi.mba_ranges_t(pfn) mba = idaapi.gen_microcode( mbr, hf, None, idaapi.DECOMP_NO_WAIT | idaapi.DECOMP_NO_CACHE, idaapi.MMAT_GLBOPT3 ) except Exception: return if mba is None: return G = Graph() ctree_state, ctree_expr, ctree_int, ctree_str, micro_int = [], [], [], [], [] # node level for i in range(mba.qty): mb = mba.get_mblock(i) minsn = mb.head blk = [] while minsn: ins = parse_minsn(minsn, micro_int) blk.append(ins) minsn = minsn.next vp = idaapi.qstring_printer_t(None, True) mb._print(vp) G.add_node(mb.serial, feat=blk, raw_data=vp.s) for succ in mb.succset: G.add_edge(mb.serial, succ) G.remove_featempty_nodes() if not G.have_nodes(): return # add a fake edge if there is no edge if not G.have_edges(): G.add_edge(G.graph['nodes'][0]['id'], G.graph['nodes'][0]['id']) # graph level ctree_fea = CtreeFeature(ctree_state, ctree_expr, ctree_int, ctree_str) ctree_fea.apply_to(cfunc.body, None) G.graph['graph']['c_state'], G.graph['graph']['c_expr'], G.graph['graph']['c_int'], G.graph['graph'][ 'c_str'], G.graph['graph']['m_int'] = ctree_state, ctree_expr, ctree_int, ctree_str, micro_int G.graph['graph']['arg_num'] = len(cfunc.argidx) func_bytes = b'' for start, end in idautils.Chunks(pfn.start_ea): fb = idaapi.get_bytes(start, end-start) func_bytes += fb G.graph['graph']['hash'] = hashlib.md5(func_bytes).hexdigest() return G.graph
def get_branch_table_instrs(): """ Return all jsr or jmp instructions with pc in the operand :return: List of PC-relative jmp and jsr dispatch instructions """ instrs = [] # Iterate instructions in functions for funcea in idautils.Functions(): for (startea, endea) in idautils.Chunks(funcea): for head in idautils.Heads(startea, endea): instr = idc.GetDisasm(head).split() if instr[0] == 'jsr' or instr[0] == 'jmp': if 'pc' in instr[1]: instrs.append(instr) # Iterate instructions not in a function addr = idaapi.find_not_func(0, 1) while addr != idc.BADADDR: instr = idc.GetDisasm(addr).split() if instr[0] == 'jsr' or instr[0] == 'jmp': if 'pc' in instr[1]: instrs.append(instr) addr = idaapi.find_not_func(addr, 1) return instrs
def getListOfFunctions(): res = [] for segea in idautils.Segments(): for funcea in idautils.Functions(segea, idc.SegEnd(segea)): functionName = idc.GetFunctionName(funcea) for (startea, endea) in idautils.Chunks(funcea): res.append(Function(functionName, startea, endea)) return res
def clear_function_instructions(self, address): """Clear paint from a function instructions.""" for start_ea, end_ea in idautils.Chunks(address): for ea in idautils.Heads(start_ea, end_ea): color = self._get_paint_instruction(ea) # Clear it only if it hasn't been colorized by the user color = color if color != self._bg_color else self.DEFCOLOR self._set_paint_instruction(ea, color)
def init_func_from_ida(self): # get current address # and get the function address from current address current_addr = idc.here() self.func_name = idc.GetFunctionName(current_addr) for chunk in idautils.Chunks(idc.LocByName(self.func_name)): self.func_addr = chunk return self.func_name, self.func_addr
def paint_function_instructions(self, address): """Paint a function's instructions with the user color.""" for start_ea, end_ea in idautils.Chunks(address): for ea in idautils.Heads(start_ea, end_ea): color = self._get_paint_instruction(ea) # Only color instructions that aren't colored yet to keep # an existing user-defined color if color == self.DEFCOLOR: self._set_paint_instruction(ea, self._bg_color)
def calc_file_version_hash(): version_obj = {} version_obj['functions'] = {offset: list(idautils.Chunks(offset)) for offset in idautils.Functions()} version_str = repr(version_obj) version_hash = hashlib.md5(version_str).hexdigest() log('match_action').info("file version string: %s", version_str) log('match_action').info("file version hash: %s", version_hash) return version_hash
def clear_function_instructions(self, address): """ Clear function instructions :param address: an address within the function """ for start_ea, end_ea in idautils.Chunks(address): for ea in idautils.Heads(start_ea, end_ea): color = self.get_paint_instruction(ea) # clear only if it's not colorized by user color = color if color != self.bg_color else self.DEFCOLOR self.set_paint_instruction(ea, color)
def find_switch(func_ea): # get all chunks that belong to a function, because apparently they're not contiguous or some shit for (start_ea, end_ea) in idautils.Chunks(func_ea): for head in idautils.Heads(start_ea, end_ea): switch = idaapi.get_switch_info_ex(head) if switch != None: log('found switch @ %x, cases: %d' % (head, switch.get_jtable_size())) return (head, switch) return (None, None)
def paint_function_instructions(self, address): """ Paint function instructions with the user-defined background color :param address: an address within the function """ for start_ea, end_ea in idautils.Chunks(address): for ea in idautils.Heads(start_ea, end_ea): color = self.get_paint_instruction(ea) # color only instructions that aren't colored yet to keep # user-defined color if color == self.DEFCOLOR: self.set_paint_instruction(ea, self.bg_color)
def imp_cb(ea, name, ord): for xref in XrefsTo(ea, True): for func_ea in Functions(): for (startea, endea) in idautils.Chunks(func_ea): if startea < xref.frm < endea: if name: export_tag = { 'offset': (func_ea - idaapi.get_imagebase()), 'tag': name, 'feeder': 'TagApi' } tags.append(export_tag) return True
def _convert_address_to_function(func): """Convert an address that IDA has classified incorrectly into a proper function.""" # If everything goes wrong, we'll try to restore this function. orig = idc.first_func_chunk(func) # If the address is not code, let's undefine whatever it is. if not ida_bytes.is_code(ida_bytes.get_full_flags(func)): if not is_mapped(func): # Well, that's awkward. return False item = ida_bytes.get_item_head(func) itemend = ida_bytes.get_item_end(func) if item != idc.BADADDR: _log(1, 'Undefining item {:#x} - {:#x}', item, itemend) ida_bytes.del_items(item, ida_bytes.DELIT_EXPAND) idc.create_insn(func) # Give IDA a chance to analyze the new code or else we won't be able to create a # function. #ida_auto.auto_wait() autoanalyze() idc.plan_and_wait(item, itemend) else: # Just try removing the chunk from its current function. IDA can add it to another function # automatically, so make sure it's removed from all functions by doing it in loop until it # fails. for i in range(1024): if not idc.remove_fchunk(func, func): break # Now try making a function. if ida_funcs.add_func(func) != 0: return True # This is a stubborn chunk. Try recording the list of chunks, deleting the original function, # creating the new function, then re-creating the original function. if orig != idc.BADADDR: chunks = list(idautils.Chunks(orig)) if ida_funcs.del_func(orig) != 0: # Ok, now let's create the new function, and recreate the original. if ida_funcs.add_func(func) != 0: if ida_funcs.add_func(orig) != 0: # Ok, so we created the functions! Now, if any of the original chunks are not # contained in a function, we'll abort and undo. if all(idaapi.get_func(start) for start, end in chunks): return True # Try to undo the damage. for start, _ in chunks: ida_funcs.del_func(start) # Everything we've tried so far has failed. If there was originally a function, try to restore # it. if orig != idc.BADADDR: _log(0, 'Trying to restore original function {:#x}', orig) ida_funcs.add_func(orig) return False
def is_function_switch_statement(f): """ check a function for switch statement indicators adapted from: https://reverseengineering.stackexchange.com/questions/17548/calc-switch-cases-in-idapython-cant-iterate-over-results?rq=1 arg: f (IDA func_t) """ for (start, end) in idautils.Chunks(f.start_ea): for head in idautils.Heads(start, end): if idaapi.get_switch_info(head): return True return False
def calc_file_version_hash(): version_obj = [] version_obj.append( ('functions', [(offset, list(idautils.Chunks(offset))) for offset in idautils.Functions()])) # TODO: This is a little hackish way of getting the version of all vectors # of an instance. cannot make version a classmethod because vector sets are # only built by __init__ methods version_obj.append(('vectors', FunctionInstance(None, None).version())) version_str = repr(version_obj) version_hash = hashlib.md5(version_str).hexdigest() log('match_action').info("file version string: %s", version_str) log('match_action').info("file version hash: %s", version_hash) return version_hash
def build_a2fmap(): a2fmap = {} print "Generating address to functon map..." min_ea = idaapi.cvar.inf.minEA max_ea = idaapi.cvar.inf.maxEA for fea in idautils.Functions(min_ea, max_ea): ida_auto.show_addr(fea) for (startea, endea) in idautils.Chunks(fea): for addr in range(startea, endea): if addr in a2fmap: a2fmap[addr].append(fea) else: a2fmap[addr] = [fea] print "Address to function map generation complete!" return a2fmap
def ida_iter_functions(): for addr in idautils.Functions(): func = idaapi.get_func(addr) blocks = [] for block in idaapi.FlowChart(func): blocks.append( BasicBlockTuple( startEA=block.startEA, endEA=block.endEA, FAKE_FIELD_preds=[b.startEA for b in block.preds()], FAKE_FIELD_succs=[b.startEA for b in block.succs()])) yield FunctionTuple(startEA=func.startEA, endEA=func.endEA, FAKE_FIELD_chunks=list(idautils.Chunks(addr)), FAKE_FIELD_blocks=blocks)
def _convert_address_to_function(func): """Convert an address that IDA has classified incorrectly into a proper function.""" # If everything goes wrong, we'll try to restore this function. orig = idc.FirstFuncFchunk(func) # If the address is not code, let's undefine whatever it is. if not idc.isCode(idc.GetFlags(func)): if not is_mapped(func): # Well, that's awkward. return False item = idc.ItemHead(func) itemend = idc.ItemEnd(func) if item != idc.BADADDR: _log(1, 'Undefining item {:#x} - {:#x}', item, itemend) idc.MakeUnkn(item, idc.DOUNK_EXPAND) idc.MakeCode(func) # Give IDA a chance to analyze the new code or else we won't be able to create a # function. idc.Wait() idc.AnalyseArea(item, itemend) else: # Just try removing the chunk from its current function. idc.RemoveFchunk(func, func) # Now try making a function. if idc.MakeFunction(func) != 0: return True # This is a stubborn chunk. Try recording the list of chunks, deleting the original function, # creating the new function, then re-creating the original function. if orig != idc.BADADDR: chunks = list(idautils.Chunks(orig)) if idc.DelFunction(orig) != 0: # Ok, now let's create the new function, and recreate the original. if idc.MakeFunction(func) != 0: if idc.MakeFunction(orig) != 0: # Ok, so we created the functions! Now, if any of the original chunks are not # contained in a function, we'll abort and undo. if all(idaapi.get_func(start) for start, end in chunks): return True # Try to undo the damage. for start, _ in chunks: idc.DelFunction(start) # Everything we've tried so far has failed. If there was originally a function, try to restore # it. if orig != idc.BADADDR: _log(0, 'Trying to restore original function {:#x}', orig) idc.MakeFunction(orig) return False
def __patch_indirect_call_instructions(): for seg in idautils.Segments(): for func in idautils.Functions(seg): function_name = idc.get_func_name(func) print('[+] Checking function "{}"'.format(function_name)) for (startea, endea) in idautils.Chunks(func): for head in idautils.Heads(startea, endea): m = idc.print_insn_mnem(head) if m == 'call': op = idc.get_operand_type(head, 0) if op == idc.o_displ: print('{}: 0x{:08x}: {}'.format( function_name, head, idc.generate_disasm_line(head, 0))) ida_bytes.patch_word(head, 0x15ff) print('{}: 0x{:08x}: {}'.format( function_name, head, idc.generate_disasm_line(head, 0)))
def paint_function_instructions(self, address): """ Paint function instructions with the user-defined background color :param address: an address within the function """ for start_ea, end_ea in idautils.Chunks(address): for ea in idautils.Heads(start_ea, end_ea): color = self.get_paint_instruction(ea) # color only instructions that aren't colored yet to keep # user-defined color if color == self.DEFCOLOR: self.set_paint_instruction(ea, self.bg_color) else: # # TODO: IDA doesn't provide a way to hook painting, with # this we can propagate user-defined colors between users. # pass
def calc_file_version_hash(): version_obj = [] version_obj.append( ('functions', [(offset, list(idautils.Chunks(offset))) for offset in idautils.Functions()])) # TODO: This is a little hackish way of getting the version of all vectors # of an instance. cannot make version a classmethod because vector sets are # only built by __init__ methods func_vector_versions = FunctionInstance(None, None).version() version_obj.append(('function_vector_versions', func_vector_versions)) # TODO: Add function annotations as part of the version, because they're # also changing. # TODO: Add universal instance related versions version_str = repr(version_obj) version_hash = hashlib.md5(version_str).hexdigest() log('upload_action').info("file version string: %s", version_str) log('upload_action').info("file version hash: %s", version_hash) return version_hash
def generate_data_from_offset( offset): # credits to ferhat, or whoever wrote this found_values = {} chunks = idautils.Chunks(offset) for begin, end in chunks: name = "" offset = -1 ea = begin while ea != end: mnem = idc.GetMnem(ea) opnd = idc.GetOpnd(ea, 0) stack_value = idc.GetOperandValue(ea, 0) if mnem == "jz" or mnem == "jl": name = "" offset = -1 if offset == -1 and (mnem == "add" or mnem == "lea" or mnem == "sub"): offset = idc.GetOperandValue(ea, 1) if mnem == "sub": offset = 0x100000000 - offset if mnem == "push" and "offset" in opnd and "pNodeName" not in opnd: name = idc.GetString(stack_value, -1, idc.ASCSTR_C) if is_user_name(stack_value): # this should crash? wtf name = idc.NameEx(idc.BADADDR, stack_value) if mnem == "call" or mnem == "jmp": #print("{} at {}").format(name, dec_to_hex(offset)) if name: found_values[offset] = name name = "" offset = -1 ea = idc.NextNotTail(ea) return found_values
def get_upload_func_info(ea): """ get function upload info by IDA Pro Args: ea(ea_t): function address Returns: func_info(dict): function info """ func_info = {} try: hf = idaapi.hexrays_failure_t() if idaapi.IDA_SDK_VERSION >= 730: cfunc = idaapi.decompile(ea, hf, idaapi.DECOMP_NO_WAIT) else: cfunc = idaapi.decompile(ea, hf) func_info['feature'] = str(cfunc) func_info['pseudo_code'] = str(cfunc) except Exception as e: print(str(e)) return None func_info['binary_file'] = idaapi.get_root_filename() binary_sha256 = idaapi.retrieve_input_file_sha256() binary_sha256 = binary_sha256.hex() if isinstance(binary_sha256, bytes) else binary_sha256 func_info['binary_sha256'] = binary_sha256 func_info['binary_offset'] = idaapi.get_fileregion_offset(ea) func_info['platform'] = get_platform_info() func_info['name'] = idaapi.get_func_name(ea) func_bytes = b'' for start, end in idautils.Chunks(idaapi.get_func(ea).start_ea): fb = idaapi.get_bytes(start, end - start) func_bytes += fb func_bytes = func_bytes.hex() if isinstance(func_bytes, bytes) else func_bytes func_info['func_bytes'] = func_bytes return func_info
def GetCodeRefsFrom(ea): name = idc.GetFunctionName(ea) ea = idc.LocByName(name) f_start = ea f_end = idc.GetFunctionAttr(ea, idc.FUNCATTR_END) ret = [] for chunk in idautils.Chunks(ea): astart = chunk[0] aend = chunk[1] for head in idautils.Heads(astart, aend): # If the element is an instruction if idc.isCode(idc.GetFlags(head)): refs = idautils.CodeRefsFrom(head, 0) for ref in refs: loc = idc.LocByName(idc.GetFunctionName(ref)) if loc not in ret and loc != f_start: ret.append(ref) return ret
def countChunks(self): return len(list(idautils.Chunks(self.addr)))
def def_functions(s_start): num_added_functions = 0 s_addr = s_start s_end = idc.GetSegmentAttr(s_start, SEGATTR_END) #idc.SegEnd(segm) print "0x%08x 0x%08x" % (s_start, s_end) while (s_addr < s_end): print "Testing address 0x%08x" % s_addr #optimization assumes that function chunks are consecutive (no "function-in-function" monkey business) if (idaapi.get_func(s_addr)): next_func = idc.NextFunction(s_addr) ea = s_addr for c in idautils.Chunks(s_addr): #only use chunks in lookahead that do not jump over the next function and that are not smaller than where we are atm. if (c[1] > ea) and (c[1] <= next_func): ea = c[1] if ea == s_addr: s_addr += 2 else: s_addr = ea #s_addr += 4 continue else: #This is not a good optimization, there WILL be data refs to function start addresses sometimes. ''' if sum(1 for _ in (CodeRefsTo(s_addr, 1))) != 0: s_addr += 4 continue ''' #also add STMFD if ((idc.GetMnem(s_addr) == "STM") and ("SP!" in idc.GetOpnd(s_addr, 0)) and ("LR" in idc.GetOpnd(s_addr, 1))) or ( ((idc.GetMnem(s_addr) == "PUSH") or (idc.GetMnem(s_addr) == "PUSH.W") or (idc.GetMnem(s_addr) == "STR.W")) and ("LR" in idc.GetOpnd(s_addr, 0))): print "Found function at 0x%08x" % s_addr idc.MakeFunction(s_addr) f = idaapi.get_func(s_addr) if (type(f) == type(None)): print "Failed to create function! Undefined instructions?" s_addr += 2 else: num_added_functions += 1 ea = -1 for c in idautils.Chunks(s_addr): if c[1] > ea: ea = c[1] if ea != -1: s_addr = ea #failed? else: s_addr += 2 else: s_addr += 2 print "finished segment" return num_added_functions
def Chunks(self, start): """Returns list of Chunks in the Function that starts at start""" return idautils.Chunks(start)