找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

619

积分

0

好友

75

主题
发表于 4 天前 | 查看: 15| 回复: 0

本篇聚焦于去除Native代码中的间接跳转(indirect jump)和间接函数调用(indirect call)混淆。这类混淆经常被大型厂商的APP采用,其目的是显著增加静态分析的难度和时间成本。

间接跳转与间接调用的本质是隐藏静态控制流:在静态分析时,你无法直接得知某条跳转或调用指令最终会执行哪个目标地址。最直接的解决思路是让程序实际运行一次,记录运行时真实的跳转/调用目标,然后将这些目标地址“修补”回二进制文件中,从而恢复出可读的控制流。

本文将介绍两种简单、高效且在实践中可行的“暴力”修复思路。它们无需搭建完整的模拟环境,也无需进行复杂的符号化执行:

  • 方法一:暴力NOP替换(速度极快,但会丢失分支信息)
  • 方法二:利用Frida批量Hook(结果更精确,但依赖程序的实际执行路径)

如果你对间接跳转与间接调用的具体实现原理感兴趣,可以参考以下几个OLLVM混淆相关的开源项目:
https://github.com/KomiMoe/Arkari
https://github.com/amimo/goron
https://github.com/DreamSoule/ollvm17

方法一:暴力NOP替换

思路概述

对于那些只需要还原“大体执行逻辑”,而不关心精确条件分支行为的场景,我们可以采取一种最直接的方式:将函数内部所有的 BR/BLR(或类似的间接跳转/调用指令)直接替换为 NOP(空操作)指令,然后合并相邻的基本块。

这样一来,静态反编译器(如IDA)会把原本被混淆打散的控制流重新线性化,从而生成一份便于快速阅读的伪代码,帮助分析师理解大致的函数调用关系与整体数据流。

适用场景

  • 需要快速定位关键的函数调用或数据访问点,对分支还原的精确度要求不高。
  • 分析时间紧迫,希望先获得一份可读的伪代码框架,以便指导后续的深入调查。

优缺点

  • 优点:操作极其快捷,无需运行环境或动态跟踪。
  • 缺点:彻底破坏了原始的控制流结构,所有条件分支(如if/while)信息都将丢失,导致反编译器无法恢复任何分支语义。

具体操作流程

  1. 在IDA中,将光标移动到目标函数的入口地址。
  2. 运行IDC脚本 fix_blr_2_nop.py。该脚本会执行以下操作:
    • 从函数入口开始向后扫描,直到遇到 RET 指令或函数结束。
    • 列出该扫描区间内所有 BR 指令的地址。
    • 将这些指令批量 PatchNOP,并尝试触发IDA合并基本块。
  3. 按下 F5 刷新反编译视图,观察线性化后的伪代码,并继续人工分析。

操作示例

下图展示了一个被混淆函数在IDA反汇编视图中的样子,其中包含了大量的间接跳转指令(如 BR X8)。

IDA反汇编视图展示被混淆函数中的间接跳转指令

运行上述脚本并合并基本块后,原本破碎的控制流被线性连接。此时再查看反编译伪代码,虽然分支逻辑已丢失,但函数的主要调用链和数据流变得清晰可见,便于快速把握函数骨架。下图展示了修复后的效果,可以看到其中仍然存在一些受保护的间接函数调用,这些可以留给后续方法处理。

修复后反编译伪代码显示线性化的调用链

方法二:使用Frida批量Hook(更精确)

思路概述

这种方法分为静态准备和动态收集两步。首先通过静态分析,定位目标函数或模块中所有可能的间接跳转/调用指令(BRBLR等)。然后,自动生成一个 Frida Hook脚本,在目标程序运行时拦截这些指令,并捕获它们实际执行时的目标地址(通常记录为相对于模块基址的偏移)。

将运行时采集到的目标地址日志导出后,我们再使用IDA Python脚本解析日志,并将二进制文件中原来的间接跳转/调用指令,Patch 为指向具体目标地址的直接跳转/调用指令,从而尽可能精确地恢复出原始控制流。

优缺点

  • 优点:能够恢复程序实际执行过的绝大多数跳转和调用,得到的控制流图更加准确。
  • 缺点:无法覆盖未被执行到的代码路径;如果某个条件分支在本次运行中未被触发,其对应的跳转也无法修复;该方法需要目标程序能够在特定环境中运行并加载待分析的SO库。

具体操作流程

  1. 静态定位目标指令:在IDA中使用脚本扫描待修复的函数或模块,记录所有 BR/BLR 指令的地址。
  2. 自动生成Frida脚本:按 Alt+F7 运行脚本 toolchain_trace_indirect_jumps.py,它会根据上一步的扫描结果,生成一个包含了所有待Hook地址的Frida JavaScript脚本。
  3. 调整脚本:将生成的脚本中的模块名称修改为你实际要分析的SO名称,并确认日志输出格式(建议包含模块偏移、指令地址、寄存器值等关键信息)。
  4. 启动与注入:以spawn模式启动被测应用并注入Frida脚本(spawn模式有助于在进程早期就捕获到初始化流程)。
    • 使用 frida -U -f com.example.app -l script.js 命令来启动并注入。
    • 操作应用,确保执行流程经过那些被混淆的间接跳转/调用位置。
  5. 保存日志:将Frida控制台输出的地址信息保存为文本文件。
  6. 应用补丁:在IDA中运行补丁脚本 fix_indirect_jump.py(或自行编写的脚本),该脚本会解析日志文件,并将对应的间接指令替换为直接指令。
  7. 验证结果:刷新反编译视图(F5),查看修复后的控制流,并进行人工验证。

操作流程截图示例

1. 修复前的控制流与伪代码
下图展示了一个被混淆函数在修复前的反编译伪代码,控制流混乱不堪。

修复前混乱的反编译伪代码

2. 预处理(可选)
可以先用方法一的脚本进行快速NOP替换,获得一个初步的线性化视图,这有助于理解函数结构。

使用暴力NOP进行预处理后的代码

3. 生成Frida Hook脚本
运行生成脚本后,会得到一个JS文件,其中列出了需要Hook的地址。

生成Frida Hook脚本的日志输出

4. 注入并运行Hook脚本
修改脚本中的模块名,然后以spawn模式注入。下图展示了Hook脚本的核心代码,它通过拦截 android_dlopen_ext 在目标SO加载时自动安装Hook。

Frida Hook脚本代码,用于拦截库加载

注入成功后,Frida会输出捕获到的间接跳转实际目标地址。

Frida运行输出,显示捕获的跳转地址对

5. 解析日志并应用补丁
将Frida的输出保存为日志文件,运行IDA补丁脚本。脚本会读取日志,并将对应的 BL 指令修补为指向具体地址。

IDA补丁脚本运行日志,显示修补过程

6. 查看修复结果
补丁应用成功后,刷新伪代码。可以看到,之前难以理解的间接调用(如 sub_303484)已经被替换为清晰的直接函数调用(如 sub_49E8E8),控制流逻辑一目了然。

修复后清晰的反编译伪代码

小提示与实践建议

  • 组合使用:可以先使用方法一快速线性化整个函数,得到一个可读的框架,再针对其中残留的关键间接调用使用方法二进行精修。
  • 日志格式:确保Frida Hook脚本输出的日志包含模块相对偏移,这对于准确定位和修补至关重要。
  • 路径覆盖:对于未执行到的分支,可以考虑构造不同的输入或模拟执行路径来触发,但这会显著增加工作量。
  • 回退操作:如果对修补结果不满意,可以在IDA中按 Ctrl+Alt+P 打开补丁列表,删除相应补丁后重新刷新(F5)即可还原。

推荐工作流(结合使用)

在实际的 逆向工程 分析中,建议将两种方法结合使用,以达到效率与精度的平衡:

  1. 快速摸底:首先使用方法一(暴力NOP)对关键或复杂的函数进行快速线性化。这能帮助你迅速把握函数的整体调用链、数据流和主要逻辑块。
  2. 精准打击:对线性化后仍然存在大量(或关键)间接调用的位置,生成Frida Hook脚本。运行目标程序,收集这些调用的实际目标地址。
  3. 静态修复:利用收集到的日志,在IDA中批量将间接调用修补为直接调用,恢复出精确的控制流图。
  4. 人工精校:最后,手工检查复杂的条件分支和核心算法区域。如有必要,可结合动态调试、符号化执行或深入的静态分析来恢复剩余的细节逻辑。

总结

本文介绍了两种“暴力但实用”的思路,用于处理基于OLLVM的间接跳转与间接函数调用混淆,这在分析加固的 Android Native层C/C++代码 时非常常见:

  • 方法一(暴力NOP):适合在时间紧迫时快速获得一份可读的伪代码,极大节省初步分析的时间成本。
  • 方法二(Frida批量Hook):能更精确地恢复程序实际执行过的路径,但效果依赖于运行时对代码分支的覆盖程度。

两者结合使用,可以形成一个从“快速理解”到“精确还原”的高效分析流程,能够显著降低由此类 控制流混淆 带来的逆向分析门槛。

文中所涉及的脚本工具可在以下代码仓库中找到:
https://github.com/imb4dm4n/Deadly-3000

希望这篇实战分享能为你在逆向分析的道路上提供一些清晰的思路。欢迎在 云栈社区 交流更多技术细节与心得体会。




上一篇:二进制与计算机原理:硬件实现与信息存储为何选择二进制?
下一篇:glibc 整数溢出与栈信息泄露漏洞分析:影响数十年的Linux重大安全缺陷
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-24 03:07 , Processed in 0.367110 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表