def analysePrototype(self, ea, force=False): try: if not isLoaded(ea): self.log(2, "Error! Bad address: 0x%x" % ea) raise try: fname = GetFunctionName(ea) except: fname = "0x%x" % ea xrefs = self.getXrefs(ea) if len(xrefs) == 0: self.log(3, "Discarding function: %s as there is no xref" % fname) return None tmpSorterDict = {} mr = self.MAX_REFS_TO_CHECK if (len(xrefs) < self.MAX_REFS_TO_CHECK): mr = len(xrefs) xrefs = xrefs[0:mr] self.log( 3, "\n%s\nAnalysing prototype for function 0x%x" % ('-' * 50, ea)) if (not force) and (GetFunctionFlags(ea) & (FUNC_HIDDEN | FUNC_LIB)): self.log( 3, "Discarding function: %s as it is a LIB/HIDDEN function" % fname) return None idbProto = print_type(ea, False) gt = GuessType(ea) if (((idbProto != None) and (idbProto.find("__usercall") != -1)) or ((gt != None) and (gt.find("__usercall") != -1))): self.argPassMech = "mov" self.argPassType = "__usercall" else: self.argPassType = "__cdecl" self.argPassMech = "push" for ref in xrefs: argvArray = [] i = self.TRACK_BACK_LIMIT ref_p = ref proto = "int %s %s(" % (self.argPassType, fname) while (i): g = 0 ref_p = idc.PrevHead(ref_p, 0) if ((ref_p == BADADDR) or idc.GetDisasm(ref_p).startswith("call")): g = 2 break disstr = idc.GetDisasm(ref_p) if disstr.startswith(self.argPassMech): itmSize = idc.ItemSize(ref_p) if (itmSize == 5): dataVal = self.getDataValue( ref_p + self.argPassMechInsSize, itmSize - self.argPassMechInsSize) if isLoaded(dataVal) and (not isCode( GetFlags(dataVal))): proto += "void*," else: proto += "int," elif (itmSize == 2): proto += "int," else: proto += "int*," g = 1 if g == 0: argvArray.append(( itmSize, (itmSize - self.argPassMechInsSize), )) else: # push reg argvArray.append(( "BUFFER", 0x400, )) # flag to use buffer if len(self.getXrefs(ref_p)) == 1: nref = self.getXrefs(ref_p)[0] refins = idc.GetDisasm(nref) if refins.startswith(self.interConnectInstr): ref_p = nref i -= 1 if (len(argvArray) == 0): proto += "void," if ((i == 0) or (g == 2)): proto = proto[:-1] + ");" self.protoArray[proto] = argvArray self.log( 4, "Proto, args pattern found at 0x%x is: %s->%s" % (ref, proto, repr(argvArray))) try: tmpSorterDict[proto] = tmpSorterDict[proto] + 1 except: tmpSorterDict[proto] = 1 itms = sorted(tmpSorterDict.iteritems(), key=lambda (k, v): (v, k), reverse=True) ( prt, n, ) = itms[0] if (len(prt.split(",")) > self.MAX_ARGS): self.log( 2, "too many arguments in proto from protoanalyser, ignoring function" ) return None if (idbProto != None): self.log(3, "proto from IDB: %s" % idbProto) nIdbPrtArgs = len(idbProto.split(",")) if (nIdbPrtArgs > self.MAX_ARGS): self.log( 3, "too many arguments in proto from IDB, ignoring it and using parser found proto" ) idbProto = prt self.log(2, "proto found by parser: %s, occurance: %d" % (prt, n)) if (len(argvArray) > nIdbPrtArgs): argvArray = argvArray[:nIdbPrtArgs] self.protoArray[prt] = argvArray fn = Function(ea, idbProto, self.protoArray[prt], self.argPassMech) else: fn = Function(ea, prt, self.protoArray[prt], self.argPassMech) self.log(2, repr(fn)) n = (n * 100) / mr if (not force) and (self.argPassType != "__usercall") and (n < self.GCD_THRESOLD): self.log( 3, "[GCD of protos from refs is %d%%, discarding above function]" % n) return None self.log( 3, "[GCD of protos from refs is %d%%, considering above function as a candidate, ArgPassMech:%s]" % (n, self.argPassMech)) return fn except Exception, e: kprint("Exception!! @analysePrototype, ", e)
def CALL(cpu_context, ip, mnem, opvalues): """ CALL function Attempt to determine the number of arguments passed to the function which are purged on return """ func_ea = opvalues[0].value if not isinstance(func_ea, (int, long)): cpu_logger.debug("CALL 0x{:X} :: call {!r}".format(ip, func_ea)) cpu_logger.debug("Invalid function: {!r}".format(func_ea)) return # For the called function, attempt to locate the function end and examine the "retn" instruction which # will contain the number of bytes to add back to SP. # opvalue1 = idc.GetOperandValue(ip, 0) cpu_logger.debug("CALL 0x{:X} :: call 0x{:X}".format(ip, func_ea)) try: is_loaded = idc.isLoaded(func_ea) except TypeError: is_loaded = False # TODO: Disabled until we can keep track of pointer history. # # Emulate the effects of any known builtin functions. # func_name = idaapi.get_func_name(func_ea) # builtin_func = BUILTINS.get(func_name) # if builtin_func: # try: # args = cpu_context.get_function_args(func_ea) # builtin_func(cpu_context, ip, func_name, args) # except Exception as e: # cpu_logger.warn( # 'Failed to emulate builtin function: {}() at {:#08x} with error: {}'.format( # func_name, ip, e)) # For non-loaded files, reset esp based on number of stack arguments. # (We are assuming the callee is responsible for resetting the stack) if not is_loaded: try: func_data = utils.get_function_data(func_ea) for arg in func_data: loc_type = arg.argloc.atype() # Where was this parameter passed? if loc_type == 1: # ALOC_STACK # reset esp for each stack argument. cpu_context.reg_write("RSP", cpu_context.reg_read("RSP") + cpu_context._byteness) except: # If we can't get function data for non-loaded functions, then we have no way of knowing how to handle # the stack... cpu_logger.debug( "{:#08x} :: Cannot identify function information for value {:#08x}. " "Stack pointer will not be adjusted.".format(ip, func_ea)) return else: # Get address of retn instruction func_end = idc.GetFunctionAttr(func_ea, idc.FUNCATTR_END) if func_end == idc.BADADDR: cpu_logger.debug( "{:#08x} :: Could not retrieve retn instruction for called function: {:#08x}. " "Stack pointer will not be adjusted.".format(ip, func_ea)) return # Find a "retn" and see if we need to adjust rsp. # (All retn's should have the same operand so finding any of them will work). # look for retn address ea = func_end while ea > func_ea: if idc.GetMnem(ea) == "retn": sp_adjust = idc.GetOperandValue(ea, 0) # if retn doesn't adjust the stack, -1 is returned if sp_adjust != -1: cpu_context.reg_write("RSP", cpu_context.reg_read("RSP") + sp_adjust) return ea = idc.PrevHead(ea)
def prev_head(curAddr): if idaapi.IDA_SDK_VERSION <= 699: prev_eip = idc.PrevHead(curAddr) else: prev_eip = idc.prev_head(curAddr) return prev_eip
def prev(ea): '''return the previous address (instruction or data)''' return idc.PrevHead(ea, idc.MinEA())
def find_nth_arg(self, arg_idx): return idafind.find(idc.PrevHead(self.f.startEA), self.make_arg_opnd(arg_idx))
def GetFunEdgesAndBbls(function_ea): """ Get bbls of function. @function_ea - function address @return - bbls of function """ bbl = [] # bbl info [head, tail, call_num, mem_num] SingleBBS = {} # head -> pred_bbl MultiBBS = {} # head -> [pred_bbls] bbls = {} # head -> bbl bbls2 = {} # tail -> bbl edges_s = set() # set of (tail, head) edges_d = {} # dict struct. head -> of (head, ..., head) edges_count = 0 edges_s_t = set() # tmp edges set edges_d_t = {} # tmp edges dict. if not IsInstrumentIns(function_ea): return bbls, edges_d, edges_count, SingleBBS, MultiBBS f_start = function_ea f_end = idc.FindFuncEnd(function_ea) boundaries = set((f_start, )) # head of bbl for head in idautils.Heads(f_start, f_end): # If the element is an instruction if head == idaapi.BADADDR: raise Exception("Invalid head for parsing") if not idc.isCode(idc.GetFlags(head)): continue # Get the references made from the current instruction # and keep only the ones local to the function. refs = idautils.CodeRefsFrom(head, 0) refs_filtered = set() for ref in refs: if ref > f_start and ref < f_end: # can't use ref>=f_start, avoid recusion refs_filtered.add(ref) refs = refs_filtered if refs: # If the flow continues also to the next (address-wise) # instruction, we add a reference to it. # For instance, a conditional jump will not branch # if the condition is not met, so we save that # reference as well. next_head = idc.NextHead(head, f_end) if next_head != idaapi.BADADDR and idc.isFlow( idc.GetFlags(next_head)): refs.add(next_head) # Update the boundaries found so far. boundaries.update(refs) for r in refs: # enum all of next ins # If the flow could also come from the address # previous to the destination of the branching # an edge is created. if isFlow(idc.GetFlags(r)): prev_head = idc.PrevHead(r, f_start) if prev_head == 0xffffffffL: #edges_s_t.add((head, r)) #raise Exception("invalid reference to previous instruction for", hex(r)) pass else: edges_s_t.add((prev_head, r)) edges_s_t.add((head, r)) #end of for head in idautils.Heads(chunk[0], chunk[1]): last_head = 0 # NOTE: We can handle if jump xrefs to chunk address space. # get bbls. head of bbl is first ins addr, tail of bbl is last ins addr. for head in idautils.Heads(f_start, f_end): mnem = idc.GetMnem(head) if head in boundaries: if len(bbl) > 0: if bbl[0] == head: continue if True: # IsInstrumentIns(bbl[0]): bbl[1] = last_head bbls[bbl[0]] = bbl bbls2[bbl[1]] = bbl bbl = [head, 0, 0, 0] #elif self.GetInstructionType(head) == self.BRANCH_INSTRUCTION: elif mnem.startswith('j'): if len(bbl) > 0 and bbl[0] == head + idc.ItemSize(head): continue if True: # IsInstrumentIns(bbl[0]): bbl[1] = head # head + idc.ItemSize(head)) bbls[bbl[0]] = bbl bbls2[bbl[1]] = bbl bbl = [head + idc.ItemSize(head), 0, 0, 0] else: last_head = head if mnem.startswith('call'): bbl[2] += 1 #if 2 == idc.GetOpType(head, 0): # 2 Memory Reference # bbl[3] += 1 #if 2 == idc.GetOpType(head, 1): # 2 Memory Reference # bbl[3] += 1 # add last basic block if len(bbl) and bbl[0] != f_end: # and IsInstrumentIns(bbl[0]): bbl[1] = f_end bbls[bbl[0]] = bbl bbls2[bbl[1]] = bbl # edges set -> dict for e in edges_s_t: if e[0] in bbls2: bbl_head = bbls2[e[0]][0] if bbl_head in edges_d_t: edges_d_t[bbl_head].append(e[1]) else: edges_d_t[bbl_head] = [e[1]] else: print('edge (%x, %x) can not find head bbl.' % (e[0], e[1])) # a small case. e1 flow e0. # revise edges. head bbl and tail bbl of edges must be instrumented. for e0 in edges_d_t: if not IsInstrumentIns(e0): # e0 don't instrumented, skip. continue for e1 in edges_d_t[e0]: if IsInstrumentIns(e1): # e0 e1 both instrumented, add edge. if e0 in edges_d: edges_d[e0].append(e1) else: edges_d[e0] = [e1] edges_count += 1 else: # e1 don't instrumented, recursively looks for instrumented child bbls bbls_t = LookForInsChildBbls(e1, edges_d_t, []) for b in bbls_t: # add edge if e0 in edges_d: edges_d[e0].append(b) else: edges_d[e0] = [b] edges_count += 1 # revise bbls. bbl must be instrumented. for b in bbls.keys(): if not IsInstrumentIns(b): # if bbls[b][1] in bbls2: # avoid multi del # bbls2.pop(bbls[b][1]) bbls.pop(b) #print('bbls:') #i = 0 #for b in bbls: # i += 1 # print('%04d %x, %x' % (i, b, bbls[b][1])) #print('edges_d:') #i = 0 #for e0 in edges_d: # for e1 in edges_d[e0]: # i += 1 # print('%04d %x, %x' % (i, e0, e1)) for e0 in edges_d: if e0 not in bbls: print('error:%x have no head' % (e0)) # error continue for e1 in edges_d[e0]: if e1 in MultiBBS: MultiBBS[e1].append(bbls[e0]) # add Pred elif e1 in SingleBBS: MultiBBS[e1] = [SingleBBS[e1], bbls[e0]] # add Pred SingleBBS.pop(e1) # remove from SingleBBS else: SingleBBS[e1] = bbls[e0] # add Pred # del bbls which don't instrumented return bbls, edges_d, edges_count, SingleBBS, MultiBBS
def goto_func_end(self): e = idc.get_func_attr(idc.here(), idc.FUNCATTR_END) idc.jumpto(idc.PrevHead(e))
def prevItem(self, ea, bounds): return idc.PrevHead(ea, bounds)
def trace_param(ea, min_ea, op_type, op_val): ''' trace_param: ea, min_ea, op_type, op_val Taking ea as start, this function does basic backtrace of an operand (defined by op_type and op_val) until it finds a data reference which we consider the "source". It stops when ea < min_ea (usually the function start). It does not support arithmetic or complex modifications of the source. This will be improved on future versions. ''' global displ_re, msgsend, var_re ea_call = ea while ea != idc.BADADDR and ea != min_ea: ea = idc.PrevHead(ea, min_ea) if op_type == idaapi.o_reg and op_val == 0 and idaapi.is_call_insn(ea): # We have a BL/BLX that will modify the R0 # we're tracking #TODO: resolve more situation return None if idc.GetMnem(ea) in ['LDR', 'MOV']: src_op = 1 dest_op = 0 elif idc.GetMnem(ea) == 'STR': src_op = 0 dest_op = 1 else: continue if idc.GetOpType(ea, dest_op) == op_type and idc.GetOperandValue(ea, dest_op) == op_val: # Found, see where it comes from if idc.GetOpType(ea, src_op) == idc.o_mem or idc.GetOpType(ea, src_op) == idc.o_imm: #add o_imm support # Got the final reference refs = list(idautils.DataRefsFrom(ea)) if not refs: local_ref = idc.GetOperandValue(ea, src_op) far_ref = idc.Dword(local_ref) else: while len(refs) > 0: far_ref = refs[0] refs = list(idautils.DataRefsFrom(refs[0])) #patch by lc if far_ref: return far_ref elif idc.GetOpType(ea, src_op) == idc.o_displ: if ', [SP' in idc.GetDisasm(ea): if 'arg_' in idc.GetDisasm(ea): # We don't track function arguments return None # We're tracking an stack variable try: var_name = var_re.search(idc.GetDisasm(ea)).group('varname') except: print '%08x: Unable to recognize variable' % ea return None while ea != idc.BADADDR and ea > min_ea: if idc.GetMnem(ea) == 'STR' and var_name in idc.GetDisasm(ea): # New reg to track op_val = idc.GetOperandValue(ea, dest_op) break ea = idc.PrevHead(ea, min_ea) else: # New reg to track if '[LR]' in idc.GetDisasm(ea): # Optimizations use LR as general reg op_val = 14 else: try: op_val = int(displ_re.search(idc.GetDisasm(ea)).group('regnum')) except: print '%08x: Unable to recognize register' % ea return None elif idc.GetOpType(ea, src_op) == idc.o_reg: # Direct reg-reg assignment op_val = idc.GetOperandValue(ea, src_op) else: # We don't track o_phrase or other complex source operands :( return None #register R0-R3 assigned by function parameter if ea <= min_ea and op_type == idc.o_reg and op_val in range(4): f_info = get_func_info(ea) return ['pself', 'selector', f_info['fparam_type'], f_info['sparam_name']][op_val]#fix: error return None def fix_callgraph(msgsend, segname, class_param, sel_param): #class_param == 0, sel_param == 1 ''' fix_callgraph: msgsend, segname, class_param, sel_param Given the msgsend flavour address as a parameter, looks for the parameters (class and selector, identified by class_param and sel_param) and creates a new segment where it places a set of dummy calls named as classname_methodname (we use method instead of selector most of the time). ''' t1 = time.time() if not msgsend: print 'ERROR: msgSend not found' return total = 0 resolved = 0 call_table = dict() for xref in idautils.XrefsTo(msgsend, idaapi.XREF_ALL): total += 1 ea_call = xref.frm func_start = idc.GetFunctionAttr(ea_call, idc.FUNCATTR_START) if not func_start or func_start == idc.BADADDR: continue ea = ea_call method_name_ea = trace_param(ea, func_start, idc.o_reg, sel_param)#sel_param == 1 if method_name_ea and idc.isASCII(idc.GetFlags(method_name_ea)): method_name = idc.GetString(method_name_ea, -1, idc.ASCSTR_C) if not method_name: method_name = '_unk_method' else: method_name = '_unk_method' class_name_ea = trace_param(ea, func_start, idc.o_reg, class_param)#class_param == 0 if class_name_ea: class_name = idc.Name(class_name_ea) if not class_name: class_name = '_unk_class' else: class_name = '_unk_class' if method_name == '_unk_method' and class_name == '_unk_class': continue # Using this name convention, if the class and method # are identified by IDA, the patched call will point to # the REAL call and not one of our dummy functions # class_name = class_name.replace('_OBJC_CLASS_$_', '') class_name = class_name.replace('_OBJC_METACLASS_$_', '') new_name = '_[' + class_name + '_' + method_name + ']' print '%08x: %s' % (ea_call, new_name) call_table[ea_call] = new_name resolved += 1 print '\nFinal stats:\n\t%d total calls, %d resolved' % (total, resolved) print '\tAnalysis took %.2f seconds' % (time.time() - t1) if resolved == 0: print 'Nothing to patch.' return print 'Adding new segment to store new nullsubs' # segment size = opcode ret (4 bytes) * num_calls seg_size = resolved * 4 seg_start = idc.MaxEA() + 4 idaapi.add_segm(0, seg_start, seg_start + seg_size, segname, 'CODE') print 'Patching database...' seg_ptr = seg_start for ea, new_name in call_table.items(): if idc.LocByName(new_name) != idc.BADADDR: offset = idc.LocByName(new_name) - ea else: # create code and name it idc.PatchDword(seg_ptr, 0xE12FFF1E) # BX LR idc.MakeName(seg_ptr, new_name) idc.MakeCode(seg_ptr) idc.MakeFunction(seg_ptr, seg_ptr + 4) idc.MakeRptCmt(seg_ptr, new_name) offset = seg_ptr - ea seg_ptr += 4 # patch the msgsend call if idc.GetReg(ea, "T") == 1: if offset > 0 and offset & 0xFF800000: print 'Offset too far for Thumb (%08x) Stopping [%08x]' % (offset, ea) return off1 = (offset & 0x7FF000) >> 12 off2 = (offset & 0xFFF) / 2 w1 = (0xF000 | off1) w2 = (0xE800 | off2) - 1 idc.PatchWord(ea, w1) idc.PatchWord(ea + 2, w2) else: if offset > 0 and offset & 0xFF000000: print 'Offset too far (%08x) Stopping [%08x]' % (offset, ea) dw = (0xFA000000 | (offset - 8 >> 2)) if dw < 0: dw = dw & 0xFAFFFFFF idc.PatchDword(ea, dw) def make_offsets(segname): ''' change the segment's data value into offset by class name ''' segea = idc.SegByBase(idc.SegByName(segname)) segend = idc.SegEnd(segea) while segea < segend: idc.OpOffset(segea, 0) ptr = idc.Dword(segea) idc.OpOffset(ptr, 0) segea += 4 if __name__ == '__main__': print 'Preparing class references segments' make_offsets('__objc_classrefs') #TODO: what's these two segment means? make_offsets('__objc_superrefs') idaapi.analyze_area(idc.MinEA(), idc.MaxEA()) print 'Fixing callgraph' fix_callgraph(idc.LocByName('_objc_msgSend'), 'msgSend', 0, 1) fix_callgraph(idc.LocByName('_objc_msgSendSuper2'), 'msgSendSuper', 3, 1) idaapi.analyze_area(idc.MinEA(), idc.MaxEA()) print 'Done.'
def prev_head(head): prv = idc.PrevHead(head, 0) assert (prv != idc.BADADDR) return prv
#Entry point, look for specific function call for seg_ea in Segments(): for function_ea in Functions(SegStart(seg_ea), SegEnd(seg_ea)): f_start = function_ea f_end = FindFuncEnd(function_ea) for head in Heads(f_start, f_end): if isCode(GetFlags(head)): if GetMnem(head) == 'call': call_addr = GetOperandValue(head, 0) if call_addr == addr_resolve_module: module_name = resolve_module_by_hash( GetOperandValue(idc.PrevHead(head), 1)) if not module_name is None: print "[*] Resolving calls for ", module_name api_hashes = idc.NextHead(head) api_hash_src = GetOperandValue(api_hashes, 1) api_hash_dst = GetOperandValue( idc.NextHead(api_hashes), 1) resolve_module_apis(module_name, api_hash_src, api_hash_dst)
def format_bb(bb): bbtype = {0: "fcb_normal", 1: "fcb_indjump", 2: "fcb_ret", 3: "fcb_cndret", 4: "fcb_noret", 5: "fcb_enoret", 6: "fcb_extern", 7: "fcb_error"} return ("ID: %d, Start: 0x%x, End: 0x%x, Last instruction: 0x%x, Size: %d, " "Type: %s" % (bb.id, bb.startEA, bb.endEA, idc.PrevHead(bb.endEA), (bb.endEA - bb.startEA), bbtype[bb.type]))
def track_param(ea, min_ea, op_type, op_val): ''' trace_param: ea, min_ea, op_type, op_val Taking ea as start, this function does basic backtrace of an operand (defined by op_type and op_val) until it finds a data reference which we consider the "source". It stops when ea < min_ea (usually the function start). It does not support arithmetic or complex modifications of the source. This will be improved on future versions. ''' global msgsend, var_re ea_call = ea while ea != idc.BADADDR and ea != min_ea: ea = idc.PrevHead(ea, min_ea) if idc.GetMnem(ea) not in ['lea', 'mov']: continue if idc.GetOpType(ea, 0) == op_type and idc.GetOperandValue(ea, 0) == op_val: if idc.GetOpType(ea, 1) == idc.o_displ: if ', [esp' in idc.GetDisasm(ea) or ', [ebp' in idc.GetDisasm(ea): if 'arg_' in idc.GetDisasm(ea): # We don't track function arguments return None # We only track stack variables try: var_name = var_re.search(idc.GetDisasm(ea)).group('varname') op_type = idc.GetOpType(ea, 1) except: print '%08x: Unable to recognize variable' % ea return None while ea != idc.BADADDR and ea > min_ea: if idc.GetMnem(ea) == 'mov' or idc.GetMnem(ea) == 'lea' and var_name in idc.GetDisasm(ea): # New reg to track op_val = idc.GetOperandValue(ea, 0) break ea = idc.PrevHead(ea, min_ea) elif idc.GetOpType(ea, 1) == idc.o_mem: # Got the final reference refs = list(idautils.DataRefsFrom(ea)) if not refs: local_ref = idc.GetOperandValue(ea, 1) far_ref = idc.Dword(local_ref) else: while len(refs) > 0: far_ref = refs[0] refs = list(idautils.DataRefsFrom(refs[0])) return far_ref elif idc.GetOpType(ea, 1) == idc.o_reg: # Direct reg-reg assignment op_val = idc.GetOperandValue(ea, 1) op_type = idc.GetOpType(ea, 1) else: # We don't track o_phrase or other complex source operands :( return None return None
def prevMnemonic(ea, mnem, minaddr=0): res = idc.GetMnem(ea) #print "%x -> %s"% (ea, res) if res == "": return idc.BADADDR if res == mnem: return ea return prevMnemonic(idc.PrevHead(ea, minaddr), mnem, minaddr)
def getValue(ea,minEa,targetReg): global errorMessage #f.write("%x" % ea + "\n") #f.write("%x" % minEa + "\n") #f.write(str(targetReg) + "\n") #give up if you hit the top of the function if ea <= minEa: errorMessage+="ERROR: Hit top of function" return 0 if idc.GetMnem(ea) in ['ADR']: dest_op = 0 src_op = 1 srcOpType = idc.GetOpType(ea, src_op) srcOpValue = idc.GetOperandValue(ea, src_op) destOpType = idc.GetOpType(ea, dest_op) destOpValue = idc.GetOperandValue(ea, dest_op) #f.write(str(destOpValue)+"\n") if destOpType == idc.o_reg and destOpValue == targetReg and srcOpType == idc.o_imm: #found goal, so return it as a string return srcOpValue #TODO deal with adding PC or some other number to the address #should be easy with recursion. Just get return the sum of recursively tracking both addends. if idc.GetMnem(ea) in ['ADD']: dest_op = 0 src_op = 1 srcOpType = idc.GetOpType(ea, src_op) srcOpValue = idc.GetOperandValue(ea, src_op) destOpType = idc.GetOpType(ea, dest_op) destOpValue = idc.GetOperandValue(ea, dest_op) #I'm assuming the source is the PC register with a 32 bit executable if destOpType == idc.o_reg and destOpValue == targetReg and srcOpType == idc.o_reg and srcOpValue == 15: pcValue = ea + 4 #f.write("%x" % pcValue + "\n") ea = idc.PrevHead(ea) return pcValue + getValue(ea,minEa,targetReg) #TODO include how to handle this if the src is an address that probably points to a string if idc.GetMnem(ea) in ['MOV']: dest_op = 0 src_op = 1 srcOpType = idc.GetOpType(ea, src_op) srcOpValue = idc.GetOperandValue(ea, src_op) destOpType = idc.GetOpType(ea, dest_op) destOpValue = idc.GetOperandValue(ea, dest_op) if destOpType == idc.o_reg and destOpValue == targetReg: if srcOpType == idc.o_reg: #start tracking a new register. targetReg = srcOpValue ea = idc.PrevHead(ea) return getValue(ea,minEa, targetReg) if srcOpType == idc.o_imm: #this should be the address we are looking for, so return it return srcOpValue if idc.GetMnem(ea) in ['MOVT']: src_op = 1 dest_op = 0 srcOpType = idc.GetOpType(ea, src_op) srcOpValue = idc.GetOperandValue(ea, src_op) destOpType = idc.GetOpType(ea, dest_op) destOpValue = idc.GetOperandValue(ea, dest_op) #Are we writing to a register and is that register the one we are tracking? if destOpType == idc.o_reg and destOpValue == targetReg: if srcOpType == idc.o_imm: #this represents the top half of the value we want #but we need to track the register farther to know what the bottom half is ea = idc.PrevHead(ea) bottomHalf = getValue(ea,minEa,targetReg) #I should have a sanity check here to know when something has gone wrong. #I should refine my error messages to also include addresses, see dispatchExtractor for examples if errorMessage != "": errorMessage += "ERROR: Failed to get value in MOVT." return 0 else: #I hope that I'm correctly overwriting the first 16 bits with the src of MOVT topHalf = srcOpValue << 16 botHalf = bottomHalf & 0x0000ffff afterMOVT = topHalf ^ botHalf return afterMOVT else: errorMessage += "ERROR: cannot handle this type of MOVT." return 0 #This instruction has no impact on the register we are tracking. Skip it. #end of code for MOVT #keep backtracking and move on to previous instruction ea = idc.PrevHead(ea) return getValue(ea,minEa, targetReg)
def check_fmt_function(name, addr): """ Check if the format string argument is not valid """ function_head = GetFunctionAttr(addr, idc.FUNCATTR_START) while True: addr = idc.PrevHead(addr) op = GetMnem(addr).lower() dst = GetOpnd(addr, 0) if op in ("ret", "retn", "jmp", "b") or addr < function_head: return c = GetCommentEx(addr, 0) if c and c.lower() == "format": break elif name.endswith(("snprintf_chk", )): if op in ("mov", "lea") and dst.endswith( ("r8", "r8d", "[esp+10h]")): break elif name.endswith(("sprintf_chk", )): if op in ("mov", "lea") and (dst.endswith( ("rcx", "[esp+0Ch]", "R3")) or dst.endswith("ecx") and BITS == 64): break elif name.endswith(("snprintf", "fnprintf")): if op in ("mov", "lea") and (dst.endswith( ("rdx", "[esp+8]", "R2")) or dst.endswith("edx") and BITS == 64): break elif name.endswith( ("sprintf", "fprintf", "dprintf", "printf_chk")): if op in ("mov", "lea") and (dst.endswith( ("rsi", "[esp+4]", "R1")) or dst.endswith("esi") and BITS == 64): break elif name.endswith("printf"): if op in ("mov", "lea") and (dst.endswith( ("rdi", "[esp]", "R0")) or dst.endswith("edi") and BITS == 64): break # format arg found, check its type and value # get last oprend op_index = GetDisasm(addr).count(",") op_type = GetOpType(addr, op_index) opnd = GetOpnd(addr, op_index) if op_type == o_reg: # format is in register, try to track back and get the source _addr = addr while True: _addr = idc.PrevHead(_addr) _op = GetMnem(_addr).lower() if _op in ("ret", "retn", "jmp", "b") or _addr < function_head: break elif _op in ("mov", "lea", "ldr") and GetOpnd(_addr, 0) == opnd: op_type = GetOpType(_addr, 1) opnd = GetOpnd(_addr, 1) addr = _addr break if op_type == o_imm or op_type == o_mem: # format is a memory address, check if it's in writable segment op_addr = GetOperandValue(addr, op_index) seg = idaapi.getseg(op_addr) if seg: if not seg.perm & idaapi.SEGPERM_WRITE: # format is in read-only segment return print "0x%X: Possible Vulnerability: %s, format = %s" % (addr, name, opnd) return ["0x%X" % addr, name, opnd]
def GetFuncEndAddr(addr): return idc.PrevHead(idc.GetFunctionAttr(addr, idc.FUNCATTR_END))
def find_mov_dword(addr): while True: addr = idc.PrevHead(addr) if idc.GetMnem(addr) == "mov" and "eax" in idc.GetOpnd(addr, 0): #print("Found mov dword at %s" % idc.GetDisasm(addr)) return idc.GetOpnd(addr, 1)
def trace_param(ea, min_ea, op_type, op_val): ''' trace_param: ea, min_ea, op_type, op_val Taking ea as start, this function does basic backtrace of an operand (defined by op_type and op_val) until it finds a data reference which we consider the "source". It stops when ea < min_ea (usually the function start). It does not support arithmetic or complex modifications of the source. This will be improved on future versions. ''' global displ_re, msgsend, var_re ea_call = ea while ea != idc.BADADDR and ea != min_ea: ea = idc.PrevHead(ea, min_ea) if op_type == idaapi.o_reg and op_val == 0 and idaapi.is_call_insn(ea): # We have a BL/BLX that will modify the R0 # we're tracking # return None if idc.GetMnem(ea) in ['LDR', 'MOV']: src_op = 1 dest_op = 0 elif idc.GetMnem(ea) == 'STR': src_op = 0 dest_op = 1 else: continue if idc.GetOpType(ea, dest_op) == op_type and idc.GetOperandValue(ea, dest_op) == op_val: # Found, see where it comes from if idc.GetOpType(ea, src_op) == idc.o_mem: # Got the final reference refs = list(idautils.DataRefsFrom(ea)) if not refs: local_ref = idc.GetOperandValue(ea, src_op) far_ref = idc.Dword(local_ref) else: while len(refs) > 0: far_ref = refs[0] refs = list(idautils.DataRefsFrom(refs[0])) return far_ref elif idc.GetOpType(ea, src_op) == idc.o_displ: if ', [SP' in idc.GetDisasm(ea): if 'arg_' in idc.GetDisasm(ea): # We don't track function arguments return None # We're tracking an stack variable try: var_name = var_re.search(idc.GetDisasm(ea)).group('varname') except: print '%08x: Unable to recognize variable' % ea return None while ea != idc.BADADDR and ea > min_ea: if idc.GetMnem(ea) == 'STR' and var_name in idc.GetDisasm(ea): # New reg to track op_val = idc.GetOperandValue(ea, dest_op) break ea = idc.PrevHead(ea, min_ea) else: # New reg to track if '[LR]' in idc.GetDisasm(ea): # Optimizations use LR as general reg op_val = 14 else: try: op_val = int(displ_re.search(idc.GetDisasm(ea)).group('regnum')) except: print '%08x: Unable to recognize register' % ea return None elif idc.GetOpType(ea, src_op) == idc.o_reg: # Direct reg-reg assignment op_val = idc.GetOperandValue(ea, src_op) else: # We don't track o_phrase or other complex source operands :( return None return None
def get_head(va): if is_head(va): return va else: return idc.PrevHead(va)
out += chr(Byte(addr)) else: break addr += 1 return out def string_decode(string): string = base64.b64decode(string) return string decode_func = 0x401000 total_decode = 0 for addr in XrefsTo(decode_func, flags=0): next_address = addr.frm for i in range(0, 2): next_address = idc.PrevHead(next_address) if GetMnem(next_address) == "mov" and GetOpnd(next_address, 1).find("offset") != -1: String = GetOpnd(next_address, 1).split(" ")[1] add = get_name_ea(next_address, String) String = get_string(add) decode_str = string_decode(String) print_info = str(hex(next_address))[:-1] + " -> " + decode_str print(print_info) MakeComm(next_address, print_info) MakeComm(addr.frm, print_info) total_decode += 1 print "total : " + str(total_decode)
def load(self): for cat in sorted(list(self.parent.call_categories)): self._checkbox_map[cat] = qt.qcheckbox()(cat.capitalize()) for cat in sorted(self._checkbox_map.keys()): cb = self._checkbox_map[cat] cb.setCheckState(qt.qtcore().Qt.Checked) cb.clicked.connect(self.filterCallData) self._checkbox_layout.addWidget(cb) self._call_table.clear() self._call_table.setHorizontalHeaderLabels([ "Category", "Caller", "Parent Caller", "Logged API", "Called API", "Return", "Args" ]) header = self._call_table.horizontalHeader() header.setStretchLastSection(True) if self.parent.cuckoo_version.startswith(("1.3", "2.0")): self._call_table.itemDoubleClicked.connect(self.clickRow) self._call_table.setRowCount(len(self.parent.calls)) self._call_table.setWordWrap(True) row = 0 for call in self.parent.calls: called_api = "" arg_str = "\r\n".join([ "{}: {}".format(k, unicode(v)[:80].encode("unicode-escape")) for k, v in call["arguments"].items() ]) bg_color = self._COLOR_MAP.get(call.get("category", ""), qt.qcolor()(0xff, 0xff, 0xff)) self._call_table.setItem( row, 0, qt.qtablewidgetitem()(call.get("category", ""))) self._call_table.item(row, 0).setBackground(bg_color) call_addr = "" if self.parent.cuckoo_version.startswith("1.3"): call_addr = idc.PrevHead(int(call["caller"], 16)) call_addr = call.get( "caller", "0x00000000" ) if call_addr == idc.BADADDR else "0x{:08x}".format(call_addr) # cuckoo 2.0 stores call stack in "stack", but only enabled in DEBUG if self.parent.cuckoo_version.startswith( "2.0") and call["stacktrace"]: for ret_addr in call["stacktrace"]: if ret_addr.count(" ") > 2: called_api = ret_addr.split("+")[0] else: break for ret_addr in call["stacktrace"]: if ret_addr.count(" ") <= 2: call_addr = int(ret_addr.split(" @ ")[-1], 16) call_addr = idc.PrevHead(call_addr) call_addr = call.get( "caller", "0x00000000" ) if call_addr == idc.BADADDR else "0x{:08x}".format( call_addr) break ret = call["return"] if "return" in call else str( call["return_value"]) self._call_table.setItem(row, 1, qt.qtablewidgetitem()(call_addr)) self._call_table.item(row, 1).setBackground(bg_color) self._call_table.setItem( row, 2, qt.qtablewidgetitem()(call.get("parentcaller", ""))) self._call_table.item(row, 2).setBackground(bg_color) self._call_table.setItem(row, 3, qt.qtablewidgetitem()(call["api"])) self._call_table.item(row, 3).setBackground(bg_color) self._call_table.setItem(row, 4, qt.qtablewidgetitem()(called_api)) self._call_table.item(row, 4).setBackground(bg_color) self._call_table.setItem(row, 5, qt.qtablewidgetitem()(ret)) self._call_table.item(row, 5).setBackground(bg_color) self._call_table.setItem(row, 6, qt.qtablewidgetitem()(arg_str)) self._call_table.item(row, 6).setBackground(bg_color) row += 1 self._call_table.setVisible(False) #self._call_table.resizeRowsToContents() self._call_table.resizeColumnsToContents() self._call_table.setVisible(True) self._call_table.setSortingEnabled(True)
def PrevInstr(ea): return idc.PrevHead(ea, ea-15)
0x62, 0xD8, 0xCA, 0xBE ] table = list(range(256)) while result < 255: v4 += table[result] + key[result % 16] v5 = table[result] while v4 > 0xFF: v4 = key[result % 16] table[result] = table[v4] result += 1 table[v4] = v5 for addr in XrefsTo(decode_func, flags=0): next_address = idc.PrevHead(addr.frm) for i in range(0, 3): next_address = idc.PrevHead(next_address) if GetMnem(next_address) == "push" and GetOpnd(next_address, 0).find("offset") != -1: String = GetOpnd(next_address, 0).split(" ")[1] add_list.append(get_name_ea(next_address, String)) add_list = sorted(add_list) count = 0 result = [] for i in range(len(add_list)): next_address = add_list[i] count = i try:
def get_name(scan_frm, scan_to, start_place): _info(hex(scan_frm), "start scanning: " + start_place) #print hex(scan_frm), "start scanning: " + start_place # track the operand is not likely to be reasonable, so, i parse name of the operands checking_place = start_place poluted_reg_flag = False # if source are `may' polluted reg by function call, set the flag 2 True ea_cur = scan_frm ea_to = scan_to #print hex(ea) while ea_cur != idc.BADADDR and ea_cur != ea_to: ea_cur = idc.PrevHead(ea_cur, ea_to) #print "visiting:", hex(ea_cur), idc.GetDisasm(ea_cur) if idaapi.is_call_insn(ea_cur) and idc.GetOpnd( ea_cur, 0) not in ['_objc_retainAutoreleasedReturnValue']: if poluted_reg_flag == True: # break if there is a call and the checking place is `X0 - X3', for call may pollute the `X0 - X3' reg _info(hex(ea_cur), checking_place + " may pollute, return") return None # i will make a radical predict for all undecided classname (all class contains the same selector will be called) if idc.GetMnem(ea_cur) in ['LDR', 'MOV', 'LDUR', 'ADD']: # more inst?? src_op = 1 dest_op = 0 elif idc.GetMnem(ea_cur) in ['STR', 'STUR']: src_op = 0 dest_op = 1 else: continue # idc.GetOpType(ea_cur, dest_op) == idaapi.o_reg and operands = re.match(".*\s+(.+), ([^;]+)(;\w+)*", idc.GetDisasm(ea_cur)) if operands.group(dest_op + 1) == checking_place: #print hex(ea_cur) if idc.GetOpType(ea_cur, src_op) == idc.o_reg: #print "o_reg" # __text:0000000100006A00 ADD X8, X8, #selRef_class@PAGEOFF if idc.GetMnem(ea_cur) == 'ADD': try: name = re.match(".*#.*Ref_(\w+)@PAGEOFF", idc.GetDisasm(ea_cur)).group(1) return name except: return None # TODO: __text:00000001000069C4 ADD X1, X1, #cfstr_Xx@PAGEOFF ; "xx" # Direct reg-reg assignment _info(hex(ea_cur), checking_place + ' <-- ' + operands.group(src_op + 1)) checking_place = operands.group(src_op + 1) src_reg_val = idc.GetOperandValue(ea_cur, src_op) if src_reg_val >= 129 and src_reg_val <= 132: poluted_reg_flag = True else: poluted_reg_flag = False #print cur_place elif idc.GetOpType(ea_cur, src_op) == idc.o_displ: #print "idc.o_displ" ''' __text:000000010000752C LDR X0, [X28,#classRef_UIView@PAGEOFF] ; void * __text:0000000100007530 ADRP X8, #selRef_alloc@PAGE __text:0000000100007534 LDR X24, [X8,#selRef_alloc@PAGEOFF] __text:0000000100007538 STR X24, [SP,#0x100+var_A0] __text:000000010000753C MOV X1, X24 ; char * __text:0000000100007540 BL _objc_msgSend [UIView alloc], there is no implementation in the binary at all, point to the impementation in framework ''' ''' __text:00000001000076EC LDR X8, [X19,X27] __text:00000001000076F0 CBNZ X8, loc_100007AA4 __text:00000001000076F4 ADRP X8, #classRef_UILabel@PAGE __text:00000001000076F8 LDR X0, [X8,#classRef_UILabel@PAGEOFF] ; void * __text:00000001000076FC LDR X1, [SP,#0x100+var_A0] ; char * value of X1 comes from stack, it's a [Base Reg + Index Reg + Displacement] mode LDR X0, [X27,#classRef_ESInterface@PAGEOFF] ; void * STR X26, [SP,#0x100+var_A8] the below 2 blocks are the same for a selector assignment __text:00000001000069D8 ADRP X8, #selRef_setFillColor_@PAGE __text:00000001000069DC ADD X8, X8, #selRef_setFillColor_@PAGEOFF __text:00000001000069E0 LDUR X0, [X29,#var_20] __text:00000001000069E4 LDR X1, [X8] ; "setFillColor:" __text:00000001000069E8 MOV X2, X9 __text:00000001000069EC BL _objc_msgSend __text:00000001000069A4 ADRP X0, #selRef_setBounds_@PAGE __text:00000001000069A8 LDR X0, [X0,#selRef_setBounds_@PAGEOFF] __text:00000001000069AC LDUR X1, [X29,#var_40+8] __text:00000001000069B0 LDUR X30, [X29,#var_40] __text:00000001000069B4 STR X0, [SP,#0x90+var_50] __text:00000001000069B8 MOV X0, X8 ; void * __text:00000001000069BC LDR X8, [SP,#0x90+var_50] __text:00000001000069C0 STR X1, [SP,#0x90+var_58] __text:00000001000069C4 MOV X1, X8 ; char * __text:00000001000069C8 MOV X2, X30 __text:00000001000069CC LDR X3, [SP,#0x90+var_58] __text:00000001000069D0 BL _objc_msgSend __text:00000001000069FC ADRP X8, #selRef_class@PAGE __text:0000000100006A00 ADD X8, X8, #selRef_class@PAGEOFF __text:0000000100006A04 ADRP X0, #classRef_AppDelegate@PAGE __text:0000000100006A08 ADD X0, X0, #classRef_AppDelegate@PAGEOFF __text:0000000100006A0C LDUR W9, [X29,#var_28] __text:0000000100006A10 LDUR X1, [X29,#var_30] __text:0000000100006A14 LDR X0, [X0] ; _OBJC_CLASS_$_AppDelegate ; void * __text:0000000100006A18 LDR X8, [X8] ; "class" __text:0000000100006A1C STR X1, [SP,#0x90+var_60] __text:0000000100006A20 MOV X1, X8 ; char * __text:0000000100006A24 STR W9, [SP,#0x90+var_64] __text:0000000100006A28 BL _objc_msgSend ''' poluted_reg_flag = False try: name = re.match(".*#.*Ref_(\w+)@PAGEOFF", idc.GetDisasm(ea_cur)).group(1) return name except: #print idc.GetString(idc.GetOperandValue(ea_cur, 1), -1, idc.ASCSTR_C) _info( hex(ea_cur), checking_place + ' <-- ' + operands.group(src_op + 1)) checking_place = operands.group(src_op + 1) ''' __text:0000000100006A00 ADD X8, X8, #selRef_class@PAGEOFF ... __text:0000000100006A18 LDR X8, [X8] ; "class" ''' oprand = idc.GetOpnd(ea_cur, 1) if len(oprand) <= 5 and oprand.startswith( '[') and oprand.endswith(']'): checking_place = oprand[1:len(oprand) - 1] #print checking_place elif idc.GetOpType( ea_cur, src_op) == idc.o_phrase: # can find nothing at all #print "idc.o_phrase" _info(hex(ea_cur), checking_place + ' <-- ' + operands.group(src_op + 1)) checking_place = operands.group(src_op + 1) elif idc.GetOpType(ea_cur, src_op) == ida_ua.o_imm: return None else: # elif idc.GetOpType(ea_cur, src_op) == idc.o_mem: # print "o_mem" print hex(ea_cur), idc.GetDisasm( ea_cur), "<----------unknown type operand", # return exit(0) # ea_cur = idc.GetOperandValue(ea_cur, dest_op) if checking_place == "X0": # X0 point to `self' # -[ESUISendCell createSubviewsAndinitLayout] try: self_name = re.match("-\[(\w+) \w+", idc.get_func_name(ea_cur)).group(1) return self_name except: print '%08x: Unable to solve `self`' % ea_cur return None else: _info(hex(scan_frm), "reaching the start pos of the func") return None
def find(from_ea, target_obj, is_recur=True, is_bwd=False, is_opposite=False, is_trace=True): def get_opnd_idx(is_writing): return 1 if is_writing else 0 def get_opposite_opnd_idx(opnd_idx): assert opnd_idx in [0, 1] return 1 - opnd_idx def is_matched(ea, opnd_idx, target_obj): WRITE_MNEM = ['mov', 'lea', 'imul', 'sub'] # TBU obj = idaobj.IdaObj.from_opnd(ea, opnd_idx) ''' if obj == target_obj: print obj, '|', target_obj ''' return (idc.GetMnem(ea) in WRITE_MNEM) and (obj == target_obj) is_fwd = not is_bwd adjusted_ea = idc.NextHead(from_ea) if is_fwd else idc.PrevHead(from_ea) bb = idcnew.GetBB(adjusted_ea) # from_ea exclusive start_ea = idc.NextHead(from_ea) if is_fwd else bb.startEA end_ea = bb.endEA if is_fwd else from_ea heads = idautils.Heads(start_ea, end_ea) if is_bwd: heads = reversed(list(heads)) # is_fwd == is_writing self_opnd_idx = get_opnd_idx(is_fwd) if is_opposite: self_opnd_idx = get_opposite_opnd_idx(self_opnd_idx) other_opnd_idx = get_opposite_opnd_idx(self_opnd_idx) until_cond = lambda ea: is_matched(ea, other_opnd_idx, target_obj) find_cond = lambda ea: is_matched(ea, self_opnd_idx, target_obj) make_obj = lambda ea: idaobj.IdaObj.from_opnd(ea, other_opnd_idx) trace_cond = lambda ea: idcnew.GetOpType(ea, other_opnd_idx) == idc.o_reg trace = lambda ea: find(ea, make_obj(ea), is_recur, is_bwd, is_opposite, is_trace) for ea in heads: # TODO: What about xor rcx, rcx? if until_cond(ea): return None if find_cond(ea): if is_trace and trace_cond(ea): return trace(ea) else: return make_obj(ea) if is_recur: # TODO: IDAPython bug - bb.preds() always returns empty list. if is_bwd: raise NotImplementedError next_bbs = bb.succs() if is_fwd else bb.preds() # not deterministic if len(list(next_bbs)) != 1: return None next_from_ea = idcnew.PrevHead( next_bbs[0].startEA) if is_fwd else next_bbs[0].endEA return find(next_from_ea, target_obj, is_recur, is_bwd, is_opposite, is_trace) else: return None
def check_next_opnd(head, call_arguments): opnd = idc.GetOpnd(head, 1) prev_head = idc.PrevHead(head) heads = get_heads_to_address(prev_head) return check_operand(opnd, call_arguments, heads)
def _parseJsonFile(self, json_data): _process_data = {} self.parent.cuckoo_version = json_data["info"].get("version", "Unknown") self.parent.call_categories = set() for proc in json_data["behavior"]["processes"]: ppid = proc["ppid"] if self.parent.cuckoo_version.startswith("2.0") else proc["parent_id"] pid = proc["pid"] if self.parent.cuckoo_version.startswith("2.0") else proc["process_id"] _process_data[pid] = {"process_name": proc["process_name"], "parent_id": ppid, "imports": [], "network": [], "calls": [] } handles = {} exe_name = idc.GetInputFile().split(".")[0] for call in proc["calls"]: self.parent.call_categories.add(call["category"]) if self.parent.cuckoo_version.startswith("1."): args = dict([(x["name"], x["value"]) for x in call["arguments"]]) call["arguments"] = args else: args = call["arguments"] _process_data[pid]["calls"].append(call) if call["api"] in ["LdrGetDllHandle", "LdrLoadDll"]: if self.parent.cuckoo_version.startswith("1."): if call["api"] == "LdrGetDllHandle" and args["ModuleHandle"] != "0x00000000": handles[args["ModuleHandle"]] = args["FileName"] elif call["api"] == "LdrLoadDll" and call["return"] == "0x00000000": handles[args["BaseAddress"]] = args["FileName"] # cuckoo 2.0 else: if call["api"] == "LdrGetDllHandle" and args["module_address"] != "0x00000000": handles[args["module_address"]] = args["module_name"] elif call["api"] == "LdrLoadDll" and call["return_value"] == 0: handles[args["module_address"]] = args["module_name"] elif call["api"] == "LdrGetProcedureAddress": impt_type = "Indirect" if args.get("FunctionName", None): addr = idc.PrevHead(int(call["caller"], 16)) if addr != idc.BADADDR: # if the call is direct to a register or stack variable # assume that this is intentional obfuscation of GetProcAddress if idc.GetMnem(addr) == "call" and \ (re.match("^e[abcds][ipx]$", idc.GetOpnd(addr, 0)) \ or idc.GetOpnd(addr, 0).endswith("GetProcAddress") \ or idc.GetOpnd(addr, 0).startswith(("ds:dword", "dword ptr", "[e"))): impt_type = "Dynamic" _process_data[pid]["imports"].append({"addr": "0x{:08X}".format(addr), "dll": handles.get(args["ModuleHandle"], args["ModuleName"]), "proc_name": args["FunctionName"], "proc_address": args["FunctionAddress"], "type": impt_type } ) elif args.get("function_name") and call.get("stacktrace", []): addr = idc.BADADDR if call["stacktrace"][0].count(" ") in [0,2]: impt_type = "Dynamic" elif len(call["stacktrace"]) > 1: for i in range(1, len(call["stacktrace"])): cur = call["stacktrace"][i] prev = call["stacktrace"][i-1] if prev.startswith("GetProcAddress") and cur.count(" ") in [0, 2]: impt_type = "Dynamic" addr = idc.PrevHead(int(cur.split(" @ ")[-1], 16)) break else: addr = int(cur.split(" @ ")[-1], 16) addr = idc.PrevHead(addr) if idc.PrevHead(addr) != idc.BADADDR else addr else: for frm in call["stacktrace"]: if frm.count(" ") in [0, 2]: addr = idc.PrevHead(int(frm.split(" @ ")[-1], 16)) break """ # handle case where injected code below ImageBase so don"t get exe_name prepended if call["stacktrace"][0].startswith("GetProcAddress") and \ call["stacktrace"][0].count(" ") == 2 and (call["stacktrace"][1].startswith(exe_name) or \ call["stacktrace"][0].count(" ") == 0): impt_type = "Dynamic" elif call["stacktrace"][0].startswith(exe_name): impt_type = "Dynamic" addr = idc.PrevHead(int(call["stacktrace"][0].split(" @ ")[-1], 16)) """ _process_data[pid]["imports"].append({ "addr": "0x{:08X}".format(addr), "dll": handles.get(args["module_address"], args["module_address"]), "proc_name": args["function_name"], "proc_address": args["function_address"], "type": impt_type } ) self.parent.signatures = json_data["signatures"] self.parent.process_data = _process_data self.parent.process_tree = json_data["behavior"]["processtree"] del json_data
import idaapi #获取指定函数中所有指令地址的集合,FuncItems返回的是迭代器,可以强制转换为list dism_addr = list(idautils.FuncItems(here())) print type(dism_addr) print dism_addr for line in dism_addr: print hex(line), idc.GetDisasm(line) #获取下一个指令的地址 idc.NextHead(ea) #获取上一条指令的地址 idc.PrevHead(ea) #获取下一个地址 idc.NextAddr(ea) #获取上一个地址 idc.PrevAddr(ea) #遍历所有的动态调用 for func in idautils.Functions(): flags = idc.GetFunctionFlags(func) if flags & FUNC_LIB or flags & FUNC_THUNK: continue dism_addr = list(idautils.FuncItems(func)) for line in dism_addr: m = idc.GetMnem(line)