1. TEA家族简介
TEA(Tiny Encryption Algorithm)是一类轻量级分组加密算法,特点是结构简单、代码量小、实现方便。
它的家族主要包括:
- TEA
- XTEA(eXtended TEA)
- XXTEA(Corrected Block TEA)
在 CTF 逆向题中,TEA 家族常被用来:
- 对 flag 做二次加密
- 对字符串或配置表做保护
- 混入壳、校验逻辑、JNI/native 层
- 与异或、位运算、字节重排组合形成魔改题
TEA 家族之所以常见,是因为它的代码非常短,出题人改起来方便;但同时它又有明显的结构特征,只要掌握识别方法,逆向恢复并不难。
2. TEA 的基本原理
2.1 基本参数
TEA 的标准参数如下:
- 分组长度:64 bit
- 密钥长度:128 bit
- 轮结构:通常写作 32 轮循环(每轮更新左右各一次)
- 常量 delta:
0x9E3779B9
这里的 delta 来源于黄金分割相关常数,用于打破轮函数的对称性。
通常把 64 bit 明文拆成两个 32 bit:
v0v1
128 bit 密钥拆成四个 32 bit:
k0k1k2k3
2.2 标准 TEA 加密公式
TEA 每轮都更新 sum,然后交替更新左右两部分。
加密伪代码:
sum = 0;
for (i = 0; i < 32; i++) {
sum += delta;
v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
}
解密时完全反过来:
sum = delta * 32;
for (i = 0; i < 32; i++) {
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
sum -= delta;
}
3. XTEA:TEA 的扩展版本
XTEA(eXtended TEA)是对 TEA 的改进版本,主要是修改了轮函数和 key 的选取方式。
它和 TEA 最明显的区别在于:
- key 不再固定写死成
(k0, k1)和(k2, k3) - 而是通过
sum的低位和高位动态选择 key 下标
标准 XTEA 常见写法:
sum = 0;
for (i = 0; i < 32; i++) {
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]);
sum += delta;
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum >> 11) & 3]);
}
解密:
sum = delta * 32;
for (i = 0; i < 32; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum >> 11) & 3]);
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]);
}
XTEA 在逆向中的识别点
如果你在伪代码里看见:
sum & 3(sum >> 11) & 3((x << 4) ^ (x >> 5)) + x
那大概率就是 XTEA,不是普通 TEA。
4. XXTEA:可变长度块加密
XXTEA 和 TEA / XTEA 最大的不同在于:
- 它不再固定只处理两个 32 bit
- 它可以处理多个 32 bit 单元组成的数组
所以 XXTEA 非常适合在 CTF 中用来加密整段字符串、整块数据。
它的核心思路是让一个数组中的各个元素互相影响,轮函数更复杂,常见代码里会看到:
n > 1rounds = 6 + 52 / ne = (sum >> 2) & 3MX宏
经典 XXTEA 代码里最典型的特征就是:
z = v[p] += MX;
y = v[p + 1];
或者末尾还有:
z = v[n - 1] += MX;
XXTEA 的逆向特征非常强
只要你看到下面这些东西同时出现,基本就可以判定是 XXTEA:
6 + 52 / n0x9E3779B9- 数组循环更新
((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (k[(p&3)^e] ^ z))
import struct
MASK = 0xffffffff
DELTA = 0x9E3779B9
def _mx(z, y, sum_, k, p, e):
return ((((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((sum_ ^ y) + (k[(p & 3) ^ e] ^ z))) & MASK
def xxtea_encrypt_u32(v, k):
n = len(v)
if n < 2:
return v[:]
rounds = 6 + 52 // n
sum_ = 0
z = v[n - 1]
v = v[:]
while rounds > 0:
rounds -= 1
sum_ = (sum_ + DELTA) & MASK
e = (sum_ >> 2) & 3
for p in range(n - 1):
y = v[p + 1]
v[p] = (v[p] + _mx(z, y, sum_, k, p, e)) & MASK
z = v[p]
y = v[0]
v[n - 1] = (v[n - 1] + _mx(z, y, sum_, k, n - 1, e)) & MASK
z = v[n - 1]
return v
def xxtea_decrypt_u32(v, k):
n = len(v)
if n < 2:
return v[:]
rounds = 6 + 52 // n
sum_ = (rounds * DELTA) & MASK
v = v[:]
while sum_ != 0:
e = (sum_ >> 2) & 3
y = v[0]
for p in range(n - 1, 0, -1):
z = v[p - 1]
v[p] = (v[p] - _mx(z, y, sum_, k, p, e)) & MASK
y = v[p]
z = v[n - 1]
v[0] = (v[0] - _mx(z, y, sum_, k, 0, e)) & MASK
y = v[0]
sum_ = (sum_ - DELTA) & MASK
return v
def bytes_to_u32_list(data):
pad = (4 - len(data) % 4) % 4
data = data + b'\x00' * pad
return list(struct.unpack('<{}I'.format(len(data) // 4), data)), len(data) - pad
def u32_list_to_bytes(v):
return struct.pack('<{}I'.format(len(v)), *v)
def fix_key(key_bytes):
if len(key_bytes) < 16:
key_bytes = key_bytes.ljust(16, b'\x00')
elif len(key_bytes) > 16:
key_bytes = key_bytes[:16]
return list(struct.unpack('<4I', key_bytes))
def xxtea_encrypt(data, key_bytes):
v, orig_len = bytes_to_u32_list(data + struct.pack('<I', len(data)))
k = fix_key(key_bytes)
enc = xxtea_encrypt_u32(v, k)
return u32_list_to_bytes(enc)
def xxtea_decrypt(data, key_bytes):
v = list(struct.unpack('<{}I'.format(len(data) // 4), data))
k = fix_key(key_bytes)
dec = xxtea_decrypt_u32(v, k)
raw = u32_list_to_bytes(dec)
orig_len = struct.unpack('<I', raw[-4:])[0]
return raw[:orig_len]
if __name__ == "__main__":
key = b"1234567890abcdef"
data = b"hello xxtea reverse"
enc = xxtea_encrypt(data, key)
dec = xxtea_decrypt(enc, key)
print("enc =", enc.hex())
print("dec =", dec)
Comments NOTHING