IDA脚本系列_NO.001-历遍一个函数及其子函数,查找是否调用过一个API函数或函数

发布于:2023-10-25 ⋅ 阅读:(101) ⋅ 点赞:(0)

用过IDA的朋友,是否用过IDA自带的函数调用跟踪功能,右击一个函数的,点击"List Cross References From"菜单,会弹出一个窗口,列出了这个程序中调用这个函数的所有引用,这个功能确实不错,但是有点太简单。比如,我在调试一个QT5的程序时,遇到了这样一种情况,软件使用时,明显感到点击一个按钮时,会有一个10s的延时,如果我们想修改这个延时时长,我们势必要找到延时函数的地址。具体来说,假定我们通过字符串定位到了某个主函数,但是这个主函数极其复杂,我们单单使用上面的"List Cross References From"功能,我们根本很难定位到延时函数,因为引用实在太多了,一个个人工查找太麻烦,而且你也不知道主函数中到底时哪层子函数调用了延时函数api,这时,我想到了自己编一个python版的ida脚本。

使用方法也很简单,首先定位到主函数起始地址,然后按快捷键ALT + F7

选择我的python脚本 

 在弹出的对话框中,输入需要查找的StartTimer函数(_ZN13QElapsedTimer5startEv)

 可以看到左下角的IDA消息窗口显示找到两个StartTimer函数(_ZN13QElapsedTimer5startEv)

在调试的程序(QT5)目录中,我们也可以详细看到查找到的函数地址信息和函数的调用堆栈情况。 

因为这两个记录文件都是追加写入,如需重新记录,请删除这两个文件。 

完整代码如下,请保存为.py文件,并用上面的方法打开(ALT + F7):

import idautils
import idaapi
import idc
import ida_kernwin
import sys
import json
from datetime import datetime

target_api = ""
FunLevel = 0
FunLinkStrArr = []

def find_calls_to_api(func_addr,FunLevel,SourceFunName,FunLinkStr):
    #函数所在层级+1
    FunLevel += 1 
    
    # 获取当前函数的名称
    func_name = idaapi.get_func_name(func_addr)

    # 打印当前函数名称
    print(f"正在分析第{FunLevel}层函数: {func_name}({func_addr})")

    #赋值函数链
    FunLinkStr = FunLinkStr + "\n" + "====>" + "第" + str(FunLevel) + "层函数:" + func_name + "(" + str(func_addr) + ")"

    # 遍历函数中的所有指令
    for ea in idautils.FuncItems(func_addr):      
        # 获取当前指令的Mnem(操作码)字符串
        mnem = idaapi.print_insn_mnem(ea)

        
        # 检查是否调用了目标API函数
        if mnem == "call":
            # 获取指令的操作数
            operand = idc.print_operand(ea,0)
            
            # 获取调用目标的操作数
            target_operand = idc.get_operand_value(ea, 0)
                
            # 获取目标操作数所引用的函数名称
            target_function_name = idaapi.get_name(target_operand)
            # 出问题时请取消下一句的代码注释,以方便调试
            #print("funname: " + target_function_name)

            global target_api    
            if target_function_name == target_api:
                print(f"=========已找到目标函数{target_api}=========")
                print(f"******在第{FunLevel}层函数({func_name}): 0x{func_addr:X} 中调用了目标API函数 {target_api},地址:0x{ea:X}******")
                print("=" * (len("=========已找到目标函数{target_api}=========") + 25))

                with open(target_function_name + ".txt","a+")as f:
                    f.write("在第" + str(FunLevel) + "层函数(" + func_name + "): " + hex(func_addr) + " 中调用了目标API函数 " + target_api + ",地址:" + hex(ea) + "\n")
                    f.close()

                #赋值函数链"
                FunLogOut = FunLinkStr + "\n" + "====>" + "第" + str(FunLevel) + "层函数" + "(" + func_name + ")中调用了目标API函数" + target_function_name + " (address:" + hex(ea) + ")为API函数,不进入分析!"
                FunLogOut = FunLogOut + "\n" + "============================================================================================================================="
                if FunLogOut not in FunLinkStrArr:
                    FunLinkStrArr.append(FunLogOut)
                    with open(target_function_name + datetime.now().strftime("%Y%m%d")+ ".txt","a+")as f:
                        f.write(FunLogOut)
                        f.close()
                FunLogOut = ""
                    
                
                
            func_ea = idaapi.get_name_ea(idaapi.NT_NONE, target_function_name)

            if func_ea != None:
                if operand.find("sub") == 0:
                    #出问题时请取消下一句的代码注释,以方便调试
                    #print("进入子函数: " + target_function_name)

                    find_calls_to_api(func_ea,FunLevel,SourceFunName,FunLinkStr)
            else:
                print("动态操作码,无法静态调试!请使用动态调试如X64")

    print(f"第{FunLevel}层函数{func_name}:分析结束!")
    #分析函数结束,所在层级-1
    FunLevel -= 1
    FunLinkStr = ""


def main():
    # 目标API函数的名称
    global target_api
    target_api =  ida_kernwin.ask_str("",0,"输入call后的函数名")#"sub_45DFC0"

    if target_api == "":
        print("未输入任何数据,已退出!")
        
    else:
        print(f"输入的函数是:{target_api}")

    # 获取当前屏幕地址
    current_ea = idaapi.get_screen_ea()

    # 获取当前屏幕地址所在的函数的起始地址
    func_addr = idaapi.get_func(current_ea).start_ea

    # 查找当前函数以及其所有子函数是否调用了目标API函数
    SourceFunName = ""
    FunLinkStr = ""
    find_calls_to_api(func_addr,FunLevel,SourceFunName,FunLinkStr)
    print("程序运行完毕!")

if __name__=="__main__":
    main()

本文含有隐藏内容,请 开通VIP 后查看

网站公告


今日签到

点亮在社区的每一天
去签到