【wp】2022Sloth选拔赛

上周末的Sloth选拔赛,趁着还有余温先来补补自出题的wp。

整个RE部分只有checkininin有一个解,大哭,逆向手岌岌可危(

仓库传送门:scnu-sloth/hsctf-2022-trial: Sloth 2022 选拔赛 题目及wp

Reverse

checkininin

拖进IDA里面,关键逻辑就这个(题目的三个in就取自这三个xor)

由hint给的HSCTF知识科普|第三期:Reverse逆向可以知道,xor有一种特性是两次xor相同的数以后可以得到其本身。

s1[i] ^ v5[i] ^ v6[i] ^ v7[i] == s2[i]时,s1为正确的flag。

那在v5v6v7s2都是已知的情况下,用s2[i] ^ v5[i] ^ v6[i] ^ v7[i]即可恢复出应该为flag的s1。

然后把IDA里的伪代码复制过来改改就能拿到flag(python也可,不过需要处理一下负数问题x

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include <stdio.h>

int main(int a1, char **a2, char **a3)
{
int i; // [rsp+Ch] [rbp-B4h]
char v5[32]; // [rsp+10h] [rbp-B0h]
char v6[32]; // [rsp+30h] [rbp-90h]
char v7[32]; // [rsp+50h] [rbp-70h]
char s2[33] = {0}; // [rsp+70h] [rbp-50h] BYREF
// char s1[32]; // [rsp+90h] [rbp-30h] BYREF

v5[0] = -119;
v5[1] = -85;
v5[2] = 0;
v5[3] = 85;
v5[4] = -23;
v5[5] = -107;
v5[6] = -58;
v5[7] = -16;
v5[8] = 124;
v5[9] = -21;
v5[10] = 28;
v5[11] = 87;
v5[12] = 43;
v5[13] = 30;
v5[14] = 106;
v5[15] = -18;
v5[16] = 80;
v5[17] = 78;
v5[18] = -55;
v5[19] = 97;
v5[20] = 124;
v5[21] = -86;
v5[22] = 98;
v5[23] = 39;
v5[24] = -74;
v5[25] = 11;
v5[26] = -31;
v5[27] = 35;
v5[28] = 74;
v5[29] = 24;
v5[30] = -61;
v5[31] = 74;
v6[0] = 108;
v6[1] = -124;
v6[2] = -80;
v6[3] = 25;
v6[4] = -16;
v6[5] = 72;
v6[6] = -5;
v6[7] = -61;
v6[8] = -115;
v6[9] = -120;
v6[10] = 122;
v6[11] = 109;
v6[12] = -37;
v6[13] = -87;
v6[14] = 92;
v6[15] = 8;
v6[16] = 10;
v6[17] = 69;
v6[18] = 0x80;
v6[19] = -27;
v6[20] = 10;
v6[21] = 14;
v6[22] = -74;
v6[23] = -96;
v6[24] = -53;
v6[25] = 30;
v6[26] = -4;
v6[27] = 114;
v6[28] = -115;
v6[29] = 84;
v6[30] = -42;
v6[31] = 23;
v7[0] = -96;
v7[1] = 103;
v7[2] = 68;
v7[3] = -101;
v7[4] = 121;
v7[5] = -23;
v7[6] = -65;
v7[7] = 111;
v7[8] = -96;
v7[9] = -126;
v7[10] = 46;
v7[11] = -120;
v7[12] = -123;
v7[13] = 109;
v7[14] = -11;
v7[15] = 27;
v7[16] = -95;
v7[17] = -7;
v7[18] = 30;
v7[19] = 55;
v7[20] = 127;
v7[21] = -4;
v7[22] = -69;
v7[23] = 35;
v7[24] = -74;
v7[25] = 125;
v7[26] = 7;
v7[27] = 117;
v7[28] = -113;
v7[29] = -112;
v7[30] = -13;
v7[31] = 121;
s2[0] = 45;
s2[1] = 59;
s2[2] = -105;
s2[3] = -93;
s2[4] = 6;
s2[5] = 79;
s2[6] = -35;
s2[7] = 14;
s2[8] = 98;
s2[9] = -43;
s2[10] = 4;
s2[11] = -2;
s2[12] = 44;
s2[13] = -123;
s2[14] = -100;
s2[15] = -72;
s2[16] = -95;
s2[17] = -83;
s2[18] = 8;
s2[19] = -20;
s2[20] = 81;
s2[21] = 23;
s2[22] = 61;
s2[23] = -21;
s2[24] = -103;
s2[25] = 39;
s2[26] = 72;
s2[27] = 5;
s2[28] = 105;
s2[29] = -3;
s2[30] = -71;
s2[31] = 89;
for ( i = 0; i <= 31; ++i )
{
s2[i] ^= v5[i];
s2[i] ^= v6[i];
s2[i] ^= v7[i];
}
printf("%s\n", s2);
return 0;
}

真·逆向签到题。

3ZVW

hints给了一个迷宫题的wiki,其实看代码也能看出来是迷宫。而且题目名字上下颠倒再倒着读不就是MAZE嘛~(逃

很明显只有sub_7D1()这一个check,稍微分析一下(考验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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
__int64 __fastcall sub_7D1(_BYTE *a1)
{
int v2; // eax
char v3; // [rsp+1Ch] [rbp-14h]
char v4; // [rsp+1Dh] [rbp-13h]
unsigned __int8 v5; // [rsp+1Eh] [rbp-12h]
unsigned __int8 i; // [rsp+1Fh] [rbp-11h]
char v7[4][2]; // [rsp+20h] [rbp-10h]
unsigned __int64 v8; // [rsp+28h] [rbp-8h]

v8 = __readfsqword(0x28u);
if ( memcmp(a1, "hsctf{", 6uLL) || a1[37] != '}' )
return 0LL;
v7[0][0] = 0;
v7[0][1] = -1;
v7[1][0] = -1;
v7[1][1] = 0;
v7[2][0] = 0;
v7[2][1] = 1;
v7[3][0] = 1;
v7[3][1] = 0;
v3 = 1;
v4 = 1;
for ( i = 6; i <= 0x24u; ++i )
{
v2 = (char)a1[i];
if ( v2 == 'j' )
{
v5 = 0;
}
else if ( v2 > 'j' )
{
if ( v2 == 'k' )
{
v5 = 2;
}
else
{
if ( v2 != 'l' )
return 0LL;
v5 = 1;
}
}
else
{
if ( v2 != 'h' )
return 0LL;
v5 = 3;
}
if ( ((maze[v3][v4] >> v5) & 1) == 0 )
return 0LL;
v3 += v7[3 - v5][0];
v4 += v7[3 - v5][1];
if ( (maze[v3][v4] & 0x10) != 0 )
return 1LL;
}
return 0LL;
}

明显,v7是一个二维数组{{0, -1}, {-1, 0}, {0, 1}, {1, 0}},是用来指定方向的;v5是一个临时值,由根据flag的字符赋值(flag中间值必为hjkl中的一个),根据maze的对应值判断能不能过check。

v3是x,v4是y,maze题型参考前年新生赛【wp】2020HSCTF_PC逆向 | c10udlnk_Log,有一点不同的是这一次maze不是存每个点是墙还是路,而是存四周的情况,比如v5=3(即当前值为h)时,就判断(maze[v3][v4] >> v5)(二进制的第3位)是不是1,是的话(二进制为1xxx)说明没有墙,可以通过;否的话(二进制为0xxx)说明有墙,不可通过。

二进制的第4位为1说明这个点是终点,第0/1/2/3位代表当前点往左/上/右/下的情况。

存四周的情况下,画图其实是不好画的(也能画,但是懒),考虑到是选拔赛难度所以把迷宫规格加到了18*18,exp可以写一个bfs自动走迷宫就好了。

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
def getVal(arr, t):
return arr[t[0]][t[1]]

def walkMaze(maze_data, lens, start, end):
dirs = [(0,-1), (-1,0), (0,1), (1,0)]
FLAG_CHAR = ['h', 'k', 'l', 'j']
path = [start]
step = [["" for _ in range(lens)] for _ in range(lens)]
flag = ""
while len(path) != 0:
prev = path[0]
del path[0]
for i in range(4):
cur = (prev[0]+dirs[i][0], prev[1]+dirs[i][1])
if getVal(maze_data, prev)&(1<<(3-i)) != 0 and getVal(step, cur) == "":
step[cur[0]][cur[1]] = getVal(step, prev) + FLAG_CHAR[i]
path.append(cur)
return getVal(step, end)

maze = [0x03, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x09, 0x05, 0x03, 0x08, 0x01, 0x03, 0x0A, 0x0B, 0x0A, 0x0B, 0x0B, 0x0B, 0x09, 0x03, 0x0A, 0x0B, 0x0A, 0x09, 0x05, 0x05, 0x07, 0x0A, 0x0F, 0x0E, 0x08, 0x06, 0x08, 0x05, 0x04, 0x04, 0x06, 0x0F, 0x08, 0x04, 0x02, 0x0C, 0x05, 0x05, 0x05, 0x03, 0x0E, 0x09, 0x02, 0x0A, 0x0A, 0x0F, 0x0A, 0x0B, 0x08, 0x07, 0x0B, 0x0B, 0x0A, 0x08, 0x05, 0x05, 0x04, 0x07, 0x09, 0x07, 0x0B, 0x0B, 0x08, 0x04, 0x03, 0x0F, 0x08, 0x05, 0x05, 0x07, 0x0B, 0x08, 0x05, 0x05, 0x03, 0x0D, 0x04, 0x05, 0x05, 0x07, 0x08, 0x02, 0x0D, 0x06, 0x09, 0x05, 0x04, 0x05, 0x04, 0x01, 0x05, 0x05, 0x05, 0x04, 0x01, 0x04, 0x05, 0x06, 0x08, 0x02, 0x0F, 0x09, 0x04, 0x04, 0x02, 0x0F, 0x08, 0x05, 0x05, 0x05, 0x05, 0x01, 0x07, 0x08, 0x06, 0x0A, 0x09, 0x01, 0x04, 0x07, 0x0B, 0x0A, 0x08, 0x06, 0x0A, 0x0D, 0x05, 0x05, 0x07, 0x0F, 0x0D, 0x02, 0x09, 0x01, 0x06, 0x0D, 0x02, 0x0D, 0x06, 0x09, 0x02, 0x0B, 0x0B, 0x0C, 0x05, 0x05, 0x05, 0x05, 0x07, 0x0B, 0x0E, 0x0C, 0x01, 0x07, 0x09, 0x05, 0x01, 0x06, 0x09, 0x04, 0x04, 0x01, 0x05, 0x05, 0x05, 0x04, 0x04, 0x05, 0x01, 0x03, 0x0C, 0x04, 0x04, 0x04, 0x07, 0x08, 0x06, 0x0B, 0x0B, 0x0D, 0x05, 0x05, 0x06, 0x0B, 0x08, 0x07, 0x0F, 0x0F, 0x0A, 0x0B, 0x0A, 0x0B, 0x0E, 0x0B, 0x09, 0x05, 0x05, 0x05, 0x05, 0x05, 0x02, 0x0F, 0x08, 0x05, 0x04, 0x07, 0x09, 0x05, 0x02, 0x0E, 0x09, 0x05, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x03, 0x0F, 0x09, 0x07, 0x08, 0x04, 0x04, 0x06, 0x09, 0x02, 0x0C, 0x06, 0x09, 0x03, 0x0D, 0x17, 0x05, 0x05, 0x05, 0x05, 0x04, 0x06, 0x0B, 0x0A, 0x08, 0x01, 0x07, 0x0A, 0x08, 0x01, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x04, 0x07, 0x0B, 0x08, 0x07, 0x0A, 0x0B, 0x0D, 0x06, 0x0B, 0x0A, 0x0F, 0x09, 0x01, 0x04, 0x05, 0x05, 0x05, 0x02, 0x0C, 0x04, 0x02, 0x0C, 0x02, 0x0C, 0x04, 0x02, 0x0E, 0x08, 0x04, 0x06, 0x0E, 0x08, 0x04, 0x05, 0x06, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0C]
a = 18
for i in range(len(maze)):
if maze[i] & 0x10 != 0:
break
end = (i//a, i%a)
maze = list(zip(*[iter(maze)] * a))
flag = "hsctf{" + walkMaze(maze, a, (1, 1), end) + "}"
print(flag)

(其实还有个revenge版的,这次没人做出3ZVW就没放,留到下一场比赛啦~

5rn4

题外话,对国密熟悉的朋友们应该会知道描述指的是SM4(后来hint也给了)。

因为选拔赛难度,想着用来防AK所以加了个SEH(结构化异常处理)+花指令,果然防住了呢(。

花指令的说明见RE套路 - 关于逆向常客花指令 | c10udlnk_Log。这里是有点考汇编功底的(我错了,下次还敢

从没办法解析到的汇编往上看,可以看到inc edi这条是不太正常的。

edi只在前面存了一个栈上的地址,这边自增的话就错位了(当然也不排除这种可能。最不正常的应该是再下一句inc ebp,ebp存着栈底地址,理论上是不能动的。

可以把这部分试一下怎么拆分能把剩下的全部转成可读代码,然后把没用的花指令patch掉,也可以用动态调试发现接下来应该走的代码(不过这里有很多次循环SEH,用于反调试,比较阴间)。

然后把这个0xff给nop掉再整个转函数就可以生成反编译代码了。

sub_9F1032可以猜到是printf,sub_9F1262猜是scanf。

实际上就是一个sm4的实现(可以对比SM4的实现,如果看不出来就对比源码参考链接https://blog.csdn.net/weixin_42700740/article/details/102667012),密钥是`746573746f766572`,密文在byte_9FB000,可以说是一个密码题极简模板。

就是有一个魔改密文被藏在异常处理里了:

也可以看交叉引用发现(如果想到出题人这么阴间的话)

所以写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
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// sm4 code from https://blog.csdn.net/weixin_42700740/article/details/102667012

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

/******************************定义系统参数FK的取值****************************************/
const unsigned long TBL_SYS_PARAMS[4] = {
0xa3b1bac6,
0x56aa3350,
0x677d9197,
0xb27022dc
};

/******************************定义固定参数CK的取值****************************************/
const unsigned long TBL_FIX_PARAMS[32] = {
0x00070e15,0x1c232a31,0x383f464d,0x545b6269,
0x70777e85,0x8c939aa1,0xa8afb6bd,0xc4cbd2d9,
0xe0e7eef5,0xfc030a11,0x181f262d,0x343b4249,
0x50575e65,0x6c737a81,0x888f969d,0xa4abb2b9,
0xc0c7ced5,0xdce3eaf1,0xf8ff060d,0x141b2229,
0x30373e45,0x4c535a61,0x686f767d,0x848b9299,
0xa0a7aeb5,0xbcc3cad1,0xd8dfe6ed,0xf4fb0209,
0x10171e25,0x2c333a41,0x484f565d,0x646b7279
};

/******************************SBox参数列表****************************************/
const unsigned char TBL_SBOX[256] = {
0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x05,
0x2b,0x67,0x9a,0x76,0x2a,0xbe,0x04,0xc3,0xaa,0x44,0x13,0x26,0x49,0x86,0x06,0x99,
0x9c,0x42,0x50,0xf4,0x91,0xef,0x98,0x7a,0x33,0x54,0x0b,0x43,0xed,0xcf,0xac,0x62,
0xe4,0xb3,0x1c,0xa9,0xc9,0x08,0xe8,0x95,0x80,0xdf,0x94,0xfa,0x75,0x8f,0x3f,0xa6,
0x47,0x07,0xa7,0xfc,0xf3,0x73,0x17,0xba,0x83,0x59,0x3c,0x19,0xe6,0x85,0x4f,0xa8,
0x68,0x6b,0x81,0xb2,0x71,0x64,0xda,0x8b,0xf8,0xeb,0x0f,0x4b,0x70,0x56,0x9d,0x35,
0x1e,0x24,0x0e,0x5e,0x63,0x58,0xd1,0xa2,0x25,0x22,0x7c,0x3b,0x01,0x21,0x78,0x87,
0xd4,0x00,0x46,0x57,0x9f,0xd3,0x27,0x52,0x4c,0x36,0x02,0xe7,0xa0,0xc4,0xc8,0x9e,
0xea,0xbf,0x8a,0xd2,0x40,0xc7,0x38,0xb5,0xa3,0xf7,0xf2,0xce,0xf9,0x61,0x15,0xa1,
0xe0,0xae,0x5d,0xa4,0x9b,0x34,0x1a,0x55,0xad,0x93,0x32,0x30,0xf5,0x8c,0xb1,0xe3,
0x1d,0xf6,0xe2,0x2e,0x82,0x66,0xca,0x60,0xc0,0x29,0x23,0xab,0x0d,0x53,0x4e,0x6f,
0xd5,0xdb,0x37,0x45,0xde,0xfd,0x8e,0x2f,0x03,0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51,
0x8d,0x1b,0xaf,0x92,0xbb,0xdd,0xbc,0x7f,0x11,0xd9,0x5c,0x41,0x1f,0x10,0x5a,0xd8,
0x0a,0xc1,0x31,0x88,0xa5,0xcd,0x7b,0xbd,0x2d,0x74,0xd0,0x12,0xb8,0xe5,0xb4,0xb0,
0x89,0x69,0x97,0x4a,0x0c,0x96,0x77,0x7e,0x65,0xb9,0xf1,0x09,0xc5,0x6e,0xc6,0x84,
0x18,0xf0,0x7d,0xec,0x3a,0xdc,0x4d,0x20,0x79,0xee,0x5f,0x3e,0xd7,0xcb,0x39,0x48
};

//4字节无符号数组转无符号long型
void four_uCh2uLong(unsigned char* in, unsigned long* out)
{
int i = 0;
*out = 0;
for (i = 0; i < 4; i++)
*out = ((unsigned long)in[i] << (24 - i * 8)) ^ *out;
}

//无符号long型转4字节无符号数组
void uLong2four_uCh(unsigned long in, unsigned char* out)
{
int i = 0;
//从32位unsigned long的高位开始取
for (i = 0; i < 4; i++)
*(out + i) = (unsigned long)(in >> (24 - i * 8));
}

//左移,保留丢弃位放置尾部
unsigned long move(unsigned long data, int length)
{
unsigned long result = 0;
result = (data << length) ^ (data >> (32 - length));

return result;
}

//秘钥处理函数,先使用Sbox进行非线性变化,再将线性变换L置换为L'
unsigned long func_key(unsigned long input)
{
int i = 0;
unsigned long ulTmp = 0;
unsigned char ucIndexList[4] = { 0 };
unsigned char ucSboxValueList[4] = { 0 };
uLong2four_uCh(input, ucIndexList);
for (i = 0; i < 4; i++)
{
ucSboxValueList[i] = TBL_SBOX[ucIndexList[i]];
}
four_uCh2uLong(ucSboxValueList, &ulTmp);
ulTmp = ulTmp ^ move(ulTmp, 13) ^ move(ulTmp, 23);

return ulTmp;
}

//加解密数据处理函数,先使用Sbox进行非线性变化,再进行线性变换L
unsigned long func_data(unsigned long input)
{
int i = 0;
unsigned long ulTmp = 0;
unsigned char ucIndexList[4] = { 0 };
unsigned char ucSboxValueList[4] = { 0 };
uLong2four_uCh(input, ucIndexList);
for (i = 0; i < 4; i++)
{
ucSboxValueList[i] = TBL_SBOX[ucIndexList[i]];
}
four_uCh2uLong(ucSboxValueList, &ulTmp);
ulTmp = ulTmp ^ move(ulTmp, 2) ^ move(ulTmp, 10) ^ move(ulTmp, 18) ^ move(ulTmp, 24);

return ulTmp;
}

//解密函数(与加密函数基本一致,只是秘钥使用的顺序不同,即把钥匙反着用就是解密)
//len:数据长度 key:密钥 input:输入的加密后数据 output:输出的解密后数据
void decode_fun(unsigned char len, unsigned char* key, unsigned char* input, unsigned char* output)
{
int i = 0, j = 0;
unsigned long ulKeyTmpList[4] = { 0 };//存储密钥的unsigned long数据
unsigned long ulKeyList[36] = { 0 }; //用于密钥扩展算法与系统参数FK运算后的结果存储
unsigned long ulDataList[36] = { 0 }; //用于存放加密数据

/*开始生成子秘钥*/
four_uCh2uLong(key, &(ulKeyTmpList[0]));
four_uCh2uLong(key + 4, &(ulKeyTmpList[1]));
four_uCh2uLong(key + 8, &(ulKeyTmpList[2]));
four_uCh2uLong(key + 12, &(ulKeyTmpList[3]));

ulKeyList[0] = ulKeyTmpList[0] ^ TBL_SYS_PARAMS[0];
ulKeyList[1] = ulKeyTmpList[1] ^ TBL_SYS_PARAMS[1];
ulKeyList[2] = ulKeyTmpList[2] ^ TBL_SYS_PARAMS[2];
ulKeyList[3] = ulKeyTmpList[3] ^ TBL_SYS_PARAMS[3];

for (i = 0; i < 32; i++) //32次循环迭代运算
{
//5-36为32个子秘钥
ulKeyList[i + 4] = ulKeyList[i] ^ func_key(ulKeyList[i + 1] ^ ulKeyList[i + 2] ^ ulKeyList[i + 3] ^ TBL_FIX_PARAMS[i]);
}
/*生成32轮32位长子秘钥结束*/

for (j = 0; j < len / 16; j++) //进行循环加密,并将加密后数据保存
{
/*开始处理解密数据*/
four_uCh2uLong(input + 16 * j, &(ulDataList[0]));
four_uCh2uLong(input + 16 * j + 4, &(ulDataList[1]));
four_uCh2uLong(input + 16 * j + 8, &(ulDataList[2]));
four_uCh2uLong(input + 16 * j + 12, &(ulDataList[3]));

//解密
for (i = 0; i < 32; i++)
{
ulDataList[i + 4] = ulDataList[i] ^ func_data(ulDataList[i + 1] ^ ulDataList[i + 2] ^ ulDataList[i + 3] ^ ulKeyList[35 - i]);//与加密唯一不同的就是轮密钥的使用顺序
}
/*将解密后数据输出*/
uLong2four_uCh(ulDataList[35], output + 16 * j);
uLong2four_uCh(ulDataList[34], output + 16 * j + 4);
uLong2four_uCh(ulDataList[33], output + 16 * j + 8);
uLong2four_uCh(ulDataList[32], output + 16 * j + 12);
}
}

unsigned char byte_40B000[32] = {
0xF6, 0x66, 0xAE, 0xF4, 0xFE, 0x17, 0xAE, 0x18, 0xAE, 0x68, 0xC7, 0x81, 0x18, 0x86, 0xC1, 0x9B,
0xF5, 0xEA, 0xE6, 0x22, 0x8F, 0x42, 0xCF, 0xF0, 0xD0, 0x15, 0x2A, 0xB7, 0xDC, 0xB3, 0x68, 0x4F
};

int main() {
unsigned char i, len;
unsigned char rslt[32] = { 0 };
unsigned char key[20] = "746573746f766572";
for (int i = 0; i <= 512; i++) { // 做了513次交换,其实就是做了一次交换,魔改密文
byte_40B000[23] ^= byte_40B000[24];
byte_40B000[24] ^= byte_40B000[23];
byte_40B000[23] ^= byte_40B000[24];
}
decode_fun(32, key, byte_40B000, rslt);
for (int i = 0; i < 32; i++) {
printf("%c", rslt[i]);
}
return 0;
}

Misc

TapIt

附件里有一个Mikuuuuuu.mp4,给了一个演唱工具,明显是让你用这个演唱工具唱出跟mp4一样的旋律。

flag格式给了一个正则r'[0-9a-v]{30}',意思是长度为30,字符为0-9或者a-v的某一个字符。

演唱工具魔改自HFIProgramming/mikutap: A Mainland China Friendly and independent version extracted from https://aidn.jp/mikutap,逆一下mikutap.js(不要问为什么逆这个,其他看起来就是导入的js,只有这个是用户自己写的)。

画面呈现存放在数组m中:

声音则是存在data/main/main.json中,通过base64解码得到音频。

而通过测试或者逆向代码能发现,0-9a-v可以呈现32种不同的声音,16种不同的画面类型,也就是说有两个字符的画面是一种类型的,但是声音呈现是唯一的。那么就可以通过画面+声音筛选拿到flag:l0vemikufor3verdoul1ke7hi5misc

值得一提的是,原工具中每一次打开时同一个字符对应的画面类型是不同的,但是在同一次打开中同一个字符对应的画面类型是相同的(shuffle操作只在初始化的时候有做),但是本题工具中为了方便解题把这句shuffle给删掉了,所以无论多少次打开都是一样的。

本文作者: c10udlnk
本文链接: https://c10udlnk.top/p/wpFor-2022SlothTrial/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 c10udlnk' Blog (https://c10udlnk.top)!