-
-
[原创]OLLVM (二)条件跳转分支混淆
-
发表于: 2天前 81
-
前言
最近学习了OLLVM,代码工程链接。我对原有代码添加了注释并做了代码分析。
目标
- 将函数内所有的条件跳转指令转换成间接跳转指令。
- 条件跳转指令和无条件跳转指令是相对的,条件跳转如:
if.then
、if.else
。
混淆前后对比
以ll文件中的语句举例。
源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | double calculate( double a, double b, char op) { printf (calculate_print_format, a, b, op); switch (op) { case '+' : return a + b; case '-' : return a - b; case '*' : return a * b; case '/' : if (b != 0) return a / b; else // return 0.0 / 0.0; // NaN return -1; // NaN default : // return 0.0 / 0.0; // NaN return -1; // NaN } } |
对比
混淆前:
sw.bb4: ; preds = %entry %10 = load double, ptr %b.addr, align 8 %cmp = fcmp une double %10, 0.000000e+00 br i1 %cmp, label %if.then, label %if.else
混淆后:
sw.bb4: ; preds = %entry %10 = load double, ptr %b.addr, align 8 %cmp = fcmp une double %10, 0.000000e+00 %11 = select i1 %cmp, i64 1, i64 0 %12 = getelementptr [2 x ptr], ptr @_Z9calculateddc_IndirectBrTargets, i64 0, i64 %11 %EncDestAddr = load ptr, ptr %12, align 8 %13 = getelementptr i8, ptr %EncDestAddr, i64 3224706807 indirectbr ptr %13, [label %if.then, label %if.else]
sw.bb4模块代码对比图:
@_Z9calculateddc_IndirectBrTargets
是目标块地址加密数组:
@_Z9calculateddc_IndirectBrTargets = private global [2 x ptr] [ptr getelementptr (i8, ptr blockaddress(@_Z9calculateddc, %if.else), i64 -3224706807), ptr getelementptr (i8, ptr blockaddress(@_Z9calculateddc, %if.then), i64 -3224706807)]
解释混淆后的IR代码
首先定义了一个全局数组
@_Z9calculateddc_IndirectBrTargets
,它包含两个指针:- 第一个指针指向
if.else
块地址减去一个大数(3224706807) - 第二个指针指向
if.then
块地址减去同一个大数
- 第一个指针指向
在
sw.bb4
基本块中:- 加载一个double类型的值
%10
(它是源码中calculate函数的入参b的值)。 - 比较
%10
是否不等于0.0(fcmp une
)。 - 根据比较结果选择索引:如果b≠0则返回1,否则返回0。
- 使用索引从全局数组中获取对应下标的地址指针。
- 加载这个指针值到
%EncDestAddr
,它是加密后的地址。 - 然后将这个地址加上之前减去的同一个大数
3224706807
,结果是解密后的目标基本块的地址。 - 最后使用
indirectbr
指令跳转到计算出的地址,例如:indirectbr ptr %13, [label %if.then, label %if.else]
,语句解释:- ptr %13:这是间接跳转的目标地址,存储在寄存器 %13 中。这个寄存器包含了一个代码块的地址。
[label %if.then, label %if.else]
:这是可能的目标块列表。这个列表告诉优化器哪些块可能是跳转目标(帮助优化),但运行时实际跳转目标由 %6 的值决定,不限于这两个。
- 加载一个double类型的值
这个代码是在实现某种控制流混淆(control flow obfuscation)技术:
- 通过将真实的目标地址存储为"基地址+大偏移"的形式来隐藏。
- 在运行时动态计算实际跳转目标。
- 这是一种反逆向工程的保护手段。
本质上,这段代码等价于:
if (b != 0.0) goto if_then; else goto if_else;
但使用了复杂的间接跳转来实现相同功能。
这种技术使得静态分析难以确定控制流的目标,增加了逆向工程的难度。
IndirectBranch代码解析
1. 获取函数中所有以条件跳转结尾的基本块(if/else块)所跳转到的目标基本块
逻辑在NumberBasicBlock
内。目标基本块下文简写为目标块。
2. 目标基本块加密
L0级别目标基本块加密
逻辑在getIndirectTargets0函数内。根据目标块的地址加上一个随机的偏移,计算出一个新的地址,这个地址是目标块加密后的地址,多个目标块加密后的地址组成一个加密地址数组,例如:
@_Z9calculateddc_IndirectBrTargets = private global [2 x ptr] [ptr getelementptr (i8, ptr blockaddress(@_Z9calculateddc, %if.else), i64 -3224706807), ptr getelementptr (i8, ptr blockaddress(@_Z9calculateddc, %if.then), i64 -3224706807)]
这就是上文混淆后的加密地址数组。所谓的加密是指目标块地址加了-3224706807
后,就是加密了。
L1级别目标基本块加密
TODO 待分析
L2级别目标基本块加密
TODO 待分析
3. 遍历函数中的所有基本块,替换其中的条件分支为间接跳转
概览
遍历函数中的所有基本块找到其中的条件分支,判断条件分支的所有后继模块是否被第1步收集到,被收集到的情况下将其替换为间接跳转,转换前后对比:
3.1 从地址加密数组内读取目标块加密后的地址
相关代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | /* BBNumbering[Successor]返回的是后继基本块在 地址加密数组(DestBBs) 内的索引, 下面会根据索引从数组内读取加密后的地址 */ Value * const TIdx = ConstantInt::get(intType, BBNumbering[Successor0]); Value * const FIdx = ConstantInt::get(intType, BBNumbering[Successor1]); // 创建IR构建器 IRBuilder<> IRB(BI); // 获取条件值和两个目标基本块的索引 Value * const Cond = BI->getCondition(); /* 创建一个 select 指令(条件选择操作)。 举例:%11 = select i1 %cmp, i64 0, i64 1 */ Value * const Idx = IRB.CreateSelect(Cond, TIdx, FIdx); /* 计算 加密后的目标地址 的指针,即指针的指针(二级指针), 相当于 C 语言中的:ptr* elem_addr = &global_array[index] */ Value * const GEP = IRB.CreateGEP( DestBBs->getValueType(), DestBBs, {Zero, Idx}); /* 创建load语句,举例:%EncDestAddr = load ptr, ptr %12, align 8 从上一步返回的二级指针,获取加密后的目标地址 */ Value * const EncDestAddr = IRB.CreateLoad( GEP->getType(), GEP, "EncDestAddr" ); |
3.2 解密地址
L1级别目标基本块解密
相关代码:
1 2 3 4 5 6 7 8 9 | // 计算解密密钥 Value *DecKey = DecodeConstantKey; ...... // 加上某个值解密目标地址。举例:%13 = getelementptr i8, ptr %EncDestAddr, i64 1992839860 Value *DestAddr = IRB.CreateGEP( Type::getInt8Ty(Ctx), EncDestAddr, DecKey); |
3.3 间接跳转指令替换原分支指令
相关代码:
1 2 3 4 5 6 7 8 | // 创建间接跳转指令并替换原分支 IndirectBrInst *IBI = IndirectBrInst::Create(DestAddr, 2); // 间接跳转指令添加第一个后继 IBI->addDestination(BI->getSuccessor(0)); // 间接跳转指令添加第二个后继 IBI->addDestination(BI->getSuccessor(1)); // 替换指令 ReplaceInstWithInst(BI, IBI); |
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2天前
被不歪编辑
,原因:
赞赏
他的文章
赞赏
雪币:
留言: