BUU刷题记录

Buuctf刷题记录

红帽杯-XX

考点

xxtea、异或、换位

分析

整个过程就是取前四个输入作为密钥,先进行XXTEA加密,然后位置互换混淆,最后进行异或加密

取密钥

这里判断取出前四位,判断是否为数组内的元素,因为如果不是的话,最后V11=V14退出程序

v6元素

取出地址中存储的字符,保证其不为0,下面那个循环是将取出的key末尾填充0

我们的key是int型,这里传参时强制转为char型

函数内部起始段是将我们传入的key转为int型,转换大小端序,因为原本是flag,转为int之后就变成了galf

然后xxtea加密

打乱位置

异或

index初始值

这段的逻辑就是只要是3的倍数就取出来异或,index从1开始,所以0、1、2下标的字符都不会被加密,3、4、5进行一次加密,异或的值为下标为0的enc_flag,6、7、8则两次,异或的值为下标为0、1的加密后的字符,依次往后,直到21、22、23,此时异或前六位加密字符

脚本

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


#define xxtea_DELTA 0x9e3779b9
#define xxtea_MX (((xxtea_z>>5^xxtea_y<<2) + (xxtea_y>>3^xxtea_z<<4)) ^ ((xxtea_sum^xxtea_y) + (xxtea_key[(xxtea_p&3)^xxtea_e] ^ xxtea_z)))
void xxtea(uint32_t* xxtea_origin, int xxtea_n, uint32_t const xxtea_key[4])
{
uint32_t xxtea_y, xxtea_z, xxtea_sum;
unsigned xxtea_p, xxtea_rounds, xxtea_e;
if (xxtea_n > 1) /* Coding Part */
{
xxtea_rounds = 6 + 52 / xxtea_n;
xxtea_sum = 0;
xxtea_z = xxtea_origin[xxtea_n - 1];
do
{
xxtea_sum += xxtea_DELTA;
xxtea_e = (xxtea_sum >> 2) & 3;
for (xxtea_p = 0; xxtea_p < xxtea_n - 1; xxtea_p++)
{
xxtea_y = xxtea_origin[xxtea_p + 1];
xxtea_z = xxtea_origin[xxtea_p] += xxtea_MX;
}
xxtea_y = xxtea_origin[0];
xxtea_z = xxtea_origin[xxtea_n - 1] += xxtea_MX;
} while (--xxtea_rounds);
}
else if (xxtea_n < -1) /* Decoding Part */
{
xxtea_n = -xxtea_n;
xxtea_rounds = 6 + 52 / xxtea_n;
xxtea_sum = xxtea_rounds * xxtea_DELTA;
xxtea_y = xxtea_origin[0];
do
{
xxtea_e = (xxtea_sum >> 2) & 3;
for (xxtea_p = xxtea_n - 1; xxtea_p > 0; xxtea_p--)
{
xxtea_z = xxtea_origin[xxtea_p - 1];
xxtea_y = xxtea_origin[xxtea_p] -= xxtea_MX;
}
xxtea_z = xxtea_origin[xxtea_n - 1];
xxtea_y = xxtea_origin[0] -= xxtea_MX;
xxtea_sum -= xxtea_DELTA;
} while (--xxtea_rounds);
}
}

int main()
{
//0x40CEA5BC,0xE7B2B2F4,0x129D12A9,0x5BC810AE,0x1D06D73D,0xDCF800DC
unsigned int enc[6] = { 0x40CEA5BC,0xE7B2B2F4,0x129D12A9,0x5BC810AE,0x1D06D73D,0xDCF870DC };
unsigned int key[4] = { (unsigned int)0x67616c66,(unsigned int)0x0,(unsigned int)0x0,(unsigned int)0x0 };
xxtea(enc, -6, key);
for (int i = 0; i < 6; ++i)
{
printf("%c%c%c%c",((char*)&enc[i])[0], ((char*)&enc[i])[1], ((char*)&enc[i])[2], ((char*)&enc[i])[3]);
}//强制转为char然后依次取出,这样就不会逆序了
return 0;
}

异或和位置互换

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

int main()
{
unsigned char data[24] = {
0xCE, 0xBC, 0x40, 0x6B, 0x7C, 0x3A, 0x95, 0xC0, 0xEF, 0x9B, 0x20, 0x20, 0x91, 0xF7, 0x02, 0x35,
0x23, 0x18, 0x02, 0xC8, 0xE7, 0x56, 0x56, 0xFA
};
int count = 0;
for (int i = 23; i >= 0; --i)//因为最后的加密数据和前面的有关系,所以要从前往后
{
for (int j = 6 - count; j >= 0; --j)//并且分为了多轮,除去前三个不需要异或,剩下7组,又是小于8,所以从下标为6开始
{
data[i] ^= data[j];
}
if (i % 3 == 0)
{
count++;
}
}
char encode_flag2[24] = { 0 };
//交换还原
encode_flag2[2]= *data;
*encode_flag2= data[1];
encode_flag2[3]= data[2];
encode_flag2[1]= data[3];
encode_flag2[6]= data[4];
encode_flag2[4]= data[5];
encode_flag2[7]= data[6];
encode_flag2[5]= data[7];
encode_flag2[10]= data[8];
encode_flag2[8]= data[9];
encode_flag2[11]= data[10];
encode_flag2[9]= data[11];
encode_flag2[14]= data[12];
encode_flag2[12]= data[13];
encode_flag2[15]= data[14];
encode_flag2[13]= data[15];
encode_flag2[18]= data[16];
encode_flag2[16]= data[17];
encode_flag2[19]= data[18];
encode_flag2[17]= data[19];
encode_flag2[22]= data[20];
encode_flag2[20]=data[21] ;
encode_flag2[23]= data[22];
for (int i = 0; i < 24; ++i)
{
printf("0x%X,", encode_flag2[i]&0xff);
}
return 0;
}

[安洵杯 2019]crackMe

考点

SM4、Base64变表加密、换位

分析

这里我和别人不一样,我运行不了程序,所以没办法动调,只能X查看交叉引用

进去定位到主加密函数

初始化密钥

对key查看交叉引用,可以进入这里,看一下对赋值后的字符串处理

通过FindCrypt可以知道是SM4加密,所以这一段就是初始化密钥了

SM4加密

回到encode函数

可以发现最后将加密后的字符串给到了final

Base64加密

对final查看交叉引用,发现对其进行了加密

这里解释一下一些东西

我们base64就是将三个字符也就是24位转为4个6位,做索引,这里使用移位直接将三个字符成为一个int型,也就是每个左移8位,而待会取出的时候右移六位即可取出,实现了8位与6位的转换,比较有意思

在红框函数中将传入的6位索引进行+24,也就是(index+24)%64,我们可以将base64表整体左移,就相当于index+24了

再对base64表查看交叉引用,发现了将大小写转换

换位

可以看到这里将最后用来对比的字符串每两个字符进行互换

脚本

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
#include <stdio.h>
#include <string.h>
char base64_table[] = "yzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/abcdefghijklmnopqrstuvwx";
char* base64_encode(char code[], char str[])
{
int code_len = strlen(code);
int str_len ;
if (code_len%3)
{
str_len = (code_len / 3 + 1 ) * 4;
}
else
str_len = (code_len / 3) * 4;
int i = 0, i_ = 0;
for (; i < code_len; i += 3, i_ += 4)
{
str[i_] = base64_table[code[i]>> 2];
str[i_ + 1] = base64_table[((code[i] & 0x03) << 4) | ((code[i + 1] & 0xf0) >> 4)];
str[i_ + 2] = base64_table[((code[i+1] & 0x0f) << 2) | ((code[i + 2] & 0xc0) >> 6)];
str[i_ + 3] = base64_table[(code[i + 2] & 0x3f)];
}
if (code_len % 3 == 1)
{
str[i_-1] = '=';
str[i_ - 2] = '=';
}
else if (code_len % 3 == 2)
{
str[i_-1] = '=';
}
return str;
}
int findIndex(char c, char b64_table[])
{
for (int i = 0; i < 64; ++i)
{
if (c == b64_table[i])
return i;
}
}
int str_len;
char* base64_decode(char code[], char str[], char b64_table[])
{
char memstr[200] = { 0 };
memcpy(memstr, code, strlen(code));

int len = strlen(code);
if (strstr(code, "=="))
str_len = len / 4 * 3 - 2;
else if (strstr(code, "="))
str_len = len / 4 * 3 - 1;
else
str_len = len / 4 * 3;

for (int i = 0, i_ = 0; i < len; i += 4, i_ += 3)
{
str[i_] = (findIndex(memstr[i], b64_table) << 2) | (findIndex(memstr[i + 1], b64_table) & 0x30) >> 4;
str[i_+1]= (findIndex(memstr[i+1], b64_table)&0xf)<<4 | (findIndex(memstr[i + 2], b64_table) & 0x3c) >> 2;
str[i_ + 2] = (findIndex(memstr[i + 2], b64_table) & 0x03) << 6 | (findIndex(memstr[i + 3], b64_table));
}
str[str_len] = 0;
return str;
}
int main()
{
char code[100] = "1UTAOIkpyOSWGv/mOYFY4R==";
char decode[100] = { 0 };
char encode[100] = { 0 };
//两位交换
for (int i = 0; i < strlen(code); i += 2)
{
char v2 = code[i];
code[i] = code[i + 1];
code[i + 1] = v2; // 每两个之间进行交换
}


/*base64_encode(code, encode);
printf("%s\n", encode);*/
base64_decode(code, decode,base64_table);
if (decode == 0)
{
return 0;
}

for (int i = 0; i < str_len; ++i)
{
printf("%02X", decode[i] & 0xff);
}
return 0;
}

得到59D095290DF2400614F48D276906874E

SM4解密

https://the-x.cn/cryptography/Sm4.aspx

[SWPU2019]ReverseMe

考点

异或、ZUC算法

分析

先异或SWPU_2019_CTF,再异或ZUC算法生成的数据

异或

ZUC算法生成异或的值

unsigned int 的异或,通过动调也可以知道

xor

脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
unsigned int data[8] = {
0xCA3E0C86, 0x19AED798, 0xA66B77E2, 0xB077A16A, 0x05379169, 0x307BF97A, 0x104B5A43, 0x28D47D86
};
unsigned int final[8] = {
0xF80F37B3, 0x5DAEBCBC, 0x864D5ABA, 0xD3629744, 0x1624BA4F, 0x1A729F0B, 0x266D6865, 0x67C86BBA
};
int j = 0;
char k[] = { 0 };
for (int i = 0; i < 8; ++i)
{
final[i] ^= data[i];
k[j++] = ((char*)&final[i])[0];
k[j++] = ((char*)&final[i])[1];
k[j++] = ((char*)&final[i])[2];
k[j++] = ((char*)&final[i])[3];
}
k[j] = 0;
char key[] = "SWPU_2019_CTF";
for (int i = 0; i < 32; ++i)
{
k[i] ^= key[i % 13];
printf("%c", k[i]);
}

[MRCTF2020]EasyCpp

考点

异或、替换,素因数分解

分析

一些关键的都已经标识出来了

读取输入

读取9次key,并且拼接起来

异或

这里面是和1异或

素因数分解

这里只要能整除i,就重新进入递归,参数为整除i后的数,如果i遍历完不符合,结束循环,然后拼接上空格

替换

将数字换成字母,空格换为=

比较

红框数组存储的是最后比较的字符串

脚本

这里我直接手动替换算出来的

1
2
3
4
5
6
7
8
printf("%d", (293 * 8) ^ 1);
printf("%d", (1223) ^ 1);
printf("%d", (11 * 7 * 5 * 5 * 3) ^ 1);
printf("%d", (2477) ^ 1);
printf("%d", (125 * 27) ^ 1);
printf("%d", (3 * 3011) ^ 1);
printf("%d", (13 * 7 * 27) ^ 1);
printf("%d", (353 * 5 * 2) ^ 1);

md5即可

SCTF2019-Creakme

考点

AES-CBC加密、Base64加密、反调试、SMC

分析

反调试、SMC

一看到这种遍历段名称的操作,熟悉SMC自解密的应该知道这是敏感的操作,也就是解密操作

对于这种在输入开始前的反调试,采用attach to process

反调试

可以看到如果不是调试状态,就将数据强制转为函数指针

这里已经解密完并执行完程序了

对其交叉引用可以看到

AES-CBC、Base64加密

动调发现这就是加密函数

AES-CBC

可以知道该函数就是初始化密钥

从这里的明文异或结合CBC可以知道是CBC模式

明文异或

key和iv分别为sycloversyclover、sctfsctfsctfsctf

iv

这里很多this指针,但是我们还是可以通过一些特征以及FindCrypt发现这是AES中的key的初始化

轮常量异或

S表替换

AES-CBC加密

明文与iv向量的异或

内部加密

base64

进入该函数中,明显是base64加密

找到码表,发现没有被改过

比较

解密

SCTF2019-babyre

考点

base64解密、Maze、移位与异或、花指令

分析

花指令就不说了,全部都是一样的,去除即可

迷宫也没啥说的,注意这里是三维迷宫 ddwwxxssxaxwwaasasyywwdd

base64解密

可以看到这里先将我们的输入去table找索引,然后&0x3F取出最后的六位,按位与上v5左移6,这样就得到了8位的字符,加上=4的判断,类似于base64的解密操作

直接将最后的字符串加密即可c2N0Zl85MTAy

换位异或

加密流程:每四个输入存入int型的变量,然后进行xor_enc加密,将前四个字符加密后存放到第四个字符的后一个字符,依次往后。

之所以要这样进行移位是因为如果强制转为int型,由于小端序,存储的时候是12345678,这样int取出的时候就变成了4321

xor_enc

xor_enc加密流程:将第二第三第四个字符进行异或,分为四个字节去table找到对应的值,然后重新组合成int型,再经过一系列左移右移异或操作并返回,再将返回值与第一个字符异或,这样就得到了一个加密后的结果,这里要一直等到index<=29,也就是前四个字符不会加密

我们可以看到这里v10只有26,那么26、27、28、29下标的v10就赋值给了v11、v12、v13、v14

取出最后四个int型存储的十六个字节,存储到enc_flag数组,然后比较

假设int a=0x12345678,那么HIBYTE(a)=0x12,BYTE2(a)=0x34,BYTE1(a)=0x56

这样我们就知道了最后的四个int型的元素,往前推,29是怎么来的呢,是flag[25]enc(flag[26],flag[27],flag[28])得到的,所以flag[29]enc(flag[26],flag[27],flag[28])即可得到flag[25],前面的元素同理

脚本

因为涉及了ida的左右移位操作,而ida目录下defs.h有定义,所以可以直接引用

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
#include<stdio.h>
#include<string.h>
#include"defs.h"
unsigned int v3[273] = {
0x000000D6, 0x00000090, 0x000000E9, 0x000000FE, 0x000000CC, 0x000000E1, 0x0000003D, 0x000000B7,
0x00000016, 0x000000B6, 0x00000014, 0x000000C2, 0x00000028, 0x000000FB, 0x0000002C, 0x00000005,
0x0000002B, 0x00000067, 0x0000009A, 0x00000076, 0x0000002A, 0x000000BE, 0x00000004, 0x000000C3,
0x000000AA, 0x00000044, 0x00000013, 0x00000026, 0x00000049, 0x00000086, 0x00000006, 0x00000099,
0x0000009C, 0x00000042, 0x00000050, 0x000000F4, 0x00000091, 0x000000EF, 0x00000098, 0x0000007A,
0x00000033, 0x00000054, 0x0000000B, 0x00000043, 0x000000ED, 0x000000CF, 0x000000AC, 0x00000062,
0x000000E4, 0x000000B3, 0x0000001C, 0x000000A9, 0x000000C9, 0x00000008, 0x000000E8, 0x00000095,
0x00000080, 0x000000DF, 0x00000094, 0x000000FA, 0x00000075, 0x0000008F, 0x0000003F, 0x000000A6,
0x00000047, 0x00000007, 0x000000A7, 0x000000FC, 0x000000F3, 0x00000073, 0x00000017, 0x000000BA,
0x00000083, 0x00000059, 0x0000003C, 0x00000019, 0x000000E6, 0x00000085, 0x0000004F, 0x000000A8,
0x00000068, 0x0000006B, 0x00000081, 0x000000B2, 0x00000071, 0x00000064, 0x000000DA, 0x0000008B,
0x000000F8, 0x000000EB, 0x0000000F, 0x0000004B, 0x00000070, 0x00000056, 0x0000009D, 0x00000035,
0x0000001E, 0x00000024, 0x0000000E, 0x0000005E, 0x00000063, 0x00000058, 0x000000D1, 0x000000A2,
0x00000025, 0x00000022, 0x0000007C, 0x0000003B, 0x00000001, 0x00000021, 0x00000078, 0x00000087,
0x000000D4, 0x00000000, 0x00000046, 0x00000057, 0x0000009F, 0x000000D3, 0x00000027, 0x00000052,
0x0000004C, 0x00000036, 0x00000002, 0x000000E7, 0x000000A0, 0x000000C4, 0x000000C8, 0x0000009E,
0x000000EA, 0x000000BF, 0x0000008A, 0x000000D2, 0x00000040, 0x000000C7, 0x00000038, 0x000000B5,
0x000000A3, 0x000000F7, 0x000000F2, 0x000000CE, 0x000000F9, 0x00000061, 0x00000015, 0x000000A1,
0x000000E0, 0x000000AE, 0x0000005D, 0x000000A4, 0x0000009B, 0x00000034, 0x0000001A, 0x00000055,
0x000000AD, 0x00000093, 0x00000032, 0x00000030, 0x000000F5, 0x0000008C, 0x000000B1, 0x000000E3,
0x0000001D, 0x000000F6, 0x000000E2, 0x0000002E, 0x00000082, 0x00000066, 0x000000CA, 0x00000060,
0x000000C0, 0x00000029, 0x00000023, 0x000000AB, 0x0000000D, 0x00000053, 0x0000004E, 0x0000006F,
0x000000D5, 0x000000DB, 0x00000037, 0x00000045, 0x000000DE, 0x000000FD, 0x0000008E, 0x0000002F,
0x00000003, 0x000000FF, 0x0000006A, 0x00000072, 0x0000006D, 0x0000006C, 0x0000005B, 0x00000051,
0x0000008D, 0x0000001B, 0x000000AF, 0x00000092, 0x000000BB, 0x000000DD, 0x000000BC, 0x0000007F,
0x00000011, 0x000000D9, 0x0000005C, 0x00000041, 0x0000001F, 0x00000010, 0x0000005A, 0x000000D8,
0x0000000A, 0x000000C1, 0x00000031, 0x00000088, 0x000000A5, 0x000000CD, 0x0000007B, 0x000000BD,
0x0000002D, 0x00000074, 0x000000D0, 0x00000012, 0x000000B8, 0x000000E5, 0x000000B4, 0x000000B0,
0x00000089, 0x00000069, 0x00000097, 0x0000004A, 0x0000000C, 0x00000096, 0x00000077, 0x0000007E,
0x00000065, 0x000000B9, 0x000000F1, 0x00000009, 0x000000C5, 0x0000006E, 0x000000C6, 0x00000084,
0x00000018, 0x000000F0, 0x0000007D, 0x000000EC, 0x0000003A, 0x000000DC, 0x0000004D, 0x00000020,
0x00000079, 0x000000EE, 0x0000005F, 0x0000003E, 0x000000D7, 0x000000CB, 0x00000039, 0x00000048,
0x000000C6, 0x000000BA, 0x000000B1, 0x000000A3, 0x00000050, 0x00000033, 0x000000AA, 0x00000056,
0x00000097, 0x00000091, 0x0000007D, 0x00000067, 0x000000DC, 0x00000022, 0x00000070, 0x000000B2,
};
unsigned int encode(unsigned int xor_final)
{
int v2 = (v3[BYTE2(xor_final)] << 16) | v3[(unsigned __int8)xor_final] | (v3[BYTE1(xor_final)] << 8) | (v3[HIBYTE(xor_final)] << 24);
return __ROL4__(v2, 12) ^ (unsigned int)(__ROL4__(v2, 8) ^ __ROR4__(v2, 2)) ^ __ROR4__(v2, 6);
}
int main()
{
unsigned v10[30] = { 0 };
v10[26] = 0xBE040680;
v10[27] = 0xC5AF7647;
v10[28] = 0x9FCC401F;
v10[29] = 0xD8BF92EF;
for (int i = 25; i >= 0; --i)
{
v10[i] = v10[i + 4] ^ encode(v10[i + 1]^v10[i + 2]^v10[i + 3]);
}
for (int i = 0; i < 4; ++i)
{
printf("%c%c%c%c",((char*)(&v10[i]))[0], ((char*)(&v10[i]))[1], ((char*)(&v10[i]))[2], ((char*)(&v10[i]))[3]);
}

return 0;
}

这样子就不会存在int型读取时小端序的问题