TEA 家族为什么在这些领域流行
TEA(Tiny Encryption Algorithm)的设计目标是"用最少的代码实现分组加密"——核心加密循环不到 20 行 C 代码。这使它成为手游协议、IoT 固件、嵌入式设备的标配加密:代码量小、速度快、资源占用低。
逆向这些领域时,TEA/XXTEA 的识别率是 AES 之外最高的。
识别:黄金分割常量是路标
TEA 的灵魂常量是 0x9E3779B9(2^32/φ,黄金分割比的无理数近似):
void tea_encrypt(uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1], sum = 0;
uint32_t delta = 0x9E3779B9; // ← 看到这个常量,TEA 家族实锤
for (int i = 0; i < 32; i++) { // 32 轮
sum += delta;
v0 += ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
v1 += ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
}
v[0] = v0; v[1] = v1;
}IDA 里搜 0x9E3779B9(ARM 汇编里是 movw/movt 组合加载),命中即 TEA 家族。再凑近看循环结构确认变体:
- TEA:32 轮,上面的结构
- XTEA:轮数类似,但 key 的使用顺序变了(
k[sum & 3]这种索引方式) - XXTEA:轮数不固定(
6 + 52/n),处理变长块,结构更复杂
还原:标准实现直接可用
TEA 家族没有魔改传统(常量就是算法的一部分),识别后直接用标准实现:
import struct
def tea_decrypt(v, k):
v0, v1 = v
delta = 0x9E3779B9
total = (delta * 32) & 0xFFFFFFFF # 注意 32 位回绕
for _ in range(32):
v1 = (v1 - (((v0 << 4) + k[2]) ^ (v0 + total) ^ ((v0 >> 5) + k[3]))) & 0xFFFFFFFF
v0 = (v0 - (((v1 << 4) + k[0]) ^ (v1 + total) ^ ((v1 >> 5) + k[1]))) & 0xFFFFFFFF
total = (total - delta) & 0xFFFFFFFF
return v0, v1Python 移植的唯一坑是 32 位回绕:C 的 uint32 溢出自动回绕,Python 要手动 & 0xFFFFFFFF,每个运算都不能漏。
密钥获取:老套路
TEA 的 key 是 4 个 uint32(16 字节)。获取路径:
- SO 里硬编码:IDA 搜密钥使用点附近的内存引用
- hook 加密函数:找到 encrypt 函数地址后 hook 参数,key 指针直接读
- 手游特有:很多手游把 key 分段藏在 so 的不同位置,运行时拼接——hook 使用点最稳
精髓:手游协议分析的完整链路
以手游协议为例,TEA 在其中的位置:
游戏逻辑数据 → 序列化(protobuf/自定义) → TEA/XXTEA 加密 → 自定义协议头 → TCP 发送逆向链路:
- tcpdump 抓包拿到密文流(私有协议,参考"长连接抓包"篇)
- 识别协议头:长度、类型、序列号
- 识别加密层:搜
0x9E3779B9确认 TEA - 拿 key:hook 加密函数
- 解密后对拍:解密结果应该是结构化数据(protobuf 或自定义二进制),解出来是乱码 = key 错或模式错(ECB/CBC 类分组模式问题)
精髓:XXTEA 的长度陷阱
XXTEA 处理变长数据,但有个细节:它按 uint32 数组处理,明文长度不是 4 的倍数时的填充方式各实现不同(补 0、补长度字节、PKCS7 风格都有)。对拍时先用 4 的倍数长度的数据验证算法,再研究尾巴怎么处理。
总结
- TEA 家族识别靠
0x9E3779B9黄金分割常量 - 变体区分:TEA 固定 32 轮,XTEA 看 key 索引,XXTEA 变长
- Python 移植的坑全在 32 位回绕
- 手游链路:协议头 → TEA 层 → 序列化层,逐层剥离
交流微信:run1255