TEA家族

一、tea算法

1.简介:

TEA算法全称微型加密算法(Tiny Encryption Algorithm)是一种简单容易实现的加密算法,通常只需要很少的代码就可以实现。

TEA算法是由剑桥大学计算机实验室的David Wheeler和Roger Needham于1994年发明的一种分组密码算法。其特点是加密速度快,密钥长度为128比特(16字节),明文和密文块长度均为64比特(8字节)。TEA算法采用Feistel网络结构,通过一系列的线性变换和密钥轮来确保加密的安全性。

2.tea算法核心特征:

  1. .DELTA值和十六个字节(128bit)的密钥

  2. .可以利用ida的插件findcypto识别tea算法

  3. .x -=0x61c88647和x +=0x9e3779b9,这两个值是等价的,可能会在反汇编中看到

3.tea加密算法原理:

1
uint32_t v[2] = {*(uint32_t *)&input[i], *(uint32_t *)&input[i+4]};

这行代码将 input 数组中的字节强制转换为 uint32_t 类型。这里就体现了小端序: 如果输入字符串是 “ABCDEFGH” 在内存中存储为:’A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’, ‘H’ 小端序系统会将其解释为: v[0] = 0x44434241 (DCBA) v[1] = 0x48474645 (HGFE)

v[0] 的十六进制值 0x44434241 表示字符序列 'D', 'C', 'B', 'A'

  • 0x44434241 在内存中的字节顺序是:0x41, 0x42, 0x43, 0x44
  • 对应字符:'A', 'B', 'C', 'D'

加密后的到整数:0x56371439(必须得到这样的密文才可以还原明文,或者按照小端序存储的顺序输入密文:input[] ={0x39,0x14,0x37,0x56},在进行*(uint32_t *)&input[i]转化位32位小端序存储,得到的还是uint32_t v[i]={0x56371439},他们两个等价!!                                                                                                                              内存中每个32位元素的顺序是不变的,一个元素的值是按小端序存储的

加密后的内存存储: 39 14 37 56

解密后的结果:0x44434241(得到的是加密还原后的uint32_t v[2]小端序方式存储的数据,表示的字节序列为’D’,’C’,’B’,’A’)

最终结果:ABCD(小端序存储在内存中存储的数据A,B,C,D,是明文一个一个字节输入的顺序)

还原时把得到的小端序明文转化成可读的字符串:

3.1例题

加密以及小端序输出的过程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main() {
uint32_t k[4] = {1, 2, 3, 4};
int8_t input[33] = {0};

scanf("%32s", input);
for (int i = 0; i < 32; i+=8) { //处理 v 数组中的两个 32 位整数:v[0] 和 v[1]。每个 v[j] 代表一个 32 位的数据块
//64个字节为一组,分为4组. 每组8个字节,每组两个32位整数。
uint32_t v[2] = {*(uint32_t *)&input[i], *(uint32_t *)&input[i+4]};//每8个字节转为了小端序!!
encrypt(v, k); //加密后得到的同样是小端序,但是解密不需要变换密文,直接使用即可。
for (int j = 0; j < 2; j++) { // 这一段主要是把 v 按单字节输出,另外可以了解一下 “大小端序” 在这题是如何体现的
//处理 v 数组中的两个 32 位整数:v[0] 和 v[1]。每个 v[j] 代表一个 32 位的数据块
//这里又将得到的小端序转换为大端序,做了一次变换。
for (int k = 0; k < 4; k++) { //将每个 32 位整数分解为 4 个字节
printf("%#x, ", v[j] & 0xff); //& 0xff - 这是位运算中的"与"操作,0xff 是十六进制数,等于十进制的 255,二进制表示为 11111111
v[j] >>= 8; //>>= 8 - 这是右移操作,将 v[j] 的值向右移动 8 位,处理下一个字节,相当于将 v[j] 的值除以 256,2^8=256
//这里的输出先取最低位实际上也是小端序格式输出。因为小端序是低位字节在低地址。
}
}
}
return 0;
}

当你输⼊正确 flag 时会输出这样的数据: 210

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(*输出密文在小端序系统的内存中的存在方式,每32bit为一组)

内存中每个32位元素的顺序是不变的,一个元素的值是按小端序存储的

解密过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main()
{
uint32_t raw[8]={0x89546517, 0x324665ed, 0xfda9583d, 0x97615ee2, 0x91f160e4, 0xa2e9e973, 0x999acb59, 0x7de1b1ec};
// 数值的数学表示(大端序格式):0x89546517
//数值的内存布局(小端序存储):0x17, 0x65, 0x54, 0x89
//内存中每个32位元素的顺序是不变的,一个元素的值是按小端序存储的
//所以他可以等价于 uint_8{0x17,0x65,0x54,0x89,0xed,...} ,再进行uint32_t的转换。
uint32_t k[4] = {1, 2, 3, 4};
for (int i=0;i<4;i++){
uint32_t v[2]={raw[2*i],raw[2*i+1]}; //巧妙!!!!!!!!
tea_decry(v, k);
// 将解密后的32位整数转换为字符(考虑小端序)
char* ptr = (char*)v;// 将v(uint32_t数组)的地址强制转换为char*指针,现在ptr指向 !""内存""! 中第一个字节(原文正确顺序的第一个字节)
for (int j = 0; j < 8; j++) {
printf("%c", ptr[j]);
}
}
}

3.2大小端序的体现:代码验证完整流程

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
#include <stdio.h>
#include <stdint.h>

int main() {
// 原始明文 "ABCD"
char text[] = "ABCD";
printf("原始明文: %s\n", text);
printf("原始字节: ");
for(int i = 0; i < 4; i++) printf("%02X ", text[i]);
printf("\n");

// 转换为整数(小端序解释)
uint32_t v = *(uint32_t*)text;
printf("小端序整数: 0x%08X\n", v);

// 查看整数在内存中的存储
uint8_t* bytes = (uint8_t*)&v;
printf("整数内存存储: ");
for(int i = 0; i < 4; i++) printf("%02X ", bytes[i]);
printf("\n\n");

// 现在假设我们对整数v进行某种"加密"(简单异或示例)
uint32_t encrypted = v ^ 0x12345678;
printf("加密后整数: 0x%08X\n", encrypted);

// 查看加密后整数的内存存储
uint8_t* enc_bytes = (uint8_t*)&encrypted;
printf("加密后内存存储: ");
for(int i = 0; i < 4; i++) printf("%02X ", enc_bytes[i]);
printf("\n");

// 解密
uint32_t decrypted = encrypted ^ 0x12345678;
printf("解密后整数: 0x%08X\n", decrypted);

// 转换回字符串
char* result = (char*)&decrypted;
printf("最终结果: %s\n", result);

return 0;
}
输出结果:

text

1
2
3
4
5
6
7
8
9
原始明文: ABCD
原始字节: 41 42 43 44
小端序整数: 0x44434241
整数内存存储: 41 42 43 44

加密后整数: 0x56371439
加密后内存存储: 39 14 37 56
解密后整数: 0x44434241
最终结果: ABCD

小端序(Little-Endian)解析:

  1. 内存布局(地址从低到高):

    text

1
2
| 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' |
↑i i+1 i+2 i+3 i+4 i+5 i+6 i+7

强制类型转换

  • &input[i] 指向 'A' 的地址(i=0
  • *(uint32_t *)&input[i] 会将连续的4字节 'A','B','C','D' 解释为一个 uint32_t

小端序的存储规则

  • 最低有效字节(LSB)在低地址

  • 实际解释为 uint32_t 时,字节顺序是反的:

    text

  • 'A' (0x41) → 最低字节(LSB)
    'B' (0x42)
    'C' (0x43)
    'D' (0x44) → 最高字节(MSB)
    
    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

    - 最终 `v[0] = 0x44434241`(即 `DCBA` 的十六进制拼接)

    **同理**:

    - `v[1]` 读取 `'E','F','G','H'` → `0x48474645`(`HGFE`)

    ```c++
    #include <stdio.h>
    #include <stdint.h>

    int main() {
    char input[9] = "ABCDEFGH";

    uint32_t v0 = *(uint32_t *)&input[0];
    uint32_t v1 = *(uint32_t *)&input[4];

    printf("v0 = 0x%08x\n", v0); // 输出: 0x44434241
    printf("v1 = 0x%08x\n", v1); // 输出: 0x48474645

    // 按字节分解查看
    printf("v0 bytes: %c %c %c %c\n",
    (v0 & 0xff), ((v0 >> 8) & 0xff), //v0 & 0xff是取最低的8位(最低有效字节)
    ((v0 >> 16) & 0xff), ((v0 >> 24) & 0xff));
    // 输出: A B C D

    return 0;
    }

加密过程中,64位的明文分成两个32位(4字节)的部分即v0,v1。

例如,一个 64 位整数 0x1122334455667788 会被拆分为:

  • v0 = 0x11223344(高 32 位)
  • v1 = 0x55667788(低 32 位)

明文按8字节为一组再进行分为两个4字节的元素。

uint32_t v*(v在这里是数组,定义了这个数组v每个元素都是32位,往后可以把每个元素如v[0],v[1]赋值给v0,v1。最后加密完再把每个v0,v1重新赋值给数组v)。

uint32_t k* 同理。

  • 严格遵循无符号32位运算

3.3加密过程:

//配合dev里面的teaNSSCTF脚本食用,题目是revers,NSSCTF的TEA文件
  1. 初始化:

    uint32_t k*:初始化一个每个元素都是32位的数组k,选择一个128bit的密钥,分为四个32位无符号整数k[1],k[2],k[3],k[4]赋值到数组k里面。

    uint32_t v*:初始化一个每个元素都是32位的数组v,两个32位组成一组(v[0],v[1])一起参与接下来的32轮加密。 //:v[0],v[1]是明文的左右部分。

  2. 加密过程:

    • 初始化变量:

      uint32_t v0=v[0],v1=v[1]

      uint32_t sum=0

      uint32_t delta = 0x9e3779b9

    • 进行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],v[1]:

      v[0]=v0 ; v[1]=v1

      加密后的结果实际上就是小端序数据!!!。数值的内存布局(小端序存储):0x17, 0x65, 0x54, 0x89。          输出的密文:0x89546517

      内存中每个32位元素的顺序是不变的,一个元素的值是按小端序存储的

  3. 加密解密完整代码:

    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
    #include <stdint.h>
    #include <stdio.h>

    //加密函数:
    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 = 0x9e3779b9; //作为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;
    //加密后的结果实际上就是小端序数据!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    }

    //解密函数:
    void tea_dec(uint32_t* v, uint32_t* k) {
    uint32_t v0 = v[0], v1 = v[1]; // v0、v1分别是密文的左、右半部分
    uint32_t delta = 0x9e3779b9; //作为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()
    {
    uint32_t v[2]={1,2},k[4]={2,2,3,4};
    // v为要加密的数据是两个32位无符号整数
    // k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
    printf("加密前原始数据:%u %u\n",v[0],v[1]);
    encrypt(v, k);
    printf("加密后的数据:%u %u\n",v[0],v[1]);
    decrypt(v, k);
    printf("解密后的数据:%u %u\n",v[0],v[1]);
    return 0;
    }

二、xtea算法

1.简介:

使用与TEA相同的简单运算,但四个子密钥采取不正规的方式进行混合以阻止密钥表攻击。

2.加解密过程:

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 <stdint.h>

/* take 64 bits of data in v[0] and v[1] and 128 bits of key[0] - key[3] */

void encipher(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++) {
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
//sum & 3:对sum进行按位与操作,相当于 sum % 4,因为3的二进制是0011,所以这个操作取sum的最低2位。
sum += delta;
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
}
v[0]=v0; v[1]=v1;
}

void decipher(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;
}

int main()
{
uint32_t v[2]={1,2};
uint32_t const k[4]={2,2,3,4};
unsigned int r=32;//num_rounds建议取值为32
// v为要加密的数据是两个32位无符号整数
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
printf("加密前原始数据:%u %u\n",v[0],v[1]);
encipher(r, v, k);
printf("加密后的数据:%u %u\n",v[0],v[1]);
decipher(r, v, k);
printf("解密后的数据:%u %u\n",v[0],v[1]);
return 0;
}

三、XXTEA算法

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
55
56
57
58
59
60
61
62
63
64
65
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))

void btea(uint32_t *v, int n, uint32_t const key[4])
{
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) /* Coding Part */
{
rounds = 6 + 52/n;
sum = 0;
z = v[n-1];
do
{
sum += DELTA;
e = (sum >> 2) & 3;
for (p=0; p<n-1; p++)
{
y = v[p+1];
z = v[p] += MX;
}
y = v[0];
z = v[n-1] += MX;
}
while (--rounds);
}
else if (n < -1) /* Decoding Part */
{
n = -n;
rounds = 6 + 52/n;
sum = rounds*DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--)
{
z = v[p-1];
y = v[p] -= MX;
}
z = v[n-1];
y = v[0] -= MX;
sum -= DELTA;
}
while (--rounds);
}
}


int main()
{
uint32_t v[2]= {1,2};
uint32_t const k[4]= {2,2,3,4};
int n= 2; //n的绝对值表示v的长度,取正表示加密,取负表示解密
// v为要加密的数据是两个32位无符号整数
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
printf("加密前原始数据:%u %u\n",v[0],v[1]);
btea(v, n, k);
printf("加密后的数据:%u %u\n",v[0],v[1]);
btea(v, -n, k);
printf("解密后的数据:%u %u\n",v[0],v[1]);
return 0;
}

TEA/XTEA/XXTEA系列算法

tea 加密解密算法(面向ctf-reverse使用,光速学会tea逆向套路

TEA加密算法在C++中的实现及应用

逆向算法之TEA算法

[看雪原创]TEA、XTEA、XXTEA加解密过程及案例

XTEA加密算法实现过程