-
-
[原创][推荐][原创]用奥卡姆剃刀解决 ollvm 间接跳转和函数调用
-
-
[原创][推荐][原创]用奥卡姆剃刀解决 ollvm 间接跳转和函数调用
上篇介绍了 native 去混淆中字符串加解密的技巧,本篇聚焦去除 native 中的间接跳转(indirect jump)和间接函数调用(indirect call)混淆。这类混淆经常被大厂 APP 使用,用以增加静态分析的难度和时间成本。
间接跳转与间接调用的本质是隐藏静态控制流:静态分析无法直接得知某条跳转或调用会落到哪个目标。最直接的思路是让程序运行一次,记录实际执行时的跳转/调用目标,然后把这些目标 patch 回二进制,从而恢复可读的控制流。
本文介绍两种简单、高效且实践可行的“暴力”修复思路,无需完整模拟环境或复杂的符号化执行:
对间接跳转与间接调用实现感兴趣的朋友,可以参考 ollvm 混淆项目:
34aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6w2L8$3#2A6e0h3!0W2i4K6u0r3b7i4u0C8j5i4u0A6
7c3K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6S2L8h3W2E0L8#2)9J5c8X3N6G2M7X3!0F1
aa3K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6p5M7X3g2S2L8g2y4G2N6h3I4W2i4K6u0r3L8$3I4D9N6X3@1I4y4H3`.`.
思路概述:
对于只需还原“大体执行逻辑”而不关心精确分支行为的场景,可以直接把函数内的所有 BR/BLR(或类似的间接跳转/调用指令)替换为 NOP,然后合并基本块。这样静态反编译会把原本被混淆的控制流线性化,便于快速查看调用关系与整体逻辑。
适用场景:
优缺点:
具体流程(推荐步骤):
示例截图:

运行脚本并合并基本块后的效果(可以看到这里还用到了间接函数调用保护):

思路概述:
通过静态分析先定位所有可能的间接跳转/调用指令(BR、BLR 等),自动生成 Frida hook 脚本,在运行时捕获这些指令的实际目标地址(通常以模块基址的相对偏移记录)。把运行时采集到的目标地址写入日志后,使用 IDA Python 脚本把原来的间接跳转/调用 patch 为直接跳转/调用(或直接把目标地址写回),从而尽可能恢复原始控制流。
优缺点:
具体流程 :
示例流程截图:
静态分析 修复前的控制流与伪代码:

运行 fix_blr_2_nop.py 作为预处理(可选):

生成 Frida hook 脚本:

调整脚本中的模块名并以 spawn 模式注入:


将 Frida 输出保存到日志文件,运行IDA 脚本 fix_indirect_jump.py 解析并 patch:

补丁生效后刷新伪代码:

小提示与实践建议:
本文给出两种“暴力但实用”的思路来处理基于 ollvm 的间接跳转与间接函数调用混淆:
两者结合使用,通常能大幅降低分析成本并较好地恢复可读控制流。
代码仓库: c9aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6A6L8h3t1@1k6r3@1@1L8W2)9J5c8V1c8W2j5h3c8D9P5g2)9J5k6o6x3H3x3o6l9`.
- 方法1:暴力 NOP(快速但破坏分支信息)
- 方法2:利用 Frida 批量 hook(更精确,但依赖实际执行路径)
- 想快速定位函数调用、数据访问,而不要求分支精确还原;
- 分析时间紧张,需要先得到可读伪代码以便后续调查。
- 优点:快捷、无需运行环境或动态跟踪;
- 缺点:破坏原始控制流,条件分支(if/while)将丢失,导致反编译器无法恢复分支语义。
- 从函数入口向后扫描到下一个 RET(或函数结束)位置;
- 列出这一区间内的 BR 指令地址;
- 将这些指令 patch 为 NOP 并尝试合并基本块;
- 优点:能恢复很多实际执行过的跳转/调用,得到更准确的控制流;
- 缺点:不能覆盖未执行到的路径;条件跳转对应的分支如果未走到也无法修复;需要可运行环境并能加载目标 SO。
- 使用 Frida 的 spawn + resume 或 frida -f 来启动并注入;
- 让程序执行到这些间接跳转/调用位置;
- 先用方法1 快速线性化函数,得到可读伪代码,再针对剩余的间接函数调用用方法2 精修;
- 确保 Frida hook 输出包含模块相对偏移;
- 对于未执行到的分支,可以考虑构造输入或模拟路径以触发更多分支,但工作量会增加;
- patch 不满意,可以Ctl + ALT + P 打开补丁界面,把补丁删除后,F5刷新。
- 在 IDA 中把光标移动到目标函数入口;
- 运行脚本 fix_blr_2_nop.py ,脚本会:
- 从函数入口向后扫描到下一个 RET(或函数结束)位置;
- 列出这一区间内的 BR 指令地址;
- 将这些指令 patch 为 NOP 并尝试合并基本块;
- 刷新反编译(F5)观察伪代码并继续人工分析。
- 静态定位目标指令:在 IDA 中扫描待修复函数,记录 BR/BLR 指令地址;
- 自动生成 Frida 脚本:ALT+F7运行脚本
toolchain_trace_indirect_jumps.py 生成一个包含所有待 hook 地址的 Frida 脚本;
- 调整脚本:把脚本中的模块名改为你要分析的 so 名称,确认 log 输出格式(建议输出模块偏移、指令地址、寄存器值等);
- 启动被测进程并注入(建议使用 spawn 模式以便在早期捕获流程):
- 使用 Frida 的 spawn + resume 或 frida -f 来启动并注入;
- 让程序执行到这些间接跳转/调用位置;
- 保存日志:把 Frida 的输出保存为文本文件;
- 在 IDA 中运行补丁脚本:使用 fix_indirect_jump.py(或自写脚本)解析日志并将间接跳转/调用替换为直接跳转/调用或填写目标地址;
- 刷新反编译查看修复结果并人工验证。
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!
最后于 6天前
被dreameriii编辑
,原因: 链接