在对抗 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
[原创]深入浅出 Ollvm 混淆原理及反混淆技术
337 浏览
11 回复
XiuSi神太帅了
感谢分享
学习一下
感谢分享
学习学习
tql
高级
tql
正好分析了一个样本遇上了,学习一下
学习学习
学习一下