博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
利用IDA Python静态分析函数调用路径
阅读量:7136 次
发布时间:2019-06-28

本文共 5309 字,大约阅读时间需要 17 分钟。

在挖掘设备的固件漏洞时,会面临没有源代码、无法动态跟踪调试的情况,此时就需要进行静态的人工分析。在静态人工分析过程中,往往需要围绕危险函数、用户输入提取需要重点分析的执行路径,以有效缩小分析范围。本文利用IDA Python脚本,实现了自动提取函数正、反向调用关系的功能,可有效辅助分析危险函数调用路径,用户输入流向等。

一、问题描述

近期在研究某款设备,由于该设备使用MIPS架构,IDA Pro的F5无法使用,安装的RetDec插件也不给力,给出的伪代码不忍直视;还有不少代码IDA Pro没有识别出来,且无法识别库函数;此外,固件使用传统的嵌入式操作系统(非类Linux)实现,不存在进程等高级概念,因此也没有解决动态调试的问题。在这种情况下,似乎只能人工静态分析了。

进行初步分析之后,发现需要先解决两个问题:一是将IDA pro无法识别的代码强制转换成代码,以完善IDA pro的交叉引用关系;二是以文本方式提取函数调用关系(正向、反向),取IDA pro的Xrefs graph to和Xrefs graphp from功能,IDA pro的这两个功能对稍微复杂一定的文件根本不存在实用性——显示的图形根本看不清。

二、强制转换未解析的代码

针对第一个问题,考虑到MIPS的指令均为32bit,可利用IDAPython遍历指定的地址空间,把未定义的部分全部转换成代码。具体的代码如下:

def define_func(beg, end):

cur = begif beg%4 != 0:    cur = beg + 4 - beg%4 # 对齐end = end - end%4while cur < end:    if ida_kernwin.user_cancelled():        print('Cancelled')        break    cur_func = ida_funcs.get_func(cur)    print("cur 0x%08x" % cur)    if cur_func is None:        if ida_funcs.add_func(cur):            cur = ida_funcs.get_func(cur).endEA        else:            cur = cur + 4    else:        cur = cur_func.endEA

使用时步骤如下:

1、按shift+f2,在Execute script窗口中,Script language选择Python

2、把上述代码粘贴到Please enter script body中

3、点击Run,关闭Execute script窗口。

4、在Output window下方的Python【IDC】按钮右侧,执行define_func(0x8000000,0x80002000),参数仅作示例,根据实际情况调整

三、获取函数调用树

1、正向调用树
正向调用树以指定函数为起点,根据指定递归深度,获取其所有子函数,通常应用于跟踪用户输入数据的流向。实现思路如下:遍历指定函数(由参数指定)代码,如果当前指令为函数调用,则递归,直到达到递归深度或者没有子函数的函数。具体代码如下:

import idautils

call_chain = [] # 存放正向调用链信息

def gen_call_chain(func_name, osintneting):

del call_chain[:]f_call_out = open('d:\\call.csv', 'w')get_my_callee(func_name, osintneting, f_call_out)f_call_out.close()

def get_my_callee(func_name, osintneting, fl):

#print('call %s %d' % (func_name, osintneting))if ida_kernwin.user_cancelled():    print('Cancelled')    fl.close()    exit()str = '{0}\t'.format(func_name)call_chain.append(str)addr = get_name_ea(0, func_name)# 获取所有子函数dism_addr = list(idautils.FuncItems(addr))xref_froms = []for ea in dism_addr:    if ida_idp.is_call_insn(ea) is False:        continue    else:        callee = get_first_fcref_from(ea)        if callee != addr:            xref_froms.append(callee)xref_froms = set(xref_froms)# 嵌套结束条件osinteneting_end = Falseif len(xref_froms) == 0:    osinteneting_end = Trueelif osintneting == -1:    osinteneting_end = Falseelif osintneting == 1:    osinteneting_end = Trueif osinteneting_end is True:    for callee in call_chain:        sys.stdout.write(callee)        fl.write(callee)    sys.stdout.write('\r\n')    fl.write('\r\n')    call_chain.pop()    return# 深度优先for xref_from in xref_froms:    callee_name = get_func_name(xref_from)    if osintneting == -1:        get_my_callee(callee_name, -1, fl)    else:        get_my_callee(callee_name, osintneting - 1, fl)call_chain.pop()

使用方法参照“强制转换未解析的代码”一节中的方法,调用gen_call_chain函数即可。gen_call_chain函数的第一个参数是函数名,第二参数是递归的次数限制,如果为-1,则会一直递归到叶子函数(无子函数的函数)。在生成调用树时,每条调用路径对应一行文本,在IDA pro的Output window的输出如下

Python>gen_call_chain('start', 5)

start sub_4010E0 sub_400DD0 sub_401B40 sub_401B80

start sub_4010E0 sub_400DD0 sub_401B40 sub_401A00

start sub_4010E0 sub_400DD0 sub_444750 sub_472EB0

start sub_4010E0 sub_400DD0 sub_444750 sub_43F8C0

start sub_4010E0 sub_400DD0 sub_444750 sub_472FE0

start sub_4010E0 sub_400DD0 sub_444750 sub_43F920

start sub_4010E0 sub_400DD0 sub_40EFF0 sub_40EE10

2、反向调用树

反向调用树以指定函数为起点,根据指定递归深度,获取其所有父函数,通常应用于跟踪危险函数被调用的路径。实现思路如下:先获取引用指定函数(由参数指定)的函数,然后依次递归,直到达到递归深度或者没有父函数的函数。具体代码如下:

import idautils

r_call_chain = [] # 存放反向调用链信息

def gen_r_call_chain(func_name, osintneting):

del r_call_chain[:]f_r_call_out = open('d:\\r_call.csv', 'w')get_my_caller(func_name, osintneting, f_r_call_out)f_r_call_out.close()

def get_my_caller(func_name, osintneting, fl):

if ida_kernwin.user_cancelled():    print('Cancelled')    fl.close()    exit()str = '{0}\t'.format(func_name)r_call_chain.append(str)addr = get_name_ea(0, func_name)addr_ref_to = get_first_fcref_to(addr)# 嵌套结束条件 osinteneting_end = Falseif addr_ref_to == BADADDR:    osinteneting_end = Trueelif osintneting == -1:    osinteneting_end = Falseelif osintneting == 1:    osinteneting_end = Trueif osinteneting_end is True:    length = len(r_call_chain)    for idx in range(length):        fl.write(r_call_chain[length - idx - 1])        sys.stdout.write(r_call_chain[length - idx - 1])    fl.write("\n")    sys.stdout.write('\r\n')    r_call_chain.pop()    return# 深度优先while (addr_ref_to != BADADDR) and (addr_ref_to != addr):    parent_func_name = get_func_name(addr_ref_to)    get_my_caller(parent_func_name, osintneting - 1, fl)    addr_ref_to = get_next_fcref_to(addr, addr_ref_to)    if addr_ref_to == BADADDR:        r_call_chain.pop() # 如果没有引用函数,弹出当前函数        break

使用方法参照“强制转换未解析的代码”一节中的方法,调用gen_r_call_chain函数即可。gen_r_call_chain函数的第一个参数是函数名,第二参数是递归的次数限制,如果为-1,则会一直递归到顶层函数(无父函数的函数)。在生成调用树时,每条调用路径对应一行文本,在IDA pro的Output window的输出如下:

Python>gen_r_call_chain('sub_4432E0', 5)

start sub_4010E0 sub_400DD0 sub_4432E0

sub_47D3F0 sub_4767D0 sub_474770 sub_4432E0

sub_496370 sub_4767D0 sub_474770 sub_4432E0

sub_480930 sub_47D020 sub_4432E0

sub_48C450 sub_47D020 sub_4432E0

sub_499C60 sub_47D020 sub_4432E0

sub_480930 sub_47D020 sub_4432E0

sub_48C450 sub_47D020 sub_4432E0

四、小结

通过上述IDAPython脚本,可方便获取指定函数的调用树。调用树的输出有两处,一处IDA pro的Output window;另一处是指定的文件,文件路径是硬编码的,各位看官可自行修改。暂时没有以插件方式实现,有兴趣的同学可以尝试下。

转载于:https://blog.51cto.com/watertoeast/2287039

你可能感兴趣的文章
结构体运算符(->)
查看>>
VMware虚拟机磁盘压缩
查看>>
Windows Phone SDK 7.1 中文版本发布
查看>>
使用Python中HTTPParser模块进行简单的html解析
查看>>
学习之路四:各种异步操作我也来山寨一下 → 思维导图
查看>>
Application review: Northwind Starter Kit
查看>>
Ubuntu下gedit的相关问题
查看>>
Oracle单行函数
查看>>
Qt中如何 编写插件 加载插件 卸载插件
查看>>
完美兼容IE、Opera、Firefox、360、搜狗的“添加收藏”的最精简代码
查看>>
MapReduce PLinq 简单示例
查看>>
NYOJ 26
查看>>
体验版:在百度搜索"2012世界末日"所展现的地震效果的源代码(嵌入到我的博客里)...
查看>>
[原创]桓泽学音频编解码(10):AAC 无损解码模块算法分析
查看>>
[原创]桓泽学音频编解码(13):AC3 位分配模块算法分析
查看>>
C# Stream 和 byte[] 之间的转换
查看>>
/usr/bin/ld: i386:x86-64 architecture of input file `command.o' is incompatible with i386 output
查看>>
C#开源汇总
查看>>
linux和windows中设置环境变量经常使用命令
查看>>
Hibernate(1)——数据访问层的架构模式<转>
查看>>