diff --git a/ibt.py b/ibt.py index 179478c..321cb17 100644 --- a/ibt.py +++ b/ibt.py @@ -1,10 +1,10 @@ +import opxref import idautils -from idaapi import * -import idc - +import idaapi +import idc class IdaBackTracer: - send_api = ["WSASendTo","Send","SendTo"] + send_api = ["WSASendTo","WSASend","send","sendto"] registers=['eax', 'ebx', 'ecx', 'edx', 'esi', 'edi', 'esp', 'ebp'] def __init__(self): @@ -15,6 +15,8 @@ def __init__(self): def get_func_args_cmnt(adr): args_type = [] num_args = GetFrameArgsSize(adr) / 4 + if not list(CodeRefsTo(adr, 1)): #check whether list of refernces is not empty + return address = list(CodeRefsTo(adr, 1))[0] arguments_counter = 0 @@ -23,61 +25,94 @@ def get_func_args_cmnt(adr): if mn == 'push': arguments_counter += 1 cmnt = Comment(address) - args_type.append(cmnt) + if cmnt is not None: #check whether any comment exists + args_type.append(cmnt) if arguments_counter == num_args: return args_type address = PrevHead(address,minea=0) def trace_reg(self, adr, reg): + print 'start',hex(adr),reg start = GetFunctionAttr(adr, FUNCATTR_START) end = GetFunctionAttr(adr, FUNCATTR_END) func_args = self.get_func_args_cmnt(start) - print func_args address = PrevHead(adr, minea=0) if adr == start: - return None - + return None while start <= address <= end: + op1 = GetOpnd(address,0) + op2 = GetOpnd(address,1) + r1 = re.search('([[])([a-z]+)([-+][0-9a-zA-Z_]+)([]])',op1) # remove dword ptr for operand 1 + r2 = re.search('([[])([a-z]+)([-+][0-9a-zA-Z_]+)([]])',op2) # remove dword ptr for operand 2 + if r1: + op1 = r1.group(0) + if r2: + op2 = r2.group(0) mn = GetMnem(address) if mn in ['mov', 'movsx', 'movzx', 'xchg', 'lea']: - op1 = GetOpnd(address,0) - op2 = GetOpnd(address,1) - idaapi.decode_insn(address) if idaapi.cmd.Op2.type == idaapi.o_displ: next_reg = op2[1:4] if 'bp' in op2 and reg in op1: op_2 = op2[5:-1] print '%s: %s %s -> %s' % (hex(address),mn,op1,op_2) - for s in func_args: - if op_2.lower() in s.lower(): + if func_args is not None: + Arg_info = opxref.ArgRef(address,1) + # Arg_info ---> count,[list of refernces] + if Arg_info[1]: print '%s found in arguments of sub_%s' % (op_2,format(start, 'x')) - list_xref = list(CodeRefsTo(start, 1)) - index = func_args.index(s) + 1 - buffer_arg = self.get_arg(list_xref[0], index) - print 'send buffer is %d arg of sub_%s : %s' % (index, format(list_xref[0], 'x'), - idc.GetDisasm(buffer_arg)) - return self.trace_reg(buffer_arg,GetOpnd(buffer_arg, 0)) - return self.trace_reg(address,op_2) + for xref_i in CodeRefsTo(start, 1): + buffer_reg=self.get_arg(xref_i,Arg_info[0]) + if buffer_reg: + print 'send buffer is %d arg of sub_%s : %s' % (Arg_info[0], format(xref_i,'x'), idc.GetDisasm(buffer_reg)) + self.trace_reg(buffer_reg,GetOpnd(buffer_reg,0)) + else: + return self.trace_reg(address,op_2) + else: + return self.trace_reg(address,op_2) elif next_reg in self.registers and reg in op1: print '%s: %s %s -> %s' % (hex(address),mn,op1,op2) return self.trace_reg(address,next_reg) - else: + elif idaapi.cmd.Op2.type == idaapi.o_reg or idaapi.cmd.Op2.type == idaapi.o_mem or idaapi.cmd.Op2.type == idaapi.o_phrase: if reg in op1: + print '%s: %s %s -> %s' % (hex(address),mn,op1,op2) if idaapi.o_reg is idaapi.cmd.Op2.type and 'eax' in GetOpnd(address,1): - has_call, c, adr = self.has_call_inst(address,0) + has_call, c, call_adr = self.has_call_inst(address,0) if has_call: print '%s found as a candidate for DS initialization %d instructions after %s' % ( GetFunctionName(GetOperandValue(address,0)), c, idc.GetDisasm(address)) - if self.check_init(GetOperandValue(adr,0)): - print '%s contains pointer to a heap allocated memory region %s' % ( + if self.check_init(GetOperandValue(call_adr,0)): + test_adr = address + changed_value = False + # check whether before current instrction the value of eax is not changed. + while call_adr < test_adr <= address: + if GetOpnd(test_adr,0) == 'eax': + if GetMnem(test_adr) in ['mov', 'movsx', 'movzx', 'xchg', 'lea']: + changed_value = True + test_adr = PrevHead(test_adr,minea=0) + if not changed_value: + print '%s contains pointer to a heap allocated memory region %s' % ( GetOpnd(address,1) , GetDisasm(address)) - - print '%s: %s %s -> %s' % (hex(address),mn,op1,op2) + # when the return value of function is from eax, then finish the back trace + return return self.trace_reg(address,op2) - + #if all instructions traced back but don't exist any mov instruction + elif start == address: + if func_args is not None: + if not op2: + Arg_info = opxref.ArgRef(adr,0) + else: + Arg_info = opxref.ArgRef(adr,1) + if Arg_info[1]: + print '**%s found in arguments of sub_%s' % (reg,format(start,'x')) + for xref_i in CodeRefsTo(start, 1): + buffer_arg=self.get_arg(xref_i,Arg_info[0]) + if buffer_arg: + print 'send buffer is %d arg of sub_%s : %s' % (Arg_info[0], format(xref_i,'x'), idc.GetDisasm(buffer_arg)) + self.trace_reg(buffer_arg,GetOpnd(buffer_arg,0)) + address=PrevHead(address,minea=0) @staticmethod @@ -96,11 +131,13 @@ def has_call_inst(address, count): ''' @staticmethod - def traverseCalls(adr): - print 'entering into %s' % GetFunctionName(adr) + def has_heap_alloc(adr): + print 'entering into %s' % Name(adr) print 'searching for heap_alloc calls inside' flags=GetFunctionFlags(adr) + if flags == -1: + return None, False start=GetFunctionAttr(adr,FUNCATTR_START) end=GetFunctionAttr(adr,FUNCATTR_END) @@ -109,7 +146,7 @@ def traverseCalls(adr): #ignore library functions if flags & idaapi.FUNC_THUNK or flags & idaapi.FUNC_LIB: - return + return None , False #get list all ea's of current function routine disasm_addr = list(idautils.FuncItems(adr)) @@ -125,7 +162,7 @@ def traverseCalls(adr): if op_flags & idaapi.FUNC_LIB: name = Name(op_addr) - if name in ('GetProcessHeap','HeapAlloc','LocalHeap'): + if name in ('GetProcessHeap','HeapAlloc','LocalAlloc'): print 'Heap allocation routine found at %s' % GetFunctionName(ea) heap_found=True call_list.append(name) @@ -136,9 +173,11 @@ def traverseCalls(adr): return call_list, heap_found def check_init(self, adr): - call_list, heap_flag = self.traverseCalls(adr) + call_list, heap_flag = self.has_heap_alloc(adr) if heap_flag: return True + if call_list is None: + return False for funcName in call_list: funcAddr = LocByName(funcName) return self.check_init(funcAddr) @@ -148,7 +187,8 @@ def check_init(self, adr): def get_arg(address, argument_number): # It traces back maximum 10 instructions - argument_counter = 0 + argument_counter = 0 + other_funcs_argsize = 0 if GetMnem(address) != 'call': return None @@ -157,11 +197,43 @@ def get_arg(address, argument_number): if GetMnem(address) == 'push': argument_counter += 1 - + if argument_counter == argument_number: return address return None + + #find buf member in _WSABUF struct + def wsa_buf_finder(self,address): + start = GetFunctionAttr(address, FUNCATTR_START) + end = GetFunctionAttr(address, FUNCATTR_END) + arg_adr = self.get_arg(address, 2) + reg = GetOpnd(arg_adr,0) + adr = PrevHead(arg_adr, minea=0) + while start <= adr <= end: + op1 = GetOpnd(adr,0) + op2 = GetOpnd(adr,1) + if GetMnem(adr) in ['mov', 'movsx', 'movzx', 'xchg', 'lea']: + idaapi.decode_insn(adr) + if idaapi.cmd.Op2.type == idaapi.o_displ: + if re.search('[e]*[bs][p]',op2) and reg == op1: + op = GetOpnd(adr,1) + rn = re.search('([0-9A-F]+)',op) + value = GetOperandValue(adr,1) + base = value - int (rn.group(0),16) + refs = opxref.OpXref(adr,1) + for ref in refs: + for i in range(2): + if re.search('[e]*[bs][p]',GetOpnd(ref,i)):# [bp,ebp,sp,esp] + op = GetOpnd(ref,i) + rn = re.search('([0-9A-F]+)',op) + value = GetOperandValue(ref,i) + offset = value - int (rn.group(0),16) + # second member of _WSABUF struct is char* buf + if abs(int(offset) - int(base)) == 4: + return ref,GetOpnd(ref,1) + + adr = PrevHead(adr, minea=start) def main(): ibt = IdaBackTracer() @@ -173,19 +245,21 @@ def main(): for ibt.api, ref in ibt.xrefs.iteritems(): for address in list(ref): - if ibt.api == "WSASendTo": - arg_adr = ibt.get_arg(address, 2) + if ibt.api == "WSASendTo" or ibt.api == "WSASend": + print hex(address) + arg_adr , reg = ibt.wsa_buf_finder(address) print idc.GetDisasm(address) print idc.GetDisasm(arg_adr) - print GetOpnd(arg_adr, 0) - # TODO: Add trace function for none reg arguments like push 0, push [eax], push [0x40000000] - if GetOpnd(arg_adr, 0) in ibt.registers: - ibt.trace_reg(arg_adr, GetOpnd(arg_adr, 0)) - #print '%d st occurance of %s in %s : %s'%(count[ibt.api], ibt.api, hex(adr),idc.GetDisasm(adr)) - #print 'send buffer is %d arg of %s : %s' % (2, format(buffer,'%x'), idc.GetDisasm(buffer)) - #ibt.trace_reg(buffer,GetOpnd(buffer, 0)) + if reg in ibt.registers: + ibt.trace_reg(arg_adr, reg) + else: # "send,sendto" + print idc.GetDisasm(address) + arg_adr = ibt.get_arg(address, 2) + print idc.GetDisasm(arg_adr) + print GetOpnd(arg_adr,0) + ibt.trace_reg(arg_adr, GetOpnd(arg_adr,0)) if __name__ == "__main__": main() diff --git a/opxref.py b/opxref.py new file mode 100644 index 0000000..cb8d9e4 --- /dev/null +++ b/opxref.py @@ -0,0 +1,205 @@ +import idautils +from idaapi import* +from idc import * + +xrefs=[] + +def getStack(address): + stackFrame = GetFrame(address) + lastEntry = GetLastMember(stackFrame) + zero = GetFrameLvarSize(address) + local_var = False + count =0 + stack =[] + while count <= lastEntry: + localName = GetMemberName(stackFrame,count) + size = GetMemberSize(stackFrame, count) + STRID = GetMemberStrId(stackFrame, count) + flag = GetMemberFlag(stackFrame,count) + offset = GetMemberOffset(stackFrame, localName) + if localName ==None or size ==None or flag ==-1: + count +=1 + continue + if localName == ' r': + local_var = True + count +=1 + continue + if local_var == False: + stack.append((localName,STRID,-(zero-offset),-(zero-offset),-(zero-offset)+size)) + if STRID != -1: + last = GetLastMember(STRID) + offs = 0 + while offs <= last: + mem_Name = GetMemberName(STRID,offs) + stack.append((mem_Name,STRID,-(zero-offset)+ offs,-(zero-offset),-(zero-offset)+size)) + offs = GetStrucNextOff(STRID, offs) + else: + stack.append((localName,STRID,offset-zero,offset-zero,offset-zero+size)) + if STRID != -1: + last = GetLastMember(STRID) + offs = 0 + while offs <= last: + mem_Name = GetMemberName(STRID,offs) + stack.append((mem_Name,STRID,offs+offset-zero,offset-zero,offset-zero+size)) + offs = GetStrucNextOff(STRID, offs) + count+=size + #stack (name,structID,offset_in_stack,start_struct,end_struct) + return stack +def search_xrefs(address,reg,offset,start,end): #search all instructions in the function + disasm_addr = list(idautils.FuncItems(address)) + for ea in disasm_addr: + op1 = GetOpnd(ea,0) + op2 = GetOpnd(ea,1) + r1 = re.search('([[])([a-z]+)([-+][0-9a-zA-Z_.]+)([-+][0-9a-zA-Z_.]+)([]])',op1) # [ebp+80h+Buffers] operand 1 + r2 = re.search('([[])([a-z]+)([-+][0-9a-zA-Z_.]+)([-+][0-9a-zA-Z_.]+)([]])',op2) # [ebp+80h+Buffers] operand 2 + if r1: + rn = re.search('([0-9A-F]+)',op1) + value = GetOperandValue(ea,0) + offs = value - int (rn.group(0),16) + if start <= offs < end and reg == r1.group(0)[1:4]: + xrefs.append(ea) + elif r2: + rn = re.search('([0-9A-F]+)',op2) + value = GetOperandValue(ea,1) + offs = value - int (rn.group(0),16) + if start<= offs < end and reg == r2.group(0)[1:4]: + xrefs.append(ea) + + else: + alt_offset = offset + idaapi.op_dec(ea,1) + idaapi.op_dec(ea,0) + op1 = GetOpnd(ea,0) + r1 = re.search('([a-z]+)([-+][0-9a-fx]+)',op1) # remove dword ptr for operand 1 + if r1: + op_displ = r1.group(0) # dword ptr [ebp+8]--->ebp+8 + OpStkvar(ea,0) + else: + op2 = GetOpnd(ea,1) + r2 = re.search('([a-z]+)([-+][0-9a-fx]+)',op2) #remove dword ptr for operand 2 + if r2: + op_displ = r2.group(0) + OpStkvar(ea,1) + else: + continue + if offset>0: + while start <= alt_offset < end: + if op_displ == reg+'+'+str(alt_offset): + xrefs.append(ea) + break + alt_offset+=1 + elif offset<0: + while start <= alt_offset < end: + if op_displ == reg+str(alt_offset): + xrefs.append(ea) + break + alt_offset+=1 + return xrefs +def OpXref(address,n): + del xrefs[:] + if n == 0 or n == 1: + op = GetOpnd(address,n) + r = re.search('([[])([a-z]+)([-+][0-9a-zA-Z_.]+)([-+][0-9a-zA-Z_.]+)([]])',op) # [ebp+80h+Buffers] + if r: + reg = r.group(0)[1:4] + rn = re.search('([0-9A-F]+)',op) + value = GetOperandValue(address,n) + offs = value - int (rn.group(0),16) + stack = getStack(address) + for s in stack:#find offset in the stack + if s[2]!= 0: + if s[3] <= offs < s[4]: #ebp+8 + ID = s[1] + search_xrefs(address,reg,s[2],s[3],s[4]) + if ID != -1: #all members of a structure + for st in stack: + if st[1] == ID: + if st[3]!= s[3]: + search_xrefs(address,reg,st[2],st[3],st[4]) + break + else: + idaapi.op_dec(address,n) + op = GetOpnd(address,n) + OpStkvar(address,n) + r = re.search('([[])([a-z]+)([-+][0-9a-fx]+)([]])',op) # remove word ptr and etc. + if r: + Op = r.group(0) + reg=Op[1:4] + sign = Op[4] + offs=Op[5:-1] + stack = getStack(address) + for s in stack: + #find offset in the stack + neg_test = s[2]<0 and sign =='-' and -s[3] >= int(offs) > -s[4] #ebp-8 + pos_test = s[2]>0 and sign =='+' and s[3] <= int(offs) < s[4] #ebp+8 + if neg_test or pos_test: + ID = s[1] + search_xrefs(address,reg,s[2],s[3],s[4]) + if ID != -1: #all members of a structure + for st in stack: + if st[1] == ID: + if st[3]!= s[3]: + search_xrefs(address,reg,st[2],st[3],st[4]) + break + + return xrefs + + +def ArgRef(address,n): # if operand #n in address is a function argument shows it's index and references + del xrefs[:] + if n == 0 or n == 1: + op = GetOpnd(address,n) + count = 0 + r = re.search('([[])([a-z]+)([-+][0-9a-zA-Z_.]+)([-+][0-9a-zA-Z_.]+)([]])',op) # [ebp+80h+Buffers] + if r: + reg = r.group(0)[1:4] + rn = re.search('([0-9A-F]+)',op) + value = GetOperandValue(address,n) + offs = value - int (rn.group(0),16) + stack = getStack(address) + for s in stack: + #find offset in the stack + if s[2] ==0: + count = 0 + elif s[2]>0: + count+=1 + if s[3] <= offs < s[4]: #ebp+8 + ID = s[1] + search_xrefs(address,reg,s[2],s[3],s[4]) + if ID != -1: #all members of a structure + for st in stack: + if st[1] == ID: + if st[3]!= s[3]: + search_xrefs(address,reg,st[2],st[3],st[4]) + break + else: + idaapi.op_dec(address,n) + op = GetOpnd(address,n) + OpStkvar(address,n) + count = 0 + r = re.search('([[])([a-z]+)([-+][0-9a-fx]+)([]])',op) # remove word ptr and etc. + if r: + Op = r.group(0) + reg=Op[1:4] + sign = Op[4] + offs=Op[5:-1] + stack = getStack(address) + for s in stack: + #find offset in the stack + if s[2] == 0: + count = 0 + elif s[2]>0 and sign == '+': + count+=1 + if s[3] <= int(offs) < s[4]: #ebp+8 + ID = s[1] + search_xrefs(address,reg,s[2],s[3],s[4]) + if ID != -1: #all members of a structure + for st in stack: + if st[1] == ID: + if st[3]!= s[3]: + search_xrefs(address,reg,st[2],st[3],st[4]) + break + + + return count, xrefs +