逆向工程实战手册 第 9 章

第 9 章 常见加密与算法模式识别

逆向中 90% 的"加密"不是密码学教科书里的算法,而是开发者的 DIY:XOR、位移、查表、简单置换。先识别模式,再决定反推策略

📍 知识点地图 | 主题:加密与算法模式 | 前置:第4章 | 后续:第34章 | 核心概念:XOR家族、S-Box、识别信号、穷举

9.1 加密模式识别流程

1. 找到处理输入数据的函数
2. 观察操作特征(异或/移位/查表/数学运算)
3. 识别模式(见下)→ 决定反推方法
4. 写脚本验证(Python 快速实现)
5. 对照输出确认(拿已知明文测试)

9.2 最常见:XOR 家族

# 单字节 XOR —— 穷举 256
for key in range(256):
    out = bytes(c ^ key for c in cipher)
    if b"flag" in out or is_printable(out):
        print(f"key={key} -> {out}")

# 已知明文 XOR —— 用 flag{ 前缀推 key
known = b"flag{"
key = bytes(c ^ p for c, p in zip(cipher, known))
print(key)

# 位置索引 XOR —— key 含 i
# 模式: c[i] ^ i 或 c[i] ^ (i & 0xff)
out = bytes(c ^ i for i, c in enumerate(cipher))
# 循环 key XOR: key 长度不定 → 先推测 key 长度(频率分析/已知明文)

识别信号

  • 反编译里大量 ^ 0xXX / ^ i 操作
  • 常量表是密码本(S-Box)
  • 数据看起来"随机但长度相同"

9.3 置换 / 查表(S-Box)

特征:程序里有一张 256 字节的表,输入查表输出
识别: .rodata 里的 256 字节数组 + 下标访问

应对: - 正向表存在 → 直接建反向表 rev[out] = in - 非双射变换(多个输入映射同一输出)→ 记录所有可能输入,最后用可打印字符/flag 格式过滤

rev = {}
for i in range(256):
    rev.setdefault(table[i], []).append(i)
# 对每个输出字节,尝试所有候选输入

9.4 数学类(乘除、模运算、GF 域)

模式 反推
线性方程 y = ax + b 求逆元
矩阵变换 高斯消元(GF(2^8) 域内)
格/整数约束 M*x = v (mod 2^32) CVP/LLL 格基规约(fpylll)
累加校验 反推目标值
指令计数器作为状态 逐字节模拟

9.5 标准算法识别

识别特征表

算法 特征
MD5/SHA 家族 初始化常量(0x67452301 等)+ 四轮循环
AES S-Box 256 表 + 轮数结构
RC4 256 字节 KSA + 两下标交换
RSA 大数运算 + 公钥指数 0x10001
Base64 64 字符表 A-Za-z0-9+/
TEA/XTEA 0x9E3779B9 常量
CRC32 0xEDB88320 多项式表
Blowfish P 数组 + 4 个 S-Box

标准库识别方法

  • IDA FLIRT / Lumina:识别库函数(尤其 C++ STL、OpenSSL)
  • 字符串:算法名称常出现在错误消息/调试输出
  • 导入表EVP_*AES_* → OpenSSL;java.security → JCE

常见 DIY 变体

TEA 变体: delta 被改 → 识别常量是否 0x9E3779B9
AES 变体: S-Box 被混淆 → 比较 .rodata 与标准 S-Box
XOR+查表组合: 两层变换,逐层反推
RC4+VM: 密钥藏在 VM 指令流里 → 先还原 VM

9.6 自动识别工具

# Ciphey — 不知道什么编码时自动检测+解密
ciphey -t "密文"
cat file | ciphey

# CyberChef — 在线拖拽式解码(万能瑞士军刀)
# https://gchq.github.io/CyberChef/

# haiti — 哈希类型识别
haiti hash值

# RsaCtfTool — RSA 自动攻击
RsaCtfTool --publickey pub.pem --private

# CyberChef 的 Magic 操作: 自动尝试常见编码链

9.7 逆向中的"解密"黄金三步

1. 找数据来源: 密文常量在哪(.rodata / 文件 / 网络)
2. 找变换函数: 谁引用了这些数据(xrefs_to)
3. 找 key: 硬编码 key / 从环境推导 / 从日志泄露

实战案例:Android 日志泄露密钥(HackIT)——key 和 iv 由 logcat 输出,直接把日志解析出来就是密钥。先找日志,再找代码。

9.8 编码识别速查

开头 7b...           → 多半 flag{ 开头
A-Za-z0-9+/ 末尾 =   → Base64
0x 开头长数字        → 十六进制
\xNN 形式           → 转义字符串
U 开头+%            → URL 编码
0xdeadbeef 类       → 字节序敏感, 注意大小端

9.9 完整实战演示:CTF 常见加密链一次解到底

以一道 CTF 题为蓝本,展示完整"识别 → 反推 → 写脚本"流程。

题目:逆向一个 checker,程序从 .rodata 读取 32 字节密文,用 encrypt(input) 和密文比对。

Step 1: 静态识别变换逻辑(Ghidra/IDA 伪代码)

void encrypt(char *in, char *out) {
    for (int i = 0; i < 32; i++) {
        char c = in[i];
        c ^= 0x5A;                    // ① 单字节 XOR key=0x5A
        c ^= i;                       // ② 位置索引 XOR
        c = (c + 7) & 0xFF;           // ③ 加法变换
        out[i] = SBOX[c];             // ④ 查表(SBOX 在 .rodata)
    }
}
// 校验: memcmp(encrypt(input), cipher, 32) == 0

Step 2: 逆向变换(从后往前逐层解)

# 反向层: SBOX 反查表(256 字节数组,从 .rodata 提取)
SBOX = bytes.fromhex("...")          # 用 IDA/rabin2 从 .rodata 导出
rev = [0] * 256
for i in range(256):
    rev[SBOX[i]] = i                 # 双射 → 直接反查

# 反向层: 减法 7
# 反向层: XOR i
# 反向层: XOR 0x5A
cipher = bytes.fromhex("9d 1e f2 ...")   # 程序里存的目标值

flag = bytearray()
for i in range(32):
    c = cipher[i]
    c = rev[c]                        # ④ 反查表
    c = (c - 7) & 0xFF                # ③ 减 7
    c ^= i                            # ② 反向 XOR 位置
    c ^= 0x5A                         # ① 反向 XOR key
    flag.append(c)
print(flag.decode())                  # flag{...}

Step 3: 验证

输出应为可打印 ASCII + flag 格式 → 校验成功
如果输出乱码 → 层顺序/表提取有误,检查 SBOX 是否双射、层序是否对

常见层与反向操作对照表

正向变换 反向操作 Python 示例
c ^= K c ^= K(XOR 自反) c ^= 0x5A
c ^= i c ^= i c ^= i
c += N c -= N(mod 256) c = (c - 7) & 0xFF
c -= N c += N c = (c + 7) & 0xFF
c = SBOX[c] 反查表 c = rev[c](需双射)
c = (c * A + B) % M 模逆元 + 减 B gcd(A,M)==1
c = c << 3 \| c >> 5 循环右移 c = ((c >> 3) \| (c << 5)) & 0xFF
字符替换(置换) 反向置换表 建 rev 映射

自动化辅助:Ciphey 先试一遍

$ ciphey -t "$(xxd -p cipher.txt)"
# 如果密文是标准编码(Base64/URL/XOR 单字节等),Ciphey 直接出答案
# 自定义多层变换(如上面的 4 层)Ciphey 解不了 → 按 Step 2 手工

动手练习

  1. 用 Python 实现:单字节 XOR 穷举、已知明文 XOR 推 key。
  2. 拿一个含 256 字节 S-Box 的样本,写反向表脚本还原。
  3. 用 Ciphey 自动解一串多重复合的编码(Base64 → URL → XOR)。
  4. 用 haiti + CyberChef 练习哈希/编码识别。

深入阅读

  • 仓库:skills/reverse-engineering/crypto-decode-tools.md(Ciphey/CyberChef/haiti/RsaCtfTool 完整用法)
  • 仓库:skills/reverse-engineering/SKILL.md(Common Encryption Patterns 章节)
  • 仓库:skills/reverse-engineering/patterns.md(S-Box/keystream、Fisher-Yates、Xorshift)
  • 仓库:skills/reverse-engineering/patterns-ctf*.md(byte-at-a-time 密码、CVP/LLL、GF(2^8) 高斯消元等)
  • 仓库:skills/reverse-engineering/field-notes.md(S-Box/Keystream、Hex-Encoded 比较)