113 lines
9.8 KiB
Markdown
113 lines
9.8 KiB
Markdown
---
|
||
name: re-whitebox
|
||
description: >
|
||
白盒加密分析:白盒实现识别、密钥提取。
|
||
触发词:白盒、whitebox、白盒加密、密钥隐藏
|
||
capabilities: [crypto-identification, key-extraction]
|
||
---
|
||
|
||
# 白盒加密分析
|
||
|
||
## 何时使用 / 何时不用
|
||
|
||
- 用:加密实现"看不到密钥"——无标准库 API、密钥不在文件/内存/资源里显式存在(密钥被藏进实现本身)
|
||
- 用:反编译看到大段只读查找表(几百 KB 起)+ 函数体几乎全是查表逻辑,没有标准算法结构(无 Crypt* 调用、无标准 S-box 常量、无 OpenSSL 符号)
|
||
- 用:DRM/许可证/App 内加密、固件授权校验里怀疑白盒实现(Chow 类白盒 AES、白盒 SM4 等)
|
||
- 不用:普通加密实现(走 [[re-crypto-id]] → [[re-crypto-keys]] → [[re-crypto-decrypt]] 常规链路)
|
||
- 不用:算法与密钥都已知(直接解密)
|
||
- 前置检查:加壳/混淆样本先脱壳去混淆([[re-anti-analysis]]),白盒表与代码被壳层污染时无法分析
|
||
|
||
## 工具准备
|
||
|
||
静态为主(免沙箱);涉及"表加密运行时解密"时需动态取表(默认沙箱,[[re-analyze/platform-tips]] 最高原则)。所有工具先验证再使用。
|
||
|
||
### python3 —— 表提取与密钥推导脚本
|
||
|
||
- 安装与验证见 [[re-proto-rev]] 工具准备(python3);大表处理建议 numpy(`pip install numpy`)
|
||
|
||
### 反编译器 —— 定位查表代码与表地址
|
||
|
||
- [[re-ghidra]](默认,导入→自动分析→反编译);[[re-ida]] / [[re-radare2]] 按 `RE_DECOMPILER` 会话变量
|
||
- 验证见对应技能工具准备;表地址/大小以反编译工具的 Data 视图为准
|
||
|
||
### [[re-angr]] —— 可选(符号执行辅助索引关系求解)
|
||
|
||
- 全平台: `pip install angr`
|
||
- 验证: `python3 -c "import angr; print(angr.__version__)"`
|
||
- 用途: 查表代码被混淆/索引关系复杂时,符号执行直接求"输入字节 → 表项"的对应关系
|
||
|
||
### [[re-memdump]] —— 表加密(运行时解密)时动态取表
|
||
|
||
- 默认转储优先(gcore,见 [[re-analyze/platform-tips]]「直读 vs 转储」决策表)
|
||
- 验证见 [[re-memdump]] 工具准备
|
||
|
||
## 操作步骤
|
||
|
||
按顺序执行,每步产物(表文件/脚本/恢复密钥)存档 sha256 + 路径,供报告引用([[re-ioc]])。
|
||
|
||
1. **白盒实现识别(查找表特征、无标准库调用的大段查表代码)**:
|
||
```sh
|
||
# 静态侧: 找 .rodata/.data 里的查表候选——256 条 4/8 字节条目、连续、值分布均匀
|
||
objdump -h sample.bin | grep -E 'rodata|data'
|
||
# 反编译确认: 函数体是否"state 异或常量 → 查表 → 组合"结构、有无 Crypt* / OpenSSL 符号
|
||
```
|
||
- 特征清单: ① 大段只读表(白盒 AES 一轮 4 张 256×4 字节表,Chow 类编码后表集可达 700KB-1MB+)② 函数体以查表为主、无标准轮函数形态 ③ 无标准常量([[re-crypto-id]] 的 AES S-box/Rcon 指纹不命中)④ 无密码学 API 导入([[re-imports]] 干净)
|
||
- 反编译确认: 对表地址做 xref(Ghidra `Ctrl+Shift+F`),看引用函数的形态——"查表 + 组合"而非"轮函数 + 常数表"即为白盒候选
|
||
- 与 [[re-crypto-id]] 的分工: 常规指纹命中 → 标准算法链路;指纹全不命中但有大表 → 白盒假设(见坑 4)
|
||
|
||
2. **表提取**:
|
||
- Ghidra: 选中表数据段右键 Export(或脚本 `getBytes` 整段 dump);IDA: `ida_bytes.get_bytes(start, size)` 导出
|
||
- 解析脚本(按条目宽度/字节序):
|
||
```python
|
||
import struct
|
||
data = open('tables.bin','rb').read()
|
||
for off in range(0, len(data), 4): # 4 字节条目(AES T 表): 每条 [S(x)<<24 | ...]
|
||
v = struct.unpack_from('<I', data, off)[0]
|
||
# 记录值分布/每字节位模式,输出供步骤 3 分析
|
||
```
|
||
- 记录: 每张表地址、尺寸、条目宽度、被哪些函数引用——表↔函数↔轮数的对应关系是密钥恢复的地图
|
||
|
||
3. **密钥恢复(表逆向 → 密钥推导,或已知明文攻击)**:
|
||
- **思路 A 表逆向**: 白盒表里嵌着密钥——首轮表 `T0[x]` 编码了 `S(x) ^ k0`(x 为输入字节),末轮表编码最后一轮密钥;编码层(仿射/字节映射)简单时直接反解 `k0`
|
||
- **思路 B 已知明文攻击**: 固定输入字节查表得输出,构造"输入→输出"对,编码层可建模时求解(仿射编码解线性方程、字节映射查碰撞)
|
||
- **思路 C 动态取表**: 表自身加密(见坑 3)→ 运行时 [[re-memdump]] 转储后在内存搜表特征,取解密后的表再走思路 A/B
|
||
- **思路 D DCA(差分计算分析)**: 编码层复杂(Chow 全编码)手工反推不现实 → 用 SideChannelMarvels 的 daredevil 对白盒运行轨迹做 DCA(差分计算分析)恢复密钥
|
||
- 密钥假设验证: 恢复出的候选密钥先与标准实现输出对照(步骤 4),不要直接信
|
||
|
||
4. **与标准实现对照(同一算法不同实现)**:
|
||
```python
|
||
from Crypto.Cipher import AES # pip install pycryptodome
|
||
key = bytes.fromhex('<候选密钥>')
|
||
c = AES.new(key, AES.MODE_ECB)
|
||
print(c.encrypt(b'hello whitebox!!').hex())
|
||
# 与样本白盒实现对同一明文/密文的输出比对
|
||
```
|
||
- 对照确认: ① 算法与参数(AES-128/192/256、分组 16 字节、ECB/CBC 模式、轮数)② 候选密钥正确性 ③ 白盒表与标准 T 表/轮函数的对应关系
|
||
- 变体检查: SM4(32 轮、8 位分组字节)/ DES 白盒/自定义——按分组大小与轮数先定族,再套对应结构(见坑 4)
|
||
|
||
5. **报告(密钥/算法/影响)**:
|
||
- 内容: 密钥(16/24/32 字节 hex)、算法与变体、提取方法(表逆向/已知明文/DCA/动态)、影响评估(密钥泄露 → 全部数据/流量可解密、DRM 可伪造)、证据(表地址、sha256、对照输出)
|
||
- 密钥提取后即可转 [[re-crypto-decrypt]] 解密目标数据/流量;密钥与表哈希进 [[re-ioc]]
|
||
|
||
## 跨域联合
|
||
|
||
- [[re-protocol]]:本技能是该网关加密链路的白盒分支——加密识别/密钥/解密三件套遇到"查表加密"时转入
|
||
- [[re-crypto-id]]:前置——常量表指纹(S-box/Rcon/CRC)全部不命中的"查表加密"→ 转本技能;白盒表也会被指纹工具误认(见坑 4)
|
||
- [[re-crypto-keys]]:常规密钥提取路径(硬编码/内存搜索)失效时的白盒分支——密钥在表里不在显式位置
|
||
- [[re-crypto-decrypt]]:下游——恢复密钥后解密数据/流量
|
||
- [[re-binary-core]]:反编译底座([[re-ghidra]] / [[re-ida]] / [[re-radare2]])
|
||
- [[re-angr]]:可选——符号执行辅助表索引/编码层求解
|
||
- [[re-deobfuscate]]:白盒 + 混淆组合时先还原控制流(坑 2)
|
||
- [[re-memdump]]:表加密(运行时解密)时动态取表(坑 3)
|
||
- [[re-malware]]:恶意样本通信加密/勒索的白盒实现分析
|
||
- 引用 [[re-analyze/platform-tips]] 静态优先原则与「直读 vs 转储」决策表(动态取表时机)
|
||
|
||
## 常见坑与陷阱
|
||
|
||
- **白盒表巨大(内存/时间)**:现象——dump/解析几百 MB 表后脚本卡死、内存爆掉,分析拖几天;原因——Chow 类白盒表集 700KB-1MB+,叠加编码/混淆后更大;对策——分块处理(按表地址范围分片读)、numpy 向量化解析、先用 xref 过滤"只分析被引用的表";表文件先压缩存档再分析
|
||
- **混淆 + 白盒组合**:现象——找到表但查表代码全被控制流平坦化/花指令包住,索引关系读不出来;原因——白盒与代码混淆叠加([[re-anti-analysis]] 域手法,防自动化分析);对策——先 [[re-deobfuscate]] 还原控制流再做索引分析;或跳过静态还原,用 [[re-angr]] 符号执行直接求"输入字节 → 表项"对应关系;两条路都走不通再上 DCA(不依赖代码语义)
|
||
- **表加密(二次保护)**:现象——静态 dump 的表熵高/全是密文,反编译却看到表被解密的前置循环;原因——白盒表自身加密,运行时才解密(防静态提取);对策——[[re-memdump]] 动态转储,转储时机在解密循环执行之后([[re-memdump]] 坑: 转储时机过早拿不到表、看到解密数据立刻保存);取到明文表后再走步骤 3
|
||
- **算法变体误判**:现象——按 AES 白盒结构提取的表完全套不上,白折腾;原因——实现是 SM4/DES 白盒或自定义变体,编码方式/轮结构不同;对策——步骤 4 先定族(分组大小、轮数、与标准实现对照输出),确认算法后再按对应结构提取表;编码层不同(仿射/字节映射)导致表逆向困难时用 DCA 而非手工反推
|
||
- **表指纹误命中**:现象——在表里"搜到"疑似 S-box/常量,误判标准算法走错链路;原因——白盒编码后的表可能碰巧包含部分标准常量片段(或被静态库未引用代码污染);对策——命中后必须 xref 确认被加密路径引用([[re-crypto-id]] 同样有此坑),再对照轮数与分组大小
|
||
- **白盒实现三流派(先定流派再选攻法)**:现象——拿到白盒目标不知用哪种攻击方法;原因——白盒 AES 有三类实现,攻法不同:①**Chow 查找表**(2004 BGE 代数攻击攻克、2008 Michiels 通用化、2013 Lepoint 以 2^22 复杂度恢复密钥);②**Bringer 扰乱项**(插入额外扰乱方程+线性编码,Xiao-Lai 实现 2010 被 Mulder 攻破);③**Biryukov ASASA**(Affine-Sbox 交替层,代数/统计/分解攻击);对策——先识别流派:Chow 特征 = 查找表集(TBoxes=SubBytes+AddRoundKey 合并、TyiTableBoxes=MixColumns 预计算、ioInvTable 随机混淆、shiftTab 把 ShiftRows 嵌入表生成);ASASA = 仿射-非线性交替层;Chow 结构提取密钥走代数攻击(合并特定查找表去非线性);DFA(差分故障分析)从 MixColumns 入手是经典攻击入口
|