Native代码混淆与反混淆:OLLVM三大技术和破解方法
逆向 native 代码遇到混淆是最头疼的。so 库被 OLLVM 混淆后,IDA 反编译出来一团乱码。这篇讲混淆原理和反混淆思路。一、OLLVM 三大混淆技术
[*]控制流平坦化:把正常的多分支 if/while 结构打碎,用一个 dispatcher 循环替代。所有基本块变成 case 分支,由一个状态变量控制跳转。逆向时很难看出原始逻辑。
[*]虚假控制流:在基本块前后插入永远为真的判断(如 x*x>=0),让 IDA 以为有分支,增加分析复杂度。
[*]指令替换:把简单运算拆成复杂等价运算。比如 a+b 替换成 a-(-b),再替换成 a^(~(~b))。
二、反混淆方法
[*]符号执行:用 angr 框架的符号执行,求解每个分支的实际条件,重建控制流。对简单混淆有效,复杂混淆会路径爆炸。
import angr
proj = angr.Project('libtarget.so', auto_load_libs=False)
cfg = proj.analyses.CFGEmulated()
# 找到目标函数地址
func_addr = 0x1234
# 符号执行恢复控制流
state = proj.factory.blank_state(addr=func_addr)
[*]动态执行收集路径:用 Frida 在运行时 hook 目标函数,收集实际执行路径。跑几次不同输入,拼出真实控制流。
[*]IDA 脚本去平坦化:写 IDAPython 脚本,识别 dispatcher 模式,按状态变量值重建基本块顺序。GitHub 上有现成的 deflat 脚本。
[*]Patch 掉虚假分支:识别 bogus 分支的特征(如恒真条件),直接 NOP 掉判断和死代码。
三、实战流程
[*]IDA 加载 so,找到目标函数
[*]看反编译结果判断用了哪些混淆
[*]如果有平坦化:跑 deflat 脚本
[*]如果有虚假控制流:写 pattern 匹配 NOP 掉
[*]如果指令替换:人工识别等价运算还原
[*]实在不行:动态调试,在关键点断下来看实际执行路径
四、工具推荐
[*]IDA Pro + Hex-Rays:反编译+脚本
[*]angr:符号执行框架
[*]Frida:动态 hook 收集执行路径
[*]Ghidra:免费替代 IDA,有反混淆插件
https://www.metk.cn/img/posts/native_obfuscate.jpg
混淆和反混淆是猫鼠游戏。没有不可逆的混淆,只有成本高低。大部分商业 App 的混淆可以用 deflat + 动态调试搞定。 LZ是天才,坚定完毕 楼猪V5啊 工具链用对了能省很多事,但核心还是得耐心跟。想了解下有没有遇到反调试的坑? 如果只是学习目的,建议从开源项目练手,别碰线上。 遇到卡住的地方,换个思路往往比死磕更快。 实际项目里最实用的往往是最朴素的抓包手段。 现在的防护思路是增加时间成本,让你算不过来。这套思路对其他 App 通用吗? 多平台的支持是个大工程,工作量远超预期。想了解下有没有遇到反调试的坑? 建议把分析过程记录下来,下次遇到类似的能省一半时间。想了解下有没有遇到反调试的坑?