tea,xtea,xxtea

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

tea

简介

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

代码实现

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

示例

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
#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位块大小。

代码实现

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

示例

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 <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会对密文进行分块,然后每个块都会进行解密,解密后的结果再进行拼接,最终形成明文。

代码实现

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

示例

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#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,定位到关键函数

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
__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加密函数

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
__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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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}


Python中PIL库中的Image类

逆向中可能会遇到有关图像的问题,总结了一些Python的PIL库中的Image类的一些函数,通过这些函数可以对数据进行一系列操从而得到我们想要的图片

函数 参数 作用 使用举例
new mode, size, color 新建图像 Image.new(‘RGB’, (200, 100), ‘red’)
open image_name 加载图像 img = Image.open(‘cat.jpg’)
save file_name, file_format 保存图像 img.save(‘picture.jpg’)
crop (left, upper, right, lower) 裁剪图片 crop_img = img.crop((420, 150, 860 , 560))
paste im, (x, y) 粘贴图片方法 img.paste(crop_img, (0, 0))
resize (width, height) 调整图片大小 resized_img = img.resize((width, height))
thumbnail (width, height) 制作缩略图,等比例缩放图片大小 img.thumbnail((w//3, h//3))
rotate angle 围绕其中心逆时针旋转给定的度数。返回此图像的副本。 rotate_img = img.rotate(30)
show 显示此图像。此方法主要用于调试目的。 img.show()
copy 复制图像。返回一个新的Image对象。 copy_img = img.copy()
crop (left, upper, right, lower) 矩形元组参数。返回一个新的Image对象。对原图没有影响。 裁剪图片。 crop_img = img.crop((420, 150, 860 , 560))
transpose method,通过FLIP_LEFT_RIGHT和FLIP_TOP_BOTTOM可以分别实现水平竖直镜像翻转 创建并返回此图像的转置副本。 img.transpose(Image.FLIP_LEFT_RIGHT)
filter filter 对此图像应用图像增强滤镜。返回一个新的Image对象。 img.filter(ImageFilter.BLUR)
convert mode 转换图像模式。返回一个新的Image对象。 img.convert(‘L’)
getpixel xy 返回给定位置的像素值。 img.getpixel((0,0))
putpixel xy, value 修改给定位置的像素值。 img.putpixel((0,0), value)
point lut, mode 将查找表应用于图像。返回一个新的Image对象。 img.point(lut)
split 将图像拆分为单独的波段。返回一个元组,其中包含单独的波段。 r, g, b = img.split()
merge mode, bands 将单独的波段合并为单个多波段图像。返回一个新的Image对象。 img = Image.merge(‘RGB’, (r, g, b))
load 分配存储并加载图像数据。返回一个像素访问对象。 px = img.load()

例题

[CTFShow愚人杯]easy_re

32位无壳,拖进IDA看看

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // ebx
int v4; // ecx
unsigned int v5; // edi
unsigned int v6; // kr00_4
int v8; // [esp-4h] [ebp-414h]
int v9; // [esp+10h] [ebp-400h]
int v10; // [esp+18h] [ebp-3F8h] BYREF
int v11; // [esp+1Ch] [ebp-3F4h] BYREF
char v12[1004]; // [esp+20h] [ebp-3F0h] BYREF

sub_401460();
sub_401700(v12);
sub_401460();
std::istream::operator>>(std::cin, &v10);
std::istream::operator>>(std::cin, &v11);
v3 = v10 % 299;
v4 = v11 % 299;
v5 = 0;
v9 = v11 % 299;
v6 = strlen(v12);
if ( v6 )
{
do
{
v8 = dword_403AA0[300 * v3 + v4] ^ v12[v5];
v3 = (v8 + v3) % 299;
v9 = (v8 + v9) % 300;
std::ostream::operator<<(std::cout, v8);
sub_401460();
v4 = v9;
++v5;
}
while ( v5 < v6 );
}
sub_401460();
return 0;
}

只看得出来是个300*300的一个数组
记住403AA0地址是从28A0到5A6E3
尝试用x32dbg看看
img
也就是说用两个300以内数字去加密一段话
可以得到后面一大串的数字,尝试直接枚举出这两个key
利用flag的base64值是’ZmxhZ’来判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from base64 import *
from pwn import u32

def decrypt(k1,k2,cipher):
tk1,tk2 = k1,k2
m = []
for v8 in cipher[::-1]:
k1 = (k1-v8)%299
k2 = (k2-v8)%300
m.append(d_3aa0[300*k1+k2] ^ v8)
base64_text = bytes(m[::-1])
if base64_text[:5]==b'ZmxhZ':
flag=b64decode(base64_text).decode('utf-8')
print(base64_text,tk1,tk2,'\n',flag)

flag=''
data = open('re1.exe', 'rb').read()[0x28a0: 0x5a6e0]
d_3aa0 = [u32(data[i:i+4]) for i in range(0, len(data), 4)]
a = [90,171,198,235,229,43,246,92,198,203,233,228,6,128,215,68,201,4,220,214,169,245,208,199,112,170,119,251,244,58,237,4,70,231,200,45,186,137,247,225,243,13,145,139,190,146,194,242,253,56,239,5,41,225,105,51,247,79,170,231,88,64,224,138,222,220,229,88,43,117,236,189,228,205,150,65,26,205,232,141,116,149,185,89,212,251,16,215,205,17,238,22,245,77,220,198,224,248,223,209,205,167,223,210,165,247,190,3,5,246,243,228,181,33,42,207,174,138,244,118,192,22,219,60,80,229,144,219,133,211,221,229,190,58,151,240,183,207,221,60,77,217,220,74,105,220,221,165,85,174,43,183,188,190,252,255,130,137,189,201,239,181,150,143,214,203,26,211,103,222,105,87,214,179,83,185,104,206,229,172,221,117,163,57,106,200,46,165,193,135,243,166,168,209,144,52,210,12,58,10,103,5,211,55,172,76,88,250,136,245,167,139,241,26,92,97,139,241,137,27,53,211,251,191,240,173,14,231,241,242,255,122,144,97,234,36,175,155,253,35,156,229,19,166,191,140,195,218,130,35,200,178,245,41,162,243,214,222,87,83,195,144,55,159,208,241,193,233,204,228,196,105,84,58,220,226,1,47,248,138,177,124,236,53,210,79,250,106,27,244,251,203,210,103,213,218,183,4,40,28,12,175,52,224,203,89,176,174,175,233,43,20,103,152,201,4,148,76,241,103,135,139,136,246,80,184,255,194,149,239,206,207,246,166,20,63,202,199,177,214,60,99,74,211,219,94,247,193,40,212,197,175,30,244,41,24,113,27,249,213,225,55,188,193,165,220,174,252,105,154,74,126,174,255,110,169,103,44,246,255,98,251,211,87,171,62,67,250,69,149,18,77,159,137,168,231,187,97,174,115,243,44,128,151,90,246,83,11,138,67,184,22,53,228,230,252,76,112,20,136,131,90,233,248,67,207,61,212,113,62,239,203,201,66,83,179,16,209,253,63,206,208,101,150,196,145,101,220,22,79,241,69,237,219,97,87,20,22,240,244,218,7,237,42,14,8,38,115,141,102,206,191,142,55,196,200,142,98,16,129,53,52,50,197,53,219,2,66,152,192,245,243,69,26,132,240,164,90,246,200,53,89,221,119,139,76,47,132,53,47,249,26,53,141,113,69,76,152,121,193,53,176,97,135,205,206,237,108,251,38,216,108,12,220,209,194,26,243,217,231,36,117,235,106,205,43,254,75,209,141,239,200,5,183,219,166,113,9,16,154,116,144,238,208,245,136,173,16,103,107,114,17,208,181,196,98,212,133,211,252]

for k1 in range(299):
for k2 in range(300):
decrypt(k1,k2,a)

运行结果
img
提示研究一下加密矩阵,尝试把d_3aa0中的元素输出来发现很像RGB值,尝试转化成图片。最后输出
img
flagctfshow{d244daeb-7182-4c98-bec6-0c99329ab71f}

exp

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
from base64 import *
from pwn import u32

def decrypt(k1,k2, cipher):
tk1,tk2 = k1,k2
m = []
for v8 in cipher[::-1]:
k1 = (k1-v8)%299
k2 = (k2-v8)%300
m.append(d_3aa0[300*k1+k2] ^ v8)
base64_text = bytes(m[::-1])
if base64_text[:5]==b'ZmxhZ':
flag=b64decode(base64_text).decode('utf-8')
print(base64_text,tk1,tk2,'\n',flag)

flag=''
data = open('re1.exe', 'rb').read()[0x28a0: 0x5a6e0]
d_3aa0 = [u32(data[i:i+4]) for i in range(0, len(data), 4)]
a = [90,171,198,235,229,43,246,92,198,203,233,228,6,128,215,68,201,4,220,214,169,245,208,199,112,170,119,251,244,58,237,4,70,231,200,45,186,137,247,225,243,13,145,139,190,146,194,242,253,56,239,5,41,225,105,51,247,79,170,231,88,64,224,138,222,220,229,88,43,117,236,189,228,205,150,65,26,205,232,141,116,149,185,89,212,251,16,215,205,17,238,22,245,77,220,198,224,248,223,209,205,167,223,210,165,247,190,3,5,246,243,228,181,33,42,207,174,138,244,118,192,22,219,60,80,229,144,219,133,211,221,229,190,58,151,240,183,207,221,60,77,217,220,74,105,220,221,165,85,174,43,183,188,190,252,255,130,137,189,201,239,181,150,143,214,203,26,211,103,222,105,87,214,179,83,185,104,206,229,172,221,117,163,57,106,200,46,165,193,135,243,166,168,209,144,52,210,12,58,10,103,5,211,55,172,76,88,250,136,245,167,139,241,26,92,97,139,241,137,27,53,211,251,191,240,173,14,231,241,242,255,122,144,97,234,36,175,155,253,35,156,229,19,166,191,140,195,218,130,35,200,178,245,41,162,243,214,222,87,83,195,144,55,159,208,241,193,233,204,228,196,105,84,58,220,226,1,47,248,138,177,124,236,53,210,79,250,106,27,244,251,203,210,103,213,218,183,4,40,28,12,175,52,224,203,89,176,174,175,233,43,20,103,152,201,4,148,76,241,103,135,139,136,246,80,184,255,194,149,239,206,207,246,166,20,63,202,199,177,214,60,99,74,211,219,94,247,193,40,212,197,175,30,244,41,24,113,27,249,213,225,55,188,193,165,220,174,252,105,154,74,126,174,255,110,169,103,44,246,255,98,251,211,87,171,62,67,250,69,149,18,77,159,137,168,231,187,97,174,115,243,44,128,151,90,246,83,11,138,67,184,22,53,228,230,252,76,112,20,136,131,90,233,248,67,207,61,212,113,62,239,203,201,66,83,179,16,209,253,63,206,208,101,150,196,145,101,220,22,79,241,69,237,219,97,87,20,22,240,244,218,7,237,42,14,8,38,115,141,102,206,191,142,55,196,200,142,98,16,129,53,52,50,197,53,219,2,66,152,192,245,243,69,26,132,240,164,90,246,200,53,89,221,119,139,76,47,132,53,47,249,26,53,141,113,69,76,152,121,193,53,176,97,135,205,206,237,108,251,38,216,108,12,220,209,194,26,243,217,231,36,117,235,106,205,43,254,75,209,141,239,200,5,183,219,166,113,9,16,154,116,144,238,208,245,136,173,16,103,107,114,17,208,181,196,98,212,133,211,252]

for k1 in range(299):
for k2 in range(300):
decrypt(k1,k2,a)

from PIL import Image

img = Image.new('RGB', (300, 300))
pixels = img.load()

for i in range(299):
for j in range(300):
pixels[i, j] = d_3aa0[i * 300 + j]
img = img.transpose(Image.FLIP_LEFT_RIGHT).rotate(90)
img.save('a.png')
img.show()

其他

np.fromstringreshape都是NumPy中常用的数组操作函数。

np.fromstring

np.fromstring函数用于从字符串或字节序列中创建NumPy数组。它将输入字符串或字节序列解析为一组数字,并返回一个NumPy数组。该函数的参数包括:要解析的字符串或字节序列、用于解析数字的数据类型和用于分隔数字的分隔符。
例如,下面的代码使用np.fromstring函数从一个字符串中创建一个NumPy数组:

1
2
3
4
5
import numpy as np

s = "1 2 3 4 5"
a = np.fromstring(s, dtype=int, sep=' ')
print(a)

输出:

1
[1 2 3 4 5]

reshape

reshape函数用于改变数组的形状。它将一个NumPy数组重新排列为一个具有不同行数和列数的新数组。该函数的参数包括:要重排的数组、一个包含新形状的元组或整数列表。

例如,下面的代码创建一个二维数组,然后使用reshape函数将其重新排列为一个具有不同行数和列数的新数组:

1
2
3
4
5
import numpy as np

a = np.array([[1, 2], [3, 4], [5, 6], [7, 8]])
b = a.reshape((2, 4))
print(b)

输出:

1
2
[[1 2 3 4]
[5 6 7 8]]

reshape函数可以用于改变数组的维度和形状,但必须保证原始数组中元素的总数与新数组中元素的总数相同。因此,如果重排数组的形状会改变元素的数量,将会引发错误。

参考