千家信息网

利用IDA Python静态分析函数调用路径

发表于:2026-10-31 作者:千家信息网编辑
千家信息网最后更新 2026年10月31日,在挖掘设备的固件漏洞时,会面临没有源代码、无法动态跟踪调试的情况,此时就需要进行静态的人工分析。在静态人工分析过程中,往往需要围绕危险函数、用户输入提取需要重点分析的执行路径,以有效缩小分析范围。本文
千家信息网最后更新 2026年10月31日利用IDA Python静态分析函数调用路径

在挖掘设备的固件漏洞时,会面临没有源代码、无法动态跟踪调试的情况,此时就需要进行静态的人工分析。在静态人工分析过程中,往往需要围绕危险函数、用户输入提取需要重点分析的执行路径,以有效缩小分析范围。本文利用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 0xx" % 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;另一处是指定的文件,文件路径是硬编码的,各位看官可自行修改。暂时没有以插件方式实现,有兴趣的同学可以尝试下。

函数 代码 递归 分析 参数 路径 深度 方法 问题 强制 静态 危险 人工 功能 情况 文件 文本 用户 设备 跟踪 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网高科技 互联网新科技 互联网最新的科技 润盈互联网科技 掌上梦互联网科技 深圳期待互联网科技 成都狮子座互联网科技 中国互联网金融科技基金 盛创互联网科技有限公司 互联网大会黑科技排名 互联网科技项目计划书材料类 成都铂石互联网科技做什么的 互联网金融中的现代科技 宝灵互联网科技有限公司 中国航天科技大学国际工业互联网 2021互联网科技公司排名 中国科技和美国互联网 北京军民互联网科技公司 互联网科技创新方面的资料 小牛互联网科技有限公司地址 互联网金融监管科技数据 朗涯互联网科技 佰商互联网科技有限公司 昭通互联网科技选哪家 东莞新华互联网科技学校 威龙国际互联网科技公司 互联网科技公司营业执照怎么办理 山东星卡互联网科技有限公司官网 广州有谷有贝互联网科技有限公司 科技发展第三阶段互联网时代
0