4、大模型辅助逆向分析:让反编译代码“开口说话”
逆向分析,说白了就是跟二进制代码“死磕”。以前我们拿到一个固件,IDA里反编译出来一堆sub_123456,看得人头皮发麻。现在有了大模型,情况完全不一样了。我个人习惯是把LLM当成一个“会读汇编的同事”,它能帮我们做四件事:注释代码、重命名函数、理解伪代码、恢复数据结构。
4.1 反编译代码注释:给代码“贴标签”
你想想看,一段几百行的反编译代码,全是变量a、b、c,函数名都是sub_xxxx。这谁受得了?大模型可以逐段分析,自动生成注释。
我是怎么做的:
- 把IDA反编译的C伪代码片段复制出来
- 丢给LLM,告诉它“这是某路由器固件的http处理函数,请逐行注释”
- LLM会识别出字符串常量、函数调用模式,给出类似“// 解析HTTP请求头,提取Content-Length字段”这样的注释
实际案例:
// 原始反编译代码
v5 = sub_8000A1C0(v4, "admin");
if ( v5 )
{
v6 = sub_8000B340(v4, "password");
// ... 几十行类似代码
}
// LLM注释后
v5 = strcmp(v4, "admin"); // 检查用户名是否为"admin"
if ( v5 )
{
v6 = strcmp(v4, "password"); // 检查密码字段
// ... 后续是登录验证逻辑
}
我的小技巧:不要一次性把整个函数丢进去。分段注释,每段控制在50-80行。我试过,效果最好。
4.2 函数重命名:告别sub_123456
说实话,固件逆向最痛苦的就是函数命名。一个固件里几百个sub函数,找关键函数像大海捞针。大模型可以根据函数内部的特征调用,自动给出有意义的名称。
我常用的流程:
- 选中一个sub函数,复制它的伪代码
- 问LLM:“这个函数做了什么?请给出3个候选名称”
- LLM会分析函数内的系统调用、字符串引用、算法模式
- 我从中选一个最贴切的,比如
parse_http_header、check_auth_token
举个例子,我曾经分析一个IoT固件,有个sub_80012345函数,里面全是memcpy、sprintf、strstr调用。LLM告诉我:“这大概率是一个格式化日志输出的函数”。我直接重命名为log_format_output。后来验证,确实如此。
避坑指南:我曾经遇到过LLM把加密函数误判为校验函数。原因是函数里同时出现了CRC和AES的常量。嗯,这里要注意:如果函数内部有多个算法特征,最好让LLM给出“主要功能”和“次要功能”两个标签。
4.3 伪代码理解:把C语言“翻译”成人话
反编译出来的伪代码,有时候比汇编还难懂。尤其是那些编译器优化过的代码,循环展开、内联函数、死代码消除...看着就头大。大模型擅长做“语义压缩”,把复杂的逻辑用自然语言描述出来。
我常用的prompt模板:
请用中文解释以下反编译代码的功能,要求:
1. 指出输入参数和返回值
2. 描述核心算法逻辑
3. 标注可能的安全漏洞点
[粘贴代码]
LLM会给出类似这样的输出:
- 功能:解析用户输入的JSON数据,提取"cmd"字段并执行对应命令
- 输入:char* json_str(用户输入的JSON字符串)
- 返回值:int(0表示成功,-1表示解析失败)
- 安全风险:未对输入长度做校验,可能存在缓冲区溢出
我个人经验:LLM对常见的漏洞模式非常敏感。比如格式化字符串漏洞、整数溢出、命令注入,它基本一眼就能看出来。我有个习惯:每次拿到新固件,先让LLM跑一遍所有函数的伪代码,标记出“高危”函数,然后优先分析这些。
4.4 数据结构恢复:把“内存布局”画出来
这是大模型最让我惊喜的能力。固件里经常有复杂的结构体,比如网络协议包、文件系统元数据、设备配置块。以前我们要手动推算偏移量,现在LLM可以自动恢复。
具体做法:
- 找到结构体相关的内存访问代码,比如
*(_DWORD *)(ptr + 0x10) - 把代码片段和已知的字符串常量喂给LLM
- LLM会推断出结构体成员:
// 原始代码
v3 = *(_DWORD *)(ptr + 0x10); // 偏移0x10
v4 = *(_DWORD *)(ptr + 0x14); // 偏移0x14
if ( v3 == 0x12345678 ) // 魔数校验
// LLM恢复的结构体
struct device_config {
uint32_t magic; // 0x00, 魔数0x12345678
uint32_t version; // 0x04
uint32_t flags; // 0x08
uint32_t checksum; // 0x0C
uint32_t data_offset; // 0x10
uint32_t data_length; // 0x14
};
我的经验:LLM恢复结构体时,最好提供一些“线索”。比如把固件里的字符串常量、已知的协议规范告诉它。我曾经分析一个私有协议,把协议文档的片段贴进去,LLM直接给出了完整的结构体定义,准确率超过90%。
4.5 知识体系:大模型辅助逆向的核心逻辑
下面这张图,是我自己总结的“大模型辅助逆向四步法”。说白了,就是让LLM从四个维度帮你理解代码:
这张图的核心思想是:反编译代码是输入,经过LLM的四个维度处理,输出的是“可读、可理解、可分析”的代码。我个人觉得,这四个步骤不是线性的,而是迭代的。比如你先让LLM注释代码,然后根据注释重命名函数,再反过来用重命名后的函数名去理解更复杂的逻辑。
4.6 实战中的注意事项
最后说几个我踩过的坑:
- 上下文窗口限制:LLM一次能处理的代码量有限。我习惯把大函数拆成小段,每段配一个“功能摘要”。
- 幻觉问题:LLM有时候会“脑补”不存在的功能。比如它可能说“这个函数实现了AES加密”,但实际上只是普通的XOR。一定要交叉验证。
- 固件特有优化:很多固件用GCC的-Os优化,代码风格跟桌面软件完全不同。LLM可能误判。我一般会先告诉LLM“这是ARM Cortex-M3的固件,使用-Os优化”。
总结一下:大模型辅助逆向,不是替代人工,而是加速。它把我们从“看汇编猜功能”的苦活里解放出来,让我们能更专注于漏洞发现和逻辑分析。我个人觉得,未来三年内,不会用LLM做逆向的工程师,效率会被拉开一个数量级。