Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nayer #13

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
160 changes: 117 additions & 43 deletions ibt.py
Original file line number Diff line number Diff line change
@@ -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):
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)

Expand All @@ -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))
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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

Expand All @@ -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()
Expand All @@ -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()
Loading