From 9e8554c6f8a1af6314e98f6deb73f9927aa0f6df Mon Sep 17 00:00:00 2001 From: shin3r Date: Sun, 23 Apr 2017 00:01:50 -0700 Subject: [PATCH 1/7] this module find xrefs to/from stack variables --> e.g. [ebp+Buffer], [ebp+arg_0] --- opxref.py | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 opxref.py diff --git a/opxref.py b/opxref.py new file mode 100644 index 0000000..7efa657 --- /dev/null +++ b/opxref.py @@ -0,0 +1,107 @@ +import idautils +from idaapi import * +import idc +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): + disasm_addr = list(idautils.FuncItems(address)) + for ea in disasm_addr: + offset = start + idaapi.op_dec(ea,1) + idaapi.op_dec(ea,0) + op1 = GetOpnd(ea,0) + r1 = re.search('([a-z]+)([-+][0-9a-fx]+)',op1) + 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) + if r2: + op_displ = r2.group(0) + OpStkvar(ea,1) + else: + continue + if offset>0: + while offset < end: + if op_displ == reg+'+'+str(offset): + xrefs.append(hex(ea)) + break + offset+=1 + elif offset<0: + while offset < end: + if op_displ == reg+str(offset): + xrefs.append(hex(ea)) + break + offset+=1 + return xrefs +def OpXref(address,n): + del xrefs[:] + if n == 0 or n == 1: + 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 + + From b19ab40eb04ddb49bafaeecd2c3fe9f27b0ed2c7 Mon Sep 17 00:00:00 2001 From: shin3r Date: Tue, 25 Apr 2017 01:02:25 -0700 Subject: [PATCH 2/7] add function to find refernces to arguments (only) and the index of argument --- opxref.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/opxref.py b/opxref.py index 7efa657..880679e 100644 --- a/opxref.py +++ b/opxref.py @@ -105,3 +105,34 @@ def OpXref(address,n): 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: + 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 From e765d1d11f7bf33d5eef93207b45fedf4e8d5006 Mon Sep 17 00:00:00 2001 From: shin3r Date: Tue, 25 Apr 2017 11:16:55 -0700 Subject: [PATCH 3/7] identify argument by searching offset in stack not by searching string. ---> if op_2.lower() in s.lower(): modified --- ibt.py | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/ibt.py b/ibt.py index 179478c..3c6c3ae 100644 --- a/ibt.py +++ b/ibt.py @@ -1,7 +1,7 @@ import idautils from idaapi import * import idc - +import opxref class IdaBackTracer: send_api = ["WSASendTo","Send","SendTo"] @@ -33,7 +33,6 @@ def trace_reg(self, 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 @@ -50,16 +49,16 @@ def trace_reg(self, adr, reg): 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 = 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]) + 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) 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) @@ -69,6 +68,7 @@ def trace_reg(self, adr, reg): 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) if has_call: + print '%s: %s %s -> %s' % (hex(address),mn,op1,op2) 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)): @@ -77,7 +77,18 @@ def trace_reg(self, adr, reg): print '%s: %s %s -> %s' % (hex(address),mn,op1,op2) 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: + for s in func_args: + Arg_info = ArgRef(address,1) + if Arg_info[1]: + print '%s found in arguments of sub_%s' % (value,format(start,'x')) + for xref_i in CodeRefsTo(start, 1): + buffer_arg=self.get_arg(xref_i,Arg_info[0]) + 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 From 30ef8ff8459346bcb496e8d42d24c570f98360aa Mon Sep 17 00:00:00 2001 From: shin3r Date: Thu, 27 Apr 2017 23:26:35 -0700 Subject: [PATCH 4/7] find refernces to displacements such as [ebp+3Ch+arg_0] --- opxref.py | 227 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 149 insertions(+), 78 deletions(-) diff --git a/opxref.py b/opxref.py index 880679e..315e372 100644 --- a/opxref.py +++ b/opxref.py @@ -1,7 +1,9 @@ import idautils -from idaapi import * -import idc +from idaapi import* +from idc import * + xrefs=[] + def getStack(address): stackFrame = GetFrame(address) lastEntry = GetLastMember(stackFrame) @@ -43,64 +45,104 @@ def getStack(address): count+=size #stack (name,structID,offset_in_stack,start_struct,end_struct) return stack -def search_xrefs(address,reg,offset,start,end): +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: - offset = start - idaapi.op_dec(ea,1) - idaapi.op_dec(ea,0) op1 = GetOpnd(ea,0) - r1 = re.search('([a-z]+)([-+][0-9a-fx]+)',op1) + 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: - op_displ = r1.group(0) # dword ptr [ebp+8]--->ebp+8 - OpStkvar(ea,0) + rn = re.search('([0-9A-F]+)',op1) + value = GetOperandValue(ea,0) + offs = value - int (rn.group(0),16) + if offs == offset and reg == r1.group(0)[1:4]: + xrefs.append(hex(ea)) + elif r2: + rn = re.search('([0-9A-F]+)',op2) + value = GetOperandValue(ea,1) + print 'val',value, 'rn',int (rn.group(0),16) + offs = value - int (rn.group(0),16) + print offs,offset,reg,r2.group(0)[1:4] + if offs == offset and reg == r2.group(0)[1:4]: + xrefs.append(hex(ea)) + else: - op2 = GetOpnd(ea,1) - r2 = re.search('([a-z]+)([-+][0-9a-fx]+)',op2) - if r2: - op_displ = r2.group(0) - OpStkvar(ea,1) + 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: - continue - if offset>0: - while offset < end: - if op_displ == reg+'+'+str(offset): - xrefs.append(hex(ea)) - break - offset+=1 - elif offset<0: - while offset < end: - if op_displ == reg+str(offset): - xrefs.append(hex(ea)) - break - offset+=1 + 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(hex(ea)) + break + alt_offset+=1 + elif offset<0: + while start <= alt_offset < end: + if op_displ == reg+str(alt_offset): + xrefs.append(hex(ea)) + break + alt_offset+=1 return xrefs def OpXref(address,n): del xrefs[:] if n == 0 or n == 1: - 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 + 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 @@ -108,31 +150,60 @@ def OpXref(address,n): 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: - 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 + 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 + + + From 36ef084d5271afd2f739d2861736b13bfab72d95 Mon Sep 17 00:00:00 2001 From: shin3r Date: Fri, 28 Apr 2017 15:59:18 -0700 Subject: [PATCH 5/7] fix some problems and add some conditions to work fine program in special cases --- ibt.py | 51 +++++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/ibt.py b/ibt.py index 3c6c3ae..5a66038 100644 --- a/ibt.py +++ b/ibt.py @@ -1,7 +1,7 @@ +import opxref import idautils -from idaapi import * -import idc -import opxref +import idaapi +import idc class IdaBackTracer: send_api = ["WSASendTo","Send","SendTo"] @@ -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,7 +25,8 @@ 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 @@ -35,14 +38,12 @@ def trace_reg(self, adr, reg): func_args = self.get_func_args_cmnt(start) address = PrevHead(adr, minea=0) if adr == start: - return None - + return None while start <= address <= end: + op1 = GetOpnd(address,0) + op2 = GetOpnd(address,1) 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] @@ -50,7 +51,7 @@ def trace_reg(self, adr, reg): op_2 = op2[5:-1] print '%s: %s %s -> %s' % (hex(address),mn,op1,op_2) if func_args is not None: - Arg_info = ArgRef(address,1) # Arg_info ---> count,[list of refernces] + 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')) for xref_i in CodeRefsTo(start, 1): @@ -77,17 +78,19 @@ def trace_reg(self, adr, reg): print '%s: %s %s -> %s' % (hex(address),mn,op1,op2) return self.trace_reg(address,op2) - #if all instructions traced back but don't exist any mov instruction + #if all instructions traced back but don't exist any mov instruction elif start == address: if func_args is not None: - for s in func_args: - Arg_info = ArgRef(address,1) - if Arg_info[1]: - print '%s found in arguments of sub_%s' % (value,format(start,'x')) - for xref_i in CodeRefsTo(start, 1): - buffer_arg=self.get_arg(xref_i,Arg_info[0]) - 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)) + 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]) + 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) @@ -107,11 +110,13 @@ def has_call_inst(address, count): ''' @staticmethod - def traverseCalls(adr): + def has_heap_alloc(adr): print 'entering into %s' % GetFunctionName(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) @@ -136,7 +141,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) @@ -147,9 +152,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) From a5f9fa35da3dcb0d529693f80031b281fe9de1e5 Mon Sep 17 00:00:00 2001 From: shin3r Date: Sat, 6 May 2017 00:05:19 -0700 Subject: [PATCH 6/7] add condition to find all references to such displacements: [esp+2Ch+Buffers.buf] --- opxref.py | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/opxref.py b/opxref.py index 315e372..cb8d9e4 100644 --- a/opxref.py +++ b/opxref.py @@ -50,22 +50,20 @@ def search_xrefs(address,reg,offset,start,end): #search all instructions in the 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 + 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 offs == offset and reg == r1.group(0)[1:4]: - xrefs.append(hex(ea)) + 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) - print 'val',value, 'rn',int (rn.group(0),16) offs = value - int (rn.group(0),16) - print offs,offset,reg,r2.group(0)[1:4] - if offs == offset and reg == r2.group(0)[1:4]: - xrefs.append(hex(ea)) + if start<= offs < end and reg == r2.group(0)[1:4]: + xrefs.append(ea) else: alt_offset = offset @@ -87,13 +85,13 @@ def search_xrefs(address,reg,offset,start,end): #search all instructions in the if offset>0: while start <= alt_offset < end: if op_displ == reg+'+'+str(alt_offset): - xrefs.append(hex(ea)) + xrefs.append(ea) break alt_offset+=1 elif offset<0: while start <= alt_offset < end: if op_displ == reg+str(alt_offset): - xrefs.append(hex(ea)) + xrefs.append(ea) break alt_offset+=1 return xrefs @@ -101,7 +99,7 @@ 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] + 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) @@ -152,7 +150,7 @@ def ArgRef(address,n): # if operand #n in address is a function argument shows i 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] + 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) @@ -203,7 +201,5 @@ def ArgRef(address,n): # if operand #n in address is a function argument shows i break - return count, xrefs - - + return count, xrefs From 1efabf72d246d7a5925536c575a1fb601614e174 Mon Sep 17 00:00:00 2001 From: shin3r Date: Sat, 6 May 2017 00:09:34 -0700 Subject: [PATCH 7/7] add wsa_buf_finder function to find address of Buffer.buf member in _WSABUF struct > in case that eax is the output of a near function that allocates a heap, check whether before current instrction the value of eax is not changed. > remove dword ptr prefix from operands --- ibt.py | 110 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 83 insertions(+), 27 deletions(-) diff --git a/ibt.py b/ibt.py index 5a66038..321cb17 100644 --- a/ibt.py +++ b/ibt.py @@ -4,7 +4,7 @@ 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): @@ -33,6 +33,7 @@ def get_func_args_cmnt(adr): 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) @@ -42,6 +43,12 @@ def trace_reg(self, adr, reg): 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']: idaapi.decode_insn(address) @@ -51,32 +58,45 @@ def trace_reg(self, adr, reg): op_2 = op2[5:-1] print '%s: %s %s -> %s' % (hex(address),mn,op1,op_2) if func_args is not None: - Arg_info = opxref.ArgRef(address,1) # Arg_info ---> count,[list of refernces] + 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')) for xref_i in CodeRefsTo(start, 1): buffer_reg=self.get_arg(xref_i,Arg_info[0]) - 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)) + 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: %s %s -> %s' % (hex(address),mn,op1,op2) 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: @@ -89,9 +109,10 @@ def trace_reg(self, adr, reg): 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]) - 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)) - + 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 @@ -111,7 +132,7 @@ def has_call_inst(address, count): @staticmethod def has_heap_alloc(adr): - print 'entering into %s' % GetFunctionName(adr) + print 'entering into %s' % Name(adr) print 'searching for heap_alloc calls inside' flags=GetFunctionFlags(adr) @@ -125,7 +146,7 @@ def has_heap_alloc(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)) @@ -166,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 @@ -175,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() @@ -191,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()