算法详解
TEA
在加密的过程中要加密的明文使用2个32位无符号整数(2×4字节),秘钥为4个32位无符号整数(4×4字节),更长的明文可通过分为多个4字节为单位的小组分别进行加密(循环)
加密流程示意图:

实战识别
在逆向分析实战中判断TEA
算法的可从其3行核心加密中出现的右移4左移5,两行各有3个小括号互相异或的加密流程和单次加密循环32次以及运算中出现的sum
和delta
变量看出
代码实现
下面是TEA
算法加密过程的C语言函数实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <stdio.h> #include <stdint.h> void encrypt(uint32_t* temp, uint32_t* key) { uint32_t v0 = temp[0], v1 = temp[1]; int sum = 0; uint32_t delta = 0x9e3779b9; for (int i = 0; i < 32; i++) { sum += delta; v0 += ((v1 << 4) + key[0]) ^ (v1 + sum) ^ ((v1 >> 5) + key[1]); v1 += ((v0 << 4) + key[2]) ^ (v0 + sum) ^ ((v0 >> 5) + key[3]); } temp[0] = v0; temp[1] = v1; }
|
下面是TEA
算法解密过程的C语言代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include <stdio.h> #include <stdint.h> void decrypt(uint32_t* temp, uint32_t* key) { uint32_t v0 = temp[0], v1 = temp[1]; int sum = 0x9e3779b9 * 32; uint32_t delta = 0x9e3779b9; for (int i = 0; i < 32; i++) { v1 -= ((v0 << 4) + key[2]) ^ (v0 + sum) ^ ((v0 >> 5) + key[3]); v0 -= ((v1 << 4) + key[0]) ^ (v1 + sum) ^ ((v1 >> 5) + key[1]); sum -= delta; } temp[0] = v0; temp[1] = v1; } int main() { uint32_t key[4] = { }; uint32_t temp[2]; uint8_t flag[32] = { }; for (int i = 0; i < 32; i += 8) { temp[0] = *(uint32_t*)&flag[i]; temp[1] = *(uint32_t*)&flag[i + 4];
decrypt(temp, key);
for (int j = 0; j < 2; j++) { for (int m = 0; m < 4; m++) { printf("%c", temp[j] & 0xff); temp[j] >>= 8; } } } return 0; }
|
魔改
魔改TEA的一些方法:

腾讯TEA
TEA的变种,标准TEA中使用的32轮加密,而腾讯只用了16轮。
CBC模式的TEA
将明文分组与前一个密文分组进行异或运算,然后再进行加密,对于第一组的话就设置一个初始值来和第一组明文异或。
每一轮是取v0和v1,data1, data2和v0,v1异或,异或之后的data1和data2传入指针进行tea加密,之后再将加密之后的赋值回的v0和v1。
逆向方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| void encrypt(uint32_t* v, uint32_t* k) { uint32_t v0 = v[0], v1 = v[1], sum = 0, i;
data1 ^= v0; data2 ^= v1; v0 = data1; v1 = data2;
uint32_t delta = 0x6e75316c; uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; for (i = 0; i < 32; i++) { sum += delta; v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1) ^ (sum + i); v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3) ^ (sum + i); } data1 = v0; data2 = v1; }
|
例题
[HGAME 2023 week1]a_cup_of_tea
阅读反汇编代码,恢复部分函数名和符号如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| int __fastcall main(int argc, const char **argv, const char **envp) { int v3; char *v4; __m128i si128; int Buf2[8]; __int16 v8; __int128 Buf1; __int128 v10[2]; __int16 v11;
Buf2[0] = 0x2E63829D; Buf1 = 0i64; memset(v10, 0, sizeof(v10)); v11 = 0; Buf2[1] = 0xC14E400F; si128 = _mm_load_si128((const __m128i *)&xmmword_1400022B0); Buf2[2] = 0x9B39BFB9; Buf2[3] = 0x5A1F8B14; Buf2[4] = 0x61886DDE; Buf2[5] = 0x6565C6CF; Buf2[6] = 0x9F064F64; Buf2[7] = 0x236A43F6; v8 = '}k'; printf("nice tea!\n> "); scanf("%50s"); sub_1400010B4((unsigned int *)&Buf1, si128.m128i_i32); sub_1400010B4((unsigned int *)&Buf1 + 2, si128.m128i_i32); sub_1400010B4((unsigned int *)v10, si128.m128i_i32); sub_1400010B4((unsigned int *)v10 + 2, si128.m128i_i32); v3 = memcmp(&Buf1, Buf2, 0x22ui64); v4 = "wrong..."; if ( !v3 ) v4 = "Congratulations!"; printf(v4); return 0; }
|
可知Buf2
为密文,si128
是密文,核心加密算法在sub_1400010B4
,其分组进行了四次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| __int64 __fastcall sub_1400010B4(unsigned int *a1, int *a2) { int v2; int v3; int v4; int v5; int v6; unsigned int v7; __int64 v8; unsigned int v9; __int64 result;
v2 = *a2; v3 = 0; v4 = a2[1]; v5 = a2[2]; v6 = a2[3]; v7 = *a1; v8 = 32i64; v9 = a1[1]; do { v3 -= 1412567261; v7 += (v3 + v9) ^ (v2 + 16 * v9) ^ (v4 + (v9 >> 5)); result = v3 + v7; v9 += result ^ (v5 + 16 * v7) ^ (v6 + (v7 >> 5)); --v8; } while ( v8 ); *a1 = v7; a1[1] = v9; return result; }
|
由>>5
和*16(即<<4)
易知其为TEA加密算法,由此即可写出解密代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| #include<stdio.h> #include<string.h> #include<stdint.h> int main() { unsigned int key1 = 0x12345678; unsigned int key2 = 0x23456789; unsigned int key3 = 0x34567890; unsigned int key4 = 0x45678901; unsigned int sum = 0x79bde460; int i = 32; unsigned int enc1 = 0x9F064F64; unsigned int enc2 = 0x236A43F6; int enc[2] = { 0 }; do { enc2 -= (sum + enc1) ^ (key3 + 16 * enc1) ^ (key4 + (enc1 >> 5)); enc1 -= (sum + enc2) ^ (key1 + 16 * enc2) ^ (key2 + (enc2 >> 5)); sum -= -1412567261; --i; } while (i); *enc = enc1; enc[1] = enc2; for (int j = 0; j < 2; j++) { for (int m = 0; m < 4; m++) { printf("%c", enc[j] ); enc[j] >>= 8; } } printf("%c%c", 0x6b, 0x7d); return 0;
}
|
注意在原程序的密文定义中v8紧跟在enc数组的栈内地址后面,其没有进行tea加密,而是以明文形式存储着密文的最后两位内容
[HDCTF 2023]enc

进入main_0函数,经分析可知"input key"
阶段进行的是TEA
加密,并给出了key秘钥的内容(此处笔误写成了RC4)

查看栈段的调用发现v7和v8是连着的

传入RC4加密函数是v7的地址,根据后面if中的条件中对v7和v8的判断得出密文,可用TEA解密代码得出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| #include <stdio.h> #include <stdint.h> void decrypt(uint32_t* temp, uint32_t* key) { uint32_t v0 = temp[0], v1 = temp[1]; int sum = 0xC6EF3720; uint32_t delta = 0x9e3779b9; for (int i = 0; i < 32; i++) { v1 -= ((v0 << 4) + key[2]) ^ (v0 + sum) ^ ((v0 >> 5) + key[3]); v0 -= ((v1 << 4) + key[0]) ^ (v1 + sum) ^ ((v1 >> 5) + key[1]); sum -= delta; } temp[0] = v0; temp[1] = v1; }
int main() { uint32_t key[4] = { 0x12,0x34,0x56,0x78 }; uint32_t temp[2]; temp[0] = 0x60FCDEF7; temp[1] = 0x236DBEC; decrypt(temp, key); for (int j = 0; j < 2; j++) { for (int m = 0; m < 4; m++) { printf("%d", temp[j] & 0xff); temp[j] >>= 8; } } return 0; }
|
得出解密结果为00030004(要以整数输出),即key为3
点开最后处理v6的sub_411302函数

发现是一堆未识别出的数据

打断点进入调试模式

步进运行程序将loc_0041D000地址的数据分析为指令,再在loc_0041D000
处按P将其分析为函数,分析伪代码可知该函数为RC4加密

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| #include <stdio.h> #include <stdlib.h> void swap(unsigned char* a, unsigned char* b) { unsigned char temp = *a; *a = *b; *b = temp; } void RC4_dec(unsigned char* ciphertext, int length, const char* key) { int i, j; unsigned char* S; unsigned char k[256]; int keylen = 0; while (key[keylen] != '\0') { keylen++; } for (i = 0; i < 256; i++) { k[i] = key[i % keylen]; } S = (unsigned char*)malloc(256 * sizeof(unsigned char)); for (i = 0; i < 256; i++) { S[i] = i; } j = 0; for (i = 0; i < 256; i++) { j = (j + S[i] + k[i]) % 256; swap(&S[i], &S[j]); } i = 0; j = 0; for (int n = 0; n < length; n++) { i = (i + 1) % 256; j = (j + S[i]) % 256; swap(&S[i], &S[j]); int t = (S[i] + S[j]) % 256; ciphertext[n] ^= S[t]; } free(S); } int main() { unsigned char res[] = { 0x0F, 0x94, 0xAE, 0xF2, 0xC0, 0x57, 0xC2, 0xE0, 0x9A, 0x45, 0x37, 0x50, 0xF5, 0xA0, 0x5E, 0xCB, 0x2C, 0x16, 0x28, 0x29, 0xFE, 0xFF, 0x33, 0x46, 0x0E, 0x57, 0x82, 0x22, 0x52, 0x26, 0x2B, 0x6E, 0xE4, 0x82, 0x24 }; const char key[] = "you_are_master"; RC4_dec(res, sizeof(res), key); for (int i = 0; i < sizeof(res); i++) printf("%c", res[i]); return 0; }
|
用RC4代码解密即可
