第 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 手工
动手练习
- 用 Python 实现:单字节 XOR 穷举、已知明文 XOR 推 key。
- 拿一个含 256 字节 S-Box 的样本,写反向表脚本还原。
- 用 Ciphey 自动解一串多重复合的编码(Base64 → URL → XOR)。
- 用 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 比较)