一,算法特征
tea算法的主要特征表现在sum和delta变量,以及3行核心加密中出现的右移4左移5,两行各有3个小括号互相异或
在题目中看到这些特征时就应该警醒这是tea相关算法
delta的值一般为0x9E3779B9(-0x61C88647),但题目中往往会改变它的值,并不影响算法的逆向
二,加解密脚本
(一)tea加密脚本
注释写的很详细,可以辅助了解原理
void tea_enc(uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1]; // v0、v1分别是明文的左、右半部分
uint32_t sum = 0; // sum用作加密过程中的一个累加变量
uint32_t delta = 0xd33b470; //作为sum每次累加的变化值,题目中往往会修改此值
for (int i = 0; i < 32; i++) { // tea加密进行32轮
//以下3行是核心加密过程,题目中往往会对部分细节做出修改(但由于异或的对称性质,根本不需要记,写解密函数时照抄就行了)
sum += delta;
v0 += ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
v1 += ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
}
// v0和v1只是加密的临时变量,因此加密后的内容要还给v数组
v[0] = v0;
v[1] = v1;
}
(二)tea解密脚本
void tea_dec(uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1]; // v0、v1分别是密文的左、右半部分
uint32_t delta = 0xd33b470; //作为sum每次累加的变化值,题目中往往会修改此值
uint32_t sum = 32 * delta; //此处需要分析32轮加密结束后sum的值与delta的变化, 以此处加密为例子,32轮每次sum+=delta,因此最后sum=32*delta
for (int i = 0; i < 32; i++) { // tea加密进行32轮
//根据加密时的顺序颠倒下面3行的顺序,将加法改为减法(异或部分都是整体,不用管),就是逆向解密过程
v1 -= ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
v0 -= ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
sum -= delta;
}
// 因此解密后的内容要还给v数组
v[0] = v0;
v[1] = v1;
}
(三)tea解题实例
下面用两道比较经典的tea算法实战例题来搞清楚这个脚本如何套用,建议自己先尝试一下然后再看wp,两题都看一下,因为tea算法的题目经常对明文形式、输出格式等做变换,全部例举出来很废精力,还是要学会自己举一反三
省略了密钥k和密文input的获取过程,仔细分析main函数中调用tea解密的部分,理解如何套用编写好的tea解密函数
重点抓准——无论明文是什么形式、有多长,解密时一定是每次传入两个uint32_t(32位无符号整数)
(1)例题1->[MoeCTF 2022]ezTea
#include <stdint.h>
#include <stdio.h>
void tea_dec(uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1]; // v0、v1分别是密文的左、右半部分
uint32_t delta = 0xd33b470; //作为sum每次累加的变化值,题目中往往会修改此值
uint32_t sum = 32 * delta; //此处需要分析32轮加密结束后sum的值与delta的变化, 以此处加密为例子,32轮每次sum+=delta,因此最后sum=32*delta
for (int i = 0; i < 32; i++) { // tea加密进行32轮
//根据加密时的顺序颠倒下面3行的顺序,将加法改为减法(异或部分都是整体,不用管),就是逆向解密过程
v1 -= ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
v0 -= ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
sum -= delta;
}
// 解密后的内容要还给v数组
v[0] = v0;
v[1] = v1;
}
int main() {
// k为加解密密钥,4个32位无符号整数,密钥长度为128位
uint32_t k[4] = {1, 2, 3, 4};
// v为要加解密的数据,两个32位无符号整数
//但是稍微难一点点的都不会直接加密两个uint32_t,除非签到题。像这里的例子就是给了32个uint8_t
//(常见的题目还有给几个uint32_t的,其实无非是拆开写成0x17、0x65...和连着写成0x1765的区别)
//在后面的循环里每次传两组,每组4个组成uint32_t用于tea算法
int8_t input[33] = {0x17, 0x65, 0x54, 0x89, 0xed, 0x65, 0x46, 0x32, 0x3d, 0x58, 0xa9, 0xfd, 0xe2, 0x5e, 0x61, 0x97,
0xe4, 0x60, 0xf1, 0x91, 0x73, 0xe9, 0xe9, 0xa2, 0x59, 0xcb, 0x9a, 0x99, 0xec, 0xb1, 0xe1, 0x7d};
for (int i = 0; i < 32; i += 8) {
//每组4个组成uint32_t用于tea算法,tea算法每次加解密操作的v一定是两个uint_32,至于怎么传入两个uint_32,题目有各种呈现方式,需要做题者自行分析
uint32_t v[2] = {*(uint32_t*)&input[i], *(uint32_t*)&input[i + 4]};
tea_dec(v, k);
// tea输出字符的固定算法,外层循环两次是因为明文分为左半和右半两个uint32_t
// 内层循环4次是因为一个字符占1个字节即8位,每次&0xff可以摘下最后1字节打印出对应ASCII字符,然后>>8准备下一字节
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 4; k++) {
printf("%c", v[j] & 0xff);
v[j] >>= 8;
}
}
}
return 0;
}
(2)例题2->[GWCTF 2019]xxor
#include <stdint.h>
#include <stdio.h>
void tea_dec(uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1]; // v0、v1分别是密文的左、右半部分
uint32_t delta = 1166789954; //作为sum每次累加的变化值,题目中往往会修改此值
uint32_t sum = 64 * delta; //题目中的tea加密轮数为64轮,因此解密时要做出对应的修改,最终sum是64倍的delta
for (int i = 0; i < 64; i++) { // tea加密进行64轮
//根据加密时的顺序颠倒下面3行的顺序,将加法改为减法(异或部分都是整体,不用管),就是逆向解密过程
v1 -= (v0 + sum + 20) ^ ((v0 << 6) + k[2]) ^ ((v0 >> 9) + k[3]) ^ 0x10;
v0 -= (v1 + sum + 11) ^ ((v1 << 6) + k[0]) ^ ((v1 >> 9) + k[1]) ^ 0x20;
sum -= delta;
}
// 解密后的内容要还给v数组
v[0] = v0;
v[1] = v1;
}
int main() {
// k为加解密密钥,4个32位无符号整数,密钥长度为128位
uint32_t k[4] = {2, 2, 3, 4};
// v为要加解密的数据,两个32位无符号整数
//但是稍微难一点点的都不会直接加密两个uint32_t,除非签到题。像这里的例子就是给了6个uint32_t
//但是在加解密时一定是拆开进行,每次传入两个uint32_t
uint32_t v[6];//根据题目解出密文是这些(z3求方程未知数的解)
//这里给数组v的元素赋值时不能写成{-548868226, 550153460, ...}这种形式
//否则会报类型转换的错误,但是单独给每个元素赋值就可以,不清楚原因,但是也算学到一招,避免以后遇到这种问题没法解决
v[0] = -548868226;
v[1] = 550153460;
v[2] = 3774025685;
v[3] = 1548802262;
v[4] = 2652626477;
v[5] = -2064448480;
for(int i = 0; i <= 2; i++){//循环3次,每次传入两个uint32_t解密得到明文
tea_dec(&v[2 * i], k);
}
for (int i = 0; i < 6; i++) {//根据题目提示,打印出明文的十六进制形式
printf("%X", v[i]);//结果:666C61677B72655F69735F6772656174217D
}
//然后你可以找解密网站进行十六进制转字符串,也可以开个python脚本,直接运行下面这两行代码进行十六进制转字符串
/*
hex_data = '666C61677B72655F69735F6772656174217D'
print(bytes.fromhex(hex_data).decode())
*/
return 0;
}