论坛首页 逆向工程技术区 阅读主题

[原创]深入浅出 Ollvm 混淆原理及反混淆技术

337 浏览 11 回复
#1 楼主 2026-06-01 21:09:10
在对抗 OLLVM(Obfuscator-LLVM)混淆的征途中,理解其混淆原理是制定还原策略的第一步。本文将深入探讨 OLLVM 的三大核心混淆手段:指令替换、虚假控制流、控制流平坦化,并分享实用的还原技巧。所用附件:测试样本 (ollvm_bcf-fla-sub.zip)指令替换(Instruction Substitution) 是一种通过数学等价变换来增加代码复杂度的混淆技术。它的核心思想是将程序中原本简单的指令(如加法、异或),替换为一段功能等效但逻辑极其晦涩的指令序列。OLLVM 的指令替换大量采用了 MBA(Mixed Boolean-Arithmetic,混合布尔算术) 表达式。​定义​:简单来说,MBA 混淆将基础的算术运算(如 x + y)替换为复杂的位运算(And, Or, Xor, Not)与算术运算的组合。​示例:
原本一条简单的异或指令:在经过 OLLVM 混淆后,可能会变成如下形式(基于数学公式 (x + y) - 2 * (x & y) == x ^ y):甚至更复杂的嵌套多项式:这种变换在保持数学结果绝对不变的前提下,带来了两个显著的恶果:面对“指令替换”带来的垃圾代码,手动分析显然是不现实的。我们主要依赖自动化工具来进行代数简化。D-810 是一款基于 IDA 的强大反混淆插件,它能在反编译阶段动态地优化指令流。案例:test-sub以下是一段经过 OLLVM 指令替换混淆后的 RC4 算法代码,可以看到逻辑极其复杂,充斥着大量冗余的位运算:混淆前:开启 D-810 后 (F5 刷新) :可以看到,虽然 D-810 成功去除了一部分混淆,但核心逻辑(RC4 的异或操作)依然被更深层次的 MBA 表达式掩盖,未能完全还原。当 D-810 无法完全还原,或者你需要处理极高强度的 MBA 表达式时,GAMBA 是你的终极武器。该工具的详细使用方法请移步:[分享]Ollvm 指令替换混淆还原神器:GAMBA 使用指南还原 RC4 核心:我们继续处理 D-810 遗留的那三行“残渣”代码:​原始残渣:第一步:变量代换
为了方便 GAMBA 处理,我们进行如下重命名:替换后的表达式:第二步:逐个击破
使用 GAMBA 依次简化这三个表达式:简化 ​V16_i​​ 的赋值逻辑: ​输出结果​:v5 | v6简化 ​v5​​ 的逻辑: ​输出结果​:~V16_i & V12_v9简化 ​v6​​ 的逻辑: ​输出结果​:V16_i & ~V12_v9第三步:终极合并
现在我们将简化后的结果代回原逻辑:合并后的表达式为:再次交给 GAMBA 进行最终简化:​输出结果​:V16_i ^ V12_v9​结论:
经过 GAMBA 的层层抽丝剥茧,这段复杂的代码最终还原为:这正是 RC4 加密算法 中最经典的异或操作!通过 D-810 初步清洗 + GAMBA 深度还原 的组合拳,我们成功击穿了 OLLVM 的指令替换混淆。虚假控制流(Bogus Control Flow) 是 OLLVM 通过向代码中注入永远不会执行的“死代码”块和难以预测的条件跳转,来干扰控制流图(CFG)分析的一种混淆技术。​不透明谓词 (Opaque Predicate) :​不可达块 (Unreachable Block) :D-810 内置了强大的不透明谓词匹配器,能够自动识别常见的 OLLVM 谓词模式并将其优化掉,这里不再做演示。OLLVM 常使用全局变量(通常未初始化,位于 .bss​ 段)作为不透明谓词的判断条件。
核心思路:既然 IDA 不知道这些变量的值,我们就人为地给它赋予一个定值,并告诉 IDA 这个值是“只读”的。这样 IDA 的反编译器就会触发常量传播(Constant Propagation) 优化,自动剪除死代码分支。案例:test-bcf​实战步骤:定位变量:在伪代码中找到干扰判断的全局变量(如 x_9​, y_10​),双击进入 .bss 段。 ​修改段属性:按 Alt + S 打开段编辑窗口。勾选 "Read-only" 或去掉 "Write" 权限,确保 IDA 认为该段不可写。赋予初值:将这些变量批量赋值(例如全改为2)。 ​辅助脚本 (IDA Python) :最后一步:
在 .bss​ 段对相关变量执行 "Convert to data" (快捷键 D​),然后回到反编译界面按 F5 刷新。你会发现大量分支因条件确定而被 IDA 自动优化消失了。简化效果:如果你不想修改段属性,或者变量分布比较零散,可以直接修改汇编指令,将对全局变量的读取替换为立即数(常量) 。原理:
原指令:mov eax, ds:x_9​ (从内存读值,值不确定)
修改后:mov eax, 0 (直接赋常量)​IDA Python 自动化脚本:此方法直接从汇编层面切断了不透明谓词的来源,IDA 在重新分析时会发现这些寄存器都是定值,从而优化掉虚假分支。简化效果和方法B一样。控制流平坦化(Control Flow Flattening,简称 FLA 或 CFF) 是一种旨在摧毁程序结构信息的重度混淆技术。它通过引入一个中央分发器,将原函数中原本层级分明、先后有序的基本块(Basic Blocks)全部“拍扁”,使得它们在控制流图(CFG)上看起来像是在同一个层级上。原始 CFG:混淆后 CFG:要理解并还原 FLA,必须识别出以下组件:面对 FLA 混淆,我们的目标是重建真实块之间的直接跳转关系,即:如果块 A 执行完后 state​ 变成了 2,而 state=2​ 对应的是块 B,那么我们就把块 A 的结尾直接修改为 JMP Block_B,从而绕过分发器。D-810 的强大之处在于它不仅能处理 MBA混淆 和 不透明谓词,还内置了针对 FLA 的启发式去混淆规则。去混淆前:去混淆后:可以看到去混淆程度是很大的,十分接近原逻辑。Deflat 是经典的基于符号执行的去混淆工具。它利用 Angr 框架模拟执行,能够精准地计算出每个基本块的“下一跳”目标。​项目地址​: f91K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6U0M7e0j5%4y4o6x3#2x3o6f1J5z5g2)9J5c8X3c8W2k6X3I4S2N6l9`.`.​使用命令:​原理:示例:test-fla查看去混淆后的文件 test-fla_recovered :可以看到还原程度还是不错的,部分残留可以手动 NOP 清除一下:通过模拟执行,我们可以记录程序在运行时的真实轨迹 (Trace) ,从而无视复杂的静态混淆逻辑。​核心技术路径:案例:test-fla我们先查看ida的流程图,可以很明显的看出该程序的序言、分发器、return块、真实块、预处理器我们需要先让 IDA 告诉我们哪些是“真实块”,哪些是“分发器/虚假块”。​分析逻辑:​提取脚本 (Extract Blocks) :有了块列表,我们使用 Unicorn 模拟执行,记录真实的跳转路径。具体的 Unicorn 框架学习文章:[原创]深入浅出

...(已截断)

---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-289508.htm
#2 2026-06-01 21:09:10
XiuSi神太帅了
#3 2026-06-01 21:09:10
感谢分享
#4 2026-06-01 21:09:10
学习一下
#5 2026-06-01 21:09:10
感谢分享
#6 2026-06-01 21:09:10
学习学习
#7 2026-06-01 21:09:10
tql
#8 2026-06-01 21:09:10
高级
#9 2026-06-01 21:09:10
tql
#10 2026-06-01 21:09:10
正好分析了一个样本遇上了,学习一下
#11 2026-06-01 21:09:10
学习学习
#12 2026-06-01 21:09:10
学习一下

请登录后参与讨论

立即登录 注册账号