def Heads(start=idaapi.cvar.inf.minEA, end=idaapi.cvar.inf.maxEA): """ Get a list of heads (instructions or data) @param start: start address (default: inf.minEA) @param end: end address (default: inf.maxEA) @return: list of heads between start and end """ ea = start if not idc.isHead(idc.GetFlags(ea)): ea = idaapi.next_head(ea, end) while ea != idaapi.BADADDR: yield ea ea = idaapi.next_head(ea, end)
def Texts(*args): """ Enumerate text search matches @param <range>: see getrange @param searchstr: string or regex @param flags: for instance SEARCH_REGEX @return: list of addresses matching searchstr Example:: for ea in Texts(FirstSeg(), BADADDR, "LDR *PC, =", SEARCH_REGEX): f = idaapi.get_func(ea) if f and f.startEA==ea: n= idaapi.get_name(BADADDR, ea) if not n.startswith("sub_"): MakeName(ea, "j_%s" %n) Will search for functions containing only "LDR PC, =xxxxx", and rename them as j_XXXXX. """ (first, last)= getrange(args) i= getstringpos(args) if i<0: raise Exception("missing searchstring") searchstr= args[i] flags = args[i+1] if i+1<len(args) else 0 ea= idaapi.find_text(first, 0, 0, searchstr, idaapi.SEARCH_DOWN|flags) while ea!=idaapi.BADADDR and ea<last: yield ea ea= idaapi.find_text(idaapi.next_head(ea, last), 0, 0, searchstr, idaapi.SEARCH_DOWN|flags)
def __init__(self, func): self.src = func ea = func.startEA self.ea = ea self.name = idaapi.get_func_name(ea) self.elts = [] if idaapi.is_func_tail(func): self.name += "_tail_" + str(ea) fr = idaapi.get_frame(func) struc_vars = [] if fr == None: self.frame = None else: self.frame = struct.frame_node(fr, func) struc_vars = self.frame.struct_vars while ea < func.endEA: if idaapi.isData(idaapi.getFlags(ea)): self.elts.append(data.data_node(ea, idaapi.next_not_tail(ea))) else: if ea == func.startEA and not (idaapi.is_func_tail(func)): self.elts.append(insn.insn_node(ea, struc_vars)) else: self.elts.append( names.name_wrap_insn(ea, insn.insn_node(ea, struc_vars))) ea = idaapi.next_head(ea, func.endEA)
def __init__(self, func): self.src = func ea = func.startEA self.ea = ea self.name = idaapi.get_func_name(ea) self.elts = [] if idaapi.is_func_tail(func): self.name += "_tail_"+str(ea) fr = idaapi.get_frame(func) struc_vars = [] if fr == None: self.frame = None else: self.frame = struct.frame_node(fr, func) struc_vars = self.frame.struct_vars while ea < func.endEA: if idaapi.isData(idaapi.getFlags(ea)): self.elts.append(data.data_node(ea, idaapi.next_not_tail(ea))) else: if ea == func.startEA and not (idaapi.is_func_tail(func)): self.elts.append(insn.insn_node(ea, struc_vars)) else: self.elts.append(names.name_wrap_insn(ea, insn.insn_node(ea, struc_vars))) ea = idaapi.next_head(ea, func.endEA)
def Texts(*args): """ Enumerate text search matches @param <range>: see getrange @param searchstr: string or regex @param flags: for instance SEARCH_REGEX @return: list of addresses matching searchstr Example:: for ea in Texts((FirstSeg(), BADADDR), "LDR *PC, =", SEARCH_REGEX): f = idaapi.get_func(ea) if f and f.start_ea==ea: n= idaapi.get_name(BADADDR, ea) if not n.startswith("sub_"): MakeName(ea, "j_%s" %n) Will search for functions containing only "LDR PC, =xxxxx", and rename them as j_XXXXX. """ (first, last) = getrange(args) i = getstringpos(args) if i < 0: raise Exception("missing searchstring") searchstr = args[i] flags = args[i + 1] if i + 1 < len(args) else 0 ea = idaapi.find_text(first, idaapi.SEARCH_DOWN | flags, 0, 0, searchstr) while ea != idaapi.BADADDR and ea < last: yield ea ea = idaapi.find_text(idaapi.next_head(ea, last), idaapi.SEARCH_DOWN | flags, 0, 0, searchstr)
def Heads(*args): """ Enumerate array items @param <range>: see getrange @return: list of all heads """ (first, last) = getrange(args) ea = first if ea < last and not idaapi.is_head(idaapi.get_full_flags(ea)): ea = idaapi.next_head(ea, last) while ea != BADADDR and ea < last: yield ea ea = idaapi.next_head(ea, last)
def Heads(*args): """ Enumerate array items @param <range>: see getrange @return: list of all heads """ (first, last)= getrange(args) ea= first if ea<last and not idaapi.isHead(idaapi.getFlags(ea)): ea= idaapi.next_head(ea, last) while ea!=BADADDR and ea<last: yield ea ea= idaapi.next_head(ea, last)
def _build_metadata(self): """ Collect node metadata from the underlying database. """ current_address = self.address node_end = self.address + self.size # loop through the node's entire range and count its instructions # NOTE: we are assuming that every defined 'head' is an instruction while current_address != idaapi.BADADDR: self.instruction_count += 1 current_address = idaapi.next_head(current_address, node_end)
def append_cmt(self, ea, cmt, rptble=False): if len(cmt) > 1024: rs_log("warning, comment needs to be splitted (from 0x%x)" % ea) nh = idaapi.next_head(ea, ida_idaapi.BADADDR) if nh == ida_idaapi.BADADDR: rs_log('[x] failed to find next instruction candidate') return self.append_cmt(nh, cmt[1024:], rptble) cmt = cmt[:1024] idaapi.append_cmt(ea, cmt, rptble)
def append_cmt(self, ea, cmt, rptble=False): if len(cmt) > 1024: print "[*] warning, comment needs to be splitted (from 0x%x)" % ea nh = idaapi.next_head(ea, idaapi.BADADDR) if nh == idaapi.BADADDR: print "[*] failed to find next instruction candidate" return self.append_cmt(nh, cmt[1024:], rptble) cmt = cmt[:1024] idaapi.append_cmt(ea, cmt, rptble)
def trace_block(self, blk, node, operand): """ 在一个基本块内回溯 """ operand_t = operand cur_t = node['addr'] while operand_t != None and cur_t >= blk.start_ea: cur_t, operand_t = self.trace_handle(cur_t, operand_t) if cur_t == self.func.start_ea: FELogger.info("查找到函数起始") if check_operand_arg(operand_t): FELogger.info("由函数参数传入: %s" % (operand_t.name)) operand_t = None return (idaapi.next_head(cur_t, idaapi.BADADDR), operand_t)
def NonFuncs(*args): """ Enumerate code which is not in a function @param ea: where to start @param endea: BADADDR, or end address @return: list of addresses containing code, but not in a function Example:: for ea in NonFuncs(FirstSeg(), BADADDR): if not MakeFunction(ea): Jump(ea) break Wait() Will try to change non-function code to function until MakeFunction fails """ (first, last)= getrange(args) ea = first while ea!=idaapi.BADADDR and ea<last: nextcode= idaapi.find_code(ea, idaapi.SEARCH_NEXT|idaapi.SEARCH_DOWN) thischunk= idaapi.get_fchunk(ea) nextchunk= idaapi.get_next_fchunk(ea) if thischunk: ea= thischunk.endEA elif idaapi.isCode(idaapi.getFlags(ea)): yield ea ea= idaapi.next_head(ea, last) elif nextchunk is None: return elif nextcode<nextchunk.startEA: yield nextcode ea= nextcode else: ea= nextchunk.endEA
def NonFuncs(*args): """ Enumerate code which is not in a function @param <range>: see getrange @return: list of addresses containing code, but not in a function Example:: for ea in NonFuncs((FirstSeg(), BADADDR)): if not MakeFunction(ea): Jump(ea) break Wait() Will try to change non-function code to function until MakeFunction fails """ (first, last) = getrange(args) ea = first while ea != idaapi.BADADDR and ea < last: nextcode = idaapi.find_code(ea, idaapi.SEARCH_NEXT | idaapi.SEARCH_DOWN) thischunk = idaapi.get_fchunk(ea) nextchunk = idaapi.get_next_fchunk(ea) if thischunk: ea = thischunk.end_ea elif idaapi.is_code(idaapi.get_full_flags(ea)): yield ea ea = idaapi.next_head(ea, last) elif nextchunk is None: return elif nextcode < nextchunk.start_ea: yield nextcode ea = nextcode else: ea = nextchunk.end_ea
def Heads(start, end): """ Get a list of heads (instructions or data) @param start: start address (this one is always included) @param end: end address @return: list of heads between start and end """ headlist = [] headlist.append(start) ea = start while 1: ea = idaapi.next_head(ea, end) if ea == idaapi.BADADDR: break else: headlist.append(ea) return headlist
def workaround_Functions(start=idaapi.cvar.inf.minEA, end=idaapi.cvar.inf.maxEA): """ Get a list of functions @param start: start address (default: inf.minEA) @param end: end address (default: inf.maxEA) @return: list of heads between start and end @note: The last function that starts before 'end' is included even if it extends beyond 'end'. """ func = idaapi.get_func(start) if not func: func = idaapi.get_next_func(start) while func and func.startEA < end: startea = func.startEA yield startea func = idaapi.get_next_func(startea) addr = startea while func and startea == func.startEA: addr = idaapi.next_head(addr, end) func = idaapi.get_next_func(addr)
def process_function(arch, func_ea): func_end = idc.FindFuncEnd(func_ea) packet = DismantlerDataPacket() ida_chunks = get_chunks(func_ea) chunks = set() # Add to the chunks only the main block, containing the # function entry point # chunk = get_flow_code_from_address(func_ea) if chunk: chunks.add(chunk) # Make "ida_chunks" a set for faster searches within ida_chunks = set(ida_chunks) ida_chunks_idx = dict(zip([c[0] for c in ida_chunks], ida_chunks)) func = idaapi.get_func(func_ea) comments = [idaapi.get_func_cmt(func, 0), idaapi.get_func_cmt(func, 1)] # Copy the list of chunks into a queue to process # chunks_todo = [c for c in chunks] while True: # If no chunks left in the queue, exit if not chunks_todo: if ida_chunks: chunks_todo.extend(ida_chunks) else: break chunk_start, chunk_end = chunks_todo.pop() if ida_chunks_idx.has_key(chunk_start): ida_chunks.remove(ida_chunks_idx[chunk_start]) del ida_chunks_idx[chunk_start] for head in idautils.Heads(chunk_start, chunk_end): comments.extend((idaapi.get_cmt(head, 0), idaapi.get_cmt(head, 1))) comment = '\n'.join([c for c in comments if c is not None]) comment = comment.strip() if comment: packet.add_comment(head, comment) comments = list() if idc.isCode(idc.GetFlags(head)): instruction = arch.process_instruction(packet, head) # if there are other references than # flow add them all. if list(idautils.CodeRefsFrom(head, 0)): # for each reference, including flow ones for ref_idx, ref in enumerate( idautils.CodeRefsFrom(head, 1)): if arch.is_call(instruction): # This two conditions must remain separated, it's # necessary to enter the enclosing "if" whenever # the instruction is a call, otherwise it will be # added as an uncoditional jump in the last else # if ref in list(idautils.CodeRefsFrom(head, 0)): packet.add_direct_call(head, ref) elif ref_idx > 0 and arch.is_conditional_branch( instruction): # The ref_idx is > 0 in order to avoid processing the # normal flow reference which would effectively imply # that the conditional branch is processed twice. # It's done this way instead of changing the loop's head # from CodeRefsFrom(head, 1) to CodeRefsFrom(head, 0) in # order to avoid altering the behavior of other conditions # which rely on it being so. # FIXME # I don't seem to check for the reference here # to point to valid, defined code. I suspect # this could lead to a failure when exporting # if such situation appears. I should test if # it's a likely scenario and probably just add # an isHead() or isCode() to address it. packet.add_conditional_branch_true(head, ref) packet.add_conditional_branch_false( head, idaapi.next_head(head, chunk_end)) # If the target is not in our chunk list if not address_in_chunks(ref, chunks): new_chunk = get_flow_code_from_address(ref) # Add the chunk to the chunks to process # and to the set containing all visited # chunks if new_chunk is not None: chunks_todo.append(new_chunk) chunks.add(new_chunk) elif arch.is_unconditional_branch(instruction): packet.add_unconditional_branch(head, ref) # If the target is not in our chunk list if not address_in_chunks(ref, chunks): new_chunk = get_flow_code_from_address(ref) # Add the chunk to the chunks to process # and to the set containing all visited # chunks if new_chunk is not None: chunks_todo.append(new_chunk) chunks.add(new_chunk) #skip = False for ref in idautils.DataRefsFrom(head): packet.add_data_reference(head, ref) # Get a data reference from the current reference's # location. For instance, if 'ref' points to a valid # address and such address contains a data reference # to code. target = list(idautils.DataRefsFrom(ref)) if target: target = target[0] else: target = None if target is None and arch.is_call(instruction): imp_name = idc.Name(ref) imp_module = get_import_module_name(ref) imported_functions.add((ref, imp_name, imp_module)) packet.add_indirect_virtual_call(head, ref) elif target is not None and idc.isHead(target): # for calls "routed" through this reference if arch.is_call(instruction): packet.add_indirect_call(head, target) # for unconditional jumps "routed" through this reference elif arch.is_unconditional_branch(instruction): packet.add_unconditional_branch(head, target) # for conditional "routed" through this reference elif arch.is_conditional_branch(instruction): packet.add_conditional_branch_true(head, target) packet.add_conditional_branch_false( head, idaapi.next_head(head, chunk_end)) f = FunctionAnalyzer(arch, func_ea, packet) instrumentation.new_packet(packet) instrumentation.new_function(f)
def process_function(arch, func_ea): func_end = idc.FindFuncEnd(func_ea) packet = DismantlerDataPacket() ida_chunks = get_chunks(func_ea) chunks = set() # Add to the chunks only the main block, containing the # function entry point # chunk = get_flow_code_from_address(func_ea) if chunk: chunks.add( chunk ) # Make "ida_chunks" a set for faster searches within ida_chunks = set(ida_chunks) ida_chunks_idx = dict(zip([c[0] for c in ida_chunks], ida_chunks)) func = idaapi.get_func(func_ea) comments = [idaapi.get_func_cmt(func, 0), idaapi.get_func_cmt(func, 1)] # Copy the list of chunks into a queue to process # chunks_todo = [c for c in chunks] while True: # If no chunks left in the queue, exit if not chunks_todo: if ida_chunks: chunks_todo.extend(ida_chunks) else: break chunk_start, chunk_end = chunks_todo.pop() if ida_chunks_idx.has_key(chunk_start): ida_chunks.remove(ida_chunks_idx[chunk_start]) del ida_chunks_idx[chunk_start] for head in idautils.Heads(chunk_start, chunk_end): comments.extend( (idaapi.get_cmt(head, 0), idaapi.get_cmt(head, 1)) ) comment = '\n'.join([c for c in comments if c is not None]) comment = comment.strip() if comment: packet.add_comment(head, comment) comments = list() if idc.isCode(idc.GetFlags(head)): instruction = arch.process_instruction(packet, head) # if there are other references than # flow add them all. if list( idautils.CodeRefsFrom(head, 0) ): # for each reference, including flow ones for ref_idx, ref in enumerate(idautils.CodeRefsFrom(head, 1)): if arch.is_call(instruction): # This two conditions must remain separated, it's # necessary to enter the enclosing "if" whenever # the instruction is a call, otherwise it will be # added as an uncoditional jump in the last else # if ref in list( idautils.CodeRefsFrom(head, 0) ): packet.add_direct_call(head, ref) elif ref_idx>0 and arch.is_conditional_branch(instruction): # The ref_idx is > 0 in order to avoid processing the # normal flow reference which would effectively imply # that the conditional branch is processed twice. # It's done this way instead of changing the loop's head # from CodeRefsFrom(head, 1) to CodeRefsFrom(head, 0) in # order to avoid altering the behavior of other conditions # which rely on it being so. # FIXME # I don't seem to check for the reference here # to point to valid, defined code. I suspect # this could lead to a failure when exporting # if such situation appears. I should test if # it's a likely scenario and probably just add # an isHead() or isCode() to address it. packet.add_conditional_branch_true(head, ref) packet.add_conditional_branch_false( head, idaapi.next_head(head, chunk_end)) # If the target is not in our chunk list if not address_in_chunks(ref, chunks): new_chunk = get_flow_code_from_address(ref) # Add the chunk to the chunks to process # and to the set containing all visited # chunks if new_chunk is not None: chunks_todo.append(new_chunk) chunks.add(new_chunk) elif arch.is_unconditional_branch(instruction): packet.add_unconditional_branch(head, ref) # If the target is not in our chunk list if not address_in_chunks(ref, chunks): new_chunk = get_flow_code_from_address(ref) # Add the chunk to the chunks to process # and to the set containing all visited # chunks if new_chunk is not None: chunks_todo.append(new_chunk) chunks.add(new_chunk) #skip = False for ref in idautils.DataRefsFrom(head): packet.add_data_reference(head, ref) # Get a data reference from the current reference's # location. For instance, if 'ref' points to a valid # address and such address contains a data reference # to code. target = list( idautils.DataRefsFrom(ref) ) if target: target = target[0] else: target = None if target is None and arch.is_call(instruction): imp_name = idc.Name(ref) imp_module = get_import_module_name(ref) imported_functions.add((ref, imp_name, imp_module)) packet.add_indirect_virtual_call(head, ref) elif target is not None and idc.isHead(target): # for calls "routed" through this reference if arch.is_call(instruction): packet.add_indirect_call(head, target) # for unconditional jumps "routed" through this reference elif arch.is_unconditional_branch(instruction): packet.add_unconditional_branch(head, target) # for conditional "routed" through this reference elif arch.is_conditional_branch(instruction): packet.add_conditional_branch_true(head, target) packet.add_conditional_branch_false( head, idaapi.next_head(head, chunk_end)) f = FunctionAnalyzer(arch, func_ea, packet) instrumentation.new_packet(packet) instrumentation.new_function(f)
def next(cls, ea=None, count=1): ea = ui.current.address() if ea is None else ea res = idaapi.next_head(ea, idaapi.BADADDR) return cls.next(res, count - 1) if count > 1 else res
def next(ea): return idaapi.next_head(ea, idaapi.BADADDR)
continue ptr2 = Qword(ptr1) func = Qword(ptr2) # Rename function idaapi.set_name(func, 'func_%02x_%lx' % (i, func)) print '[+] Processing: %lx %lx %lx %lx' % (addr, ptr1, ptr2, func), GetFunctionName(func) # functions end at `call ___cxa_throw` instrucion. # extend endEA to `call __Unwind_Resume` for x in Heads(func, func + 512): if GetDisasm(x) == 'call __Unwind_Resume': SetFunctionEnd(func, idaapi.next_head(x, func + 512)) break # Find next hop. Look for instruction: `mov dword ptr [rax], XXh` # where XX is the func_tbl_5555557671E0 entry of the next function. end = GetFunctionAttr(func, FUNCATTR_END) node = {'id': i, 'ea': func, 'next': -1, 'insns': []} start_log = False # for each instruction in function for insn in Heads(func, end): # Check for next hop instruction if idc.GetMnem(insn) == 'mov': if idc.GetOpnd(insn, 0) == 'dword ptr [rax]': node['next'] = idc.GetOperandValue(insn, 1) print '\tNext hop found: %x -> %x' % (i, node['next'])