tea,xtea,xxtea


在逆向过程中,常常会遇到tea加密,本文将系统地总结一下tea,xtea,xxtea

tea

简介

TEA加密算法是一种分组密码算法,其明文密文块为64比特,密钥长度为128比特。TEA算法利用不断增加的Delta(黄金分割率)值作为变化,使得每轮的加密是不同,该加密算法的迭代次数可以改变,建议的迭代次数为32轮。
值得一提的是Delta值一般为0x9E3779B9(-0x61C88647),这是判定TEA加密的特征之一

代码实现

#include <stdint.h>

void encrypt (uint32_t* v, uint32_t* k) {
    uint32_t v0=v[0], v1=v[1], sum=0, i;           /* set up */
    uint32_t delta=0x9e3779b9;                     /* a key schedule constant */
    uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];   /* cache key */
    for (i=0; i < 32; i++) {                       /* basic cycle start */
        sum += delta;
        v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
        v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);  
    }                                              /* end cycle */
    v[0]=v0; v[1]=v1;
}

void decrypt (uint32_t* v, uint32_t* k) {
    uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i;  /* set up */
    uint32_t delta=0x9e3779b9;                     /* a key schedule constant */
    uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];   /* cache key */
    for (i=0; i<32; i++) {                         /* basic cycle start */
        v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
        v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
        sum -= delta;                                   
    }                                              /* end cycle */
    v[0]=v0; v[1]=v1;
}

示例

#include <stdio.h>
#include <stdint.h>

void encrypt (uint32_t* v, uint32_t* k) {
    uint32_t sum = 0; 
    uint32_t v0 = v[0], v1 = v[1];
    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++) {
        sum += delta;
        v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
        v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
    }

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

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;
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];

    for (int 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;
    }

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

int main(){
    uint32_t v[2] = {0x3e8947cb,0xcc944639};
    uint32_t k[4]= {0x1122,0x2233,0x3344,0x4455};
    printf("Data is : %x %x\n", v[0], v[1]);
    encrypt(v, k);
    printf("Encrypted data is : %x %x\n", v[0], v[1]);
    decrypt(v, k);
    printf("Decrypted data is : %x %x\n", v[0], v[1]);
    return 0;
}
/*
Data is : 3e8947cb cc944639
Encrypted data is : 35ef37b1 464adcc9
Decrypted data is : 3e8947cb cc944639
*/

xtea

简介

XTEA是TEA的扩展,与TEA相比,XTEA使用更长的密钥和更多的迭代轮数,从而提高了安全性。XTEA使用128位密钥和64位块大小,而TEA使用64位密钥和64位块大小。

代码实现

#include <stdint.h>

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

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++) {
        v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
        sum += delta;
        v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
    }
    v[0]=v0; v[1]=v1;
}

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;
}

示例

#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 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++) {
        v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
        sum += delta;
        v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
    }
    v[0]=v0; v[1]=v1;
}

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;
}

int main(){
    uint32_t v[3]={0x61657478,0x5f73695f,0x0};
    uint32_t v1[2]={0x79736165,0x0};
    uint32_t const k[4]={0X95C4C,0X871D,0X1A7B7,0X12C7C7};
    unsigned int r=32;//num_rounds建议取值为32
    // v为要加密的数据是两个32位无符号整数
    // k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
    printf("Data is:%s%s\n",(char*)v,(char*)v1);
    encrypt(r, v, k);
    encrypt(r, v1, k);
    printf("Encrypted data is:%u %u %u\n",v[0],v[1],v1[0]);
    decrypt(r, v, k);
    decrypt(r, v1, k);
    printf("Decrypted data is:%s%s\n",(char*)v,(char*)v1);
    return 0;
}
/*
Data is:xtea_is_easy
Encrypted data is:543258208 1827651953 369487673
Decrypted data is:xtea_is_easy
*/

xxtea

简介

XXTEA使用更复杂的运算方式,它的块大小可以是任意的,密钥也可以是任意长度的。在加密时,XXTEA会对明文进行分块,然后每个块都会进行加密,加密后的结果再进行拼接,最终形成密文。在解密时,XXTEA会对密文进行分块,然后每个块都会进行解密,解密后的结果再进行拼接,最终形成明文。

代码实现

#include <stdint.h>
#include <string.h>

#define DELTA 0x9E3779B9
#define MX (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[(p & 3) ^ e] ^ z)

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;
        for (p = 0; p < n; p++) {
            y = v[p + 1];
            z = v[p] += MX;
        }
        y = v[0];
        z = v[n] += MX;
    }
}

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];
            y = v[p] -= MX;
        }
        z = v[n];
        y = v[0] -= MX;
        sum -= DELTA;
    }
}

示例

#include <stdint.h>
#include <stdio.h>
#include <string.h>

#define DELTA 0x9E3779B9
#define MX (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[(p & 3) ^ e] ^ z)

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;
        for (p = 0; p < n; p++) {
            y = v[p + 1];
            z = v[p] += MX;
        }
        y = v[0];
        z = v[n] += MX;
    }
}

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];
            y = v[p] -= MX;
        }
        z = v[n];
        y = v[0] -= MX;
        sum -= DELTA;
    }
}

int main() {
    // The message to encrypt and decrypt
    char message[] = "xxtea_is_easy_too";
    // The secret key for encryption and decryption
    char key[] = "secret_key";

    // Calculate the required lengths for the data and key
    size_t message_len = strlen(message);
    size_t key_len = strlen(key);

    // Calculate the number of uint32_t values required to store the data and key
    size_t data_len = (message_len + 3) / 4;
    size_t key_data_len = (key_len + 3) / 4;

    // Allocate memory for the uint32_t arrays for the data and key
    uint32_t *data = calloc(data_len, sizeof(uint32_t));
    uint32_t *key_data = calloc(key_data_len, sizeof(uint32_t));

    // Copy the data and key into the uint32_t arrays
    memcpy(data, message, message_len);
    memcpy(key_data, key, key_len);

    // Encrypt the data using the key
    xxtea_encrypt(data, data_len, key_data);

    // Print the encrypted data
    printf("Encrypted data: ");
    for (size_t i = 0; i < message_len; i++) {
        printf("%02x", ((uint8_t*)data)[i]);
    }
    printf("\n");

    // Decrypt the data using the key
    xxtea_decrypt(data, data_len, key_data);

    // Print the decrypted data
    printf("Decrypted data: %s\n", (char*)data);

    // Free the allocated memory
    free(data);
    free(key_data);

    return 0;
}
/*
Original data:
xxtea_is_easy_too

Encrypted data:
1C178B4E 4A63ABD4 7B734D34 4B7B858C

Decrypted data:
78787465 615F6973 5F656173 795F746F
6F
可以看到,原始数据为字符串"xxtea_is_easy_too",经过XXTEA加密后得到了一组十六进制的密文,然后再将这个密文进行解密后,得到了原始数据。
需要注意的是,解密后的结果多出了一个0x6F,这是因为在加密时,为了填充满64位的数据块,加密函数对数据进行了填充。在解密时,需要将填充的字节去掉。在本例中,填充的是一个字节0x0F,因此在解密时会多出一个0x6F。
*/

逆向中TEA系列加密的识别

解决逆向题大部分出现TEA的场合都是 识别算法->编写对应解密程序
分析二进制文件中的算法的时候有几个识别的特征

  • 可能存在针对64bit以及128bit数字的操作(输入的msg和key) ,一般会用无符号的32位的数组表示
  • 存在先进行位移,然后异或的类似操作((z>>5^y<<2) 这类混合变换)(z>>5^y<<2)就是xxtea加密了,存在(v0 << 4)(v0 >> 5)移位就是tea和xtea加密了
  • 前面一个复杂的混合变换的结果可能会叠加到另一个值上,两者相互叠加(Feistel 结构)
  • 获取密钥的时候,会使用某一个常量值作为下标(key[(sum>>11) & 3])存在轮换的方式获得密钥 就是xtea或者xxtea了
  • 会在算法开始定义一个delta,并且这个值不断的参与算法,但是从来不会受到输入的影响(delta数值如果没有魔改就是0x9e3779b9)如果出现了0x9e3779b9这个数字一般就能确定是TEA加密系列

例题

[GDOUCTF2023]tea

查壳是64位无壳,ida64直接查看字符串
img
交叉引用一下you are right,定位到关键函数

__int64 sub_140016230()
{
  char *v0; // rdi
  __int64 i; // rcx
  char v3[32]; // [rsp+0h] [rbp-20h] BYREF
  char v4; // [rsp+20h] [rbp+0h] BYREF
  int v5; // [rsp+24h] [rbp+4h]
  int v6; // [rsp+44h] [rbp+24h]
  int v7[12]; // [rsp+68h] [rbp+48h] BYREF
  _DWORD v8[16]; // [rsp+98h] [rbp+78h] BYREF
  int v9[31]; // [rsp+D8h] [rbp+B8h] BYREF
  int j; // [rsp+154h] [rbp+134h]
  int k; // [rsp+174h] [rbp+154h]
  int l; // [rsp+194h] [rbp+174h]

  v0 = &v4;
  for ( i = 102i64; i; --i )
  {
    *(_DWORD *)v0 = -858993460;
    v0 += 4;
  }
  sub_14001137F(&unk_140023009);
  v5 = 32;
  v6 = 0;
  v7[0] = 1234;
  v7[1] = 5678;
  v7[2] = 9012;
  v7[3] = 3456;
  memset(v8, 0, 0x28ui64);
  v9[15] = 0;
  v9[23] = 0;
  sub_1400113E8();
  for ( j = 0; j < 10; ++j )
    sub_1400111FE("%x", &v8[j]);                // 读入16进制数组v8
  sub_140011339(v7);                            // v7 = [2233,4455,6677,8899]
  sub_140011145(v8, v9);                        // v8赋值给v9
  sub_1400112B7(v8, v7);                        // xtea加密v8,v7为key
  v6 = sub_140011352(v8);                       
  // 判断v8是否等于[0x1A800BDA,0xF7A6219B,0x491811D8,0xF2013328,0x156C365B,
  //0x3C6EAAD8,0x84D4BF28,0xF11A7EE7,0x3313B252,0xDD9FE279]
  if ( v6 )
  {
    sub_140011195("you are right\n");
    for ( k = 0; k < 10; ++k )
    {
      for ( l = 3; l >= 0; --l )
        sub_140011195("%c", (unsigned __int8)((unsigned int)v9[k] >> (8 * l)));// 输出flag
    }
  }
  else
  {
    sub_140011195("fault!\nYou can go online and learn the tea algorithm!");
  }
  sub_140011311(v3, &unk_14001AE90);
  return 0i64;
}

简单分析如上,重点看一下xtea加密函数

__int64 __fastcall sub_140011900(__int64 a1, __int64 a2)
{
  __int64 result; // rax
  int v3; // [rsp+44h] [rbp+24h]
  int i; // [rsp+64h] [rbp+44h]
  unsigned int v5; // [rsp+84h] [rbp+64h]
  unsigned int v6; // [rsp+C4h] [rbp+A4h]

  result = sub_14001137F(&unk_140023009);
  for ( i = 0; i <= 8; ++i )
  {
    v5 = 0;
    v6 = 256256256 * i;
    v3 = i + 1;
    do
    {
      ++v5;
      *(_DWORD *)(a1 + 4i64 * i) += v6 ^ (*(_DWORD *)(a1 + 4i64 * v3)
                                        + ((*(_DWORD *)(a1 + 4i64 * v3) >> 5) ^ (16 * *(_DWORD *)(a1 + 4i64 * v3)))) ^ (v6 + *(_DWORD *)(a2 + 4i64 * (v6 & 3)));
      *(_DWORD *)(a1 + 4i64 * v3) += (v6 + *(_DWORD *)(a2 + 4i64 * ((v6 >> 11) & 3))) ^ (*(_DWORD *)(a1 + 4i64 * i)
                                                                                       + ((*(_DWORD *)(a1 + 4i64 * i) >> 5) ^ (16 * *(_DWORD *)(a1 + 4i64 * i))));
      v6 += 256256256;
    }
    while ( v5 <= 0x20 );
    result = (unsigned int)(i + 1);
  }
  return result;
}

32次迭代的变体xtea加密,可以看出来delta值被魔改了,是v6=256256256

exp

def xtea_decrypt(data,key):
  for j in range(8,-1,-1):
    i = 0
    delta = 256256256
    sum = delta * (32 + j)
    n = j + 1
    while i <= 32:
        i += 1
        data[n] = (data[n] - (((key[(sum >> 11) & 3]) + sum) ^ (((data[j] << 4) ^ (data[j] >> 5)) + data[j]))) & 0xffffffff
        data[j] = (data[j] - (((key[sum & 3] + sum) ^ ((data[n] << 4) ^ (data[n] >> 5)) + data[n]) ^ sum)) & 0xffffffff
        sum -= delta
  return data

v8 = [0x1A800BDA,0xF7A6219B,0x491811D8,0xF2013328,0x156C365B,
      0x3C6EAAD8,0x84D4BF28,0xF11A7EE7,0x3313B252,0xDD9FE279]
key = [2233,4455,6677,8899]

flag = xtea_decrypt(v8,key)

for i in range(10):
    for j in range(3,-1,-1):
        print(chr((flag[i] >> (j * 8)) & 0xFF), end='')

有个地方要特别注意的就是读入的v8是16进制无符号32位整数
如果不进行& 0xffffffff,运行结果是乱码
这耗费了我好多时间检查,问题出在C和Python对无符号整数的处理方式不同。在C中,当无符号整数溢出时,它会回绕到0。但是,在Python中,当整数溢出时,它们会自动升级为长整数。所以需要通过& 0xffffffff来保证解密出来是无符号32位整数。
最终运行结果HZCTF{hzCtf_94_re666fingcry5641qq}
根据题目要求flagNSSCTF{hzCtf_94_re666fingcry5641qq}

flag

NSSCTF{hzCtf_94_re666fingcry5641qq}

[津门杯2021]GoodRe

把tea的运算操作都以函数形式呈现,耐心分析每个函数作用
然后重命名简化反汇编结果,以便分析
img
这是sub_1B30函数内部,通过分析各个函数可推断出是tea
img

exp

def tea_decrypt(data,key):
  i = 0
  delta = 0x830A5376 ^ 0x1D3D2ACF
  sum = delta * 32 & 0xffffffff
  while i < 32:
      i += 1
      data[1] -= ((data[0]<<4) + key[2]) ^ (data[0] + sum) ^ ((data[0]>>5) + key[3]) 
      data[1] = data[1] & 0xffffffff
      data[0] -= ((data[1]<<4) + key[0]) ^ (data[1] + sum) ^ ((data[1]>>5) + key[1]) 
      data[0] = data[0] & 0xffffffff
      sum -= delta
  return data
code =[0x79AE1A3B, 0x596080D3, 0x80E03E80, 0x846C8D73, 
       0x21A01CF7,0xC7CACA32, 0x45F9AC14, 0xC5F5F22F]
key = [17,17,17,17]
flag = ''
for j in range(0,8,2):
  endata = code[j:j+2] 
  dedata = tea_decrypt(endata,key)
  flag += hex(dedata[0])[2:]
  flag += hex(dedata[1])[2:]
print(flag.upper())
#7DEA3F6D3B3D6C0C620864ADD2FA2AE1A61F2736F0060DA0B97E8356D017CE59

flag

flag{7DEA3F6D3B3D6C0C620864ADD2FA2AE1A61F2736F0060DA0B97E8356D017CE59}

参考

posted @ 2023-04-18 17:00  Tree_24  阅读(478)  评论(0编辑  收藏  举报