其他
使用IDA Python寻找二进制漏洞
因为这不是一个新的方法(Halvar Flake在2001年做过关于IDA脚本自动分析漏洞的研究),但令人惊讶的是,这个话题没有被更多的说明。这可能是因为在现代操作系统上想要利用漏洞日渐复杂困难。然而,这对于自动化部分的漏洞研究还是有价值的。
这里,我们选择了简单漏洞中的一个栈溢出漏洞,其可由strcpy函数导致将用户可以控制的字符串拷贝到栈缓存区中。既然我们知道了我们要寻找什么,我们可以开始考虑如何取自动化寻找这种漏洞。
使用IDAPython API很容易做到这一点。使用如下的代码片段来打印二进制文件中所有的函数名:
print(GetFunctionName(functionAddr))
if “strcpy” in GetFunctionName(functionAddr):
print hex(functionAddr)
首先我们要获取strcpy交叉引用的地方,然后我们要检查这其中的每个地方是否真正调用了strcpy函数。总结一下就有下面的代码:
# Check each function to look for strcpy
if "strcpy" in GetFunctionName(functionAddr):
xrefs = CodeRefsTo(functionAddr, False)
# Iterate over each cross-reference
for xref in xrefs:
# Check to see if this cross-reference is a function call
if GetMnem(xref).lower() == "call":
print hex(xref)
第一种方法是依赖IDA自动分析后对已知函数的注释。上图中,IDA已经自动检测到了_strcpy函数的dest参数并标记。
# Get the start address of the function that we are in
function_head = GetFunctionAttr(addr, idc.FUNCATTR_START)
steps = 0
arg_count = 0
# It is unlikely the arguments are 100 instructions away, include this as a safety check
while steps < 100:
steps = steps + 1
# Get the previous instruction
addr = idc.PrevHead(addr)
# Get the name of the previous instruction
op = GetMnem(addr).lower()
# Check to ensure that we haven’t reached anything that breaks sequential code flow
if op in ("ret", "retn", "jmp", "b") or addr < function_head:
return
if op == "push":
arg_count = arg_count + 1
if arg_count == arg_num:
# Return the operand that was pushed to the stack
return GetOpnd(addr, 0)
# Assume opnd is “eax”
# Find the start address of the function that we are searching in
function_head = GetFunctionAttr(_addr, idc.FUNCATTR_START)
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 == "lea" and GetOpnd(_addr, 0) == opnd:
# We found the destination buffer, check to see if it is in the stack
if is_stack_buffer(_addr, 1):
print "STACK BUFFER STRCOPY FOUND at 0x%X" % addr
break
# If we detect that the register that we are trying to locate comes from some other register
# then we update our loop to begin looking for the source of the data in that other register
elif _op == "mov" and GetOpnd(_addr, 0) == opnd:
op_type = GetOpType(_addr, 1)
if op_type == o_reg:
opnd = GetOpnd(_addr, 1)
addr = _addr
else:
break
lea ebx [ebp-0x24]
...
mov eax, ebx
...
push eax
...
inst = DecodeInstruction(addr)
return get_stkvar(inst[idx], inst[idx].addr) != None
inst = DecodeInstruction(addr)
return get_stkvar(inst[idx], inst[idx].addr) != None
def find_arg(addr, arg_num):
# Get the start address of the function that we are in
function_head = GetFunctionAttr(addr, idc.FUNCATTR_START)
steps = 0
arg_count = 0
# It is unlikely the arguments are 100 instructions away, include this as a safety check
while steps < 100:
steps = steps + 1
# Get the previous instruction
addr = idc.PrevHead(addr)
# Get the name of the previous instruction
op = GetMnem(addr).lower()
# Check to ensure that we havent reached anything that breaks sequential code flow
if op in ("ret", "retn", "jmp", "b") or addr < function_head:
return
if op == "push":
arg_count = arg_count + 1
if arg_count == arg_num:
#Return the operand that was pushed to the stack
return GetOpnd(addr, 0)
for functionAddr in Functions():
# Check each function to look for strcpy
if "strcpy" in GetFunctionName(functionAddr):
xrefs = CodeRefsTo(functionAddr, False)
# Iterate over each cross-reference
for xref in xrefs:
# Check to see if this cross-reference is a function call
if GetMnem(xref).lower() == "call":
# Since the dest is the first argument of strcpy
opnd = find_arg(xref, 1)
function_head = GetFunctionAttr(xref, idc.FUNCATTR_START)
addr = xref
_addr = xref
while True:
_addr = idc.PrevHead(_addr)
_op = GetMnem(_addr).lower()
if _op in ("ret", "retn", "jmp", "b") or _addr < function_head:
break
elif _op == "lea" and GetOpnd(_addr, 0) == opnd:
# We found the destination buffer, check to see if it is in the stack
if is_stack_buffer(_addr, 1):
print "STACK BUFFER STRCOPY FOUND at 0x%X" % addr break
# If we detect that the register that we are trying to locate comes from some other register
# then we update our loop to begin looking for the source of the data in that other register
elif _op == "mov" and GetOpnd(_addr, 0) == opnd:
op_type = GetOpType(_addr, 1)
if op_type == o_reg:
opnd = GetOpnd(_addr, 1)
addr = _addr
else:
break
看雪ID:微笑明天
https://bbs.pediy.com/user-821225.htm