[GDOUCTF 2023]Tea
- 无壳,64位程序
程序执行回显如下:
我们要输入8个8字节的数据,经过校验后,即可获得flag。我们看一下输入的数据有点像Tea算法中的128位需要加密的数据格式.
IDA分析:
通过字符串窗口,我们可以找到字符串"you are right!\n".交叉引用(X键)到相关函数去.
字符串所在函数代码:
__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 m; // [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]);
sub_140011339(v7);
sub_140011145(v8, v9);
sub_1400112B7(v8, v7);
v6 = sub_140011352(v8);
if ( v6 )
{
sub_140011195("you are right\n");
for ( k = 0; k < 10; ++k )
{
for ( m = 3; m >= 0; --m )
sub_140011195("%c", (unsigned __int8)((unsigned int)v9[k] >> (8 * m)));
}
}
else
{
sub_140011195("fault!\nYou can go online and learn the tea algorithm!");
}
sub_140011311(v3, &unk_14001AE90);
return 0i64;
}
sub_1400113E8函数
__int64 sub_1400113E8(void)
{
return sub_140011B00();
}
int sub_140011B00()
{
sub_14001137F(&unk_140023009);
printf("This is the input format for you geting of flag hex \n");
printf("0x12345678 0x12345678 0x12345678 0x12345678 0x12345678 0x12345678 0x12345678\n");
printf("The end of flag:\nHZCTF{This_is_the_fake_flag}\n");
return printf("input your get the flag:\n");
}
- 程序运行时,打印的东西
for ( j = 0; j < 10; ++j )
scanf("%x", &v8[j]);
- 对我们的v8数组输入十六进制字符串,因为在上一个函数中,指定了我们要输入字符串的格式为hex
sub_140011339函数
__int64 __fastcall sub_140011339(__int64 a1)
{
return sub_1400117D0(a1);
}
__int64 __fastcall sub_1400117D0(_DWORD *a1)
{
char *v1; // rdi
__int64 i; // rcx
char v4[32]; // [rsp+0h] [rbp-20h] BYREF
char v5; // [rsp+20h] [rbp+0h] BYREF
int v6; // [rsp+28h] [rbp+8h]
int v7; // [rsp+2Ch] [rbp+Ch]
int v8; // [rsp+30h] [rbp+10h]
int v9; // [rsp+34h] [rbp+14h]
v1 = &v5;
for ( i = 14i64; i; --i )
{
*(_DWORD *)v1 = -858993460;
v1 += 4;
}
sub_14001137F(&unk_140023009);
v6 = 2233;
v7 = 4455;
v8 = 6677;
v9 = 8899;
*a1 = 2233;
a1[1] = v7;
a1[2] = v8;
a1[3] = v9;
return sub_140011311(v4, &unk_14001AD00);
}
- 传入的参数为v7,但是在函数内部通过指针被改变了值
- 原来的v7值为1234,5678,9012,3456
- 改变后的v7值为2233,4455,6677,8899
sub_140011145(v8, v9);
代码如下:
__int64 __fastcall sub_140011145(__int64 a1, __int64 a2)
{
return sub_140012030(a1, a2);
}
__int64 __fastcall sub_140012030(__int64 a1, __int64 a2)
{
__int64 result; // rax
int i; // [rsp+24h] [rbp+4h]
result = sub_14001137F(&unk_140023009);
for ( i = 0; i < 10; ++i )
{
*(_DWORD *)(a2 + 4i64 * i) = *(_DWORD *)(a1 + 4i64 * i);
result = (unsigned int)(i + 1);
}
return result;
}
- 传入的是v8,v9数组,实际上只是把v8数组的每个值赋给了v9
sub_1400112B7(v8, v7);
代码如下:
__int64 __fastcall sub_1400112B7(__int64 a1, __int64 a2)
{
return sub_140011900(a1, a2);
}
__int64 __fastcall sub_140011900(__int64 a1, __int64 key)
{
__int64 result; // rax
int v3; // [rsp+44h] [rbp+24h]
int i; // [rsp+64h] [rbp+44h]
unsigned int v5; // [rsp+84h] [rbp+64h]
unsigned int sum; // [rsp+C4h] [rbp+A4h]
result = sub_14001137F(&unk_140023009);
for ( i = 0; i <= 8; ++i )
{
v5 = 0;
sum = 0xF462900 * i; // sum = delta * i
v3 = i + 1;
do
{
++v5;
*(_DWORD *)(a1 + 4i64 * i) += sum ^ (*(_DWORD *)(a1 + 4i64 * v3)
+ ((*(_DWORD *)(a1 + 4i64 * v3) >> 5) ^ (16 * *(_DWORD *)(a1 + 4i64 * v3)))) ^ (sum + *(_DWORD *)(key + 4i64 * (sum & 3)));
*(_DWORD *)(a1 + 4i64 * v3) += (sum + *(_DWORD *)(key + 4i64 * ((sum >> 11) & 3))) ^ (*(_DWORD *)(a1 + 4i64 * i)
+ ((*(_DWORD *)(a1 + 4i64 * i) >> 5) ^ (16 * *(_DWORD *)(a1 + 4i64 * i))));
sum += 0xF462900; // sum += delta
}
while ( v5 <= 32 ); // 循环32次
result = (unsigned int)(i + 1);
}
return result;
}
- 存在一个变量与一个固定的常数做运算,且常数不受影响,常数为0xF462900
- 存在移位+异或运算
- 存在Feistal结构,即把运算后的值相加并且给新的变量
- do...while循环33次
- for循环8次
通过上述信息,可以初步判断这个题目是一个魔改的Xtea算法,xtea算法在另外一篇博客已经讲过:https://www.cnblogs.com/qsons/p/17493267.html.不过这个题设了一些坑,而且还是一道魔改的Xtea算法
正常的Xtea算法特征如下:
- Delta为0x7E3779B9
- v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3])
- v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3])
*(_DWORD *)(a1 + 4i64 * i) += sum ^ (*(_DWORD *)(a1 + 4i64 * v3)
+ ((*(_DWORD *)(a1 + 4i64 * v3) >> 5) ^ (16 * *(_DWORD *)(a1 + 4i64 * v3)))) ^ (sum + *(_DWORD *)(key + 4i64 * (sum & 3)));
*(_DWORD *)(a1 + 4i64 * v3) += (sum + *(_DWORD *)(key + 4i64 * ((sum >> 11) & 3))) ^ (*(_DWORD *)(a1 + 4i64 * i)
+ ((*(_DWORD *)(a1 + 4i64 * i) >> 5) ^ (16 * *(_DWORD *)(a1 + 4i64 * i))));
经过还原后,大致代码如下所示:
a1[i] += sum ^ (a1[v3] + ((a1[v3] >> 5) ^ (a1[v3] << 4)) ^ (sum + key[sum & 3])
a1[v3] += (sum + key[(sum >> 11) & 3] ) ^ (a1[i] + (a1[i] >> 5) ^ (a1[i] << 4))
这里的*16其实就相当于<<4,因为左移N位,相当于乘以2^N次方.
不同点
- 在a1[i]进行移位+异或运算时,多了个sum进行异或,原型应该是(sum + v1) ^ a ^ c的.
- delta被魔改为0xF462900
- 密文一共有10个32位无符号整型数据
- sum进行解密时,不再是sum << 5.这里在for循环里面,也对sum做了乘以i的处理。
- 轮数为33轮。
实际上这里的XTEA算法,就是对密文一共10组,每二组进行一次加密。加密轮次为33轮,常数为0xF462900.
v6 = sub_140011352(v8);
__int64 __fastcall sub_140011352(__int64 a1)
{
return sub_140011B60(a1);
}
__int64 __fastcall sub_140011B60(__int64 a1)
{
char *v1; // rdi
__int64 i; // rcx
unsigned int v3; // edi
char v5[32]; // [rsp+0h] [rbp-20h] BYREF
char v6; // [rsp+20h] [rbp+0h] BYREF
BOOL v7; // [rsp+24h] [rbp+4h]
int v8[15]; // [rsp+48h] [rbp+28h]
int j; // [rsp+84h] [rbp+64h]
v1 = &v6;
for ( i = 34i64; i; --i )
{
*(_DWORD *)v1 = -858993460;
v1 += 4;
}
sub_14001137F(&unk_140023009);
v7 = 0;
v8[0] = 0x1A800BDA;
v8[1] = 0xF7A6219B;
v8[2] = 0x491811D8;
v8[3] = 0xF2013328;
v8[4] = 0x156C365B;
v8[5] = 0x3C6EAAD8;
v8[6] = 0x84D4BF28;
v8[7] = 0xF11A7EE7;
v8[8] = 0x3313B252;
v8[9] = 0xDD9FE279;
for ( j = 0; j < 10; ++j )
v7 = *(_DWORD *)(a1 + 4i64 * j) == v8[j];
v3 = v7;
sub_140011311(v5, &unk_14001AD80);
return v3;
}
- 循环10次,v8[j] == a1[j]
密文如下:
v8[0] = 0x1A800BDA;
v8[1] = 0xF7A6219B;
v8[2] = 0x491811D8;
v8[3] = 0xF2013328;
v8[4] = 0x156C365B;
v8[5] = 0x3C6EAAD8;
v8[6] = 0x84D4BF28;
v8[7] = 0xF11A7EE7;
v8[8] = 0x3313B252;
v8[9] = 0xDD9FE279;
解密脚本如下:
C语言脚本:
#include <stdio.h>
#include <stdint.h>
#define delta 0xF462900
int main()
{
uint32_t key[4] = {2233,4455,6677,8899};
uint32_t Data[10] = { 0x1A800BDA ,0xF7A6219B ,0x491811D8,0xF2013328,0x156C365B, 0x3C6EAAD8,0x84D4BF28,0xF11A7EE7,0x3313B252,0xDD9FE279 };
unsigned int j;
int i;
unsigned int sum;
for (i = 8; i >= 0; i--)
{
j = 33;
sum = delta * (i + j);
while(j--)
{
sum -= delta;
Data[i + 1] -= (sum + key[(sum >> 11) & 3]) ^ ((Data[i] + ((Data[i] >> 5) ^ (Data[i] << 4))));
Data[i] -= sum ^ (Data[i + 1] + ((Data[i + 1] >> 5) ^ (Data[i + 1] << 4))) ^ (sum + key[sum & 3]);
}
}
for (int i = 0; i < 10; i++)
{
printf("%x", Data[i]);
}
return 0;
}
#485a4354467b687a4374665f39345f726536363666696e676372793536343171717d0
最后十六进制解码即可
注意点:
- 对Sum进行处理时,是33+i轮次。33代表的是内部对数据的总加密轮次。外部是对每数据二二一组加密的轮次
- for循环需要逆算法来,从8迭代到0.
- while循环中,+=变-=即可,其他不变。
- 加密时顺序sum+=delta要最先放上来
Python代码下:
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='')
#HZCTF{hzCtf_94_re666fingcry5641qq}
注意点:
- 在C语言中是有无符号32整型这种内置函数的,python没有,所以需要约束0xFFFFFFFF,也就是保留其低32位。
- 在最后取flag时,flag为单个字符,每个字符不可能超过0xFF,所以也要用与运算取其低8位的二进制数据。
Over~~~
标签:__,sub,Tea,sum,a1,2023,v8,rsp,GDOUCTF From: https://www.cnblogs.com/qsons/p/17493712.html