复现期间去郑州玩了一趟,先复现一下数据安全的 re 题,当时在线下做了nm 90%最后发现密钥都写脸上了就是没出😅😅😅

Butterfly

安卓逆向,先看 AndroidManifest.xmlMainActivity

1677394832891

check 函数是 static 方法,明显要逆 so ,解压以后 ida 打开,是个 arm 架构的。

字符串窗口可以搜到 check 字符,查查交叉引用,用 JNIEnv * 修复一些函数以后,发现注册了一个 native 方法

1677395132780

1677395199174

sub_1FD80 就是我们要找的 check 函数,点进去翻翻找到了 yyyyyy 这个函数,显然是被混淆了,但是没有关系,直接选中汇编创建函数然后反编译即可

1677395325905

同时发现相邻的函数

1677395451300

非常可疑,点进去一眼 AEScipher 就是 yyyyyy 里的 v6,密钥和 iv 都是 1234567890123456

1677395740995

cyberchef 一把梭 flag{welc0me_backTo_obfuscation}

PZGalaxy

直接 view - source 一下就能找到源码,一看是个 RC4 加密

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
function Leaf(k, p) {
var s = [], j = 0, x, res = '';
for (var i = 0; i < 256; i++) {
s[i] = i;
}
for (i = 0; i < 256; i++) {
j = (j + s[i] + k.charCodeAt(i % k.length)) % 256;
x = s[i];
s[i] = s[j];
s[j] = x;
}
i = 0;
j = 0;
for (var y = 0; y < p.length; y++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
x = s[i];
s[i] = s[j];
s[j] = x;
res += String.fromCharCode(p.charCodeAt(y) ^ s[(s[i] + s[j]) % 256]);
}
return res;
}

form.addEventListener("submit", (ev) => {
ev.preventDefault();

var date = document.getElementById('date').value;

var enc = ['¦', 'p', ':', 'Ü', '\x92', 'Ã', '\x97', 'ó', '\x1A', 'ß', '\b', 'Ö', 'A', ' ', '5', '\x90', '{', '\x06', 'Ô', '÷', 's', '_', '\x1D', ':', 'I', 'L', 'C', 'X', 'Ñ', '¹', 'O', '\x99', '\x85', '3', 'à', 'i', '|'];
flag = Leaf(date, enc.join(''));

if ( flag.substring(0, 4) == "flag" && date.length == 8 && date.substring(0, 4) == "2023"){
Galaxy('Neko.jpg', 50);
alert(flag);
} else{
Galaxy('Fish.jpg', 50);
alert("鱼怎么抓痒?");
}
}

注意这行判断 flag.substring(0, 4) == "flag" && date.length == 8 && date.substring(0, 4) == "2023",说明我们需要求的是日期 date ,这个日期的长度是 8,是 RC4 的密钥,我们需要对密钥的后四位进行爆破,把得到的结果输入到网站中就可以获得 flag,和之前西湖论剑 babyRE 的脚本基本一模一样

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
#include <bits/stdc++.h>
using namespace std;

unsigned char key[8];
unsigned char s_box[256];
unsigned char tmp[36];
unsigned char rc4_cipher[] =
{
0xa6, 0x70, 0x3a, 0xdc, 0x92, 0xc3, 0x97, 0xf3, 0x1a, 0xdf, 0x8d , 0x64, 0x12, 0x03, 0x59, 0x07, 0xb6, 0xd4, 0xf7, 0x73, 0x5f, 0x1d, 0x3a, 0x49, 0x4c, 0x43, 0x58, 0xd1, 0xb9, 0x4f, 0x99, 0x85, 0x33, 0xe0, 0x69, 0x7c
};
void rc4_init(unsigned char *s,unsigned char *key, unsigned long Len)
{
int i = 0,j = 0;
unsigned char T[256] = {0};
unsigned char tmp = 0;
for(i = 0; i < 256; i++) {
s[i] = i;
T[i] = key[i % Len];
}
for(i = 0; i < 256; i++) {
j = (j + s[i] + T[i]) % 256;
swap(s[i], s[j]);
}
}

void rc4_decrypt(unsigned char *s,unsigned char *Data,unsigned long Len)
{
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for(k = 0; k < Len; k++)
{
i = (i + 1) % 256;
j = (j + s[i]) % 256;
swap(s[i], s[j]);
t = (s[i] + s[j]) % 256;
Data[k] ^= s[t];
}
}

bool check()
{
if(tmp[0] == 'f' && tmp[1] == 'l' && tmp[2] == 'a' && tmp[3] == 'g')return 1;
return 0;
}


int main()
{
key[0] = '2';
key[1] = '0';
key[2] = '2';
key[3] = '3';
for(key[4] = '0'; key[4] <= '1'; key[4]++)
for(key[5] = '0'; key[5] <= '9'; key[5]++)
for(key[6] = '0'; key[6] <= '3'; key[6]++)
for(key[7] = '0'; key[7] <= '9'; key[7]++)
{

for(int i = 0; i < 36; i++)tmp[i] = rc4_cipher[i];
rc4_init(s_box, key, 8);
rc4_decrypt(s_box, tmp, 36);
if(check())
{
for(int i = 0; i < 8; i++)printf("%c", key[i]);
return 0;
}
}
}

运行可以得到正确的密钥是 20230127,放到网页中输入一下得到 flag{HitYourSoulAndSeeYouInTheGalaxy}

refuse_re

一打开感觉看不出啥东西,考虑直接动调。

1677224679242

前面就是个判断颜色的按照提示和源码输入 bor 即可绕过,然后发现所有的函数都被 call $5类型的花指令给混淆了,但是没啥用,直接F7就能调到主函数

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
// local variable allocation has failed, the output may be wrong!
unsigned __int64 __fastcall sub_40291F(__int64 a1, __int64 a2)
{
char v2; // al
char v3; // al
unsigned __int64 result; // rax
int i; // [rsp+4h] [rbp-28Ch]
int j; // [rsp+4h] [rbp-28Ch]
int k; // [rsp+4h] [rbp-28Ch]
int m; // [rsp+4h] [rbp-28Ch]
int n; // [rsp+4h] [rbp-28Ch]
int ii; // [rsp+4h] [rbp-28Ch]
int v11; // [rsp+8h] [rbp-288h]
_BYTE plain_1[2]; // [rsp+10h] [rbp-280h] OVERLAPPED
__int64 v13; // [rsp+18h] [rbp-278h]
char v14; // [rsp+20h] [rbp-270h]
_BYTE plain_2[2]; // [rsp+30h] [rbp-260h] OVERLAPPED BYREF
__int64 v16; // [rsp+38h] [rbp-258h]
char v17; // [rsp+40h] [rbp-250h]
_BYTE key[2]; // [rsp+50h] [rbp-240h] OVERLAPPED
__int64 v19; // [rsp+58h] [rbp-238h]
char v20; // [rsp+60h] [rbp-230h]
_BYTE cipher[4]; // [rsp+70h] [rbp-220h] OVERLAPPED
__int64 v22; // [rsp+78h] [rbp-218h]
__int64 v23; // [rsp+80h] [rbp-210h]
__int64 v24; // [rsp+88h] [rbp-208h]
char v25[240]; // [rsp+90h] [rbp-200h] BYREF
_BYTE input[31]; // [rsp+180h] [rbp-110h] BYREF
int v27; // [rsp+278h] [rbp-18h] BYREF
__int16 v28; // [rsp+27Ch] [rbp-14h]
char v29; // [rsp+27Eh] [rbp-12h]
char v30; // [rsp+27Fh] [rbp-11h] BYREF
unsigned __int64 v31; // [rsp+288h] [rbp-8h]

v31 = __readfsqword(0x28u);
*(_QWORD *)plain_1 = 0LL;
v13 = 0LL;
v14 = 0;
*(_QWORD *)plain_2 = 0LL;
v16 = 0LL;
v17 = 0;
*(_QWORD *)key = 0LL;
v19 = 0LL;
v20 = 0;
*(_QWORD *)cipher = 0x153CF38D66CCDCDCLL;
v22 = 0xDFD7D94616EF5E50LL;
v23 = 0x39A7DC6577442750LL;
v24 = 0xF64DD887B7FCB68LL;
*(_QWORD *)input = 0LL;
*(_QWORD *)&input[8] = 0LL;
memset(&input[16], 0, 0xE8uLL);
v27 = 0;
v28 = 0;
v29 = 0;
sub_4043D8(&v27, a2, &v30);
sub_4043F7(input);
for ( i = 0; input[i]; ++i )
;
if ( i == 32 ) // flag 长度为32
{
for ( j = 0; j <= 15; ++j ) // 生成密钥
{
sub_404444();
key[j] = v2;
}
for ( k = 0; k <= 15; ++k ) // 数据分组
{
plain_1[k] = input[k];
plain_2[k] = input[k + 16];
}
sub_404462();
sub_40449B();
for ( m = 0; m <= 15; ++m )
{
sub_4044D4();
plain_2[m] = v3;
}
sub_40450C();
v11 = 1;
for ( n = 0; n <= 15; ++n )
{
if ( plain_1[n] != cipher[n] )
v11 = 0;
}
for ( ii = 0; ii <= 15; ++ii )
{
if ( plain_2[ii] != cipher[ii + 16] )
v11 = 0;
}
if ( v11 == 1 )
sub_404545();
else
sub_404564(plain_2, v25);
}
else
{
sub_404425();
}
result = __readfsqword(0x28u) ^ v31;
if ( result )
sub_45FE30();
return result;
}

然后一直调,发现是个稍微魔改的AES,密钥扩展好像魔改了,但是没啥用,直接把轮密钥 dump 出来。

1677225481031

需要注意的是魔改了 AddRoundKey,在异或轮密钥以后又异或了 0x23

1677225383086

此外数据分成两轮进行加密,第一轮加密以后 plain2 = (plain2 ^ plain1) + 1,最后是分别比对,可以把 cipher 也给 dump 出来。

给出 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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
#include <stdio.h>
#include <stdint.h>
#include <memory.h>
typedef enum {
AES_CYPHER_128,
AES_CYPHER_192,
AES_CYPHER_256,
} AES_CYPHER_T;
/*
* Encryption Rounds
*/
int g_aes_key_bits[] = {
/* AES_CYPHER_128 */
128,
/* AES_CYPHER_192 */
192,
/* AES_CYPHER_256 */
256,
};
int g_aes_rounds[] = {
/* AES_CYPHER_128 */
10,
/* AES_CYPHER_192 */
12,
/* AES_CYPHER_256 */
14,
};
int g_aes_nk[] = {
/* AES_CYPHER_128 */
4,
/* AES_CYPHER_192 */
6,
/* AES_CYPHER_256 */
8,
};
int g_aes_nb[] = {
/* AES_CYPHER_128 */
4,
/* AES_CYPHER_192 */
4,
/* AES_CYPHER_256 */
4,
};

static const uint32_t g_aes_rcon[] = {
0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000,
0x40000000, 0x80000000,
0x1b000000, 0x36000000, 0x6c000000, 0xd8000000, 0xab000000, 0xed000000,
0x9a000000
};

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

}
}

uint8_t aes_xtime(uint8_t x) {
return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
}
uint8_t aes_xtimes(uint8_t x, int ts) {
while (ts-- > 0) {
x = aes_xtime(x);
}
return x;
}
uint8_t aes_mul(uint8_t x, uint8_t y) {
return ((((y >> 0) & 1) * aes_xtimes(x, 0)) ^
(((y >> 1) & 1) * aes_xtimes(x, 1)) ^
(((y >> 2) & 1) * aes_xtimes(x, 2)) ^
(((y >> 3) & 1) * aes_xtimes(x, 3)) ^
(((y >> 4) & 1) * aes_xtimes(x, 4)) ^
(((y >> 5) & 1) * aes_xtimes(x, 5)) ^
(((y >> 6) & 1) * aes_xtimes(x, 6)) ^
(((y >> 7) & 1) * aes_xtimes(x, 7)));
}

void inv_shift_rows(AES_CYPHER_T mode, uint8_t *state) {
uint8_t *s = (uint8_t *)state;
int i, j, r;
for (i = 1; i < g_aes_nb[mode]; i++) {
for (j = 0; j < g_aes_nb[mode] - i; j++) {
uint8_t tmp = s[i];
for (r = 0; r < g_aes_nb[mode]; r++) {
s[i + r * 4] = s[i + (r + 1) * 4];
}
s[i + (g_aes_nb[mode] - 1) * 4] = tmp;
}
}
}
uint8_t inv_sub_sbox(uint8_t val) {
return g_inv_sbox[val];
}
void inv_sub_bytes(AES_CYPHER_T mode, uint8_t *state) {
int i, j;
for (i = 0; i < g_aes_nb[mode]; i++) {
for (j = 0; j < 4; j++) {
state[i * 4 + j] = inv_sub_sbox(state[i * 4 + j]);
}
}
}
void inv_mix_columns(AES_CYPHER_T mode, uint8_t *state) {
uint8_t y[16] = { 0x0e, 0x0b, 0x0d, 0x09, 0x09, 0x0e, 0x0b, 0x0d,
0x0d, 0x09, 0x0e, 0x0b, 0x0b, 0x0d, 0x09, 0x0e
};
uint8_t s[4];
int i, j, r;
for (i = 0; i < g_aes_nb[mode]; i++) {
for (r = 0; r < 4; r++) {
s[r] = 0;
for (j = 0; j < 4; j++) {
s[r] = s[r] ^ aes_mul(state[i * 4 + j], y[r * 4 + j]);
}
}
for (r = 0; r < 4; r++) {
state[i * 4 + r] = s[r];
}
}
}
int aes_decrypt(AES_CYPHER_T mode, uint8_t *data, int len, uint8_t *key) {
uint8_t w[256] = { 0xCA, 0x9D, 0x7F, 0xF8, 0xA4, 0x09, 0x90, 0x04, 0xFA, 0xD4,
0x06, 0x61, 0xE9, 0x3B, 0x77, 0x5A, 0x29, 0x68, 0xC1, 0xE6,
0x8D, 0x61, 0x51, 0xE2, 0x77, 0xB5, 0x57, 0x83, 0x9E, 0x8E,
0x20, 0xD9, 0x32, 0xDF, 0xF4, 0xED, 0xBF, 0xBE, 0xA5, 0x0F,
0xC8, 0x0B, 0xF2, 0x8C, 0x56, 0x85, 0xD2, 0x55, 0xA1, 0x6A,
0x08, 0x5C, 0x1E, 0xD4, 0xAD, 0x53, 0xD6, 0xDF, 0x5F, 0xDF,
0x80, 0x5A, 0x8D, 0x8A, 0x17, 0x37, 0x76, 0x91, 0x09, 0xE3,
0xDB, 0xC2, 0xDF, 0x3C, 0x84, 0x1D, 0x5F, 0x66, 0x09, 0x97,
0x34, 0x36, 0xFE, 0x5E, 0x3D, 0xD5, 0x25, 0x9C, 0xE2, 0xE9,
0xA1, 0x81, 0xBD, 0x8F, 0xA8, 0x16, 0x67, 0xF4, 0xB9, 0x24,
0x5A, 0x21, 0x9C, 0xB8, 0xB8, 0xC8, 0x3D, 0x39, 0x05, 0x47,
0x95, 0x2F, 0x87, 0xDE, 0xAC, 0x4F, 0xDD, 0xFF, 0x30, 0xF7,
0x65, 0x37, 0x0D, 0xCE, 0x60, 0x70, 0x98, 0xE1, 0x56, 0x98,
0x54, 0x9F, 0x8B, 0x67, 0x64, 0x68, 0xEE, 0x50, 0x69, 0xA6,
0x8E, 0x20, 0xF1, 0x47, 0xFA, 0x39, 0xF4, 0x86, 0x71, 0x5E,
0x90, 0xEE, 0x9F, 0x0E, 0xF9, 0x48, 0x11, 0x2E, 0x08, 0x0F,
0xFD, 0x09, 0x82, 0x04, 0x8C, 0x57, 0x12, 0xEA, 0x13, 0x59,
0xEB, 0xA2, 0x02, 0x77, 0xE3, 0xAD, 0x18, 0x50, 0x4E, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xF0, 0x7E, 0xAB, 0x05, 0xFD, 0x7F, 0x00, 0x00,
0xFF, 0x44, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xD0, 0x7E, 0xAB, 0x05,
0xFD, 0x7F, 0x00, 0x00, 0x10, 0x7E, 0xAB, 0x05, 0xFD, 0x7F,
0x00, 0x00, 0x00, 0xB1, 0xD0, 0x33, 0xE5, 0xF6, 0x66, 0xEE,
0x31, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x32, 0x33, 0x33,
0x33, 0x33, 0x34, 0x34, 0x34, 0x34
};
/* round key */
uint8_t s[4 * 4] = { 0 };
/* state */
int nr, i, j;
/* key expansion */
// aes_key_expansion(mode, key, w);
/* start data cypher loop over input buffer */
for (i = 0; i < len; i += 4 * g_aes_nb[mode]) {
/* init state from user buffer (cyphertext) */
for (j = 0; j < 4 * g_aes_nb[mode]; j++)
s[j] = data[i + j];
/* start AES cypher loop over all AES rounds */
for (nr = g_aes_rounds[mode]; nr >= 0; nr--) {
/* do AddRoundKey */
aes_add_round_key(mode, s, w, nr);
if (nr > 0) {
if (nr < g_aes_rounds[mode]) {
/* do MixColumns */
inv_mix_columns(mode, s);
}
/* do ShiftRows */
inv_shift_rows(mode, s);
/* do SubBytes */
inv_sub_bytes(mode, s);
}
}
/* save state (cypher) to user buffer */
for (j = 0; j < 4 * g_aes_nb[mode]; j++)
data[i + j] = s[j];
}
return 0;
}

int main() {

uint8_t buf[] = {0xdc, 0xdc, 0xcc, 0x66, 0x8d, 0xf3, 0x3c, 0x15, 0x50, 0x5e, 0xef, 0x16, 0x46, 0xd9, 0xd7, 0xdf};
uint8_t buf1[] = {0xdc, 0xdc, 0xcc, 0x66, 0x8d, 0xf3, 0x3c, 0x15, 0x50, 0x5e, 0xef, 0x16, 0x46, 0xd9, 0xd7, 0xdf};

uint8_t key[] =
{0xCA, 0x9D, 0x7F, 0xF8, 0xA4, 0x09, 0x90, 0x04, 0xFA, 0xD4, 0x06, 0x61, 0xE9, 0x3B, 0x77, 0x5A};

aes_decrypt(AES_CYPHER_128, buf, sizeof(buf), key);

for(int i = 0; i < 16; i++)printf("%c", buf[i]);

uint8_t buf2[] = {0x50, 0x27, 0x44, 0x77, 0x65, 0xdc, 0xa7, 0x39, 0x68, 0xcb, 0x7f, 0x7b, 0x88, 0xdd, 0x64, 0xf};

aes_decrypt(AES_CYPHER_128, buf2, sizeof(buf2), key);

for(int i = 0; i < 16; i++) buf2[i] = (buf2[i] - 1) ^ buf1[i];

for(int i = 0; i < 16; i++)printf("%c", buf2[i]);

return 0;
}

运行得到 flag{fa9ad36bd2de1586d944cf7b2935dd91}

dll_puzzle(魔王题部分复现)

大概是👶目前为止遇到过最逆天的逆向题了吧🤔对着 WP 怼了一天大概看懂了一半,后面的解密流程还有一堆包含彩蛋的代码实在看不懂了,晚上看了大爹的视频分享实在是泪目,建议加入睡前故事合集反复观看。

这里分享一种动调的方法,可能会比静态嗯逆要简单一些(可能,应该,会,简单,一些,吧,大概)。

程序启动和调试方法

程序是一个 dll 样本,不能直接运行,需要附加运行,可以用 regsvr32,rundll32,loaddll 等进行启动

1
2
regsvr32 RegisterServer.dll
rundll32 RegisterServer.dll,DllEntryPoint

一些静态分析

先扔进 exeinfo ,发现没有壳,是 32-bit dll 文件。

1677595360181

再用 ida 打开程序,入口点停在了 DllEntryPoint 这里

1677595498437

我们随便翻翻里面的函数,sub_1000BD57 里面用到的 __security_cookie 是用于防止栈溢出,显然是系统函数,不用看,后面的样本分析过程中会遇到大量的类似系统函数,我们需要善于利用搜索引擎搜索一些可见字符或者常量等用来识别某个函数到底干了什么。

然后一瞟左边函数窗口发现有 TLSCallBack 函数,可以 ctrl + E 一下并发现题目中的一个彩蛋,同时也能发现回调函数的入口。

1677596274186

然后进回调函数看看,刚点开人就有点麻

1677596409920

只能一步一步分析,先观察一些被调用了很多次的函数,比如 sub_1000B6E0sub_1000B930这种,出现频率很高而且看起来比较可疑的函数。

1677596525646

sub_1000B6E0

观察回调函数中 sub_1000B6E0 的两个参数,发现第一个参数总是 -2097490458,进入第一个 if 分支,我们来看看这个分支里都干了些什么。

1677596672729

首先用一个全局变量存储了 sub_1000AB30 的返回值,然后再作为参数传入 sub_1000AB60 中,先看sub_1000AB30

1677596748702

一个经典 return 9花指令,简单分析一下

1677597262496

去除的方法就是 callretn 全给 nop 掉,那么我们接着看正常的代码

支线任务:获取 kernel32.dll 基 址

参考 [原创]详解七句汇编获取Kernel32模块地址-编程技术-看雪论坛-安全社区|安全招聘|bbs.pediy.com (kanxue.com)

1
2
3
4
5
6
7
8
9
10
GetKernel32Base proc
assume fs:nothing ;开始使用fs寄存器
mov eax, dword ptr fs : [30h] ;指向PEB结构
mov eax, dword ptr[eax + 0Ch] ;指向LDR Ptr32 _PEB_LDR_DATA
mov eax, dword ptr[eax + 0Ch] ;指向InLoadOrderModuleList _LIST_ENTRY
mov eax, dword ptr[eax] ;移动_LIST_ENTRY
mov eax, dword ptr[eax] ;指向Kernel32
mov eax, dword ptr[eax + 18h] ;指向DllBase基址
ret
GetKernelBase endp

1677597760301

比较一下,发现基本是一模一样,可以认定这几行汇编是为了获得 kernel32 模块的地址。

再来看 sub_1000AB60 函数

1677598576267

先搜搜函数中涉及的一些常量,可能是一些加密算法的初始化变量

1677598991295

一搜发现是 SM3 加密算法,再一看 SM3 算法里的初始化常数

1677599163203

和反编译出来的一模一样,于是实锤,在加密函数的最后发现还异或了第二个参数 a2

1677599244056

猜测应该是从 kernel32.dll 模块中找到某个 APIhash 值进行校验之后返回该 API 的地址。

我们回到函数开始的地方

1677599347686

发现了这几行代码,a1 是我们之前获得的 kernel32.dll 的模块的基址,显然这里是从 kernel32.dll 中获取一些信息的操作。

支线任务: PE文件结构和PEB导出表

[原创]完美实现GetProcAddress-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com (kanxue.com)

或者这篇 TEB/PEB定位PE文件导入导出表-teb表-初识逆向的博客-CSDN博客

通过 PEView 工具我们也可以找到 Export Table 的地址

1677634148143

导出表的结构为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics; // 0
DWORD TimeDateStamp; // 0x4
WORD MajorVersion; // 0x6
WORD MinorVersion; // 0x8
DWORD Name; // 0xC 指向该导出表文件名字字符串
DWORD Base; // 0x10 导出函数起始序号
DWORD NumberOfFunctions; // 0x14 地址表成员个数 导出序号最大-最小
DWORD NumberOfNames; // 0x18 以函数名字导出的函数个数
DWORD AddressOfFunctions; // 0x1C 导出函数地址表RVA
DWORD AddressOfNames; // 0x20 导出函数名称表RVA
DWORD AddressOfNameOrdinals; // 0x24 导出函数序号表RVA
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

那么现在再来看这段代码,以及最后的比对代码

1677643641922

1677643656902

a1 是我们之前获得的 kernel32.dll 的模块的基址,显然该函数就是返回了文件基址 + 一个偏移的函数地址,那么 v58 就是 *PIMAGE_EXPORT_DIRECTORY这个结构体指针,取出一些值组后算出来一个 hash 值,比较如果成功,就返回一个地址,并且这个地址的基址是 a1,也就是我们之前通过七行汇编代码来获取的 kernel32.dll 的基址,也就是说,通过这个函数,我们可以动态获取一个 API 的地址,完整函数如下

1677655571189

sub_1000B930

再来看 sub_1000B930 函数

1677655903152

我们可以先看看它在回调函数中是怎么调用的,发现它的返回值作为参数放在了 createMutexA 函数 (synchapi.h) - Win32 apps | Microsoft Learn 里面,同时可以知道第三个参数 v3 应该是一个字符串,也就是说 sub_1000B930 返回的应该也是一个字符串,并且我们注意到 byte_10023E08 里面全是可见字符,应该就是一个单表代换,纯静态分析的时候,模拟执行这个 do-while 循环,返回值是一个 fake_flag

1677656033828

或者直接抄下来

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
178
179
180
181
182
183
184
185
186
187
188
#include <bits/stdc++.h>
using namespace std;

unsigned char buf1[] =
{
0x0A, 0x35, 0x10, 0x3D, 0x02, 0x2E, 0x1E, 0x0F, 0x12, 0x1E,
0x0F, 0x12, 0x18, 0x37, 0x45, 0x22, 0x12, 0x18, 0x1E, 0x1E,
0x1E, 0x40, 0x32, 0x3C, 0x31, 0x0A, 0x35, 0x10, 0x3D, 0x02,
0x2E, 0x1C, 0x0F, 0x12, 0x1C, 0x0F, 0x12, 0x18, 0x37, 0x45,
0x22, 0x12, 0x18, 0x1C, 0x1C, 0x1E, 0x40, 0x32, 0x3C, 0x31,
0x0A, 0x37, 0x1B, 0x00, 0x22, 0x0E, 0x33, 0x32, 0x32, 0x22,
0x0D, 0x1B, 0x2E, 0x40, 0x0F, 0x1B, 0x0E, 0x22, 0x22, 0x10,
0x1B, 0x18, 0x43, 0x33, 0x10, 0x00, 0x1B, 0x0D, 0x33, 0x10,
0x10, 0x42, 0x10, 0x32, 0x1B, 0x42, 0x10, 0x1B, 0x3A, 0x43,
0x33, 0x0D, 0x1B, 0x0F, 0x3A, 0x0F, 0x0C, 0x22, 0x13, 0x1F,
0x14, 0x1A, 0x1C, 0x22, 0x40, 0x0F, 0x22, 0x46, 0x1B, 0x33,
0x10, 0x1C, 0x43, 0x40, 0x00, 0x1B, 0x42, 0x0C, 0x1B, 0x18,
0x0D, 0x43, 0x13, 0x1B, 0x13, 0x22, 0x13, 0x43, 0x0D, 0x3A,
0x1B, 0x40, 0x10, 0x00, 0x1B, 0x0D, 0x22, 0x0F, 0x0C, 0x40,
0x0D, 0x0C, 0x1B, 0x3A, 0x43, 0x33, 0x0D, 0x1B, 0x2F, 0x0D,
0x43, 0x32, 0x0D, 0x40, 0x13, 0x1F, 0x0A, 0x02, 0x2E, 0x22,
0x13, 0x42, 0x00, 0x40, 0x1B, 0x20, 0x1B, 0x35, 0x10, 0x3E,
0x29, 0x3E, 0x17, 0x1B, 0x0F, 0x2F, 0x22, 0x30, 0x42, 0x40,
0x1C, 0x1B, 0x35, 0x22, 0x0D, 0x0F, 0x42, 0x43, 0x10, 0x0A,
0x35, 0x10, 0x3D, 0x02, 0x2E, 0x1C, 0x0F, 0x12, 0x1C, 0x0F,
0x12, 0x18, 0x37, 0x45, 0x22, 0x12, 0x18, 0x1C, 0x1C, 0x1E,
0x40, 0x32, 0x3C, 0x31, 0x0A, 0x07, 0x10, 0x22, 0x2D, 0x2F,
0x22, 0x30, 0x0C, 0x22, 0x00, 0x1B, 0x22, 0x0D, 0x0D, 0x43,
0x0D, 0x1B, 0x43, 0x30, 0x30, 0x33, 0x0D, 0x0D, 0x22, 0x00,
0x3C, 0x1B, 0x39, 0x42, 0x0F, 0x40, 0x0F, 0x0F, 0x22, 0x13,
0x0E, 0x1C, 0x3A, 0x1B, 0x2F, 0x0D, 0x43, 0x30, 0x22, 0x00,
0x33, 0x0D, 0x22, 0x1B, 0x0C, 0x22, 0x0D, 0x13, 0x42, 0x10,
0x40, 0x0C, 0x22, 0x00, 0x1F, 0x0A, 0x28, 0x0F, 0x2C, 0x1C,
0x42, 0x30, 0x22, 0x10, 0x0F, 0x22, 0x1F, 0x42, 0x10, 0x42,
0x0A, 0x3B, 0x22, 0x08, 0x33, 0x42, 0x0D, 0x22, 0x1B, 0x1C,
0x42, 0x30, 0x22, 0x10, 0x0F, 0x22, 0x1B, 0x18, 0x42, 0x1C,
0x22, 0x1B, 0x0C, 0x43, 0x1B, 0x35, 0x22, 0x0D, 0x42, 0x18,
0x3A, 0x3C, 0x14, 0x14, 0x11, 0x40, 0x13, 0x2F, 0x1C, 0x22,
0x1B, 0x1C, 0x42, 0x30, 0x22, 0x10, 0x0F, 0x22, 0x1B, 0x18,
0x42, 0x1C, 0x22, 0x05, 0x14, 0x14, 0x3F, 0x1D, 0x40, 0x13,
0x22, 0x27, 0x14, 0x04, 0x1C, 0x40, 0x32, 0x26, 0x18, 0x1C,
0x40, 0x32, 0x1B, 0x42, 0x10, 0x1B, 0x2E, 0x22, 0x2D, 0x1B,
0x0F, 0x0C, 0x0D, 0x42, 0x10, 0x32, 0x1B, 0x0B, 0x42, 0x0C,
0x2E, 0x43, 0x33, 0x0C, 0x1B, 0x0E, 0x1C, 0x40, 0x10, 0x45,
0x46, 0x0F, 0x33, 0x30, 0x2E, 0x1B, 0x40, 0x0F, 0x1B, 0x40,
0x40, 0x0E, 0x0E, 0x30, 0x30, 0x00, 0x00, 0x22, 0x22, 0x18,
0x18, 0x29, 0x29, 0x1E, 0x1E, 0x3E, 0x3E, 0x17, 0x17, 0x21,
0x21, 0x14, 0x0A, 0x47, 0x0D, 0x0D, 0x43, 0x0D, 0x0A, 0x1D,
0x40, 0x13, 0x22, 0x0A, 0x04, 0x1C, 0x40, 0x32, 0x0A, 0x18,
0x1C, 0x40, 0x32, 0x3D, 0x00, 0x22, 0x18, 0x40, 0x33, 0x1C,
0x0C, 0x31, 0x0A, 0x1D, 0x40, 0x13, 0x22, 0x0A, 0x02, 0x2E,
0x22, 0x37, 0x10, 0x0F, 0x0B, 0x22, 0x0D, 0x02, 0x43, 0x02,
0x2E, 0x22, 0x07, 0x1C, 0x0C, 0x42, 0x13, 0x40, 0x0C, 0x22,
0x38, 0x33, 0x22, 0x0F, 0x0C, 0x42, 0x43, 0x10, 0x24, 0x18,
0x41, 0x42, 0x18, 0x22, 0x02, 0x2E, 0x22, 0x07, 0x10, 0x42,
0x35, 0x22, 0x0D, 0x0F, 0x22, 0x37, 0x10, 0x00, 0x47, 0x35,
0x22, 0x0D, 0x3A, 0x0C, 0x2E, 0x42, 0x10, 0x32, 0x0A, 0x29,
0x15, 0x0A, 0x06, 0x43, 0x33, 0x1B, 0x22, 0x35, 0x22, 0x0D,
0x1B, 0x2E, 0x40, 0x35, 0x22, 0x1B, 0x0C, 0x2E, 0x40, 0x0C,
0x1B, 0x18, 0x22, 0x22, 0x1C, 0x42, 0x10, 0x32, 0x1B, 0x0B,
0x2E, 0x22, 0x0D, 0x22, 0x1B, 0x3A, 0x43, 0x33, 0x16, 0x0D,
0x22, 0x1B, 0x10, 0x43, 0x0C, 0x1B, 0x0F, 0x33, 0x0D, 0x22,
0x1B, 0x42, 0x18, 0x1B, 0x3A, 0x43, 0x33, 0x16, 0x0D, 0x22,
0x1B, 0x40, 0x0B, 0x40, 0x45, 0x22, 0x1B, 0x43, 0x0D, 0x1B,
0x0F, 0x0C, 0x42, 0x1C, 0x1C, 0x1B, 0x00, 0x0D, 0x22, 0x40,
0x13, 0x42, 0x10, 0x32, 0x2A, 0x0A, 0x11, 0x06, 0x11, 0x02,
0x47, 0x09, 0x1B, 0x04, 0x37, 0x03, 0x41, 0x07, 0x3B, 0x47,
0x0A, 0x06, 0x43, 0x33, 0x16, 0x1C, 0x1C, 0x1B, 0x0D, 0x22,
0x13, 0x22, 0x13, 0x0E, 0x22, 0x0D, 0x1B, 0x3A, 0x43, 0x33,
0x1B, 0x00, 0x43, 0x10, 0x16, 0x0C, 0x1B, 0x0E, 0x22, 0x1C,
0x42, 0x22, 0x35, 0x22, 0x1B, 0x42, 0x10, 0x1B, 0x40, 0x10,
0x3A, 0x1B, 0x43, 0x18, 0x1B, 0x0C, 0x2E, 0x42, 0x0F, 0x1B,
0x18, 0x40, 0x0C, 0x22, 0x1B, 0x30, 0x0D, 0x40, 0x2F, 0x1F,
0x1B, 0x06, 0x43, 0x33, 0x16, 0x0D, 0x22, 0x1B, 0x42, 0x10,
0x1B, 0x30, 0x43, 0x10, 0x0C, 0x0D, 0x43, 0x1C, 0x1B, 0x43,
0x18, 0x1B, 0x3A, 0x43, 0x33, 0x0D, 0x1B, 0x43, 0x0B, 0x10,
0x1B, 0x1C, 0x42, 0x18, 0x22, 0x1F, 0x0A, 0x11, 0x06, 0x11,
0x02, 0x47, 0x09, 0x1B, 0x04, 0x37, 0x03, 0x41, 0x07, 0x3B,
0x47, 0x0A, 0x2E, 0x0C, 0x0C, 0x2F, 0x0F, 0x05, 0x44, 0x44,
0x0B, 0x0B, 0x0B, 0x1F, 0x42, 0x10, 0x0C, 0x22, 0x1C, 0x1F,
0x30, 0x10, 0x44, 0x30, 0x43, 0x10, 0x0C, 0x22, 0x10, 0x0C,
0x44, 0x0B, 0x0B, 0x0B, 0x44, 0x30, 0x10, 0x44, 0x01, 0x2E,
0x44, 0x0F, 0x33, 0x2F, 0x2F, 0x43, 0x0D, 0x0C, 0x44, 0x40,
0x0D, 0x0C, 0x42, 0x30, 0x1C, 0x22, 0x0F, 0x44, 0x29, 0x29,
0x29, 0x29, 0x29, 0x15, 0x15, 0x29, 0x25, 0x44, 0x0E, 0x43,
0x40, 0x0D, 0x00, 0x0F, 0x20, 0x40, 0x10, 0x00, 0x20, 0x45,
0x42, 0x0C, 0x0F, 0x44, 0x42, 0x10, 0x0C, 0x22, 0x1C, 0x20,
0x32, 0x40, 0x1C, 0x42, 0x1C, 0x22, 0x43, 0x20, 0x0E, 0x43,
0x40, 0x0D, 0x00, 0x0F, 0x1F, 0x2E, 0x0C, 0x13, 0x1C, 0x0A,
0x43, 0x2F, 0x22, 0x10, 0x0A, 0x2E, 0x0C, 0x0C, 0x2F, 0x0F,
0x05, 0x44, 0x44, 0x0C, 0x2E, 0x22, 0x30, 0x2E, 0x43, 0x42,
0x30, 0x22, 0x42, 0x0F, 0x3A, 0x43, 0x33, 0x0D, 0x0F, 0x1F,
0x0B, 0x2E, 0x40, 0x0C, 0x42, 0x0F, 0x0C, 0x2E, 0x22, 0x13,
0x40, 0x0C, 0x0D, 0x42, 0x2D, 0x1F, 0x30, 0x43, 0x13, 0x44,
0x0A, 0x07, 0x10, 0x22, 0x2D, 0x2F, 0x22, 0x30, 0x0C, 0x22,
0x00, 0x1B, 0x30, 0x2E, 0x40, 0x0D, 0x40, 0x30, 0x0C, 0x22,
0x0D, 0x1B, 0x42, 0x10, 0x1B, 0x04, 0x1C, 0x40, 0x32, 0x1B,
0x18, 0x42, 0x22, 0x1C, 0x00, 0x3C, 0x0A, 0x04, 0x40, 0x0C,
0x40, 0x1C, 0x1B, 0x22, 0x2D, 0x30, 0x22, 0x2F, 0x0C, 0x42,
0x43, 0x10, 0x0A, 0x41, 0x42, 0x30, 0x22, 0x10, 0x0F, 0x22,
0x1B, 0x30, 0x2E, 0x22, 0x30, 0x45, 0x0F, 0x33, 0x13, 0x1B,
0x18, 0x40, 0x42, 0x1C, 0x22, 0x00, 0x3C, 0x0A, 0x04, 0x40,
0x0C, 0x40, 0x1C, 0x1B, 0x22, 0x2D, 0x30, 0x22, 0x2F, 0x0C,
0x42, 0x43, 0x10, 0x0A, 0x04, 0x42, 0x1C, 0x22, 0x1B, 0x30,
0x43, 0x0D, 0x0D, 0x33, 0x2F, 0x0C, 0x22, 0x00, 0x3C, 0x1B,
0x02, 0x2E, 0x42, 0x0F, 0x1B, 0x2F, 0x0D, 0x43, 0x32, 0x0D,
0x40, 0x13, 0x1B, 0x2E, 0x40, 0x0F, 0x1B, 0x0E, 0x22, 0x22,
0x10, 0x1B, 0x13, 0x40, 0x10, 0x42, 0x2F, 0x33, 0x1C, 0x40,
0x0C, 0x22, 0x00, 0x1B, 0x40, 0x10, 0x00, 0x1B, 0x13, 0x40,
0x3A, 0x0E, 0x22, 0x1B, 0x42, 0x0C, 0x16, 0x0F, 0x1B, 0x42,
0x10, 0x18, 0x22, 0x30, 0x0C, 0x22, 0x00, 0x1B, 0x0E, 0x3A,
0x1B, 0x40, 0x1B, 0x48, 0x42, 0x0D, 0x33, 0x0F, 0x1B, 0x43,
0x0D, 0x1B, 0x30, 0x0D, 0x40, 0x30, 0x45, 0x22, 0x00, 0x1F,
0x1B, 0x02, 0x2E, 0x42, 0x0F, 0x1B, 0x18, 0x42, 0x1C, 0x22,
0x1B, 0x0B, 0x43, 0x10, 0x16, 0x0C, 0x1B, 0x0B, 0x43, 0x0D,
0x45, 0x1B, 0x40, 0x10, 0x3A, 0x13, 0x43, 0x0D, 0x22, 0x1F,
0x0A, 0x02, 0x2E, 0x22, 0x13, 0x42, 0x00, 0x40, 0x1B, 0x20,
0x1B, 0x35, 0x10, 0x3E, 0x29, 0x3E, 0x17, 0x1B, 0x0F, 0x2F,
0x22, 0x30, 0x42, 0x40, 0x1C, 0x1B, 0x35, 0x22, 0x0D, 0x0F,
0x42, 0x43, 0x10, 0x0A, 0x48, 0x22, 0x0D, 0x42, 0x18, 0x42,
0x30, 0x40, 0x0C, 0x42, 0x43, 0x10, 0x1B, 0x18, 0x40, 0x42,
0x1C, 0x22, 0x00, 0x3C, 0x14, 0x03, 0x10, 0x30, 0x43, 0x0D,
0x0D, 0x22, 0x30, 0x0C, 0x1B, 0x35, 0x22, 0x0D, 0x42, 0x18,
0x42, 0x30, 0x40, 0x0C, 0x42, 0x43, 0x10, 0x1B, 0x0D, 0x22,
0x0F, 0x33, 0x1C, 0x0C, 0x3C, 0x0A, 0x48, 0x22, 0x0D, 0x18,
0x42, 0x30, 0x40, 0x0C, 0x42, 0x43, 0x10, 0x1B, 0x47, 0x0D,
0x0D, 0x43, 0x0D, 0x3C, 0x0A, 0x03, 0x10, 0x2F, 0x33, 0x0C,
0x26, 0x3D, 0x0A, 0x28, 0x0F, 0x46, 0x29, 0x2D, 0x28, 0x29,
0x3E, 0x2D, 0x0A, 0x28, 0x0F, 0x31, 0x19, 0x0A, 0x47, 0x10,
0x30, 0x26, 0x3D, 0x0A, 0x28, 0x0F, 0x46, 0x29, 0x2D, 0x28,
0x29, 0x3E, 0x2D, 0x0A, 0x28, 0x0F, 0x31, 0x19, 0x0A, 0x28,
0x0F, 0x0A, 0x1C, 0x22, 0x10, 0x26, 0x28, 0x00, 0x0A, 0x28,
0x0F, 0x02, 0x2E, 0x40, 0x10, 0x45, 0x0F, 0x1B, 0x18, 0x43,
0x0D, 0x1B, 0x33, 0x0F, 0x42, 0x10, 0x32, 0x1B, 0x0C, 0x2E,
0x42, 0x0F, 0x1B, 0x0F, 0x43, 0x18, 0x0C, 0x0B, 0x40, 0x0D,
0x22, 0x3C, 0x14, 0x0A, 0x28, 0x0F, 0x02, 0x2E, 0x42, 0x0F,
0x1B, 0x0F, 0x43, 0x18, 0x0C, 0x0B, 0x40, 0x0D, 0x22, 0x23,
0x2F, 0x0D, 0x43, 0x0E, 0x1C, 0x22, 0x13, 0x34, 0x1B, 0x2E,
0x40, 0x0F, 0x1B, 0x0E, 0x22, 0x22, 0x10, 0x1B, 0x0D, 0x22,
0x32, 0x42, 0x0F, 0x0C, 0x22, 0x0D, 0x22, 0x00, 0x1B, 0x0C,
0x43, 0x05, 0x14, 0x0A, 0x28, 0x0F, 0x28, 0x0F, 0x1B, 0x20,
0x1B, 0x28, 0x0F, 0x14, 0x0A, 0x28, 0x0F, 0x39, 0x33, 0x0D,
0x40, 0x0C, 0x42, 0x43, 0x10, 0x05, 0x1B, 0x1E, 0x36, 0x36,
0x36, 0x44, 0x1E, 0x3E, 0x44, 0x17, 0x3E, 0x14, 0x14, 0x14,
0x0A, 0x28, 0x0F, 0x07, 0x10, 0x2E, 0x22, 0x2D, 0x1C, 0x42,
0x18, 0x3A, 0x1B, 0x0F, 0x0C, 0x0D, 0x42, 0x10, 0x32, 0x1B,
0x40, 0x10, 0x00, 0x1B, 0x0F, 0x33, 0x0E, 0x13, 0x42, 0x0C,
0x1B, 0x0C, 0x2E, 0x22, 0x1B, 0x18, 0x1C, 0x40, 0x32, 0x3C,
0x3C, 0x3C, 0x0A, 0x48, 0x22, 0x0D, 0x18, 0x42, 0x30, 0x40,
0x0C, 0x42, 0x43, 0x10, 0x1B, 0x2F, 0x40, 0x0F, 0x0F, 0x22,
0x00, 0x3C, 0x0A, 0x07, 0x10, 0x22, 0x2D, 0x2F, 0x22, 0x30,
0x0C, 0x22, 0x00, 0x1B, 0x22, 0x0F, 0x0C, 0x22, 0x0D, 0x1B,
0x22, 0x32, 0x32, 0x05, 0x1B, 0x18, 0x1C, 0x40, 0x32, 0x3D,
0x17, 0x18, 0x3E, 0x2B, 0x00, 0x2B, 0x21, 0x2A, 0x2A, 0x2A,
0x49, 0x00
};

unsigned char str[] =
{
0x64, 0x7A, 0x54, 0x49, 0x46, 0x3A, 0x59, 0x55, 0x71, 0x4D,
0x24, 0x77, 0x74, 0x72, 0x62, 0x73, 0x6E, 0x53, 0x5F, 0x6D,
0x0A, 0x36, 0x27, 0x33, 0x66, 0x3B, 0x50, 0x20, 0x6C, 0x47,
0x31, 0x2E, 0x2D, 0x34, 0x65, 0x28, 0x4F, 0x35, 0x3D, 0x5D,
0x25, 0x30, 0x3F, 0x37, 0x5C, 0x78, 0x68, 0x70, 0x63, 0x7D,
0x67, 0x75, 0x29, 0x76, 0x39, 0x41, 0x51, 0x44, 0x79, 0x52,
0x21, 0x7B, 0x32, 0x5B, 0x61, 0x4C, 0x69, 0x6F, 0x2F, 0x6B,
0x2C, 0x45, 0x56, 0300
};

char flag[233];

void GetTableValue(int j)
{
int i = 0;
do
{
flag[i] = str[buf1[j]];
i++;
j++;
}while(buf1[j] != 10);
}

int main()
{
GetTableValue(start_j);

cout << flag;
}

动态调试

大概搞明白上面两个函数是在干嘛以后,我们可以尝试对文件进行动态调试,我们在 TlsCallBackDllEntryPoint 两个函数中分别下断,然后附加到 regsvr32.exe上进行调试,注意要写命令行参数。

1677656421704

根据之前的分析,猜测执行完 GetAPIAddress 以后可以获得一个返回值 ,我们可以查看寄存器列表来得知返回的 API 名称。

同时,我们步入 GetTableAddress 函数时,可以看到 sub_5981A990 执行完以后,返回值是一个 \0 空字符串,那么猜测该函数是申请一个空字符串,在第一次运行 GetTableAddress 时返回值是

1677656862693

我们接着往下看,一路跑到函数 sub_59811000,点进去一看

1677657239229

发现是一个检测了超级多调试器的反调试函数,但是绕过很简单,改一下返回值就行。接着调试程序,发现一个可疑的条件语句

1677657435425

由于采用了或的关系,且 sub_59811280 函数里面添加了一个 VEH 异常处理

1677657487950

显然也是一个反调试,同时也可以猜测条件语句的后半段也是一段反调试,绕过 VEH 反调试,查看 GetAPIAddress 的返回值,果然 ,发现是 IsDebuggerPresent反调试函数。

1677657679161

进入条件分支内部, 发现还有两个反调试

1677657755105

由于都是 if 条件语句,我们直接全给 nop 掉即可

1677657804779

然后发现了一个之前没遇到过的函数 sub_5981A670

1677657867798

同时我们还观察到如果之前触发了反调试,那么步入的分支是一个类似的函数

1677657994341

看起来只有一些常量不一样,通过一些表的搜索可以看出这是一个 SM4 算法,且在正确的分支中还有一个

1677658260397

这样一个判断函数,用来判定 SM4s_box 是否正确,显然在不同的分支中获得的 s_box 是不同的,所解出来的明文也是不同的,相对应的源码可以在这里找到: NEWPLAN/SMx: 国家商用加密算法 SMx(SM2,SM3,SM4) (github.com)

到此,回调函数大致分析完了,我们进入 DllEntryPoint 函数中继续跟踪调试。

1677660730505

主要是逆这个函数,我们看到了熟悉的 return 9, patch 掉以后可以显示出下面的代码,发现最后还有花指令

1677660819665

是个 jz-jnz 型的花指令,patch 掉即可,最后在用快捷键 U, P, Alt + P 修一修就行。

然后一直调试,可以看到是在做一堆文件操作,lpFileName 里面变成了 lincense.ini,应该是从这里面读取了某些东西,我们来看看这个 ini 文件里有啥

1
2
3
4
5
6
7
8
9
10
11
12
13
14
;THIS IS AUTO GENERATED BY VN2023 SUITE, DO NOT MODIFY IT MANUALLY!
[Assignments]
ProductKey=99NC1
RegisterTo=DESKTOP_VNPLAYER
Period=9999
RegisterMethod=Local

[LASTRUN]
Token=2044

;TO VERIFY YOUR FLAG, CHANGE THIS FIELD
[Game]
Flag=577269746520596f757220466c61672048657265
TheAnswerToTheUltimateQuestionOfLifeTheUniverseAndEverything=42

1677662197078

后面的一些代码是从 GetTableValue 函数里面获取一些特殊的字符串,然后作为参数扔到GetPrivateProfileString 函数

1677664476794

猜测是从 lincense.ini 文件中检索相应的字符串,也就是 flagTheAnswerToTheUltimateQuestionOfLifeTheUniverseAndEverything 后面的内容,flag 存在 lpReturnedString 中, 后面那个终极浪漫的数字存在 v48 中并且转成数字与一个 key 进行异或,这个异或函数就是 cxxstd_atoi

1677661064684

可以直接写脚本得到正确的 key

1
2
3
4
5
6
7
8
9
10
#include <bits/stdc++.h>
using namespace std;
unsigned char key[] =
{
10, 72, 24, 25, 4, 94, 92, 5, 83, 108, 120, 105, 29, 66, 115, 42
};
int main()
{
for(int i = 0; i < 16; i++)printf("0x%x ,", key[i] ^ 42);
}

正确的密钥为

1
key = [0x20, 0x62, 0x32, 0x33, 0x2e, 0x74, 0x76, 0x2f, 0x79, 0x46, 0x52, 0x43, 0x37, 0x68, 0x59, 0x00]

最后就是 sub_10001BA2 函数了,去掉花指令之后反编译看到一个巨大的 if ,一看就是求解一个方程组。

我们把方程组复制出来,放到文本编辑软件中稍微删改一下变成符合 python 语法的条件语句,然后上 z3 求解

1
2
3
4
5
6
7
8
9
10
11
12
13
from z3 import *
sol = Solver()
cipher = [BitVec('cipher[%d]' % i, 16) for i in range(80)]

for i in range(80):
sol.add(cipher[i] >= 0)

sol.add(5 * cipher[1] + 8 * cipher[0] == 2669, 6 * cipher[1] + 4 * cipher[2] == 1402, 3 * cipher[3] + 6 * cipher[2] == 873, 7 * cipher[4] + 6 * cipher[3] == 2627, 8 * cipher[5] + 4 * cipher[4] == 2516, 9 * cipher[6] + 5 * cipher[5] == 2781, 3 * cipher[6] + 4 * cipher[7] == 1228, 5 * cipher[8] + 9 * cipher[7] == 1821, 7 * cipher[9] + 5 * cipher[8] == 554, 6 * cipher[10] + 9 * cipher[9] == 825, 5 * cipher[11] + 6 * cipher[10] == 1857, 3 * cipher[11] + 4 * cipher[12] == 1627, 7 * cipher[13] + 5 * cipher[12] == 2279, 3 * cipher[14] + 9 * cipher[13] == 1974, 6 * cipher[15] + 6 * cipher[14] == 2382, 7 * cipher[15] + 8 * cipher[16] == 1903, 8 * cipher[17] + 8 * cipher[16] == 1336, 3 * cipher[18] + 6 * cipher[17] == 822, 5 * cipher[19] + 8 * cipher[18] == 381, 3 * cipher[20] + 5 * cipher[19] == 823, 5 * cipher[21] + 5 * cipher[20] == 1680, 6 * cipher[21] + 8 * cipher[22] == 2116, 9 * cipher[23] + 3 * cipher[22] == 1059, 3 * cipher[23] + 8 * cipher[24] == 1314, 3 * cipher[25] + 9 * cipher[24] == 1641, 5 * cipher[25] + 8 * cipher[26] == 2148, 3 * cipher[27] + 3 * cipher[26] == 669, 5 * cipher[28] + 9 * cipher[27] == 953, 5 * cipher[29] + 7 * cipher[28] == 1896, 3 * cipher[30] + 3 * cipher[29] == 1275, 5 * cipher[31] + 7 * cipher[30] == 2874, 9 * cipher[32] + 6 * cipher[31] == 1518, 7 * cipher[33] + 5 * cipher[32] == 1312, 6 * cipher[34] + 5 * cipher[33] == 2148, 9 * cipher[34] + 8 * cipher[35] == 2979, 3 * cipher[35] + 4 * cipher[36] == 476, 6 * cipher[37] + 3 * cipher[36] == 1047, 7 * cipher[38] + 4 * cipher[37] == 1488, 6 * cipher[39] + 6 * cipher[38] == 1320, 5 * cipher[40] + 6 * cipher[39] == 1784, 3 * cipher[41] + 8 * cipher[40] == 1994, 7 * cipher[42] + 3 * cipher[41] == 712, 3 * cipher[43] + 3 * cipher[42] == 1002, 5 * cipher[44] + 7 * cipher[43] == 2094, 3 * cipher[45] + 3 * cipher[44] == 465, 5 * cipher[46] + 6 * cipher[45] == 1479, 3 * cipher[47] + 6 * cipher[46] == 1281, 7 * cipher[48] + 4 * cipher[47] == 1064, 3 * cipher[49] + 4 * cipher[48] == 985, 3 * cipher[50] + 4 * cipher[49] == 922, 7 * cipher[51] + 8 * cipher[50] == 1672, 9 * cipher[51] + 4 * cipher[52] == 1740, 6 * cipher[53] + 7 * cipher[52] == 1185, 6 * cipher[54] + 9 * cipher[53] == 711, 4 * cipher[55] + 4 * cipher[54] == 256, 7 * cipher[56] + 8 * cipher[55] == 744, 5 * cipher[57] + 8 * cipher[56] == 1674, 6 * cipher[58] + 3 * cipher[57] == 834, 3 * cipher[59] + 4 * cipher[58] == 348, 9 * cipher[59] + 4 * cipher[60] == 952, 3 * cipher[60] + 4 * cipher[61] == 1117, 7 * cipher[62] + 9 * cipher[61] == 1853, 6 * cipher[63] + 9 * cipher[62] == 399, 3 * cipher[64] + 6 * cipher[63] == 1011, 6 * cipher[65] + 9 * cipher[64] == 3171, 8 * cipher[66] + 8 * cipher[65] == 1760, 7 * cipher[67] + 7 * cipher[66] == 861, 6 * cipher[68] + 6 * cipher[67] == 1710, 7 * cipher[68] + 8 * cipher[69] == 1578, 7 * cipher[70] + 9 * cipher[69] == 1280, 3 * cipher[71] + 6 * cipher[70] == 1134, 3 * cipher[72] + 8 * cipher[71] == 1003, 9 * cipher[72] + 8 * cipher[73] == 1345, 5 * cipher[74] + 4 * cipher[73] == 928, 3 * cipher[74] + 4 * cipher[75] == 824, 6 * cipher[76] + 8 * cipher[75] == 1840, 9 * cipher[77] + 7 * cipher[76] == 1200, 3 * cipher[77] + 8 * cipher[78] == 2032, 5 * cipher[79] + 5 * cipher[78] == 1795, 7 * cipher[79] + 8 * cipher[0] == 2584)

if(sol.check()):
m = sol.model()
for i in range(80):
print(hex(m[BitVec('cipher[%d]' % i, 16)].as_long()), end = ", ")

结果为:

1
cipher = [0xda, 0xb9, 0x49, 0x91, 0xfb, 0xbd, 0xcc, 0x9a, 0x57, 0x11, 0x70, 0xed, 0xe5, 0xa2, 0xac, 0xe1, 0x29, 0x7e, 0x16, 0x29, 0xce, 0x82, 0xa7, 0x3e, 0x8d, 0x7c, 0xbf, 0x20, 0x85, 0xc1, 0xe8, 0xfa, 0x2, 0xba, 0xcb, 0x90, 0xb, 0xa9, 0x74, 0x68, 0xe8, 0x2e, 0x52, 0xfc, 0x42, 0x59, 0xbd, 0x31, 0x7c, 0xa3, 0x5a, 0x88, 0x81, 0x2f, 0x30, 0x10, 0x58, 0xc2, 0x2a, 0x3c, 0x67, 0xca, 0x5, 0x3b, 0xdb, 0xc8, 0x14, 0x67, 0xb6, 0x26, 0x86, 0x6e, 0x29, 0x7a, 0x58, 0x8c, 0x78, 0x28, 0xef, 0x78]

密钥之前已经解出,直接 cyberchef 一下

1677671906884

再转成字符串得到 flag{3f27d7470d8967fd344ec7f1261e64b3}