攻防世界-re部分题解(四)

最近比较忙,而且题目越做越快,平时的练习除非特别值得注意的,都不会写的太详细了

serial-150

这题本身的算法没什么难度,主要就是花指令的去除,去除之后就只是简单的字符串比较了,很容易。

1
EZ9dmq4c8g9G7bAV

testre

主要就是一个base58,很好做,尝试了一下ghidra的效果发现不尽如人意,所以主要还是采用IDA做题。

1
flag{base58_is_boring}

simple-check-100

这题没什么好写的,虽然前面的check函数进行了检测,但是后面的计算和前面的输入没什么关系,并且直接把flag给输出了,所以直接gdb调试改了eax的值直接输出flag就好了

1
flag_is_you_know_cracking!!!

secret-string-400

一直不知道这是什么,后来发现这竟然是个压缩包,里面有个网页和调用的js,js里面可以看到把机器码转成了命令然后执行,所以在执行之前输出一下

1
console.log(command);

在console里找到了关键判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var f=window.machine.registers[1].userinput
var i = f.length
var nonce = 'groke';
var j = 0;
var out = [];
var eq = true;
while(j < i){
out.push(f.charCodeAt(j) ^ nonce.charCodeAt(j%5))
j++;
}
var ex = [1, 30, 14, 12, 69, 14, 1, 85, 75, 50, 40, 37, 48, 24, 10, 56, 55, 46, 56, 60];
if (ex.length == out.length) {
j = 0;
while(j < ex.length){
if(ex[j] != out[j])
eq = false;
j += 1;
}
if(eq){
alert('YOU WIN!');
}else{
alert('NOPE!');
}}else{alert('NOPE!');}

过程非常简单,也是闲着无聊,写了个什么都没有的网页

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>decode</title>
<script type='text/javascript' src='out.js'></script>
</head>
<body>
<br/>
<input type='button' onclick="run()" value='decode'></button>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
function run() {
const nonce = 'groke';
let j = 0;
let flag ='';
const ex = [1, 30, 14, 12, 69, 14, 1, 85, 75, 50, 40, 37, 48, 24, 10, 56, 55, 46, 56, 60];
let i = ex.length;
while (j < i) {
flag+=String.fromCharCode(ex[j] ^ nonce.charCodeAt(j % 5));
j++;
}
alert(flag);
}

点击按钮获得flag

1
flag is: WOW_so_EASY

windows_reverse2

首先是脱壳,看雪脱壳工具就可以,也可以手动脱壳,IDA研究之后发现中间一个函数的作用不是很明朗,OD调试一下根据结果猜测可能是16进制转base64,试了一下就对了。

1
ADEBDEAEC7BE

Newbie_calculations

这题的就是直接会输出flag,但是进行了大量费事而且毫无意义的计算,需要仔细分析一下每一部分的函数作用,简化计算的过程,总共只有三个运算函数,很简单,就不一一分析了,直接计算flag:

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
#include<iostream>

using namespace std;

int* sub(int *a, int b) {
*a -= b;
return a;
}

int* add(int *a, int b) {
*a += b;
return a;
}

int* mul(int *a, int b) {
*a *= b;
return a;
}

int __cdecl main(int argc, const char **argv, const char **envp) {
int *v3; // eax
int *v4; // eax
int *v5; // eax
int *v6; // eax
int *v7; // eax
int *v8; // eax
int *v9; // eax
int *v10; // eax
int *v11; // eax
int *v12; // eax
int *v13; // eax
int *v14; // eax
int *v15; // eax
int *v16; // eax
int *v17; // eax
int *v18; // eax
int *v19; // eax
int *v20; // eax
int *v21; // eax
int *v22; // eax
int *v23; // eax
int *v24; // eax
int *v25; // eax
int *v26; // eax
int *v27; // eax
int *v28; // eax
int *v29; // eax
int *v30; // eax
int *v31; // eax
int *v32; // eax
int *v33; // eax
int *v34; // eax
int *v35; // eax
int *v36; // eax
int *v37; // eax
int *v38; // eax
int *v39; // eax
int *v40; // eax
int *v41; // eax
int *v42; // eax
int *v43; // eax
int *v44; // eax
int *v45; // eax
int *v46; // eax
int *v47; // eax
int *v48; // eax
int *v49; // eax
int *v50; // eax
int *v51; // eax
int *v52; // eax
int *v53; // eax
int *v54; // eax
int *v55; // eax
int *v56; // eax
int *v57; // eax
int *v58; // eax
int *v59; // eax
int *v60; // eax
int *v61; // eax
int *v62; // eax
int *v63; // eax
int *v64; // eax
int *v65; // eax
int *v66; // eax
int *v67; // eax
int *v68; // eax
int *v69; // eax
int *v70; // eax
int *v71; // eax
int *v72; // eax
int *v73; // eax
int *v74; // eax
int *v75; // eax
int *v76; // eax
int *v77; // eax
int *v78; // eax
int *v79; // eax
int *v80; // eax
int *v81; // eax
int *v82; // eax
int *v83; // eax
int *v84; // eax
int *v85; // eax
int *v86; // eax
int *v87; // eax
int *v88; // eax
int *v89; // eax
int *v90; // eax
int *v91; // eax
int *v92; // eax
int *v93; // eax
int *v94; // eax
int *v95; // eax
int *v96; // eax
int *v97; // eax
int *v98; // eax
int *v99; // eax
int *v100; // eax
int *v101; // eax
int *v102; // eax
int *v103; // eax
int *v104; // eax
int *v105; // eax
int *v106; // eax
int *v107; // eax
int *v108; // eax
int v109; // ST1C_4
int *v110; // eax
int *v111; // eax
int v112; // ST20_4
int *v113; // eax
int *v114; // eax
int v115; // ST20_4
int *v116; // eax
int flag[32]; // [esp+Ch] [ebp-88h]
int v121; // [esp+8Ch] [ebp-8h]

for (int i = 0; i < 32; ++i )
flag[i] = 1;
v121 = 0;
puts("Your flag is:");
v3 = mul(flag, 1000000000);
v4 = sub(v3, 999999950);
mul(v4, 2);
v5 = add(&flag[1], 5000000);
v6 = sub(v5, 6666666);
v7 = add(v6, 1666666);
v8 = add(v7, 45);
v9 = mul(v8, 2);
add(v9, 5);
v10 = mul(&flag[2], 1000000000);
v11 = sub(v10, 999999950);
v12 = mul(v11, 2);
add(v12, 2);
v13 = add(&flag[3], 55);
v14 = sub(v13, 3);
v15 = add(v14, 4);
sub(v15, 1);
v16 = mul(&flag[4], 100000000);
v17 = sub(v16, 99999950);
v18 = mul(v17, 2);
add(v18, 2);
v19 = sub(&flag[5], 1);
v20 = mul(v19, 1000000000);
v21 = add(v20, 55);
sub(v21, 3);
v22 = mul(&flag[6], 1000000);
v23 = sub(v22, 999975);
mul(v23, 4);
v24 = add(&flag[7], 55);
v25 = sub(v24, 33);
v26 = add(v25, 44);
sub(v26, 11);
v27 = mul(&flag[8], 10);
v28 = sub(v27, 5);
v29 = mul(v28, 8);
add(v29, 9);
v30 = add(&flag[9], 0);
v31 = sub(v30, 0);
v32 = add(v31, 11);
v33 = sub(v32, 11);
add(v33, 53);
v34 = add(&flag[10], 49);
v35 = sub(v34, 2);
v36 = add(v35, 4);
sub(v36, 2);
v37 = mul(&flag[11], 1000000);
v38 = sub(v37, 999999);
v39 = mul(v38, 4);
add(v39, 50);
v40 = add(&flag[12], 1);
v41 = add(v40, 1);
v42 = add(v41, 1);
v43 = add(v42, 1);
v44 = add(v43, 1);
v45 = add(v44, 1);
v46 = add(v45, 10);
add(v46, 32);
v47 = mul(&flag[13], 10);
v48 = sub(v47, 5);
v49 = mul(v48, 8);
v50 = add(v49, 9);
add(v50, 48);
v51 = sub(&flag[14], 1);
v52 = mul(v51, -294967296);
v53 = add(v52, 55);
sub(v53, 3);
v54 = add(&flag[15], 1);
v55 = add(v54, 2);
v56 = add(v55, 3);
v57 = add(v56, 4);
v58 = add(v57, 5);
v59 = add(v58, 6);
v60 = add(v59, 7);
add(v60, 20);
v61 = mul(&flag[16], 10);
v62 = sub(v61, 5);
v63 = mul(v62, 8);
v64 = add(v63, 9);
add(v64, 48);
v65 = add(&flag[17], 7);
v66 = add(v65, 6);
v67 = add(v66, 5);
v68 = add(v67, 4);
v69 = add(v68, 3);
v70 = add(v69, 2);
v71 = add(v70, 1);
add(v71, 20);
v72 = add(&flag[18], 7);
v73 = add(v72, 2);
v74 = add(v73, 4);
v75 = add(v74, 3);
v76 = add(v75, 6);
v77 = add(v76, 5);
v78 = add(v77, 1);
add(v78, 20);
v79 = mul(&flag[19], 1000000);
v80 = sub(v79, 999999);
v81 = mul(v80, 4);
v82 = add(v81, 50);
sub(v82, 1);
v83 = sub(&flag[20], 1);
v84 = mul(v83, -294967296);
v85 = add(v84, 49);
sub(v85, 1);
v86 = sub(&flag[21], 1);
v87 = mul(v86, 1000000000);
v88 = add(v87, 54);
v89 = sub(v88, 1);
v90 = add(v89, 1000000000);
sub(v90, 1000000000);
v91 = add(&flag[22], 49);
v92 = sub(v91, 1);
v93 = add(v92, 2);
sub(v93, 1);
v94 = mul(&flag[23], 10);
v95 = sub(v94, 5);
v96 = mul(v95, 8);
v97 = add(v96, 9);
add(v97, 48);
v98 = add(&flag[24], 1);
v99 = add(v98, 3);
v100 = add(v99, 3);
v101 = add(v100, 3);
v102 = add(v101, 6);
v103 = add(v102, 6);
v104 = add(v103, 6);
add(v104, 20);
v105 = add(&flag[25], 55);
v106 = sub(v105, 33);
v107 = add(v106, 44);
v108 = sub(v107, 11);
add(v108, 42);
add(&flag[26], flag[25]);
add(&flag[27], flag[12]);
v109 = flag[27];
v110 = sub(&flag[28], 1);
v111 = add(v110, v109);
sub(v111, 1);
v112 = flag[23];
v113 = sub(&flag[29], 1);
v114 = mul(v113, 1000000);
add(v114, v112);
v115 = flag[27];
v116 = add(&flag[30], 1);
mul(v116, v115);
add(&flag[31], flag[30]);
printf("CTF{");
for (int j = 0; j < 32; ++j)
printf("%c", char(flag[j]));
puts("}");
return 0;
}

直接输出flag

1
2
Your flag is:
CTF{daf8f4d816261a41a115052a1bc21ade}

easyre-153

先查壳,发现是upx,脱壳

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int pipedes[2]; // [esp+18h] [ebp-38h]
__pid_t v5; // [esp+20h] [ebp-30h]
int v6; // [esp+24h] [ebp-2Ch]
char buf; // [esp+2Eh] [ebp-22h]
unsigned int v8; // [esp+4Ch] [ebp-4h]

v8 = __readgsdword(0x14u);
pipe(pipedes);
v5 = fork();
if ( !v5 )
{
puts("\nOMG!!!! I forgot kid's id");
write(pipedes[1], "69800876143568214356928753", 29u);
puts("Ready to exit ");
exit(0);
}
read(pipedes[0], &buf, 29u);
__isoc99_scanf("%d", &v6);
if ( v6 == v5 )
{
if ( (*(_DWORD *)((_BYTE *)lol + 3) & 0xFF) == 204 )
{
puts(":D");
exit(1);
}
printf("\nYou got the key\n ");
lol(&buf);
}
wait(0);
return 0;
}

发现用到了简单的子进程和pipe。

pipe的作用就和名字一样,建立一个管道,这个管道一端是读,一端是写,按照规定,pipe[0]是读,pipe[1]是写,然后fork了一个子进程,在子进程中,返回值为0,进入if分支通过管道写入了一个字符串,然后退出子进程。在父进程中,返回的是fork出来的子进程的id,跳过if分支,在下面读取了刚刚子进程写入的字符串,关键就是lol这个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int __cdecl lol(_BYTE *a1)
{
char v2; // [esp+15h] [ebp-13h]
char v3; // [esp+16h] [ebp-12h]
char v4; // [esp+17h] [ebp-11h]
char v5; // [esp+18h] [ebp-10h]
char v6; // [esp+19h] [ebp-Fh]
char v7; // [esp+1Ah] [ebp-Eh]
char v8; // [esp+1Bh] [ebp-Dh]

v2 = 2 * a1[1];
v3 = a1[4] + a1[5];
v4 = a1[8] + a1[9];
v5 = 2 * a1[12];
v6 = a1[18] + a1[17];
v7 = a1[10] + a1[21];
v8 = a1[9] + a1[25];
return printf("flag_is_not_here");
}

这里的处理过程非常简单,还是正向的处理,但是最后输出的是一个没用的字符串,所以即使是输入子进程的pid,也不会输出真正的flag,所以还是自己动手算出来

1
2
3
4
5
6
7
8
9
10
a1 = "69800876143568214356928753"
v2 = 2 * ord(a1[1])
v3 = ord(a1[4]) + ord(a1[5])
v4 = ord(a1[8]) + ord(a1[9])
v5 = 2 * ord(a1[12])
v6 = ord(a1[18]) + ord(a1[17])
v7 = ord(a1[10]) + ord(a1[21])
v8 = ord(a1[9]) + ord(a1[25])

print(chr(v2)+chr(v3)+chr(v4)+chr(v5)+chr(v6)+chr(v7)+chr(v8))

输出结果

1
rhelheg

直接提交不对,要套上外面的格式,试了一下比赛的名称,通过。

asong

这题还是很考验对于算法的逆向能力的

首先IDA打开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
void *that_girl; // ST00_8
const char *flag; // ST08_8

that_girl = malloc(0xBCuLL);
flag = (const char *)malloc(80uLL);
sub_400BBF();
read_0((__int64)flag);
sub_400C02((__int64)flag); // 输入的是QCTF{***}的格式,在这里取出中间的部分
readfile("that_girl", (__int64)that_girl);
sub_400E54(flag, (__int64)that_girl);
return 0LL;
}

程序流程是这样,先输入flag,然后验证格式,取中间部分,然后读取另一个文件中的内容进行一些处理,然后将输入的flag和读取出来的内容进行一个比较,输出到out里面,重点来看一下几个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int __fastcall readfile(const char *a1, __int64 a2)
{
int v2; // eax
__int64 v4; // [rsp+0h] [rbp-20h]
char buf; // [rsp+13h] [rbp-Dh]
int fd; // [rsp+14h] [rbp-Ch]
unsigned __int64 v7; // [rsp+18h] [rbp-8h]

v7 = __readfsqword(0x28u);
fd = open(a1, 0, a2, a1);
while ( read(fd, &buf, 1uLL) == 1 )
{
v2 = sub_400936(buf);
++*(_DWORD *)(4LL * v2 + v4);
}
return close(fd);
}

这里明显看到进行了一个词频统计,但是看这个伪代码完全看不出来处理之后的v4和分配出来的a2有什么关联,这点在C伪代码里面不是很明显,但是在汇编里面可以看的很清楚

1
2
3
4
5
6
7
8
call    sub_400936
cdqe
lea rdx, ds:0[rax*4]
mov rax, [rbp+var_20]
add rax, rdx
mov edx, [rax]
add edx, 1
mov [rax], edx

这里其实很清楚,sub_400936就是我们用来处理的函数,它的返回值放到了raxcdqe的拓展在这里可以不用考虑,差不多算是个类型转换,ds:0是之前分配的空间,把ds:0[rax*4]的地址复制进rdx,后面的一句并没有什么意义,将这个地址中的值赋给rax,就是0,add之后rax的值就是我们需要的地址,接下来把该地址内存的的值取出来,加一然后再放回去,这个操作逻辑很容易理解。

这个函数过后构建了一个词频表,然后进行关键的变换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unsigned __int64 __fastcall sub_400E54(const char *flag, __int64 that_girl)
{
int i; // [rsp+18h] [rbp-48h]
int v4; // [rsp+1Ch] [rbp-44h]
char v5[56]; // [rsp+20h] [rbp-40h]
unsigned __int64 v6; // [rsp+58h] [rbp-8h]

v6 = __readfsqword(0x28u);
v4 = strlen(flag);
for ( i = 0; i < v4; ++i )
v5[i] = *(_DWORD *)(4LL * (signed int)sub_400936(flag[i]) + that_girl);
sub_400D33((unsigned __int8 *)v5);
sub_400DB4(v5, v4);
writefile((__int64)v5, "out", v4);
return __readfsqword(0x28u) ^ v6;
}

首先就是一个换表,然后就是两个函数的操作之后输出到了out文件里面,这个是我们已知的,再去看看两个关键函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
__int64 __fastcall sub_400D33(unsigned __int8 *a1)
{
__int64 result; // rax
_BYTE v2[5]; // [rsp+13h] [rbp-5h]

v2[4] = 0;
*(_DWORD *)v2 = *a1;
while ( dword_6020A0[*(signed int *)&v2[1]] )
{
a1[*(signed int *)&v2[1]] = a1[dword_6020A0[*(signed int *)&v2[1]]];
*(_DWORD *)&v2[1] = dword_6020A0[*(signed int *)&v2[1]];
}
result = v2[0];
a1[*(signed int *)&v2[1]] = v2[0];
return result;
}

这是一个简单的次序上的调整,可以很快的逆出来

1
2
3
4
5
6
7
8
9
10
11
12
13
_BYTE *__fastcall sub_400DB4(_BYTE *a1, int a2)
{
_BYTE *result; // rax
char v3; // [rsp+17h] [rbp-5h]
int i; // [rsp+18h] [rbp-4h]

v3 = *a1 >> 5;
for ( i = 0; a2 - 1 > i; ++i )
a1[i] = 8 * a1[i] | (a1[i + 1] >> 5);
result = &a1[i];
*result = 8 * *result | v3;
return result;
}

这个函数里有些很有意思的操作,这个或操作看着很熟悉,这是显然是一个换位的操作,但是这里用了前一个数和后一个数之间的交错换位,逆起来就有了一些难度。

我也看过一些其他大佬的wp,这里普遍都是采用了爆破的方法,我不是很喜欢,虽然爆破可能更加省时省力,我还是想更加加深一下逆向位运算的熟练程度。

经过观察我们可以很轻松的发现,只需要将所有的数拆成两部分,再按照反方向运算回去就可以了,非常简单。

这样的话所有的内容就分析完了,这里我用python来做的逆向(后悔没有用C++)

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
def sub_400936(a1):
result = a1 - 10
if a1 == 10:
result = a1 + 35
if a1 == 32 or a1 == 33 or a1 == 34:
result = a1 + 10
if a1 == 39:
result = a1 + 2
if a1 == 44:
result = a1 - 4
if a1 == 46:
result = a1 - 7
if a1 == 58 or a1 == 59:
result = a1 - 21
if a1 == 63:
result = a1 - 27
if a1 == 95:
result = a1 - 49

if a1 <= 47 or a1 > 48:
if a1 <= 64 or a1 > 90:
if 96 < a1 <= 122:
result = a1 - 87
else:
result = a1 - 55
else:
result = a1 - 48
return result


def get_that_girl():
adic = {}
fp = open("that_girl", "r")
while True:
data = fp.read(1)
if not data:
break
else:
data = sub_400936(ord(data))
if data in adic:
adic[data] = adic[data] + 1
else:
adic[data] = 1
fp.close()
return adic


def table():
t = {}
for ia in range(256):
t[sub_400936(ia)] = ia
return t


a1 = [0xec, 0x29, 0xe3, 0x41, 0xe1, 0xf7, 0xaa, 0x1d, 0x29, 0xed, 0x29, 0x99, 0x39, 0xf3, 0xb7, 0xa9, 0xe7, 0xac, 0x2b,
0xb7, 0xab, 0x40, 0x9f, 0xa9, 0x31, 0x35, 0x2c, 0x29, 0xef, 0xA8, 0x3d, 0x4b, 0xb0, 0xe9, 0xe1, 0x68, 0x7b, 0x41]
target = []
part1 = []
part2 = []
for i in range(len(a1)):
part2.append((a1[i] >> 3) & 0xff)
part1.append((a1[i] << 5) & 0xff)
target.append(part2[0] | part1[len(a1) - 1])
for i in range(1, len(a1)):
target.append(part2[i] | part1[i - 1])

dword_6020A0 = [22, 0, 6, 2, 30, 24, 9, 1, 21, 7, 18, 10, 8, 12, 17, 23, 13, 4, 3, 14, 19, 11, 20, 16, 15, 5, 25, 36,
27, 28, 29, 37, 31, 32, 33, 26, 34, 35]
i = 0
index = []
while (dword_6020A0[i]):
index.append(i)
i = dword_6020A0[i]
index.append(1)
index.reverse()

a2 = [0 for i in range(len(index))]

for i in index:
if i == 1:
a2[0] = target[i]
a2[i] = target[dword_6020A0.index(i)]

dic = get_that_girl()
dic_ = {v: k for k, v in dic.items()}

flag = 'QCTF{'
for i in a2:
flag += chr(table()[dic_[i]])
flag += '}'

print(flag)

最后一部分最开始是打算爆破,但是想到了一种更简便的方法,就是先把所有的ASCII码值遍历一遍,制作一个表,可以避免双重循环爆破,虽然实际上爆破也并不麻烦。

最后输出flag

1
QCTF{that_girl_saying_no_for_your_vindicate}

signin

找一道比较简单的题目保持一下做题的感觉

用Ghidra打开,部分函数的签名有问题,有时候将RDX误当作函数的参数,但是函数只需要两个参数,这里我打开IDA看了一下,IDA做的要比Ghidra好很多,根据汇编修改了一下函数签名,看一下主要的函数部分。

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
undefined8 FUN_00100a21(void)

{
int iVar1;
long in_FS_OFFSET;
undefined local_4a8 [16];
undefined local_498 [16];
undefined local_488 [16];
undefined local_478 [16];
undefined local_468 [112];
undefined local_3f8 [1000];
long local_10;

local_10 = *(long *)(in_FS_OFFSET + 0x28);
puts("[sign in]");
printf("[input your flag]: ");
__isoc99_scanf(&DAT_00100c26,local_468);
/* 字符串转16进制 */
FUN_0010096a(local_468,local_3f8);
__gmpz_init_set_str(local_478,"ad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35",
0x10);
__gmpz_init_set_str(local_488,local_3f8,0x10);
__gmpz_init_set_str(local_4a8,

"103461035900816914121390101299049044413950405173712170434161686539878160984549"
,10);
__gmpz_init_set_str(local_498,"65537",10);
__gmpz_powm(local_488,local_488,local_498,local_4a8);
iVar1 = __gmpz_cmp(local_488,local_478);
if (iVar1 == 0) {
puts("TTTTTTTTTTql!");
}
else {
puts("GG!");
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}

首先是需要我们输入字符串,然后进行了一些大数的操作,这里是利用了一个库,查了一下mannual,很好理解,输入之后有一个函数对我们输入的字符串进行了一些操作,进入这个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void FUN_0010096a(char *param_1,long param_2)

{
size_t sVar1;
int local_20;
int local_1c;

local_20 = 0;
local_1c = 0;
while( true ) {
sVar1 = strlen(param_1);
if (sVar1 <= (ulong)(long)local_20) break;
*(undefined *)(local_1c + param_2) = (&DAT_00302010)[(int)(param_1[local_20] >> 4)];
*(undefined *)((long)local_1c + 1 + param_2) =
(&DAT_00302010)[(int)((int)param_1[local_20] & 0xf)];
local_20 = local_20 + 1;
local_1c = local_1c + 2;
}
return;
}

看到分别取低四位和高四位的操作之后立刻反应过来是把字符串转换为16进制串,耐心看一下很好理解。

回到主函数,看到乘幂取模操作,发现是RSA,用yafu分解一下103461035900816914121390101299049044413950405173712170434161686539878160984549

分解的也很快,这个数还是不够大

1
2
prp39 = 282164587459512124844245113950593348271
prp39 = 366669102002966856876605669837014229419

然后可以直接在线解密也是可以解出来的,由于gmpy2安装不上,直接在线解密了

1
suctf{Pwn_@_hundred_years}

key

过程很复杂,动态调试绕过文件检测之后调试出flag

1
idg_cni~bjbfi|gsxb

notsequence

题目还是很不错,逻辑很清楚,仔细观察可以看出来杨辉三角就解决了,一共20层,全部组合起来取md5就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import hashlib


def create(line): # 生成杨辉三角的一行
L = [1]
for x in range(1, len(line)):
L.append(line[x] + line[x - 1])
L.append(1)
return L


L = [1]
raw_flag = '1'
for x in range(19):
L = create(L)
raw_flag += ''.join([str(i) for i in L])
# print(raw_flag)
m = hashlib.md5(raw_flag.encode()).hexdigest()

print('RCTF{' + m + '}')

输出flag

1
RCTF{37894beff1c632010dd6d524aa9604db}

zorropub

没想到会用pwntools去爆破一道逆向题……

感觉有些复杂,无脑爆破得到结果

catch-me

这题用了sse算法,而且整个过程非常复杂,所以猜测了一下其中的两个数相同,然后得到了flag

BabyXor

首先脱壳,然后观察IDA,发现函数很多进行了很多次的异或运算,因此选择直接动态调试,调试到关键函数的最后发现几个运算是算出了flag的三个部分,拼接得到flag

攻防世界-pwn部分题解(一) 攻防世界-re部分题解(三)

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×