2052 字
10 分钟
TEA、XTEA、XXTEA 加密算法逆向识别总结

原文:cnblogs.com/Tree-24/p/17330286.html 主题:逆向中 TEA 系列分组密码的原理、完整实现(带注释)与识别方法,附 CTF 例题


一、TEA(Tiny Encryption Algorithm)#

简介:分组密码。明文/密文块 64 比特(2 个 uint32),密钥 128 比特(4 个 uint32)。每轮把轮密钥累加器 sum 增加一个 Delta(黄金分割率)常量,使每轮变换不同;迭代 32 轮。特征常量 delta = 0x9E3779B9(即 -0x61C88647)是识别 TEA 家族的第一特征。

加密实现(带注释)#

#include <stdint.h>
/**
* TEA 加密(32 轮)
* @param v 64 位明文块:v[0]=高 32 位,v[1]=低 32 位(加密后被密文覆盖)
* @param k 128 位密钥:k[0]~k[3],4 个 uint32
*/
void encrypt(uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1]; // 取出明文,拆成左右两半 v0、v1
uint32_t sum = 0; // 轮密钥累加器,每轮 +delta
uint32_t delta = 0x9e3779b9; // ★ 黄金分割率常量(识别特征!)
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; // 缓存密钥,循环里少访存
for (int i = 0; i < 32; i++) { // 32 轮
sum += delta; // 每轮 sum 增加 delta → 每轮"轮密钥"不同
// 用 v1 更新 v0:三部分异或 = (v1<<4 + k0) ^ (v1+sum) ^ (v1>>5 + k1)
// 即"左移 4 位 + k0"、"右移 5 位 + k1"、"v1 + sum" 三路混合,叠加到 v0
v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
// 用刚更新过的 v0 再更新 v1(左右两半交替更新 = Feistel 结构)
v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
}
v[0] = v0; v[1] = v1; // 写回密文
}
/**
* TEA 解密:与加密完全逆序
* sum 的初值 = delta × 32 = 0xC6EF3720(加密结束时 sum 的值),每轮往回减
*/
void decrypt(uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1];
uint32_t delta = 0x9e3779b9;
uint32_t sum = delta * 32; // ★ 解密特征值 0xC6EF3720 由此而来
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
for (int i = 0; i < 32; i++) {
// 顺序相反:先还原 v1(此时 v0 还是加密后更新过的值),再还原 v0
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
sum -= delta; // sum 逆着减回 0
}
v[0] = v0; v[1] = v1;
}

理解要点:TEA = 左右两半 + 32 轮 + 每轮 sum += delta 生成轮密钥 + 移位异或混合。逆向时看到 (v<<4)(v>>5) 的移位异或组合 + 0x9E3779B9,基本可锁定 TEA。


二、XTEA(TEA 扩展版)#

简介:同样是 64 位块 / 128 位密钥,但每轮按 sum 的值轮换取用 key[0..3](而不是固定 k0..k3),使密钥使用更均匀,安全性更高。

加密实现(带注释)#

#include <stdint.h>
/**
* XTEA 加密(轮数可变,建议 32 轮)
* @param num_rounds 迭代轮数
* @param v 64 位明文块(2 个 uint32)
* @param key 128 位密钥(4 个 uint32)
*/
void encrypt(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0 = v[0], v1 = v[1], sum = 0, delta = 0x9E3779B9;
for (i = 0; i < num_rounds; i++) {
// (((v1<<4) ^ (v1>>5)) + v1):左移与右移结果异或后再加自身,是"混合"核心
// ^ (sum + key[sum & 3]):sum 的低 2 位作下标选 key[0..3] → 轮换取钥
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
sum += delta;
// key[(sum >> 11) & 3]:取 sum 的第 11、12 位做下标,与上一行错开选 key
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
}
v[0] = v0; v[1] = v1;
}
/**
* XTEA 解密:逆向执行
* sum 初值 = delta × num_rounds,每轮往回减
*/
void decrypt(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0 = v[0], v1 = v[1], delta = 0x9E3779B9, sum = delta * num_rounds;
for (i = 0; i < num_rounds; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
}
v[0] = v0; v[1] = v1;
}

与 TEA 的区别:① 混合部分从 (v<<4)+k 变成 ((v<<4) ^ (v>>5)) + v;② 密钥按 sum & 3 / (sum>>11) & 3 轮换选取。逆向识别点:出现 key[sum & 3] 这类用常量当下标轮换取钥 → XTEA/XXTEA。


三、XXTEA#

简介:运算更复杂,块大小和密钥长度任意(对明文分块逐个加密)。块间首尾相接,用前后两个块做混合。

加密实现(带注释)#

#include <stdint.h>
#define DELTA 0x9E3779B9
// MX 混合宏:不是独立函数,直接内联展开,依赖外部变量:
// y = 后一个块(v[p+1]),z = 当前块旧值(v[p]),sum = 轮密钥累加器,
// p = 当前块下标,e = 由 sum 决定的密钥轮换因子,k = 密钥
// 含义:(z>>5 ^ y<<2) + (y>>3 ^ z<<4) ← 前后两块的移位异或混合
// ^ (sum ^ y) ← 与轮密钥相关项
// + (k[(p&3)^e] ^ z) ← 密钥按 (p&3)^e 轮换选取,再异或 z
#define MX (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[(p & 3) ^ e] ^ z)
/**
* XXTEA 加密
* @param v 明文块数组(uint32 数组,长度任意,≥2)
* @param len v 中块的个数
* @param k 密钥数组(通常 4 个 uint32)
*/
void xxtea_encrypt(uint32_t *v, uint32_t len, uint32_t *k) {
uint32_t n = len - 1; // 最后一个块的下标
uint32_t y, z, sum = 0, e, p, q;
q = 6 + 52 / len; // 轮数:块越少轮数越多(保证整体安全性)
while (q-- > 0) {
sum += DELTA;
e = sum >> 2 & 3; // 由 sum 决定的密钥轮换因子
for (p = 0; p < n; p++) { // 正向处理除最后一个外的所有块
y = v[p + 1]; // y = 后一个块
z = v[p] += MX; // 当前块 += MX(宏里 z 取 v[p] 旧值参与运算)
}
y = v[0]; // 首尾相接:最后一个块的 y 取 v[0]
z = v[n] += MX;
}
}
/**
* XXTEA 解密:反向执行
* sum 初值 = q × DELTA(加密结束时的值),每轮往回减直到 0
*/
void xxtea_decrypt(uint32_t *v, uint32_t len, uint32_t *k) {
uint32_t n = len - 1;
uint32_t y, z, sum, e, p, q;
q = 6 + 52 / len;
sum = q * DELTA;
while (sum != 0) {
e = sum >> 2 & 3;
for (p = n; p > 0; p--) { // 反向处理所有块
z = v[p - 1]; // z = 前一个块
y = v[p] -= MX;
}
z = v[n];
y = v[0] -= MX;
sum -= DELTA;
}
}

理解要点

  • XXTEA 的混合宏 MX 同时用到前一个块 z 和后一个块 y,这是它和 TEA/XTEA 的最大区别;
  • 密钥下标 (p & 3) ^ e 同时依赖位置 p 和轮密钥因子 e;
  • 注意:解密输出可能带填充字节(加密时为了凑整块填充的),需按数据长度截断。

逆向识别点:出现 (z>>5 ^ y<<2) 这种”前块移位异或后块”的结构 → XXTEA。


四、逆向识别特征汇总#

  1. 存在针对 64bit / 128bit 数据的操作(输入 msg 与 key),通常用无符号 32 位数组表示;
  2. 先移位后异或的混合变换:
    • (z>>5 ^ y<<2) 前后块混合 → XXTEA
    • (v<<4)(v>>5) 移位异或 → TEA / XTEA
  3. 混合结果叠加到另一个值上、两半相互叠加(Feistel 结构);
  4. 常量下标轮换取钥key[sum & 3] / key[(sum>>11) & 3] / key[(p&3)^e])→ XTEA / XXTEA;
  5. 定义 delta 常量且不随输入变化:未魔改就是 0x9E3779B9;魔改的话(如例题中的 2562562560x830A5376^0x1D3D2ACF)需从代码中提取。

逆向流程:识别算法(看特征)→ 编写对应解密脚本(Python 注意每步 & 0xffffffff)→ 提取 key / delta / 轮数 → 解出明文。


五、例题与 flag#

1. [GDOUCTF2023]tea(XTEA 变体,delta 魔改为 256256256)#

  • 64 位无壳,IDA64 交叉引用 “you are right” 定位关键函数;
  • 读入 16 进制数组 v8[10],key 为 [2233,4455,6677,8899]
  • Python 坑:C 无符号溢出自动回绕,Python 会升为长整数,解密每步必须 & 0xffffffff,否则输出乱码;
  • flagNSSCTF{hzCtf_94_re666fingcry5641qq}

2. [津门杯2021]GoodRe(TEA,delta 魔改为 0x830A5376 ^ 0x1D3D2ACF#

  • TEA 运算以函数形式呈现,逐个分析并重命名简化反汇编;
  • 密钥 [17,17,17,17],每 2 个 32 位一组解密后拼 hex 大写;
  • flagflag{7DEA3F6D3B3D6C0C620864ADD2FA2AE1A61F2736F0060DA0B97E8356D017CE59}

六、总结#

识别口诀:看到 0x9E3779B9(或魔改 delta)+ 移位异或混合变换 + 相互叠加 Feistel 结构 → TEA 家族; key[sum&3] 轮换取钥 → XTEA/XXTEA;(z>>5 ^ y<<2) 前后块混合 → XXTEA。 解密要点:逆序执行、sum 从 delta×轮数 往回减、Python 无符号 32 位运算 & 0xffffffff

参考:TEA 系列加密解密、维基百科 TEA