首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
Android安全
发新帖
2
12
[原创]OLLVM (三)控制流平坦化
发表于: 2025-6-15 17:40
1037
[原创]OLLVM (三)控制流平坦化
不歪
3
2025-6-15 17:40
1037
## 前言 最近学习了OLLVM,代码工程<a href="elink@fedK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6*7P5h3I4U0x3K6j5&6i4K6u0r3b7Y4g2%4j5h3W2a6L8r3I4$3L8b7`.`.">链接</a>。我对原有代码添加了注释并做了代码分析。 ## 目标 1. 将除了返回基本块的所有基本块的终止指令替换成跳转到一个中间基本块,这个中间基本块根据当前的变量值来确定跳转到哪个目标基本块。 2. 将函数中的 switch 指令降级为一系列比较和跳转指令。 ## 混淆步骤 1. 预处理:将函数中的 switch 指令降级为一系列比较和跳转指令。通过`LegacyLowerSwitch.cpp`内的`LowerSwitch`类。 2. 初始化平坦化上下文。 1. 初始化一个随机密钥,用于打乱 case 值以增加反混淆难度,存储到`ScramblingKey`变量内。 2. 获取函数**第一个基本块**指针,存储到`FirstBasicBlock`变量内。 3. 遍历函数内所有基本块存放到名为`OrigBb`的列表(`vector`)内。 1. 获取所有基本块列表后: 1. 移除第一个基本块(通常为主入口),后面会重新安排流程 2. 如果第一个基本块以条件分支结束,则拆分它以便插入控制流结构 2. 这一步如果出现这几种情况,会直接退出控制流平坦化: 1. 如果存在 invoke 指令则放弃混淆 2. 如果只有一个基本块,无需平坦化 4. 删除 **第一个基本块** 的最后一条指令,为插入新的控制流结构做准备。 5. 在 **第一个基本块** 最后插入新的 switch 控制变量,名为**`%switchVar`**。 6. 在 **第一个基本块** 最后插入 store 命令,将加密后的值存储到**`%switchVar`**。加密秘钥是`ScramblingKey`,下面所有加密秘钥都是它。 7. 创建`loopEntry`和`loopEnd`基本块。在当前步骤后,loopEnd 会添加跳转到 loopEntry 的语句,形成循环。 8. 在loopEntry的最后插入load命令,load被switch命令用到的条件变量**`%switchVar`**,插入前基本块是空的。例如:`%switchVar14 = load i64, ptr %switchVar, align 8` 9. `switchDefault`基本块: 1. 创建 switchDefault 基本块,它是默认 case 块所跳转的地方,它插入到`loopEnd`之后。 2. switchDefault 最后插入跳转到 loopEnd 的指令,例如:`br label %loopEnd`。 3. **第一个基本块** 最后插入指令,跳转到`loopEntry`。 4. `loopEnd`跳回`loopEntry`,构成循环。 5. 为`loopEntry`基本块创建 switch 命令。 1. 创建空switch指令。例如:`switch i64 %switchVar14, label %switchDefault []`。 2. 将所有原始基本块加入switch的case中(除了第一个基本块,因为它已经从OrigBb删除)。执行完成后举例: ```ll switch i64 %switchVar14, label %switchDefault [ i64 -9068860717773574370, label %SwConvNodeBlock_1_ i64 -9068860718768112474, label %SwConvNodeBlock_2_11 i64 -9068860719814512285, label %SwConvLeafBlock_3_9 i64 -9068860717911456551, label %SwConvLeafBlock_3_7 ...... i64 -9068860717544530469, label %sw.bb i64 -9068860717742850501, label %sw.bb2 ...... i64 -9068860717718176324, label %if.then i64 -9068860718702902671, label %if.else i64 -9068860718179539988, label %NewDefault i64 -9068860718325703097, label %sw.default i64 -9068860718244552267, label %return ] ``` 6. 更改所有基本块的终止指令(排除第一个基本块,因为它已经跳转到`loopEntry`,排除返回基本块)。 1. **获取后继块**并删除终结指令。 2. **查找后继目标基本块在第5步中对应的case值。**例如上面的:`i64 -9068860717773574370`、`i64 -9068860718768112474`。 3. 将case值存储到**`%switchVar`**中,第4步会用到,指令例如:`store i64 %27, ptr %switchVar, align 8`。 1. 提升对抗强度:可以对case值进行加密,然后在`store`指令前生成解密指令,解密后再存储到**`%switchVar`**中。 4. 跳转到`loopEnd`。`loopEnd`会调转到`loopEntry`,会运行在上面第2\.8和第5步中创建的指令,这些指令会用到上一步存储到**`%switchVar`**中的值,它是switch的条件值,根据它选择下一个要跳转到的目标块。 7. 将寄存器变量降级为栈变量:我们在上面新增命令的时候可能破坏了SSA信息,识别到寄存器变量,然后通过调用`DemoteRegToStack`、`DemotePHIToStack`函数将寄存器变量改为由`alloc`分配的变量。逻辑详情见《将寄存器变量降级为栈变量》一节。 8. 再次调用第1步相同的逻辑,将函数中的 switch 指令降级为一系列比较和跳转指令。 ## 将寄存器变量降级为栈变量 **代码:** ```c++ void fixStack(Function *const F) { // 存放需要降级的 PHI 节点 // Try to remove phi node and demote reg to stack std::vector<PHINode *> TmpPhi; // 存放需要降级为栈的寄存器变量 std::vector<Instruction *> TmpReg; // 函数入口块 BasicBlock * const BbEntry = &*F->begin(); do { TmpPhi.clear(); TmpReg.clear(); // 遍历函数中的所有基本块和指令 for (Function::iterator TmpBBIter = F->begin(); TmpBBIter != F->end(); ++TmpBBIter) { for (BasicBlock::iterator TmpInstIter = TmpBBIter->begin(); TmpInstIter != TmpBBIter->end(); ++TmpInstIter) { // 如果是 PHI 指令 if (isa<PHINode>(TmpInstIter)) { PHINode *const Phi = cast<PHINode>(TmpInstIter); // 加入 PHI 列表 TmpPhi.push_back(Phi); continue; } // 如果不是入口块中的 alloca 指令,并且该指令逃逸了(跨块使用) const bool IsAllocaInst = isa<AllocaInst>(TmpInstIter); const bool IsEntryBB = TmpInstIter->getParent() == BbEntry; if (IsAllocaInst && IsEntryBB) { continue; } const bool IsValueEscapes = valueEscapes(&*TmpInstIter); const bool IsUsedOutsideOfBlock = TmpInstIter->isUsedOutsideOfBlock(&*TmpBBIter); // outs() << "IsAllocaInst:" << IsAllocaInst << ",IsEntryBB:" << IsEntryBB // << ",IsValueEscapes:" << IsValueEscapes // << ",IsUsedOutsideOfBlock:" << IsUsedOutsideOfBlock << "\n\n"; if (IsValueEscapes || IsUsedOutsideOfBlock) { // 加入寄存器列表 TmpReg.push_back(&*TmpInstIter); continue; } } } // 将收集到的寄存器变量降级为栈变量 for (unsigned int I = 0; I != TmpReg.size(); ++I) { Instruction *const Inst = TmpReg.at(I); DemoteRegToStack(*Inst); } // 将收集到的 PHI 节点降级为栈变量 for (unsigned int I = 0; I != TmpPhi.size(); ++I) { PHINode *const TmpPHINode = TmpPhi.at(I); DemotePHIToStack(TmpPHINode); } // 循环直到没有更多可降级内容 } while (TmpReg.size() != 0 || TmpPhi.size() != 0); } ```
回复或点赞可查看完整内容
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
最后于
2026-4-27 08:44 被不歪编辑 ,原因:
#混淆加固
收藏
・
2
点赞
・
12
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
git_99440qimingminimax
为你点赞!
6小时前
git_51951meggadf3df
非常支持你的观点!
6天前
mb_lthgjpwj
为你点赞!
2026-8-20 17:56
北袅
为你点赞!
2026-8-17 20:47
mb_cebxltxx
为你点赞!
2026-8-16 14:12
mb_wsoacceo
为你点赞!
2026-8-8 00:38
mb_zpvlfkkg
为你点赞!
2026-8-5 20:47
ddwwtom
感谢你的贡献,论坛因你而更加精彩!
2026-6-20 08:48
mb_ekhqutmo
这个讨论对我很有帮助,谢谢!
2026-6-8 11:27
wx_Q道长
为你点赞!
2026-5-20 16:03
SoulCat_
感谢你的贡献,论坛因你而更加精彩!
2026-5-9 16:20
oscar99999
感谢你的贡献,论坛因你而更加精彩!
2026-5-3 09:14
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
1
)
Thehepta
雪 币:
3596
活跃值:
(7395)
能力值:
( LV11,RANK:185 )
在线值:
发帖
23
回帖
235
粉丝
282
关注
私信
Thehepta
3
2
楼
1
2026-8-4 15:40
0
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
不歪
3
42
发帖
68
回帖
150
RANK
关注
私信
他的文章
[原创][开源]OpenSecurity:AI 驱动的多领域安全自动分析 Agent 平台
6716
[原创][AI分析Web漏洞] 掰开揉碎讲解 CyberGame 难度Hard 477 分 — Web Cache Poisoning 完整 Writeup
10473
【AI自动逆向算法】Binary Analysis Agent:构建AI驱动的二进制分析系统
18675
[原创]OLLVM (三)控制流平坦化
1037
[原创]OLLVM (二)条件跳转分支混淆
908
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部