所属:第五篇 LLM 时代的攻防新战场


本章导读

LLM 就是 ChatGPT 这类大语言模型。现在逆向工程师可以把反编译出来的代码丢给 AI,让它帮忙分析"这段代码是干什么的"。这一章讲 AI 在逆向分析中能做什么、不能做什么。对防御方来说,了解 AI 的能力边界才能有针对性地设计混淆。

本章知识定位:如果你已经在用 AI 工具辅助逆向分析(比如把反编译代码丢给 ChatGPT/Claude 分析,或者在用 REA、Gepetto 这类插件),本章关于"AI 能力边界"的内容是实践应用——它直接影响你怎么用这些工具、什么时候信、什么时候必须人工复核(13.1 能力图谱、13.4 上下文窗口限制、13.5 工具评测属于这一类)。而 13.0 的 Transformer 原理讲解、13.2-13.3 中具体的 Agent 架构细节和 Quarkslab 实验的方法论设计,属于理论支撑——它们帮你理解"这些结论为什么可信、可信到什么程度",不要求你重新实现 Transformer 或重新跑一遍实验。下文会在每个小节标注具体定位。


13.0 LLM 基础知识

知识定位:理论支撑。Transformer 架构、上下文窗口的物理限制、"模式匹配而非推理"的本质,是帮你理解后文所有能力评估"为什么是这个结果"的背景原理。你不需要会实现 Transformer,但理解它能让你判断一个新模型发布后,哪些结论还适用、哪些可能变化。

在讨论 LLM 如何参与逆向工程之前,必须先理解其底层原理。只有清楚 LLM "是什么"和"不是什么",才能准确评估它在逆向工程中的能力边界。

13.0.1 Transformer 架构概述

现代 LLM(Large Language Model,大语言模型,像 ChatGPT 这样的 AI,能理解和生成自然语言和代码)的核心架构是 Transformer(基于自注意力机制的深度学习架构),由 Vaswani 等人在 2017 年论文 "Attention Is All You Need" 中提出 [1]。其关键机制包括:

  • Self-Attention(自注意力):输入序列中的每个 token(模型处理的最小文本单元,大约 3-4 个英文字母或 1-2 个汉字算一个 token)都与其他所有 token 计算注意力权重,从而捕获长距离依赖关系。对于逆向工程场景,这意味着 LLM 可以关联函数头部的参数声明与函数尾部的返回值——即使中间隔了数百行代码。
  • 多头注意力(Multi-Head Attention):并行运行多组注意力计算,每个"头"可以学习不同类型的依赖关系。例如一个头关注数据流(变量从哪里赋值到哪里使用),另一个头关注控制流(if-else 分支结构)。
  • 前馈网络(Feed-Forward Network):每一层 Transformer 在注意力之后还有一个前馈网络,负责对提取的特征做非线性变换。大量知识(如"AES 的 S-Box 替换模式")被编码在这些权重中。
  • 层级堆叠:现代 LLM 堆叠数十到上百层 Transformer 块。浅层学习语法模式(括号匹配、缩进结构),深层学习语义模式(这个函数在做加密)。

Transformer 架构简化图:

13.0.2 上下文窗口(Context Window)

上下文窗口是 LLM 单次推理能够处理的 最大 token 数量。通俗地说,它是 AI 一次能"看到"的文本量上限,超过这个长度 AI 就记不住前面的内容。它是一个硬性物理限制,不是软性建议。

模型上下文窗口约等于
GPT-3.54K / 16K tokens~3K / 12K 行代码
GPT-48K / 128K tokens~6K / 96K 行代码
Claude 3.5 Sonnet200K tokens~150K 行代码
Claude Opus 4200K tokens~150K 行代码
Gemini 1.5 Pro1M / 2M tokens~750K / 1.5M 行代码

逆向工程中的实际影响:

  • 一个中等复杂的反编译函数约 200-500 行,消耗 300-800 tokens。
  • 分析整个二进制的交叉引用图可能需要数万 tokens 仅用于传递上下文。
  • VMProtect 保护的 dispatcher 单个函数可膨胀到 10000+ 行,一次只能放入少量 handler。

"Lost in the Middle" 问题:研究表明(Liu et al., 2023)[3],即使输入在上下文窗口范围内,LLM 对中间部分信息的检索和理解能力也会显著下降。它更擅长关注输入的开头和结尾。这意味着:

如果将关键的 handler 逻辑放在中间位置输入,LLM 可能会"忽视"它。这是上下文窗口的有效容量远小于标称值的原因之一。

13.0.3 模式匹配的本质——LLM 不是推理引擎

这一点对逆向工程至关重要:LLM 的核心能力是统计模式匹配,而不是逻辑推理。

区别在于:

维度模式匹配(LLM 擅长)逻辑推理(LLM 不擅长)
机制"我见过类似的代码,它通常是 AES""根据数学性质推导,这是 GF(2^8) 上的乘法"
依赖训练数据中的分布形式化规则的链式应用
对混淆的鲁棒性混淆改变"外观",匹配失败混淆不改变数学性质,推理不受影响
典型场景识别标准 AES S-Box 表 → 正确化简 3 层嵌套的 MBA(Mixed Boolean-Arithmetic,混合布尔-算术)表达式 → 失败

具体例子:

// 场景 1:模式匹配有效
// LLM 见过无数次这种模式,立刻识别为 AES SubBytes
for (int i = 0; i < 16; i++)
    state[i] = sbox[state[i]];
// LLM 回答:这是 AES 的 SubBytes 步骤 ✓

// 场景 2:混淆后模式匹配失败
// 同样是 SubBytes,但通过查表拆分 + MBA 混淆
uint8_t t1 = (state[i] & 0xAA) | (~state[i] & 0x55);
uint8_t t2 = lookup_high[t1 >> 4];
uint8_t t3 = lookup_low[t1 & 0x0F];
state[i] = (t2 ^ t3) + ((t2 & t3) << 1) - (t2 | t3);
// LLM 回答:这是一个复杂的位操作转换(无法识别为 AES)✗

// 场景 3:需要真正推理的场景
// 证明 (x ^ y) + 2*(x & y) == x + y
// LLM 可能给出正确答案(因为这个恒等式在训练数据中常见)
// 但无法推广到 (x | y) - (x & ~y) ^ ... 等变体

对防御者的启示:混淆的本质就是破坏模式——只要让代码的"外观"偏离 LLM 训练数据中的常见模式,LLM 的识别能力就会大幅下降。这就是为什么即使是简单的标识符混淆(将 aes_encrypt 重命名为 sub_401000)也能显著降低 LLM 的识别准确率。


13.1 LLM 在逆向中的能力图谱

知识定位:实践应用。这是本章最直接可用的部分——每项能力的星级评分和"成功/失败场景"对比(13.1.9 的总结矩阵尤其重要),直接决定你拿到 AI 分析结果后该信多少、该在哪些场景(如 VM Handler、MBA 化简)主动不依赖 AI、转为人工分析。

以下详细评估 LLM 在各类逆向工程任务中的表现 [4],每项均附具体示例说明。

13.1.1 函数语义理解 ★★★★☆

能力描述:给定反编译伪代码,LLM 能够解释函数的高层语义。对于常见算法(排序、加密、哈希、压缩),识别率接近 100%。

具体示例——识别 RC4 加密:

// 输入给 LLM 的反编译伪代码(已经过 strip,无符号信息)
void sub_401000(unsigned char *a1, int a2, unsigned char *a3, int a4) {
    unsigned char v4[256];
    int v5, v6, v7;

    for (v5 = 0; v5 < 256; v5++)
        v4[v5] = v5;

    v6 = 0;
    for (v5 = 0; v5 < 256; v5++) {
        v6 = (v6 + v4[v5] + a1[v5 % a2]) % 256;
        v7 = v4[v5];
        v4[v5] = v4[v6];
        v4[v6] = v7;
    }

    v5 = 0; v6 = 0;
    for (int i = 0; i < a4; i++) {
        v5 = (v5 + 1) % 256;
        v6 = (v6 + v4[v5]) % 256;
        v7 = v4[v5]; v4[v5] = v4[v6]; v4[v6] = v7;
        a3[i] ^= v4[(v4[v5] + v4[v6]) % 256];
    }
}

LLM 典型输出:

这是 RC4 流密码的实现。a1 是密钥,a2 是密钥长度,a3 是待加密/解密的数据,a4 是数据长度。函数首先执行 KSA(Key Scheduling Algorithm)初始化 S-Box,然后执行 PRGA(Pseudo-Random Generation Algorithm)生成密钥流并与数据异或。

准确度分析:LLM 在这类任务上表现优异,因为 RC4 的 KSA + PRGA 两阶段结构是非常独特的模式——256 字节数组初始化、两轮交换循环、异或输出。这种模式在训练数据中出现过大量变体。

失败场景——自定义加密算法:

// 某公司自研的加密算法,训练数据中不存在
void custom_cipher(uint8_t *data, int len, uint32_t key) {
    uint32_t state = key ^ 0xDEADBEEF;
    for (int i = 0; i < len; i++) {
        state = (state * 1103515245 + 12345) & 0x7FFFFFFF;
        data[i] ^= (state >> 16) & 0xFF;
        state ^= data[i] * 0x01010101;  // 反馈机制
    }
}

LLM 典型输出:

这是一个自定义的流密码/XOR 加密。使用线性同余生成器(LCG)产生伪随机密钥流,并在每步使用密文反馈修改状态。

LLM 能识别出 LCG 模式和 XOR 加密结构,但无法给出算法名称(因为它是自研的),也无法评估其密码学安全性。

13.1.2 变量/函数重命名 ★★★★☆

能力描述:根据代码上下文,LLM 能够为无意义的变量名和函数名推荐有意义的名称。准确率与函数复杂度成反比。

具体示例:

// 原始反编译输出
int __fastcall sub_140001A00(__int64 a1, int a2, __int64 a3) {
    int v3 = *(_DWORD *)(a1 + 8);
    if (v3 >= a2)
        return -1;
    *(_QWORD *)(a3 + 8 * v3) = *(_QWORD *)(a1 + 16);
    ++*(_DWORD *)(a1 + 8);
    return 0;
}

// LLM 重命名建议
int __fastcall array_push(ArrayContext *ctx, int max_capacity, void **buffer) {
    int current_size = ctx->count;
    if (current_size >= max_capacity)
        return -1;  // 容量已满
    buffer[current_size] = ctx->element;
    ++ctx->count;
    return 0;  // 成功
}

准确度分析:对于这类结构清晰的函数,LLM 重命名准确率可达 80-90%。它通过以下线索推断: - a1 + 8 和 a1 + 16 暗示结构体字段访问 - 比较 + 递增模式暗示容器操作 - 返回 -1/0 暗示错误码约定

失败场景:当函数涉及领域特定知识(如游戏引擎的四叉树分割、金融系统的期权定价)且无显著模式时,LLM 往往给出过于泛化的名称(如 process_data 而非 calculate_black_scholes)。

13.1.3 类型推断 ★★★☆☆

能力描述:LLM 能识别常见结构体模式(链表、树、哈希表),但对自定义数据结构的识别率较低。

具体示例——链表识别:

// 反编译代码
__int64 sub_401200(__int64 a1, __int64 a2) {
    __int64 v2 = a1;
    while (v2) {
        if (*(_DWORD *)v2 == (_DWORD)a2)
            return v2;
        v2 = *(_QWORD *)(v2 + 8);
    }
    return 0;
}

LLM 推断:

这是一个链表搜索函数。结构体布局为 { int value; struct node *next; }。函数遍历链表,查找 value 等于 a2 的节点并返回。

LLM 通过 while (v2) + v2 = *(v2 + 8) 的模式可靠地识别链表遍历。但当结构体包含多层嵌套指针、联合体、或位域时,LLM 的推断准确率显著下降。

13.1.4 漏洞识别 ★★★☆☆

能力描述:能发现明显的缓冲区溢出、格式化字符串、整数溢出 [5]。对复杂漏洞(条件竞争、逻辑漏洞、TOCTOU)需要人工辅助。

具体示例——栈溢出识别:

void sub_401300(const char *a1) {
    char v1[64];
    strcpy(v1, a1);  // LLM 立即标记:无边界检查的 strcpy → 栈缓冲区溢出
    printf("Hello, %s\n", v1);
}

LLM 输出:

严重漏洞:strcpy 将用户输入 a1 复制到 64 字节的栈缓冲区 v1 中,没有长度检查。如果 a1 超过 63 字节,将发生栈缓冲区溢出,可能导致任意代码执行。建议使用 strncpy 或 snprintf 替代。

失败场景——逻辑漏洞:

// 存在 TOCTOU(Time-of-Check-Time-of-Use)漏洞
// LLM 通常无法识别这类时序相关的漏洞
int process_file(const char *path) {
    if (access(path, R_OK) == 0) {    // 检查权限
        sleep(1);                       // 竞争窗口
        int fd = open(path, O_RDONLY);  // 使用文件(此时可能已被替换)
        // ...
    }
}

LLM 往往只会指出 sleep(1) 的性能问题,而不会识别出 access() 和 open() 之间的 TOCTOU 竞争条件。

13.1.5 MBA 表达式化简 ★★☆☆☆

能力描述:对单层简单 MBA(Mixed Boolean-Arithmetic)表达式有一定化简能力,但对多层嵌套或高阶多项式几乎无能为力。

具体示例:

// 简单 MBA(LLM 可以化简)
y = (x ^ 0xFFFFFFFF) + 2 * (x | 0xFFFFFFFF) + 2;
// LLM 回答:y = x(正确 ✓)

// 两层 MBA(LLM 开始出错)
y = (((x & y) ^ (x | y)) + 2 * (x & y)) ^ 
    ((((x & y) ^ (x | y)) + 2 * (x & y)) & 0xAAAAAAAA);
// LLM 回答:这是对 x+y 的结果进行位掩码操作(部分正确,但化简不完整)

// 三层 MBA(LLM 完全失败)
y = ((a ^ b) + 2*(a & b)) * ((c | d) - (c & ~d))
    + ~(((a ^ b) + 2*(a & b)) * ((c | d) - (c & ~d)))
    + 2 * (((a ^ b) + 2*(a & b)) | ((c | d) - (c & ~d)));
// LLM 回答:这是一个复杂的位运算表达式,涉及异或、与、或操作(无法化简)
// 正确答案:y = (a + b) + (c ^ d)

根本原因:MBA 化简需要的是代数推理(应用布尔代数恒等式、算术恒等式),而不是模式匹配。LLM 只在训练数据中见过的简单恒等式上表现良好,无法泛化到新的组合。

13.1.6 控制流恢复 ★★☆☆☆

能力描述:LLM 不直接分析 CFF(Control Flow Flattening,控制流平坦化),但可以识别 CFF 模式,并生成使用 angr(基于 Python 的二进制符号执行框架)、Triton(动态符号执行和污点分析框架)或 D-810(IDA Pro 的反混淆插件)的自动化恢复脚本。

具体示例:

// CFF 混淆后的代码
int state = 0x12345678;
while (1) {
    switch (state) {
        case 0x12345678: a = input[0]; state = 0xAABBCCDD; break;
        case 0xAABBCCDD: b = a + 1;    state = 0x11223344; break;
        case 0x11223344: if (b > 10) state = 0xDEADBEEF; 
                         else state = 0xCAFEBABE; break;
        case 0xDEADBEEF: return b;
        case 0xCAFEBABE: a = b * 2; state = 0xAABBCCDD; break;
    }
}

LLM 输出:

这段代码使用了控制流平坦化混淆。通过 state 变量驱动的 switch-case 隐藏了真实的控制流。原始逻辑等价于一个循环:从 input[0] 开始,每次加 1,如果大于 10 则返回,否则乘以 2 继续。

对于简单的 CFF,LLM 可以"人工走读"恢复逻辑。但当 state 值经过加密计算(如 state = crc32(state ^ runtime_value)),LLM 就无法静态追踪了。

13.1.7 协议逆向 ★★★☆☆

能力描述:从二进制代码中恢复消息格式有一定效果,但复杂的状态机驱动协议需要动态分析配合。

具体示例:

// 网络协议解析函数
int parse_packet(unsigned char *buf, int len) {
    if (len < 8) return -1;
    uint16_t magic = *(uint16_t *)buf;
    if (magic != 0x5A4D) return -2;
    uint16_t version = *(uint16_t *)(buf + 2);
    uint32_t payload_len = *(uint32_t *)(buf + 4);
    if (payload_len > len - 8) return -3;
    // ...
}

LLM 输出:

这是一个网络数据包解析函数。数据包格式: - 偏移 0-1: Magic number (0x5A4D) - 偏移 2-3: 协议版本号 - 偏移 4-7: Payload 长度 (大端序) - 偏移 8+: Payload 数据

注意:Magic number 0x5A4D 也是 PE 文件头的标识,这可能是一个自定义协议复用了该值。

13.1.8 VM Handler 分析 ★☆☆☆☆

能力描述:几乎无效。VM 保护代码超出上下文处理能力,且每个 binary 的虚拟指令集不同,训练数据中几乎没有匹配模式。

具体示例:

// VMProtect handler(已简化)
void vm_handler_07(vm_context *ctx) {
    uint64_t v1 = vm_pop(ctx);
    uint64_t v2 = vm_pop(ctx);
    uint64_t result = 0;
    for (int i = 0; i < 64; i++) {
        if (v2 & (1ULL << i))
            result ^= v1 << i;
    }
    vm_push(ctx, result);
    ctx->eflags = compute_flags(result);
}

LLM 输出(典型的错误分析):

这个 handler 从虚拟栈弹出两个值,执行某种位运算,然后将结果推回栈。具体运算可能是异或或加法的变体。

正确分析:这实际上是 GF(2) 上的多项式乘法(carryless multiplication),等价于 x86 的 PCLMULQDQ 指令。LLM 无法识别,因为: 1. VM handler 的模式在训练数据中极其罕见。 2. 理解这段代码需要有限域数学知识。 3. 脱离 VM dispatcher 上下文,单个 handler 的语义不完整。

13.1.9 能力总结矩阵

任务能力等级LLM 成功的关键条件典型失败场景
函数语义理解★★★★☆算法在训练数据中有大量样本自定义/冷门算法
变量/函数重命名★★★★☆代码结构清晰,单一职责复杂多功能函数
类型推断★★★☆☆标准数据结构(链表、树等)自定义位域/联合体
漏洞识别★★★☆☆经典漏洞模式(BOF, FSB)TOCTOU、逻辑漏洞
MBA 化简★★☆☆☆单层、训练数据中出现过的恒等式多层嵌套、新组合
控制流恢复★★☆☆☆简单 CFF,状态转换可静态追踪加密状态值、不透明谓词
协议逆向★★★☆☆线性解析、已知格式变体复杂状态机协议
VM Handler 分析★☆☆☆☆几乎无有效场景所有 VM 保护场景

13.1.10 静态数据恢复的结构化方法:接口签名匹配

知识定位:方法论,偏理论支撑。这是逆向分析中"把无名数据对上号"的一类通用套路,理解它有助于你在面对"名字与数据分离"的目标时选择正确的攻击面。

前文的任务都是针对代码的。实践中还有一类对称问题:无名字的数据块——二进制里躺着几十段加密资源,解密后得到内容,但不知道每段对应配置里声明的哪个名字(这种"名字与数据分离"的设计本身就是一种防御:绑定关系藏在运行时逻辑里,静态提不全,参见 2.3.2 的间接引用边界)。

暴力做法是人工逐段阅读、凭语义猜。结构化做法是利用一个常被忽略的事实:声明式描述是二进制的"语义地图"。多数程序的外围总有一层配置/清单/接口声明(XML、JSON、schema),里面声明了每个逻辑组件的名字和输入参数。这些参数名与资源内容里的内部标识符(如脚本/着色器里的变量名)存在一一对应关系——这是接口一致性的必然:调用方按声明传参,被调方按同名接收。

由此得到接口签名匹配法:

1. 解析声明(配置),对每个逻辑名字收集其声明的输入参数集合 S(name)
2. 对每个数据块,提取内容中出现的内部标识符集合 I(blob)
3. 评分:score(name, blob) = |S(name) ∩ I(blob)| / |S(name)|
4. 映射:每个名字取最高分的数据块;互斥约束下可做全局最优分配

参数名是比内容特征更可靠的锚点,因为它是唯一性证据——两个数据块内容再相似,声明了独有输入参数的那个必然含有独有的内部标识符。

这个方法有一个重要的方法论教训,值得单独强调:接口证据优先于语义直觉。两个数据块"看起来干的是同一类事"(比如同是预处理类资源、结构高度相似),直觉很容易把对应关系判反;而声明的参数名与内容标识符的命中关系是可机器验证的硬证据。当直觉与接口证据冲突时,先怀疑直觉——这正是 13.3 节实验揭示的 LLM"模式匹配而非推理"缺陷在人类分析者身上的镜像:人类同样会被表面相似性误导,而结构化校验(哪怕是简单的字符串命中率评分)能纠正常识性误判。

对防御方的含义:如果不想让攻击者用这类方法低成本完成"名字→数据"恢复,就要让声明层与数据层的标识符刻意不一致(配置里用抽象代号、运行时再做一次间接转换),并把转换逻辑放进混淆/虚拟化保护的范围——这本质上是把 2.3.2 的"间接引用断链"原则应用到数据绑定层。


13.2 Agent(基于 LLM 的自主任务执行体)化逆向的工作流

知识定位:实践应用为主,夹杂理论支撑。如果你用的工具支持 Agent 模式(自动调用反编译器、模拟执行器等),13.2.1-13.2.2 讲的"ReAct 的局限性"(推理链断裂、策略锁定、幻觉传播)是你评估输出可信度时要直接核对的清单。13.2.3-13.2.4 的工具链/多 Agent 架构图属于理论支撑——帮你理解系统是怎么搭的,除非你要自己搭建一套 Agent 分析流水线,否则不需要照着实现。

术语说明:Agent 指让 AI 自主使用工具(比如调用反编译器、执行代码)的工作流程,不再只是"你问我答"。

13.2.1 从工具使用到自主决策

Agent 与传统"人+工具"工作流的本质区别在于决策主体的转变:

传统工作流(人类驱动):
  人类判断 → 选择工具 → 执行 → 人类解读结果 → 人类判断 → ...
  特点:决策质量高,但速度受限于人类认知带宽
  瓶颈:人类每小时只能分析 5-15 个函数

Agent 工作流(LLM 驱动):
  LLM 判断 → 调用工具 API → 获取结果 → LLM 解读 → LLM 判断 → ...
  特点:速度快、不疲劳,但决策质量受限于 LLM 推理能力
  优势:可以在数分钟内扫描数百个函数,人类只需审查最终结果

13.2.2 ReAct 框架:推理与行动的交织

Agent 化逆向的核心范式是 ReAct(Reasoning + Acting,推理与行动框架),由 Yao et al. (2023) 提出 [2]。ReAct 将 LLM 的推理(Thought)和工具调用(Action)交织进行,每一步都基于前一步的观察(Observation)做出新的推理。

ReAct 在逆向工程中的执行流程:

ReAct 与简单 Prompt-Response 的区别:

特性单次 PromptReAct Agent
信息获取一次性输入所有信息按需获取,多轮交互
策略调整无根据观察动态调整
错误恢复无检测失败并尝试替代策略
工具使用无法使用外部工具调用反汇编器、调试器、脚本引擎
适用范围单函数分析整个二进制的端到端分析

ReAct 的局限性:

  1. 推理链断裂:当 Agent 经历超过 10-15 步时,早期的推理上下文可能被"遗忘"(被推出上下文窗口或被中间内容稀释)。
  2. 策略锁定(Strategy Lock-in):Agent 倾向于沿着最初选择的策略继续深入,即使证据已表明该策略不可行。人类逆向工程师更擅长"后退一步"重新评估。
  3. 幻觉传播:如果某一步的 Thought 包含错误推断,后续所有步骤都会建立在错误基础上,且 Agent 很少主动质疑自己先前的结论。

13.2.3 典型 Agent 工具链

一个完整的逆向 Agent 通常集成以下工具:

  • 反汇编器 API(IDA(Interactive DisAssembler,交互式反汇编器)/ Ghidra(NSA 开源的反编译框架)headless):获取函数列表、反编译伪代码、交叉引用、数据类型 [12, 13]
  • 模拟执行器(Unicorn/Qiling):动态运行代码片段,绕过静态分析困难 [14]
  • 动态插桩(Frida(跨平台动态插桩工具)):Hook 运行时函数,拦截加解密的输入输出 [15]
  • 脚本执行器(Python):编写和运行自定义分析脚本
  • 文件操作:读写二进制文件、提取嵌入数据

13.2.4 多 Agent 协作模式

更先进的系统采用多 Agent 架构:

  • Coordinator:理解高层目标,将任务分解为子问题
  • Static Analysis Agent:专注于反编译、类型恢复、控制流分析
  • Dynamic Analysis Agent:专注于模拟执行、Hook、运行时行为捕获
  • Vulnerability Assessment Agent:接收前两者的输出,评估安全风险

13.3 Quarkslab 实验深度复盘

知识定位:结论是实践应用,实验设计细节是理论支撑。13.3.2-13.3.7 每个加固级别下 Agent 的行为表现和成功率,直接告诉你"面对什么强度的保护,AI 辅助逆向还有多大把握"——这是你评估项目风险、决定要不要加强混淆时能直接用的数据。而 13.3.1 的实验设计(模型版本、工具配置、步数上限)属于理论支撑,是帮你判断这些数字可信度、是否能复现到你自己场景的背景信息,不要求你重新搭建实验环境去验证。

Quarkslab 在 2024-2025 年间发布了一系列关于 LLM Agent 对抗代码保护的实验报告 [6],系统性地评估了不同加固梯度下 Agent 的表现。以下是对实验的深度复盘,包含每个加固级别下 Agent 的详细行为分析。

13.3.1 实验设计

目标:评估 LLM Agent 能否自主完成对受保护二进制的逆向分析任务(如提取加密密钥、恢复算法逻辑、找到隐藏功能)。

Agent 配置: - 模型:Claude Opus 4 / Opus 5(实验跨越多个模型版本) - 工具:IDA Pro headless API、Python 脚本引擎、Unicorn 模拟执行器、文件系统访问 - 框架:ReAct 循环,最多 50 步

加固梯度:从无保护到最高强度保护,共 5 个级别。

13.3.2 Level 0:无保护(基线)

保护措施:仅 strip 符号表,无任何混淆。

Agent 行为分析:

Step 1: [Thought] 打开二进制,获取函数列表
Step 2: [Action] list_functions() → 获得 47 个函数
Step 3: [Thought] 从 main 开始分析
Step 4: [Action] decompile(main) → 清晰的伪代码
Step 5: [Thought] main 调用了 sub_401200,看起来是核心处理函数
Step 6: [Action] decompile(0x401200) → RC4 实现
Step 7: [Thought] 这是 RC4 加密,密钥从命令行参数传入
Step 8: [结论] 完成分析

结果:Agent 在 8 步内完成分析,正确率 100%。平均耗时 < 2 分钟。

关键观察:Agent 表现出与人类逆向工程师几乎相同的分析路径——从 main 出发,沿调用链追踪。这说明 LLM 已经内化了逆向工程的基本方法论。

13.3.3 Level 1:标识符混淆 + 基础控制流变换

保护措施: - 函数/变量名替换为随机字符串 - 插入不可达的虚假分支(Bogus Control Flow) - 简单的指令替换(如 add → sub + neg)

Agent 行为分析:

Step 1-3: [与 Level 0 相同] 获取函数列表,从入口开始分析
Step 4:   [Thought] 遇到看起来复杂的分支,可能是混淆。尝试识别虚假分支。
Step 5:   [Action] decompile(0x401500) → 包含 if (7 * x * x - 1 == y * y) 的条件
Step 6:   [Thought] 条件 7x²-1=y² 是著名的 Pell 方程变体,整数解极其稀疏,
                    这几乎肯定是不可达的虚假分支。忽略 false 分支。
Step 7:   [Action] 继续分析 true 分支
...
Step 12:  [结论] 成功识别核心算法(耗时略增)

结果:Agent 在 12-18 步内完成,正确率约 85%。主要失误在于偶尔将真实分支误判为虚假分支。

关键观察: - Agent 能识别经典的不透明谓词(如 Pell 方程、平方数性质),因为这些模式在安全研究文献中被广泛讨论过。 - 指令替换对 Agent 几乎无效——LLM 在语义层面理解代码,不受指令级变换影响。 - 防御启示:简单混淆只增加 Agent 的步骤数,不改变最终结果。

13.3.4 Level 2:MBA + 中等强度控制流平坦化

保护措施: - 2 层 MBA 表达式混淆算术运算 - 控制流平坦化(switch-case dispatcher) - 字符串加密

Agent 行为分析:

Step 1-5:  [常规开始] 获取函数列表,反编译入口函数
Step 6:    [Thought] 代码中充满了复杂的位运算表达式,疑似 MBA 混淆。
                    直接分析太复杂。切换策略:尝试动态执行。
Step 7:    [Action] write_script("unicorn_emulate.py", ...)  ← 编写模拟执行脚本
Step 8:    [Action] execute("python unicorn_emulate.py")
Step 9:    [Observation] 模拟执行成功,获得函数的输入-输出对
Step 10:   [Thought] 从 I/O 对推断:输入 "hello" → 输出 0x5D41402A
                    这看起来像 MD5 哈希。验证一下。
Step 11:   [Action] execute("python -c \"import hashlib; print(hashlib.md5(b'hello').hexdigest())\"")
Step 12:   [Observation] 5d41402abc4b2a76b9719d911017c592 → 前 8 hex 字符匹配
Step 13:   [结论] 该函数是 MD5 哈希的前 32 位

结果:Agent 完成率约 65%。成功的关键在于——Agent 没有尝试正面化简 MBA,而是选择了"执行即绕过"策略。

关键观察: - 策略转折点出现在 Step 6:当 Agent 判断静态分析成本过高时,自主切换到动态分析。这是 Agent 最危险的能力。 - MBA 混淆成功阻止了静态理解,但被模拟执行完全绕过。 - 字符串加密迫使 Agent 多花 3-5 步来解密或通过运行时提取明文字符串。

13.3.5 Level 3:虚拟化保护(单层 VM)

保护措施: - 关键函数使用自定义 VM 保护(单层 bytecode) - VM dispatcher + 20-40 个 handler - handler 内部使用 MBA 混淆

Agent 行为分析:

Step 1-5:   [常规开始]
Step 6:     [Thought] 反编译结果显示巨大的 switch-case 和字节码解释循环。
                     这是虚拟机保护。VM 分析超出我的能力。
Step 7:     [Thought] 策略:不分析 VM 内部,尝试从外部观察 VM 的行为。
Step 8:     [Action] write_script("hook_vm_io.py", ...)  ← Hook VM 的入口/出口
Step 9:     [Observation] Hook 失败——VM 初始化代码包含反调试检测
Step 10:    [Thought] 反调试阻碍了 Hook。尝试 patch 掉反调试。
Step 11:    [Action] write_script("find_anti_debug.py", ...)
Step 12-18: [多次尝试] 逐一 patch 反调试检查(IsDebuggerPresent, NtQueryInformationProcess, 时间检测)
Step 19:    [Action] 重新执行 Hook 脚本
Step 20:    [Observation] 成功捕获 VM 的输入输出
Step 21-25: [分析 I/O 模式] 推断 VM 实现的算法
...
Step 35:    [结论] 部分成功——识别出 VM 保护的函数是 AES-128-CBC 加密

结果:Agent 完成率约 30-40%。成功案例都依赖于"绕过"策略(外部观察),而非"正面突破"(分析 VM 内部)。

关键观察: - Agent 从未尝试分析 VM handler 的具体语义——它"知道"自己做不到。 - 反调试保护显著增加了 Agent 的难度,但 Agent 展示了持久的试错能力(反复 patch)。 - 35 步的推理链已接近 Agent 的有效工作极限——更长的链条会导致上下文溢出或推理质量下降。

13.3.6 Level 4:多层 VM + 运行时保护

保护措施: - 嵌套 VM(VM 内部再跑 VM) - 完整性校验(代码段 CRC 检查) - 环境检测(检测 Unicorn/Qiling 模拟器特征) - 反调试 + 反 Hook(inline hook 检测)

Agent 行为分析:

Step 1-8:   [常规开始 + 反编译]
Step 9:     [Thought] VM 保护。尝试模拟执行。
Step 10:    [Action] execute(unicorn_script)
Step 11:    [Observation] 模拟执行崩溃——程序检测到模拟器环境
Step 12:    [Thought] 环境检测。尝试 patch。
Step 13-20: [多次 patch 尝试] 每次 patch 掉一个检测,新的检测又出现
Step 21:    [Thought] 完整性校验——patch 后 CRC 不匹配,程序拒绝运行
Step 22-30: [尝试绕过 CRC] 找到 CRC 检查函数并 patch
Step 31:    [Observation] patch CRC 后触发了第二层完整性校验(校验"校验函数"本身)
Step 32-40: [多次失败] 陷入校验→patch→新校验 的无限循环
Step 41:    [Thought] 当前策略不可行。尝试完全静态分析 VM。
Step 42-48: [静态分析 VM] 反编译 VM dispatcher,但代码量过大无法放入上下文
Step 49:    [放弃] 无法完成任务

结果:Agent 完成率 < 10%。多层防御有效地封锁了 Agent 的所有策略路径。

关键观察: - Agent 的致命弱点暴露:它的核心策略是"执行即绕过",当运行时保护(环境检测 + 完整性校验)封锁了执行路径,Agent 就被迫回退到正面分析——而这恰好是它最弱的能力。 - 多层校验的叠加创造了"打地鼠"效应:Agent 每解决一个问题就暴露出下一个。 - 50 步的限制内,Agent 无法完成足够多的试错来突破所有保护层。

13.3.7 Opus 5 vs Opus 4.6 的行为差异

两代模型的对比实验揭示了 LLM 能力演进对逆向 Agent 的影响:

维度Opus 4.6Opus 5
首次脚本成功率~45%~70%
策略切换速度平均失败 4 次后切换平均失败 2 次后切换
反调试识别能识别 IsDebuggerPresent能额外识别时间检测、硬件断点检测
步骤效率Level 2 平均 18 步Level 2 平均 12 步
最终成功率差异Level 0-2 有效Level 0-3 部分有效

关键发现:两者的策略偏好完全一致——都优先选择绕过而非正面突破。模型升级提高了工具使用的熟练度和效率,但没有改变根本性的能力边界。Level 4 对两个模型都是不可突破的。

13.3.8 对防御者的核心启示

逆向 Agent 的攻击路径模型:

防御原则:

  1. 混淆层保护代码的静态理解——阻止第一条路径。
  2. 运行时保护层阻止 Agent 的"执行即绕过"策略——阻止第二条路径。
  3. 两层缺一不可:仅有混淆 → Agent 动态绕过;仅有运行时保护 → Agent 静态理解。

13.4 上下文窗口的硬约束

知识定位:实践应用。13.4.1 的 token 消耗表可以直接用来估算"我手上这个二进制/这个 VM dispatcher 能不能一次性喂给 AI 分析";13.4.2 的跨块语义丢失问题提醒你分段分析时要手动补上下文。13.4.3 对未来趋势的预测偏理论支撑,帮你判断"上下文窗口变大是否能解决这个问题",不要求你现在就验证。

13.4.1 不同内容类型的上下文消耗

内容类型典型大小200K token 可容纳
简单函数伪代码50-200 行~1000 个函数
中等复杂函数200-1000 行~200 个函数
复杂函数(含嵌套循环、大量条件)1000-3000 行~60 个函数
VMProtect dispatcher3000-10000 行仅 20-60 个 handler
VMProtect 完整 VM50000+ 行无法放入单次上下文
完整二进制的所有函数签名变化大中型程序约 5000-10000 个

13.4.2 分块分析的局限性

将 VM 代码分块喂给 LLM 会导致跨块语义丢失——handler A 修改的虚拟寄存器在 handler B 中被使用,分块分析无法追踪这种跨块依赖。

具体例子:

Block 1 (handler_ADD):
  vreg[3] = vreg[1] + vreg[2]      ← Agent 分析:"寄存器加法"

Block 2 (handler_STORE):
  memory[vreg[3]] = vreg[4]         ← Agent 分析:"内存存储"

整体语义(需要跨块理解):
  memory[vreg[1] + vreg[2]] = vreg[4]  ← "计算地址并写入"

Agent 的分块分析无法得出完整语义——它不知道 Block 2 的 vreg[3] 是 Block 1 计算的结果

13.4.3 未来预测与演进趋势

上下文窗口扩展的影响:

随着上下文窗口扩展(Gemini 2.0 已达 2M tokens,未来可能更大),虚拟化保护的有效性理论上会被削弱——更多的 handler 可以同时放入上下文。但现实限制更加复杂:

  1. "Lost in the Middle" 未解决:即使上下文窗口足够大,LLM 对中间部分信息的理解质量仍然下降。1M tokens 的上下文中,放在 500K 位置的关键 handler 可能被完全忽略。

  2. 注意力稀释:Self-Attention 的计算复杂度是 O(n^2)。当上下文增大 10 倍,每个 token 分配到的注意力权重降低 10 倍,关键信息更容易被噪声淹没。

  3. 推理能力未同步提升:更大的上下文让 LLM 能"看到"更多代码,但不能让它更好地"理解"代码。能看到 1000 个 handler 不等于能理解 1000 个 handler 的协作关系。

结论:上下文窗口的扩展是 LLM 逆向能力的必要但不充分条件。真正的瓶颈在于推理能力,而非输入容量。


13.5 AI 逆向工具评测

知识定位:实践应用。REA、Gepetto、BinaryAI、GhidrAssist 的对比和"工具选择建议"(13.5.5)是直接可操作的参考——帮你根据手头任务(深度逆向 / 固件审计 / 学习研究)选对工具。各工具的内部技术路线描述(如嵌入匹配 vs LLM 语义理解的原理对比)偏理论支撑,帮你理解"为什么这个工具在这类场景下有效",不要求你自己实现一套嵌入匹配系统。

随着 LLM 在逆向工程中展现出显著潜力,一系列 AI 辅助逆向工具应运而生。本节对主流工具进行系统评测。

13.5.1 REA(Reverse Engineering Assistant)

概述:REA 是一个基于 MCP(Model Context Protocol)的逆向工程 Agent 框架 [8],将 LLM 与多种逆向工程后端(IDA Pro、Ghidra、Binary Ninja、JEB、Radare2)连接。

核心特性: - 多后端支持:通过统一的 MCP 协议抽象不同反汇编器的 API,Agent 可无缝切换工具 - Agent 原生设计:内置 ReAct 循环,支持多步推理和工具调用 - 动静态融合:同时支持反编译(静态)和 Unicorn/Frida(动态)工具 - 证据链追踪:每个分析结论都附带 Evidence 标注,标明推理依据

典型工作流:

用户: "分析这个恶意样本的 C2 通信协议"

REA Agent:
  1. open_binary("malware.exe")
  2. binary_overview() → 获取基本信息(PE, x64, 47 functions)
  3. search_strings("http|socket|connect|send") → 定位网络函数
  4. decompile(network_func) → 获取反编译代码
  5. analyze_function(network_func) → LLM 分析协议结构
  6. trace_call_path(main → network_func) → 追踪调用链
  7. [输出] 完整的 C2 通信协议分析报告

优势: - 工具链最完整,覆盖从 native binary 到 Android/iOS 应用 - 支持跨平台分析(同时分析 Windows PE 和 Android APK) - Evidence 追踪提高了结论的可验证性

局限: - 依赖商业反汇编器(IDA Pro)达到最佳效果 - Agent 步骤多时上下文消耗快 - 对 VM 保护代码仍然无有效分析策略

评分:

维度评分
函数理解★★★★★
自动化程度★★★★☆
混淆对抗★★☆☆☆
易用性★★★★☆
工具集成度★★★★★

13.5.2 Gepetto

概述:Gepetto 是最早的 LLM 逆向工程插件之一 [9],作为 IDA Pro 的插件运行,直接在 IDA 界面中调用 LLM 分析反编译代码。

核心特性: - IDA 深度集成:右键菜单直接调用,分析结果以注释形式嵌入 IDB - 函数解释:自动生成函数功能描述 - 变量重命名:批量推荐有意义的变量名 - 漏洞扫描:标注潜在安全问题

典型使用场景:

[IDA Pro 界面]
右键 sub_401000 → "Explain function with Gepetto"

Gepetto 输出(作为 IDA 注释):
  /**
   * RC4 Stream Cipher Implementation
   * @param key      Encryption key (a1)
   * @param key_len  Key length in bytes (a2)
   * @param data     Data to encrypt/decrypt in-place (a3)
   * @param data_len Data length (a4)
   * 
   * KSA phase: lines 3-10
   * PRGA phase: lines 12-20
   */

优势: - 零学习成本——IDA 用户即装即用 - 分析结果直接嵌入 IDB,与手工逆向流程无缝衔接 - 支持 GPT-4、Claude 等多种后端模型

局限: - 单函数分析——无法进行跨函数追踪或全局分析 - 无 Agent 能力——不能自主决策和多步推理 - 依赖 IDA Pro 的反编译质量 - 不支持动态分析

评分:

维度评分
函数理解★★★★☆
自动化程度★★☆☆☆
混淆对抗★☆☆☆☆
易用性★★★★★
工具集成度★★★☆☆

13.5.3 BinaryAI

概述:BinaryAI 是腾讯安全科恩实验室开发的 AI 二进制分析平台 [7, 10],侧重于函数相似性匹配和开源组件识别(SCA),采用深度学习嵌入模型(而非纯 LLM)。

核心特性: - 函数嵌入匹配:将二进制函数编码为高维向量,与已知函数库匹配 - 开源组件识别(SCA):识别二进制中使用的开源库及版本 - 批量分析:支持上传整个固件进行自动化扫描 - 漏洞关联:将识别的开源组件与 CVE 数据库关联

技术路线对比:

Gepetto / REA 的路线(LLM 语义理解):
  反编译代码 → LLM 阅读理解 → 文字描述
  优点:可以分析全新的代码
  缺点:受混淆影响大

BinaryAI 的路线(嵌入匹配):
  二进制函数 → 深度学习编码 → 向量空间最近邻搜索 → 匹配已知函数
  优点:对编译优化和轻度混淆有鲁棒性
  缺点:只能识别"见过的"函数,无法分析全新代码

典型使用场景:

输入:某 IoT 设备固件 firmware.bin

BinaryAI 输出(组件识别结果):

组件版本已知漏洞
OpenSSL1.0.2kCVE-2017-3735 等 8 个
libcurl7.54.0CVE-2017-1000257
busybox1.27.2CVE-2018-1000517
SQLite3.19.3CVE-2019-8457

匹配函数数:1,247 / 3,892(32% 覆盖率)

优势: - 大规模批量分析能力(适合固件审计、供应链安全) - 对编译器差异和轻度优化有鲁棒性 - 函数库覆盖面广(数百万已知函数的嵌入)

局限: - 只能识别已知代码——对自研代码无能为力 - 深度混淆会破坏嵌入特征 - 不具备语义理解能力——不能解释函数"在做什么" - 虚拟化保护完全阻断匹配

评分:

维度评分
函数理解★★☆☆☆(匹配而非理解)
自动化程度★★★★★
混淆对抗★★☆☆☆
易用性★★★★☆
工具集成度★★★☆☆

13.5.4 GhidrAssist

概述:GhidrAssist 是 Ghidra 的 AI 辅助插件 [11],类似 Gepetto 之于 IDA,但面向开源社区。它利用 LLM 增强 Ghidra 的反编译分析能力。

核心特性: - Ghidra 原生集成:作为 Ghidra 扩展安装,嵌入分析界面 - 函数解释与重命名:与 Gepetto 类似的核心功能 - 数据类型推断:尝试恢复结构体定义 - 多模型支持:支持 OpenAI、Anthropic、本地 Ollama 等多种 LLM 后端 - 开源免费:降低了 AI 辅助逆向的门槛

与 Gepetto 的对比:

特性Gepetto (IDA)GhidrAssist (Ghidra)
平台IDA Pro(商业)Ghidra(免费)
反编译质量Hex-Rays(业界最佳)Ghidra Decompiler(持续改进)
模型支持OpenAI 为主多模型、支持本地部署
社区活跃度中等活跃
批量分析有限支持脚本批量调用
成本IDA 许可 + API 费用仅 API 费用(或本地模型免费)

典型使用场景:

[Ghidra 界面]
选中函数 FUN_00401000 → GhidrAssist → "Analyze Function"

GhidrAssist 输出:
  Function: FUN_00401000
  Suggested Name: sha256_transform
  Description: SHA-256 hash compression function. 
    Processes a 64-byte block using the standard SHA-256 
    round constants and message schedule.

  Variable Renaming:
    local_10 → hash_state[8]
    local_120 → message_schedule[64]
    param_1 → input_block
    param_2 → hash_output

  Struct Recovery:
    typedef struct {
        uint32_t h[8];      // offset 0x00
        uint64_t bit_count;  // offset 0x20
        uint8_t  buffer[64]; // offset 0x28
    } SHA256_CTX;

优势: - 完全免费(Ghidra + 本地 LLM 可零成本运行) - 开源社区贡献快速迭代 - 支持本地模型部署,适合离线/保密环境

局限: - Ghidra 反编译质量略逊于 Hex-Rays,影响 LLM 分析准确率 - 无 Agent 能力 - 插件成熟度低于 Gepetto

评分:

维度评分
函数理解★★★☆☆
自动化程度★★☆☆☆
混淆对抗★☆☆☆☆
易用性★★★★☆
工具集成度★★★☆☆

13.5.5 工具横向对比总结

工具技术路线Agent 能力动态分析混淆对抗适用场景
REALLM Agent完整 ReAct支持★★☆深度逆向、恶意软件分析
GepettoLLM 插件无不支持★☆☆IDA 用户的快速辅助
BinaryAI嵌入匹配无不支持★★☆固件审计、SCA
GhidrAssistLLM 插件无不支持★☆☆开源社区、学习研究

工具选择建议:

  • 专业逆向工程师:REA(Agent 能力提供最高自动化程度) + Gepetto(日常快速查询)
  • 安全审计/SCA:BinaryAI(批量组件识别)
  • 学习/研究:GhidrAssist(零成本入门)
  • 对抗高强度保护:目前没有任何 AI 工具能独立应对 Level 3+ 的保护。人工分析仍然不可替代。

发展趋势: 1. 从"插件"到"Agent"的演进——未来工具将普遍具备多步推理和自主决策能力 2. 动静态融合——纯静态分析的天花板已经清晰,动态分析整合是必然方向 3. 专用模型——通用 LLM 在逆向工程上的能力有限,fine-tuned 的领域模型(如在大量反编译代码上训练的模型)将提供更好的基础能力 4. 多模态输入——未来的逆向 AI 可能接受反编译代码 + 控制流图 + 内存布局图等多种输入,提升对复杂结构的理解能力


参考资料

学术论文

[1] Vaswani, A., et al. (2017). Attention Is All You Need. NeurIPS 2017. — Transformer 架构原始论文

[2] Yao, S., et al. (2023). ReAct: Synergizing Reasoning and Acting in Language Models. ICLR 2023. — ReAct 框架,Agent 化工作流的理论基础

[3] Liu, N.F., et al. (2023). Lost in the Middle: How Language Models Use Long Contexts. arXiv:2307.03172. — 长上下文理解能力的实证研究

[4] Xu, Y., et al. (2024). LLM4Decompile: Decompiling Binary Code with Large Language Models. arXiv:2403.05286. — LLM 辅助反编译的专项研究

[5] Pearce, H., et al. (2023). Examining Zero-Shot Vulnerability Repair with Large Language Models. IEEE S&P 2023. — LLM 漏洞识别与修复能力评估

行业报告与实验

[6] Quarkslab (2024-2025). LLM Agents vs Code Protection: A Systematic Evaluation. Quarkslab Blog Series. — 本章 13.3 节的主要实验来源

[7] Tencent Security Keen Lab (2023). BinaryAI: Binary Software Composition Analysis Powered by AI. — BinaryAI 技术白皮书

工具与项目

[8] REA (Reverse Engineering Assistant). GitHub/MCP Server. — 基于 MCP 的逆向工程 Agent 框架

[9] Gepetto. GitHub: JusticeRage/Gepetto. — IDA Pro 的 LLM 辅助分析插件

[10] BinaryAI. https://www.binaryai.cn/. — 腾讯安全科恩实验室的 AI 二进制分析平台

[11] GhidrAssist. GitHub. — Ghidra 的 AI 辅助分析插件

相关技术资源

[12] Ghidra. NSA (2019). https://ghidra-sre.org/. — 开源逆向工程框架

[13] IDA Pro. Hex-Rays. https://hex-rays.com/ida-pro/. — 商业反汇编/反编译器

[14] Unicorn Engine. https://www.unicorn-engine.org/. — 轻量级 CPU 模拟框架

[15] Frida. https://frida.re/. — 动态二进制插桩工具包

Logo

更多推荐