xxtea

考点:

xxtea加密魔改,代码审计

做题步骤:

查壳,无壳,64位

image-20231017141031687

拖到64位ida中进行反编译,但是本题没要main函数,所以要用shift+f12查看字符串的方式,通过字符串来找到主要的加密函数

image-20231017141047680

查看了“please input your flag,查看到了一段不像是密文的数据

image-20231017141105115

对于这种难找主函数的题,其实有很多方法可以去找:

首先跑一遍这个程序,看看有哪些关键语句(字符串)

image-20231017141120903

可以看到除了上面找错的那些字符,还有一个fault!You can go online and learn the tea algorithm!

shift+f12查看字符串

找到相对应的函数:

image-20231017141137349

调用函数1

在ida中有一个操作是查看上下级函数,在这里可以看到,有一个上级函数,就是执行完上级函数,就可以执行本函数,鼠标点击灰色的部分,单机一下然后摁x,点击ok就可以去查看上级函数

比如有一个main函数,引用了m1,m2,m3三个函数,然后m1引用了n1,n2两个函数,我们从n1或者n2返回上级就回到了m1,在m1,m2,m3回到上级就回到了main函数

比如可以看一下这个函数:image-20231017141156011

一直往里面走,就会看到刚开始执行程序的时候的那个fakeflag:

image-20231017141210971

所以即使没有main函数,但是这个函数yes有着类似于main函数的功能,这里会调用各个函数

然后分析一下这个段代码:

首先就是输出那些内容,一些没要用的提示,而且我输入了10个数才提示错误,所以差不多可以判断出来,被加密的数据有10位

image-20231017141225158

所以下一段是一个for循环,0~9进行遍历循环:

image-20231017141235308

调用函数2

然后是下一个被调用的函数:

image-20231017141251650

因为ida在反汇编的时候,会把一些比较复杂的数组变成指针,比如这里,的*a1=2233,其实这里就是因为把a1这个数组的a1[0]读成了指针,不用太在意,就知道是a1[0]=2233就可以了

现在得到了a1数字,这里就得到了tea加密的key

在此之前,对key有过一次定义,但是那个不是真的key,这里很容易认为这个就是tea加密的key

image-20231017141306922

调用函数3

然后继续分析下一个调用的函数,这个函数他操作他把v9赋了v8的数值,但是又好像不影响做题,后面都是对v8进行计计算加密的,据大佬说,这里是出题人用来校验flag的!

image-20231017141329555

调用函数4(tea加密函数)

然后开始下一函数,也就是加密函数,这个函数看到那么多的位运算,很明显是tea加密的特征,具体分析一下这个加密函数ida在反汇编的时候,无法正确识别出int 和char类型的数据,所以可以自行更改一下数据类型,可以看的更清楚一些

具体操作是选择int的数据类型然后按y键,然后输出char*,就变成了字符型的类型,这样看起来会清楚很多

image-20231017141341127

再看一下这个函数的传参,传入的v7整个数组,对v7进行加密的,然后通过一些操作把函数变成可以很清楚看明白的方式,我自己尽力注释一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
for ( i = 0; i <= 8; ++i )
{
v5 = 0;//v5是一个计数器,用来判断什么时候退出循环的
delta = 0xF462900 * i;
v3 = i + 1;
//v3的作用就是一个索引,位下面的flag[]数组的加密做索引的,下面可以换成flag[i+1]和flag[i]
do
{
++v5;
*&flag[4 * i] += delta ^ (*&flag[4 * v3] + ((*&flag[4 * v3] >> 5) ^ (16 * *&flag[4 * v3]))) ^ (delta + *&key[4 * (delta & 3)]);
*&flag[4 * v3] += (delta + *&key[4 * ((delta >> 11) & 3)]) ^ (*&flag[4 * i]+ ((*&flag[4 * i] >> 5) ^ (16 * *&flag[4 * i])));
//这里可以看出黑箱子,只需要知道这里是tea加密就可以了,解密的时候就-=就可以了
delta += 0xF462900;
//这里的delta是每次都是累加的
}
while ( v5 <= 32 );//循环32次,然后跳出循环
result = (i + 1);
}
return result;
}

这里有几个魔改的地方

1:delta的数值改变了

2:这里多了一个for的八次循环,就导致了delta的数值加了很多,这里要大致推算最后一次循环经过加密后的数值:delta=0xF462900*(j+32)

j代表的是外八次循环的第几次循环,32代表的是每一次内循环的次数是32次,逆向的时候就是从delta=0xF462900(8+32)开始,然后是0xF462900(7+32)进行解密直到最后0xF462900*(0+32)

这样就是实现了最终的解密

解密关键数据:

明文:

image-20231017141904763

key:

image-20231017141922590

delta:0xF462900,但是因为有外部大循环的原因,所以在脚本中的数据为:delta=0xF462900*(j+32)

然后代入一般的解题脚本,再加上一个大的循环,解题脚本如下:

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

int main()
{
unsigned int r = 32;
unsigned int const key[4] = { 2233,4455,6677,8899 };
uint32_t v[10] = {0x1a800bda,0xf7a6219b,0x491811d8,0xf2013328,0x156c365b,0x3c6eaad8,0x84d4bf28,0xf11a7ee7,0x3313b252,0xdd9fe279};
//初始化定义key和flag
int j = 0, n = 0;
for (int j = 8; j >= 0; j--)
//外部的大循环逆向
{
unsigned int i = 0;
unsigned int delta = 0xF462900, sum = delta * (32+j);
// unsigned int必须要有的,这里的数据都是无符号int型,如果不加会对下面的计算有影响
n = j + 1;
do {
i++;
v[n] -= (((key[(sum >> 11) & 3]) + sum) ^ (((v[j] << 4) ^ (v[j] >> 5)) + v[j]));
v[j] -= (((key[sum & 3] + sum) ^ ((v[n] << 4) ^ (v[n] >> 5)) + v[n]) ^ sum);
sum -= delta;
//内部tea的解密循环
} while (i <= 32);
}

for (int i = 0; i < 10; i++)
{
for (int j = 3; j>=0; j--)
{
printf("%c", (v[i] >> (j * 8)) & 0xFF);
}
//经过解密之后的数据是一个16进制的形式,这里经过一些计算给16进制转化为字符的形式进行输出
}
return 0;
}
//HZCTF{hzCtf_94_re666fingcry5641qq}

总结:

1、在找主函数的时候,可以先运行一下程序,然后根据程序中的关键字符串来定位函数

2、在ida中H键可以实现10进制和16进制的转化

3、y键可以实现在ida把误认为是int形的数据改回原来的类型,来更方便看函数

4、在ida反汇编的时候会把一些比较复杂的数组变成指针

5、tea加密的特征是一个delta和flag[i]与flag[i+1]进行的位计算