Sma11_Tim3's Studio.

攻防世界Reverse-Challenge-area

字数统计: 11.3k阅读时长: 55 min
2020/12/13 Share

研究生阶段重拾CTF,未来的路还是喜欢网络攻防,既然喜欢那就坚持下去吧。

Guess-the-Number

用Android Kill打开或者用Java Decompiler打开。

img

重点在guess_number函数,只有当my_number/5==guess_number时,打印出正确信息的if块,跟进去以后发现if块中的内容和guess_numbe没啥关系。因此最终guess_number应该为my_number/5=309137378。在命令行中执行命令,即可得到flag。

img

1
a7b08c546302cc1fd2a4d48bf2bf2ddb

Shuffle1

拖进IDA,找到main函数,F5。

image-20201118184155710

1
SECCON{Welcome to the SECCON 2014 CTF!}

re-for-50-plz-50

发现是MIPS的文件,咱也不知道是什么啊!!!!
然后没管直接打开,结果发现转函数时:

image-20201118184648648

额,MIPS文件不能够直接用F5查看伪代码。

最后经过查找资料,了解到MIPS是一个指令集
与汇编时不同的两个东西~~
必须恶补一下!!!
找到一篇好的文章~

https://www.cnblogs.com/thoupin/p/4018455.html

找一下关键的部分。

image-20201118184946291

发现了一段字符串和异或(XOR),尝试一下提取出来运算试试。

1
2
3
4
5
6
7
8
9
#!/usr/bin/python
#coding=utf-8

s1="cbtcqLUBChERV[[Nh@_X^D]X_YPV[CJ"
num=0x37
flag=''
for i in range(len(s1)):
flag+=chr(ord(s1[i])^num)
print(flag)
1
TUCTF{but_really_whoisjohngalt}

dmd-50

image-20201118185802028

进去之后发现需要比对一个字符串780438d5b6e29db0898bc4f0225935c0,但是从上面有经过一个md5函数,提取出来在线解一下。

image-20201118185948576

发现结果是grape但是类型是经过了两次md5加密的,猜测需要输入的flag是grape进行md5加密一次,测试发现正确。

1
b781cbb29054db12f88f08c6e161c199

parallel-comparator-200

是一道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
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

#define FLAG_LEN 20

void * checking(void *arg) {
char *result = malloc(sizeof(char));
char *argument = (char *)arg;
*result = (argument[0]+argument[1]) ^ argument[2];
return result;
}

int highly_optimized_parallel_comparsion(char *user_string)
{
int initialization_number;
int i;
char generated_string[FLAG_LEN + 1];
generated_string[FLAG_LEN] = '\0';

while ((initialization_number = random()) >= 64);

int first_letter;
first_letter = (initialization_number % 26) + 97;
//initialization_number=37
pthread_t thread[FLAG_LEN];
char differences[FLAG_LEN] = {0, 9, -9, -1, 13, -13, -4, -11, -9, -1, -7, 6, -13, 13, 3, 9, -13, -11, 6, -7};
char *arguments[20];
for (i = 0; i < FLAG_LEN; i++) {
arguments[i] = (char *)malloc(3*sizeof(char));
arguments[i][0] = first_letter;
arguments[i][1] = differences[i];
arguments[i][2] = user_string[i];

pthread_create((pthread_t*)(thread+i), NULL, checking, arguments[i]);
}

void *result;
int just_a_string[FLAG_LEN] = {115, 116, 114, 97, 110, 103, 101, 95, 115, 116, 114, 105, 110, 103, 95, 105, 116, 95, 105, 115};
for (i = 0; i < FLAG_LEN; i++) {
pthread_join(*(thread+i), &result);
generated_string[i] = *(char *)result + just_a_string[i];
free(result);
free(arguments[i]);
}

int is_ok = 1;
for (i = 0; i < FLAG_LEN; i++) {
if (generated_string[i] != just_a_string[i])
return 0;
}

return 1;
}

int main()
{
char *user_string = (char *)calloc(FLAG_LEN+1, sizeof(char));
fgets(user_string, FLAG_LEN+1, stdin);
int is_ok = highly_optimized_parallel_comparsion(user_string);
if (is_ok)
printf("You win!\n");
else
printf("Wrong!\n");
return 0;
}

关键代码分析

1
2
3
4
generated_string[i] = *(char *)result + just_a_string[i];

if (generated_string[i] != just_a_string[i])
return 0;

image-20201118190550516

写出对应的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define FLAG_LEN 20
char differences[FLAG_LEN] = {0, 9, -9, -1, 13, -13, -4, -11, -9, -1, -7, 6, -13, 13, 3, 9, -13, -11, 6, -7};
int main()
{
int initialization_number,first_letter;
while ((initialization_number = random()) >= 64);
first_letter = (initialization_number % 26) + 97;
for(int i=0;i<FLAG_LEN;i++)
{
printf("%c",first_letter+differences[i]);
}
printf("\n");
}
1
2
3
4
5
#!/usr/bin/python
#coding=utf-8
differences = [0, 9, -9, -1, 13, -13, -4, -11, -9, -1, -7, 6, -13, 13, 3, 9, -13, -11, 6, -7]
first_letter = 108
print (''.join([chr(first_letter+differences[i]) for i in range(len(differences))]))

secret-galaxy-300

拖进去发现主函数就两个函数,跟进去之后也没有什么特别的。

image-20201118191246053

image-20201118191333145

image-20201118191345604

拖进OD尝试动态调试一下。

直接在输出相应的语句的地方下个断点。

image-20201118191703777

运行到这里,右键智能搜索一下字符串。

image-20201118191741480

发现了一个可疑的字符串,aliens_are_around_us。提交就是flag

1
aliens_are_around_us

srm-50

image-20201118192509330

image-20201118192541883

一共两个检查的地方,一个检查邮箱一个检查数字。检查邮箱的地方发现只要有@和.这两个就可以了。重点是后面的if,简单的运算一下就能知道值。

image-20201118192738318

1
CZ9dmq4c8g9G7bAX

simple-check-100

image-20201118193435005

进去之后发现最主要的地方就是check_key函数,看一下汇编代码。

image-20201118193813147

发现check_key函数是否成功就取决于eax函数的值。

image-20201118194317962

使用gdb调试,下断点运行到test这条语句,查看一下rax寄存器的值。

image-20201118194404722

这里rax的值为0,我们将其修改为1。

image-20201118194505923

然后continue,执行就能得到flag。

image-20201118194547929

1
flag_is_you_know_cracking!!!

Mysterious

很简答的一道题。

image-20201118195041381

image-20201118195156665

可以看到判断里面v10=123,v12、v13、v14分别是xyz,因为上一条语句v10=atoi(&string)+1。所以应该输入的值为122,再加上后面三个参数xyz。

image-20201118195402248

Newbie_calculations

这题也是很有意思的,看得有点眼睛痛。

main函数里面一堆函数调用,最后打印出v120[]数组里面的值,也就是需要的flag,仔细看了一下之后发现就是3个函数。

sub_401000、sub_401100、sub_401220

image-20201118200052287

这个函数一堆操作,但是我们只关心a1和a2。第一个循环减了v4次,第二个有加了v5次,最后还加了一次,简化一下,发现最后的结果就是返回两个值相加。

接下来依次分析剩下的两个函数。最后可以得到如下结果:

sub_401000函数返回两数相加

sub_401100函数返回两数相乘

sub_401220函数返回两数相减

将整个函数重新写一遍运行出来:

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
296
297
298
299
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <stdlib.h>
using namespace std;
int *sub_401100(int *a1, int a2)//a1 * a2
{
*a1 = *a1 * a2;
return a1;
}

int *sub_401000(int *a1, int a2)//a1 + a2
{
*a1 = *a1 + a2;
return a1;
}

int *sub_401220(int *a1, int a2)//a1 - a2
{
*a1 = *a1 -a2;
return a1;
}

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

re1-100

拿到先file查一下看看。

image-20201120221014760

64位直接进ida。

shift+F12进字符串表,定位到关键字符串处。

image-20201120221107134

跟进去找到引用函数,F5。

image-20201120221159810

一大堆东西,看到我们的输入都存在了bufWrite里面。然后又把bufWrite写进了bufParentRead里。前面的几个函数看函数名是检查是否开启Debugger模式和检查输入是字符串还是数字。看到后面重点来了。

image-20201120221701866

开头结尾为{},字符串长度42。前10位和后10位分别为53fc275d81和4938ae4efd。最后和{daf29f59034938ae4efd53fc275d81053ed5be8c}做比较,中间要经过一个confuseKey函数。跟进confuseKey函数看一下。

image-20201122204422146

过程简单,现将input分成四个部分,然后分别存进szPart1,2,3,4中。然后利用strcat函数调换顺序为3412,存入input中。

因此只需将之前得到的字符串{daf29f59034938ae4efd53fc275d81053ed5be8c},分成4分按照1234的顺序排好即可。最终的flag为:

1
53fc275d81053ed5be8cdaf29f59034938ae4efd

answer_to_everything

image-20201123213510377

跟进not_the_flag()函数

image-20201123213550150

额,很简单,只要输入42就行了,结果就是kdudpeh。

根据题目描述要经过一下sha1。

image-20201123213640704

1
flag{80ee2a3fe31da904c596d993f7f1de4827c1450a}

elrond32

image-20201123221409432

找到main函数,可以参考运行函数的时候需要跟一个参数,这个参数就是input。

程序判断很简单,跟进sub_8048414函数。

image-20201123221516583

发现是switch函数,只有值为0,1,3,4,5,6,7,9时才有值。对传入的值进行(input + 1, 7 * (a2_0 + 1) % 11)操作,然后继续调用该函数。把这个函数写一下跑一遍,或者直接手算出来。可以得到

1
isengard

image-20201123221828014

运行该程序就能得到flag。

1
flag{s0me7hing_S0me7hinG_t0lki3n}

image-20201123221909551

或者将得到的结果跟进下面的函数中,直接跑也可以出结果。

1
2
3
4
5
6
key=[105,115,101,110,103,97,114,100]
v2=[0x0F,0x1F,0x04,0x09,0x1C,0x12,0x42,0x09,0x0C,0x44,0x0D,0x07,0x09,0x06,0x2D,0x37,0x59,0x1E,0x00,0x59,0x0F,0x08,0x1C,0x23,0x36,0x07,0x55,0x02,0x0C,0x08,0x41,0x0A,0x14]
a=''
for i in range(33):
a+=chr(v2[i]^key[(i%8)])
print(a)

gametime

很好玩的一道题目。一个小游戏,s—>’ ‘,m—>’m’,x—>’x’。看见什么字母按对应的就可以了,

玩个几轮就能拿到flag

image-20201124100257719

或者尝试用OD调试,nop掉sub_401260函数的返回。

image-20201124104406824

因为是最后返回值的判断,而且是连续两个判断,OD中找到sub_401260函数。

image-20201124104658133

image-20201124104634322

找到函数的最后ret的地方向上找发现箭头指的两个判断跳转是跳转至结束。所以就是最后v4!=-1&&v4==v2这两个句子,把它nop掉。

image-20201124104911446

这样程序会正常运行但是在跑的时候即使输入错误也不会跳转到结束。

跟着程序一通乱按就行了。

image-20201124105125310

tt3441810

拿到手发现是文本文件。

image-20201124112325229

用sublime打开。

image-20201124112348243

前面第一个数明显是地址,后面的是数据,把它导入到WinHex中看看。

image-20201124112428453

可以看到开头有fl之类的字符,我们写个脚本把可显示字符输出出来试试。

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
#!/usr/bin/python
#coding=utf-8
key=[0x68,0x66,0x6C,0x00,0x00,0x48,0xBF,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,
0x8D,0x34,0x24,0x48,0xBA,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0xB8,0x01,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x05,0x68,0x61,0x67,0x00,0x00,0x48,0xBF,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x8D,0x34,0x24,0x48,0xBA,0x02,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x48,0xB8,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0x05,0x68,0x7B,0x70,0x00,0x00,0x48,0xBF,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x48,0x8D,0x34,0x24,0x48,0xBA,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,
0xB8,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x05,0x68,0x6F,0x70,0x00,0x00,
0x48,0xBF,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x8D,0x34,0x24,0x48,0xBA,
0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0xB8,0x01,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x0F,0x05,0x68,0x70,0x6F,0x00,0x00,0x48,0xBF,0x01,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x48,0x8D,0x34,0x24,0x48,0xBA,0x02,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x48,0xB8,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x05,0x68,0x70,0x72,
0x00,0x00,0x48,0xBF,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x8D,0x34,0x24,
0x48,0xBA,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0xB8,0x01,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x0F,0x05,0x68,0x65,0x74,0x00,0x00,0x48,0xBF,0x01,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x48,0x8D,0x34,0x24,0x48,0xBA,0x02,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x48,0xB8,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x05,0x68,
0x7D,0x0A,0x00,0x00,0x48,0xBF,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x8D,
0x34,0x24,0x48,0xBA,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0xB8,0x01,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x05,0x48,0x31,0xFF,0x48,0xB8,0x3C,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x0F,0x05]
flag=''
for i in range(len(key)):
if(0x20<=key[i]<=0x7d):
flag+=chr(key[i])
print(flag)
flag=flag.replace('HH4$HH','')
print(flag)
flag=flag.replace('h','')
print(flag)

结果出来有很多多余的东西,把他们都清除掉。

image-20201124112609804

可以的到flag{poppopret},最后提交的结果需要去掉flag{}。

1
poppopret

re2-cpp-is-awesome

打开ida,找到main函数。

image-20201124165705900

前面的函数经过分析发现没啥用,没有提示输入,参数是在运行参数跟着的。主要的判断函数在34行。仔细看右边的比较是数组里面嵌套数组。

off_6020A0数组里面是“L3t_ME_T3ll_Y0u_S0m3th1ng_1mp0rtant_A_{FL4G}_W0nt_b3_3X4ctly_th4t_345y_t0_c4ptur3_H0wev3r_1T_w1ll_b3_C00l_1F_Y0u_g0t_1t”

dword_6020C0[v15]里面(注意align是地址对齐的伪指令,align=8意味着隔着8字节一取)。

image-20201124170320789

dword_6020C0=[ 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00,

0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00,

0x07, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x26, 0x00,

0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,

0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00,

0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,

0x03, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x03, 0x00,

0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,

0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x03, 0x00,

0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0x29, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00,

0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,

0x2B, 0x00, 0x00, 0x00]

注意因为8字节一取,所以第一个数0x24到0x05之间有一个数是0x00不能漏掉了。将中间多余的0去掉可以了。

给出脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/python
#coding=utf-8
key=[0x24, 0x00, 0x05, 0x36, 0x65,
0x07, 0x27, 0x26, 0x2D, 0x01,
0x03, 0x00, 0x0D, 0x56, 0x01,
0x03, 0x65, 0x03, 0x2D, 0x16,
0x02, 0x15, 0x03, 0x65, 0x00,
0x29, 0x44, 0x44, 0x01, 0x44,
0x2B]
flag=''
off_6020A0="L3t_ME_T3ll_Y0u_S0m3th1ng_1mp0rtant_A_{FL4G}_W0nt_b3_3X4ctly_th4t_345y_t0_c4ptur3_H0wev3r_1T_w1ll_b3_C00l_1F_Y0u_g0t_1t"
for i in range(len(key)):
flag+=off_6020A0[key[i]]
print(flag)
1
ALEXCTF{W3_L0v3_C_W1th_CL45535}

re4-unvm

打开是一个pyc文件,https://tool.lu/pyc/这个网站可以反编译得到py文件。

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
#!/usr/bin/env python
# visit http://tool.lu/pyc/ for more information
import md5
md5s = [
0x831DAA3C843BA8B087C895F0ED305CE7L,
0x6722F7A07246C6AF20662B855846C2C8L,
0x5F04850FEC81A27AB5FC98BEFA4EB40CL,
0xECF8DCAC7503E63A6A3667C5FB94F610L,
0xC0FD15AE2C3931BC1E140523AE934722L,
0x569F606FD6DA5D612F10CFB95C0BDE6DL,
0x68CB5A1CF54C078BF0E7E89584C1A4EL,
0xC11E2CD82D1F9FBD7E4D6EE9581FF3BDL,
0x1DF4C637D625313720F45706A48FF20FL,
0x3122EF3A001AAECDB8DD9D843C029E06L,
0xADB778A0F729293E7E0B19B96A4C5A61L,
0x938C747C6A051B3E163EB802A325148EL,
0x38543C5E820DD9403B57BEFF6020596DL]
print 'Can you turn me back to python ? ...'
flag = raw_input('well as you wish.. what is the flag: ')
if len(flag) > 69:
print 'nice try'
exit()
if len(flag) % 5 != 0:
print 'nice try'
exit()
for i in range(0, len(flag), 5):
s = flag[i:i + 5]
if int('0x' + md5.new(s).hexdigest(), 16) != md5s[i / 5]:
print 'nice try'
exit()
continue
print 'Congratz now you have the flag'

可以看到有一个md5之后的数组,函数的逻辑就是输入的字符串md5加密之后要和数组做比较,因此只需要进行md5解密就可以了。

两个md5在线解密网站,有一个需要收费,就换两一个解,然后拼接起来就可以了。

https://www.somd5.com/

https://www.cmd5.com/

1
ALEXCTF{dv5d4s2vj8nk43s8d8l6m1n5l67ds9v41n52nv37j481h3d28n4b6v3k}

流浪者

这题有一些难度但是其实还好,就两个函数比较复杂一点。

image-20201124201702321

可以分析得到输入的字符串先进行一个判断,字符在48~57时减48, 97~122时减87, 65~90时减29。

将得到的数存入到v5[]数组中,然后将数组传入sub_4017F0(v5)函数中,跟进函数。

image-20201124202102626

这段伪代码的意思是通过接收一个变量v5来控制数组aAbcdefghiabcde的角标获取数组中的字符并赋值给Str1,通过Str1与字符串“KanXueCTF2019JustForhappy”进行比较,如果相同则返回pass!

所以我们可以将”KanXueCTF2019JustForhappy”进行逆运算,得到Str1,就是获得flag。

根据过程写出代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/python
#coding=utf-8
import string
str1="abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ"
str2="KanXueCTF2019JustForhappy"
flag=''
for i in range(len(str2)):
for j in range(len(str1)):
if(str2[i]==str1[j]):
if (48<=(j+48)<=57):
flag+=chr(j+48)
elif (97<=(j+87)<=122):
flag+=chr(j+87)
elif (65<=(j+29)<=90):
flag+=chr(j+29)
print(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
#include <stdio.h>
int main()
{
char a[] = "KanXueCTF2019JustForhappy";
char b[] = "abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ";
int c[25];
int i;
int j;
for (i = 0; i < sizeof(a); i++)
{
for (j = 0; j < sizeof(b); j++)
{
if (a[i] == b[j])
c[i] = j;
}
}
int x;
for (i = 0; i < 25; i++)
{
for (j = 48; j <= 122; j++)
{
if (j > 57 || j < 48)
{
if (j > 122 || j < 97)
{
if (j > 90 || j < 65)
continue;
else
x = j - 29;
}
else
x = j - 87;
}
else
x = j - 48;
if (x == c[i])
printf("%c", j);
}
}
printf("\n");
return 0;
}

得到的结果加上flag{}

1
flag{j0rXI4bTeustBiIGHeCF70DDM}

666

image-20201124213612720

进去之后发现只有一个加密函数encode()。需要对比的字符串是 enflag=”izwhroz””w”v.K”.Ni”

image-20201124213702006

逻辑也很简单,将输入的字符串按照下标模3的余数分组,分别进行简单的运算,结果和enflag比较。

直接逆算法即可,异或可以直接原样操作回去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/python
#coding=utf-8
import string
str="izwhroz\"\"w\"v.K\".Ni"
key=[]
for i in str:
key.append(i)
print(key)
flag=''
for i in range(0,18,3):
flag+=chr((ord(key[i])^18)-6)
flag+=chr((ord(key[i+1])^18)+6)
flag+=chr(ord(key[i+2])^18^6)
print(flag)

这里有两个小地方需要注意,一是enflag字符串中含有””所以需要用\转义一下,否则会报错。二是因为+-运算符的优先级高于 ^ 运算所以要多加一个括号,先进行 ^运算。

1
unctf{b66_6b6_66b}

SignIn

进去之后找到main函数。

image-20201201204941917

进去之后发现主要的就是一个RSA过程,其中输入会经过一个sub_96A的函数。

image-20201201205145470

写一个对应的脚本看看大概是干嘛的

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/python
#coding=utf-8
import string
str1="ABCDEFG"
flag=''

for i in range(len(str1)):
str2 = ord(str1[i]) >> 4
str3 = ord(str1[i]) & 0xF
print(str2,end='')
print(str3,end='')
1
output:41424344454647

尝试了几个参数,发现它的作用是将一串输入转化为 16 进制 ASCII 码的形式出来。

根据21行gmpz_powm函数中的参数(&v6, &v6, &v5, &v4);

对比RSA中求明文的m = pow(c, d, N)

可以得知v6存储密文,v5存储d,v4存储N。

现在已知

1
2
3
N=103461035900816914121390101299049044413950405173712170434161686539878160984549
c=0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35
e=65537

尝试将N分离http://factordb.com/ 貌似需要翻墙才能访问,可以下载yafu来分解。

image-20201201211918494

image-20201201212552671

得到pq的值。

1
2
p = 366669102002966856876605669837014229419
q = 282164587459512124844245113950593348271

已知q、p、e可以直接求到d,最后直接解出明文即可。

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
#!/usr/bin/python3
#coding=utf-8
import string
import libnum
def egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, y, x = egcd(b % a, a)
return (g, x - (b // a) * y, y)
def modinv(a, m):
g, x, y = egcd(a, m)
if g != 1:
raise Exception('modular inverse does not exist')
else:
return x % m

p = 366669102002966856876605669837014229419
q = 282164587459512124844245113950593348271
e = 65537
d = modinv(e, (p-1)*(q-1))
print (d)
c = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35
N = 103461035900816914121390101299049044413950405173712170434161686539878160984549
m = pow(c, d, N)
print (m)
print (libnum.n2s(m))
1
suctf{Pwn_@_hundred_years}

easy_Maze

maze类:1.内存中画出一张地图(地图变换) 2.明确起点和终点 3.(四个字符对应上下左右)flag就是走出的路径

题目提示是maze类的,找上面三个关键点。先进到main函数

image-20201203191556233

逻辑很明确,step_0,step_1,step_2。

分别进去看看

image-20201203191702174

image-20201203191716164

image-20201203191822645

可以看到step_0,step_1两个步骤是把地图进行更改,step_2是判断移动是否正确。

本来将前两个步骤解出来得到正确的地图就能解题了。看了 Step_0 函数, 还是简单的, 但是 step_1 中嵌套了2个函数且很复杂. 开始我就把生成矩阵的所有函数及数据都复制到VC 再根据栈的特点改下数据的顺序, 然后运行打印出矩阵. 问题是运行后什么也没有打印, 调试发现一个函数中的内存空间和另外一个冲突, 相互覆盖值. 这个函数太多, 改起来也麻烦.

然后转向GDB调试, 但是不熟练. 又转向ida动态调试. 先下断点, 找到储存矩阵的空间的地址, 把这个地址转到数据窗口跟随. 运行到生成矩阵的下面一个函数. 得到生成的矩阵.

具体过程如下,ida动态调试教程看这个http://smalltime.work/2020/12/03/ida%E5%8A%A8%E6%80%81%E8%B0%83%E8%AF%95elf-%E6%97%A0%E5%9D%91%E8%AF%A6%E7%BB%86/

进入ida动态调试后,我们在step_2处下一个端点,然后运行程序,

image-20201203192448832

然后找到右边RSP寄存器的地址。

image-20201203192555713

找到Hex View中的对应数据,并且右键修改显示的格式。(设置为4byte_Integer和Signed),Hex View中右键Sychronize with—>RSP,就可以跟随到RSP所在的位置。

image-20201203192722054

image-20201203194653515

可以找到我们需要的数据,后面有多的不要紧我们取前49个就可以了,之前分析得到我们的迷宫是7*7的。

1
2
3
4
5
6
7
1,0,0,1,1,1,1,
1,0,1,1,0,0,1,
1,1,1,0,1,1,1,
0,0,0,1,1,0,0,
1,1,1,1,0,0,0,
1,0,0,0,1,1,1,
1,1,1,1,1,0,1,

把迷宫排列一下就可以了,之前代码中可以分析到我们要从[0] [0]走到[6] [6]去,也就是迷宫中左上角走到右下角,上下左右分别对应wsad。

image-20201203194942638

1
UNCTF{ssddwdwdddssaasasaaassddddwdds}

ReverseMe-120

进去之后发现还是有点复杂。

image-20201203212541771

最后的地方就是对比,和you_know_how_to_remove_junk_code做对比。

上面的过程一开始看不懂,进到sub_401000函数里面。后来去问了一下和搜了一下发现前面的操作就是base64解密操作。逻辑就清楚了,将输入的字符串进行base64解密,然后按位和0x25做异或操作,将结果和you_know_how_to_remove_junk_code做对比。

那么解密过程也简单了,将you_know_how_to_remove_junk_code按位异或0x25然后进行base64加密。

1
2
3
4
5
6
7
#!/usr/bin/env python3
import base64
s1="you_know_how_to_remove_junk_code"
flag=''
for i in range(len(s1)):
flag+=chr(ord(s1[i])^0x25)
print(base64.b64encode(flag.encode('utf-8')))
1
XEpQek5LSlJ6TUpSelFKeldASEpTQHpPUEtOekZKQUA=

Reversing-x64Elf-100

这题挺简单的。就是一个输入加字符串修改,然后对比。

image-20201203221604781

重点在sub_4006FD函数里面

image-20201203221638450

v3是一个二维数组,每个字符减去输入求差值要为1。解密就把这些字符对应减1就行了。

1
2
3
4
5
6
7
8
9
#!/usr/bin/env python3
v3 = "Dufhbmf"
v4 = "pG`imos"
v5 = "ewUglpt"
A=[v3,v4,v5]
flag=''
for i in range(12):
flag += chr(ord(A[i%3][2*(i//3)])-1)
print(flag)
1
Code_Talkers

IgniteMe

首先file一个看到是32位PE文件,直接ida打开,进去之后找到main函数。可以看到完整的逻辑过程。

image-20201209105955595

首先判定了一下长度,检查输入的字符串的开头为EIS{结尾为}。花括号中间的字符串判定在第32行中的sub_4011C0函数中,跟进去看一看。(部分变量名和函数名为了方便我修改了一下)

image-20201209110257155

逻辑过程很清楚,首先将输入的字符串括号中的部分赋值给v8,然后进行大小写转换,大写变小写,小写变大写,接着进行一些数值运算(第33行),具体为

1
2
3
4
byte4420B0[i](v8^0x55)+72
byte4420B0=[0x0D, 0x13, 0x17, 0x11, 0x02, 0x01, 0x20, 0x1D, 0x0C, 0x02, 0x19, 0x2F, 0x17, 0x2B, 0x24, 0x1F, 0x1E, 0x16, 0x09, 0x0F,
0x15, 0x27, 0x13, 0x26, 0x0A, 0x2F, 0x1E, 0x1A, 0x2D, 0x0C, 0x22, 0x04]
str="GONDPHyGjPEKruv{{pj]X@rF"

最后和字符串作比较,写脚本爆破即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/python
#coding=utf-8
import string
byte_4420B0=[0x0D, 0x13, 0x17, 0x11, 0x02, 0x01, 0x20, 0x1D, 0x0C, 0x02,
0x19, 0x2F, 0x17, 0x2B, 0x24, 0x1F, 0x1E, 0x16, 0x09, 0x0F,
0x15, 0x27, 0x13, 0x26, 0x0A, 0x2F, 0x1E, 0x1A, 0x2D, 0x0C,
0x22, 0x04]
str="GONDPHyGjPEKruv{{pj]X@rF"
v8=''
flag=''
for i in range(len(str)):
for j in string.printable:
x=j
if(97<=ord(j)<=122):
x=chr(ord(j)-32)
if(65<=ord(j)<=90):
x=chr(ord(j)+32)
v4=chr(byte_4420B0[i]^(ord(x)^0x55)+72)
if(v4==str[i]):
flag+=j
print('EIS{'+flag+'}')
#EIS{wadx_tdgk_aihc_ihkn_pjlm}

debug

额,这题是一道典型的.NET题目,用对应的反编译程序打开,这里推荐dnSpy和ILSpy.具体的代码如下:

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
using System;
using System.Security.Cryptography;
using System.Text;

// Token: 0x02000003 RID: 3
internal class Class0
{
// Token: 0x06000008 RID: 8 RVA: 0x000021D0 File Offset: 0x000003D0
private static void Main(string[] args)
{
string b = null;
string value = string.Format("{0}", DateTime.Now.Hour + 1);
string a_ = "CreateByTenshine";
Class0.smethod_2(a_, Convert.ToInt32(value), ref b);
string a = Console.ReadLine();
if (a == b)
{
Console.WriteLine("u got it!");
Console.ReadKey(true);
}
else
{
Console.Write("wrong");
}
Console.ReadKey(true);
}

// Token: 0x06000005 RID: 5 RVA: 0x000020FD File Offset: 0x000002FD
private static int smethod_0(int A_0, int A_1)
{
return (new int[]
{
2,
3,
5,
7,
11,
13,
17,
19,
23,
29,
31,
37,
41,
43,
47,
53,
59,
61,
67,
71,
73,
79,
83,
89,
97,
101,
103,
107,
109,
113
})[A_1] ^ A_0;
}

// Token: 0x06000006 RID: 6 RVA: 0x00002120 File Offset: 0x00000320
private static string smethod_1(string A_0)
{
byte[] bytes = Encoding.ASCII.GetBytes(A_0);
return "flag{" + BitConverter.ToString(new MD5CryptoServiceProvider().ComputeHash(bytes)).Replace("-", "") + "}";
}

// Token: 0x06000007 RID: 7 RVA: 0x00002168 File Offset: 0x00000368
private static void smethod_2(string A_0, int A_1, ref string A_2)
{
int num = 0;
if (0 < A_0.Length)
{
do
{
char c = A_0[num];
int num2 = 1;
do
{
c = Convert.ToChar(Class0.smethod_0(Convert.ToInt32(c), num2));
num2++;
}
while (num2 < 15);
A_2 += c;
num++;
}
while (num < A_0.Length);
}
A_2 = Class0.smethod_1(A_2);
}
}

可以发现是将字符串与数组中的每个数都进行异或一次,然后将结果进行一次md5加密。解密代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/python
#coding=utf-8
import string
import hashlib
tmp=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113]
str="CreateByTenshine"
flag=''
for i in range(len(str)):
num=ord(str[i])
for j in range(1,15):
num=tmp[j]^num
flag+=chr(num)
print(flag)
m=hashlib.md5(flag.encode(encoding='UTF-8')).hexdigest()
print("flag{",m,"}")

看大佬的wp发现还有另一种更加简单的方式,利用dnSpy进行动态调试。

image-20201209165454077

我们先用dnSpy打开程序,并且找到相应的代码,在第16行,也就是比较处下断点,然后点击start,接着再点一次,此时会弹窗输入,随意输入一些字符,运行到断点处就会发现下面有对应的flag出现了。(好像提交的时候需要大写Flag)

1
flag{967DDDFBCD32C1F53527C221D9E40A0B}

hackme

还是按照老规矩丢进ida里面。找到关键函数

image-20201209193250964

中间的过程有很多复杂的步骤都是用不到的。去掉一些不需要的语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
v18 = i == 22;
v17 = 10;
do
{
v6 = sub_406D90((__int64)"%s", (__int64)input, v2, v3, v4, v5);
v16 = 0;
v13 = byte_6B4270[v6 % 22];
v12 = input[v6 % 22];
v11 = v6 % 22 + 1;
v15 = 0;
while ( v15 < v11 )
{
++v15;
v16 = 1828812941 * v16 + 12345;
}
if ( v13 != ((unsigned __int8)v16 ^ v12) )
v18 = 0;
--v17;
}
while ( v17 );
if ( v18 )
v9 = sub_407470((unsigned __int64)"Congras\n");
else
v9 = sub_407470((unsigned __int64)"Oh no!\n");

逻辑也比较容易看出来,只是那个sub_406D90那个函数一直不知道是干嘛的,动态调试之后发现每次返回的值v6都不一样,估计是一个随机数生成函数。具体的逻辑就是:输入22个字符,生成10个随机数作为序号取byte_6B4270数组中的字符进行验证,因为异或操作具有可逆性,所以只需要反过来写就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/python
#coding=utf-8
index = [0x5F,0xF2,0x5E,0x8B,0x4E,0x0E,0xA3,0xAA,0xC7,0x93,0x81,0x3D,0x5F,0x74,0xA3,0x09,
0x91,0x2B,0x49,0x28,0x93,0x67]

flag = ''

for i in range(22):
v6 = i + 1
v10 = 0
v11 = 0
while v10 < v6:
v10 = v10 + 1
v11 = 1828812941 * v11 + 12345
flag += chr((index[i]^v11)&0xff)
print (flag)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>
int main()
{
int byte_6B4270[] = {0x5F, 0xF2, 0x5E, 0x8B, 0x4E, 0x0E, 0xA3, 0xAA,
0xC7, 0x93, 0x81, 0x3D, 0x5F, 0x74, 0xA3, 0x09, 0x91, 0x2B,
0x49, 0x28, 0x93, 0x67};
for (int i = 0; i < 22; i++)
{
int v11 = i + 1;
int v15 = 0;
int v16 = 0;
while (v15 < v11)
{
v15++;
v16 = 1828812941 * v16 + 12345;
}
printf("%c", v16 ^ byte_6B4270[i]);
}
return 0;
}
1
flag{d826e6926098ef46}

babyRE

这道题一开始不会做,后来看别人的wp学到了不少东西。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __cdecl (int argc, const char **argv, const char **envp)
{
char s;
int v5; // [rsp+18h] [rbp-8h]
int i; // [rsp+1Ch] [rbp-4h]

for ( i = 0; i <= 181; ++i )
{
envp = (const char **)(*((unsigned __int8 *)judge + i) ^ 0xCu);
*((_BYTE *)judge + i) ^= 0xCu;
}
printf("Please input flag:", argv, envp);
__isoc99_scanf("%20s", &s);
v5 = strlen(&s);
if ( v5 == 14 && (unsigned int)judge(&s) )
puts("Right!");
else
puts("Wrong!");
return 0;
}

这道题逻辑是很简单的,判断输入的flag长度为14,判断函数就是judge。但是问题在于judge函数因为被用了SMC自解码,所以不能够直接看到汇编代码,对此有两个解决方法。

第一种是利用IDC脚本将代码直接在IDA中还原,关于这个代码不太明白是怎么写出来的,希望有大佬能指导一下。(IDC脚本可以直接复制到ida最下面然后回车就可以了)

1
2
3
4
5
6
7
auto start,i,a;
start = 0x600b00;
for(i=0;i<=181;i++)
{
a=Byte(start+i)^0xc;
PatchByte(start+i,a);
}

然后我们再去查看judge的函数对应的汇编代码。

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
 55                                      push    rbp
.data:0000000000600B01 48 89 E5 mov rbp, rsp
.data:0000000000600B04 48 89 7D D8 mov [rbp-28h], rdi
.data:0000000000600B08 C6 45 E0 66 mov byte ptr [rbp-20h], 66h
.data:0000000000600B08 judge endp ; sp-analysis failed
.data:0000000000600B08
.data:0000000000600B0C
.data:0000000000600B0C loc_600B0C:
.data:0000000000600B0C C6 45 E1 6D mov byte ptr [rbp-1Fh], 6Dh
.data:0000000000600B10 C6 45 E2 63 mov byte ptr [rbp-1Eh], 63h
.data:0000000000600B14 C6 45 E3 64 mov byte ptr [rbp-1Dh], 64h
.data:0000000000600B18 C6 45 E4 7F mov byte ptr [rbp-1Ch], 7Fh
.data:0000000000600B1C C6 45 E5 6B mov byte ptr [rbp-1Bh], 6Bh
.data:0000000000600B20 C6 45 E6 37 mov byte ptr [rbp-1Ah], 37h
.data:0000000000600B24 C6 45 E7 64 mov byte ptr [rbp-19h], 64h
.data:0000000000600B28 C6 45 E8 3B mov byte ptr [rbp-18h], 3Bh
.data:0000000000600B2C C6 45 E9 56 mov byte ptr [rbp-17h], 56h
.data:0000000000600B30 C6 45 EA 60 mov byte ptr [rbp-16h], 60h
.data:0000000000600B34 C6 45 EB 3B mov byte ptr [rbp-15h], 3Bh
.data:0000000000600B38 C6 45 EC 6E mov byte ptr [rbp-14h], 6Eh
.data:0000000000600B3C C6 45 ED 70 mov byte ptr [rbp-13h], 70h
.data:0000000000600B40 C7 45 FC 00 00 00 00 mov dword ptr [rbp-4], 0
.data:0000000000600B47 EB 28 jmp short loc_600B71
.data:0000000000600B49 ; ---------------------------------------------------------------------------
.data:0000000000600B49
.data:0000000000600B49 loc_600B49: ; CODE XREF: .data:0000000000600B75↓j
.data:0000000000600B49 8B 45 FC mov eax, [rbp-4]
.data:0000000000600B4C 48 63 D0 movsxd rdx, eax
.data:0000000000600B4F 48 8B 45 D8 mov rax, [rbp-28h]
.data:0000000000600B53 48 01 D0 add rax, rdx
.data:0000000000600B56 8B 55 FC mov edx, [rbp-4]
.data:0000000000600B59 48 63 CA movsxd rcx, edx
.data:0000000000600B5C 48 8B 55 D8 mov rdx, [rbp-28h]
.data:0000000000600B60 48 01 CA add rdx, rcx
.data:0000000000600B63 0F B6 12 movzx edx, byte ptr [rdx]
.data:0000000000600B66 8B 4D FC mov ecx, [rbp-4]
.data:0000000000600B69 31 CA xor edx, ecx
.data:0000000000600B6B 88 10 mov [rax], dl
.data:0000000000600B6D 83 45 FC 01 add dword ptr [rbp-4], 1
.data:0000000000600B71
.data:0000000000600B71 loc_600B71: ; CODE XREF: .data:0000000000600B47↑j
.data:0000000000600B71 83 7D FC 0D cmp dword ptr [rbp-4], 13
.data:0000000000600B75 7E D2 jle short loc_600B49
.data:0000000000600B77 C7 45 FC 00 00 00 00 mov dword ptr [rbp-4], 0
.data:0000000000600B7E EB 29 jmp short loc_600BA9
.data:0000000000600B80 ; ---------------------------------------------------------------------------
.data:0000000000600B80
.data:0000000000600B80 loc_600B80: ; CODE XREF: .data:0000000000600BAD↓j
.data:0000000000600B80 8B 45 FC mov eax, [rbp-4]
.data:0000000000600B83 48 63 D0 movsxd rdx, eax
.data:0000000000600B86 48 8B 45 D8 mov rax, [rbp-28h]
.data:0000000000600B8A 48 01 D0 add rax, rdx
.data:0000000000600B8D 0F B6 10 movzx edx, byte ptr [rax]
.data:0000000000600B90 8B 45 FC mov eax, [rbp-4]
.data:0000000000600B93 48 98 cdqe
.data:0000000000600B95 0F B6 44 05 E0 movzx eax, byte ptr [rbp+rax-20h]
.data:0000000000600B9A 38 C2 cmp dl, al
.data:0000000000600B9C 74 07 jz short loc_600BA5
.data:0000000000600B9E B8 00 00 00 00 mov eax, 0
.data:0000000000600BA3 EB 0F jmp short loc_600BB4
.data:0000000000600BA5 ; ---------------------------------------------------------------------------
.data:0000000000600BA5
.data:0000000000600BA5 loc_600BA5: ; CODE XREF: .data:0000000000600B9C↑j
.data:0000000000600BA5 83 45 FC 01 add dword ptr [rbp-4], 1
.data:0000000000600BA9
.data:0000000000600BA9 loc_600BA9: ; CODE XREF: .data:0000000000600B7E↑j
.data:0000000000600BA9 83 7D FC 0D cmp dword ptr [rbp-4], 0Dh
.data:0000000000600BAD 7E D1 jle short loc_600B80
.data:0000000000600BAF B8 01 00 00 00 mov eax, 1
.data:0000000000600BB4
.data:0000000000600BB4 loc_600BB4: ; CODE XREF: .data:0000000000600BA3↑j
.data:0000000000600BB4 5D pop rbp
.data:0000000000600BB5 C3 retn
.data:0000000000600BB5 _data ends

另一种方法是通过ida的动态调试。如何使用ida动态调试可以看我之前的博客http://smalltime.work/2020/12/03/ida%E5%8A%A8%E6%80%81%E8%B0%83%E8%AF%95elf-%E6%97%A0%E5%9D%91%E8%AF%A6%E7%BB%86/。

我们可以看到从[rbp-13h]开始一直到[rbp-20h]一共有14条赋值语句。

image-20201210111123893

猜测与后面的异或有关系。接下来我们下一个断点,并且运行程序然后进行单步调试。

image-20201210111456709

Debugger—>Debugger Windows—>watch view,然后右键add watch在里面输入(int)ecx,我们就可以看到ecx的值了。经过单步调试可以发现ecx值到0x0D的时候就跳出循环了,正好对应了前面的数组长度。

image-20201210111846718

因此可以猜测是将数组与ecx的值进行异或运算。代码如下:

1
2
3
4
5
6
7
#!/usr/bin/python
#coding=utf-8
num=[0x66, 0x6D, 0x63, 0x64, 0x7F, 0x6B, 0x37, 0x64, 0x3B, 0x56, 0x60, 0x3B, 0x6E, 0x70]
flag=''
for i in range(len(num)):
flag+=chr(num[i]^i)
print(flag)
1
flag{n1c3_j0b}

EASYHOOK

这道题其实没有太搞懂,看题目是hook,所以找到了sub_401220函数然后一路向下找,最后找到了sub_401000函数,逻辑倒是还比较好懂。

image-20201210195704469

看了其他人的wp之后写出了对应的解密代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/python
#coding=utf-8
byte_40A030=[0x61, 0x6A, 0x79, 0x67, 0x6B, 0x46, 0x6D, 0x2E, 0x7F, 0x5F,
0x7E, 0x2D, 0x53, 0x56, 0x7B, 0x38, 0x6D, 0x4C, 0x6E]
print(len(byte_40A030))
byte_40A030[18]=byte_40A030[18]^0x13
for i in range(17,-1,-1):
num = byte_40A030[i]^i
if(i%2):
byte_40A030[i]=num+i
else:
byte_40A030[i+2]=num
print(''.join(map(chr,byte_40A030)))
1
flag{Ho0k_w1th_Fun}

EasyRE

这道题一开始是瞎撞出来的,整体还是比较简单。找到关键的函数sub_401080,进去之后如下:

image-20201210223819844

重点是3个我框起来的部分,明确的是input的长度是24,需要理解和注意的地方是24行v2 = (char *)&v8 + 7,由定义部分我们可以看到input的地址是ebp-24h(ebp-36),而v8的地址是ebp-14h(ebp-20),24行的语句将v8的地址+7处的字符赋值给了v2。根据栈的性质,由低地址向高地址生长。假如此时我的输入是abcdefghijklmnopqrstuvwx,那么在栈中的情况就应该是:

高地址 ebp-13 x
\ ebp-14 w
\
\ ebp-20 q
\
ebp-35 b
低地址 ebp-36 a

也就是说第24行的语句就是将输入的字符串取逆序的过程,最后一个框中的内容是将字符串逐个字符取出+1然后^6。最后与unk_402124中的字符串(xIrCj~<r|2tWsv3PtI\x7Fzndka)做对比。过程比较简单可以直接逆算法,代码如下:

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/python
#coding=utf-8
unk_402124=[ 0x78, 0x49, 0x72, 0x43, 0x6A, 0x7E, 0x3C, 0x72, 0x7C, 0x32,
0x74, 0x57, 0x73, 0x76, 0x33, 0x50, 0x74, 0x49, 0x7F, 0x7A,
0x6E, 0x64, 0x6B, 0x61]
print(len(unk_402124))
flag=''
for i in range(len(unk_402124)):
flag+=chr((unk_402124[i]^6)-1)
print(flag[::-1])
1
flag{xNqU4otPq3ys9wkDsN}

babymips

ida进去之后会发现没有办法反编译F5,同时汇编代码也不太能读懂,因为这是另一种 架构 mips 。ida有相应的插件例如jeb等,安装好之后就能看到伪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
#include <stdio.h>
#include <string.h>
char *check1="Q|j{g";
char *check2= "\x52\xfd\x16\xa4\x89\xbd\x92\x80\x13\x41\x54\xa0\x8d\x45\x18\x81\xde\xfc\x95\xf0\x16\x79\x1a\x15\x5b\x75\x1f";
void check(char *s){
int i;
for(i=5;i<strlen(s);i++){
if(i%2)
s[i]=(s[i]>>2)|((s[i]<<6)&0xff);
else
s[i]=((s[i]<<2)&0xff)|(s[i]>>6);
}
if(!strncmp(&s[5],check2,27))
printf("Right!\n");
else
printf("Wrong!\n");
}
void main(){
char s[33];
int i;
printf("Give me your flag:");
scanf("%32s",s);

for(i=0;i<32;i++)
s[i]^=(32-i);
if(!strncmp(s,check1,5))
check(s);
else
printf("Wrong\n");
}

其中的check2可以在ida中找到相应的数组。

1
0x51, 0x7C, 0x6A, 0x7B, 0x67,0x52, 0xFD, 0x16, 0xA4, 0x89, 0xBD, 0x92, 0x80, 0x13, 0x41, 0x54, 0xA0, 0x8D, 0x45, 0x18, 0x81, 0xDE, 0xFC, 0x95, 0xF0, 0x16, 0x79, 0x1A, 0x15, 0x5B, 0x75, 0x1F, 0x00

根据源码写出相应的exp即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/python
#coding=utf-8
ida_chars=[ 0x51, 0x7C, 0x6A, 0x7B, 0x67,
0x52, 0xFD, 0x16, 0xA4, 0x89, 0xBD, 0x92, 0x80, 0x13, 0x41,
0x54, 0xA0, 0x8D, 0x45, 0x18, 0x81, 0xDE, 0xFC, 0x95, 0xF0,
0x16, 0x79, 0x1A, 0x15, 0x5B, 0x75, 0x1F, 0x00]
first= "Q|j{g"
flag=''
for i in range(5,len(ida_chars)):
if(i%2):
ida_chars[i]=(ida_chars[i]>>6)|((ida_chars[i]<<2))
else:
ida_chars[i]=((ida_chars[i]>>2))|(ida_chars[i]<<6)
for i in range(32):
num = hex(ida_chars[i]^(32-i))
num = int(num,16) & 0xff
print(hex(num),end=' ')
flag+=chr(num)
print(flag)
1
qctf{ReA11y_4_B@89_mlp5_4_XmAn_}

reverse-for-the-holy-grail-350

额,这道题看着看着看晕了,对我来说有点难,整体的流程倒是很容易就能够找到。主要的逻辑函数在stringMod中:

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
__int64 __fastcall stringMod(__int64 *a1)
{
__int64 v1; // r9
__int64 v2; // r10
__int64 v3; // rcx
int v4; // er8
int *v5; // rdi
int *v6; // rsi
int v7; // ecx
int v8; // er9
int v9; // er10
unsigned int v10; // eax
int v11; // esi
int v12; // esi
int v14[24]; // [rsp+0h] [rbp-60h]

memset(v14, 0, 0x48uLL);
v1 = a1[1];
if ( v1 )
{
v2 = *a1;
v3 = 0LL;
v4 = 0;
do
{
v12 = *(v2 + v3);
v14[v3] = v12;
if ( 3 * (v3 / 3) == v3 && v12 != firstchar[v3 / 3] )
v4 = -1;//flag的3的倍数位置上的字符等于firstchar中的值
++v3; //也就是说flag中的0,3,6,9,12,15,18位
}
while ( v3 != v1 );
}
else
{
v4 = 0;
}
v5 = v14;
v6 = v14;
v7 = 0x29a;
do
{
*v6 = v7 ^ *v6; // 循环异或v7,v7初始为0x29,每次异或后加上v7%5
v7 += v7 % 5;
++v6;
}
while ( &v14[18] != v6 );
v8 = 1;
v9 = 0;
v10 = 1;
v11 = 0;
do
{
if ( v11 == 2 )
{
if ( *v5 != thirdchar[v9] )//因为v11==2时才会判定thirdchar中的字符,所以对应flag中2,5,8,11,14,17位
v4 = -1;
if ( v10 % *v5 != masterArray[v9] )// temp[0]*temp[1]%temp[2] 当与masterArray中的字符进行比对时,temp[0]中的字符为flag的1,4,7,10,13,16位与v7异或的结果,temp[1]的值为第一部分flag与v7异或的值,temp[2]的值为thirdchar中的字符。
v4 = -1;
++v9;
v10 = 1;
v11 = 0;
}
else
{
v10 *= *v5; //因为v11=2时才进入if中,所以每一轮do while要执行两次else中的语句,因为第一次v10=1,所以执行两次语句代表着v10中存储的是异或后的字符的乘积,
// 0 tmp=temp[0]
// 1 tmp=temp[0]*temp[1]
if ( ++v11 == 3 )
v11 = 0;
}
++v8;
++v5;
}
while ( v8 != 19 ); // 循环18次
return (v7 * v4);
}

根据以上的分析写出对应的解密脚本即可:

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
#!/usr/bin/env python3
#coding=utf-8
firstchar = [0x41, 0x69, 0x6E, 0x45, 0x6F, 0x61]
thirdchar = [0x2ef, 0x2c4, 0x2dc, 0x2c7, 0x2de, 0x2fc]
masterarray = [0x1d7, 0xc, 0x244, 0x25e, 0x93, 0x6c]
v7_num = []
v7=0x29a
flag1=''
flag2=''
flag3=''
for i in range(6):
flag1+=chr(firstchar[i])
for i in range(18):
v7_num.append(v7)
v7 += (v7%5)
temp=0
for i in range(2,20,3):
flag2+=chr(v7_num[i]^thirdchar[temp])
temp+=1

xor_num=[]

temp=0
for i in range(0,18,3):
xor_num.append(ord(flag1[temp])^v7_num[i])
temp+=1

temp=0
for i in range(1,19,3):
for j in range(32,128):
num=(v7_num[i]^j)*xor_num[temp]%thirdchar[temp]
if(num==masterarray[temp]):
flag3+=chr(j)
temp+=1
break
print(flag1)
print(flag3)
print(flag2)

flag=''
for i in range(6):
flag+=flag1[i]
flag+=flag3[i]
flag+=flag2[i]

print('tuctf{'+flag+'}')

我的代码很烂,因此去搜了一下其他大佬的wp写的很简洁,可以参考一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/python3
#coding:utf-8

firstchar = [0x41, 0x69, 0x6e, 0x45, 0x6f, 0x61]
thirdchar = [0x2ef, 0x2c4, 0x2dc, 0x2c7, 0x2de, 0x2fc]
masterarray = [0x1d7, 0xc, 0x244, 0x25e, 0x93, 0x6c]

xor_number=0x29a
xor_array=[]
for i in range(18):
xor_array.append(xor_number)
xor_number+=xor_number%5

flag="tuctf{"
for i in range(6):
one=firstchar[i]
three=thirdchar[i] ^ xor_array[(i*3) + 2]
for j in range(256):
if masterarray[i]==(j^xor_array[i*3+1])*(one^xor_array[i*3])%thirdchar[i]:
flag+=chr(one)+chr(j)+chr(three)
break
flag+="}"
print(flag)
1
tuctf{AfricanOrEuropean?}

serial-150

secret-string-400

又发现了一个新的类型题目,下载题目之后发现是.gz文件,在Linux下解压之后得到两个文件task.html和machine.js,熟悉web流程的都知道这是配套的。

image-20201218220942298

这个html文件是可以直接打开成网页的,配套的js文件里面过于复杂看不太懂,去网上找了一下相关的wp,大佬给出了一个解决办法,可以修改js代码里的run函数输出相应的log。

image-20201219154213967

这样F12里面console会输出相应的调试代码。

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
new 0pcode1,z,alert(1);//êObjectÿ	ÿÿÿ
alert(2);// êxÿ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!');}//ÿ ÿÿÿ
alert(3);//êxÿ ÿÿÿ
alert(4);//
alert(5);//
alert(6);//
alert(7);//

里面给了一个数组ex和一个字符串nonce=”groke”,还有的操作就是异或了,不管先将数组中的值与字符串异或输出试试。

1
2
3
4
5
6
7
8
#!/usr/bin/env python3
# coding=utf-8
num = [1, 30, 14, 12, 69, 14, 1, 85, 75, 50, 40, 37, 48, 24, 10, 56, 55, 46, 56, 60]
s1 = "groke"
flag = ''
for i in range(len(num)):
flag += chr(num[i] ^ ord(s1[i % 5]))
print(flag)
1
flag is: WOW_so_EASY

crazy

这道题本身不难,怪自己心浮气躁没有细心的好好分析。64位的elf程序,拖进ida中查找字符。

image-20201229222344498

发现了可惜字符跟进去看看。

image-20201229222553590

可以看到是C++的程序,所以整体看上去比较凌乱,重点在于27行、46行、56行和57行。

27行对应的是cin输入,46行跟进去看看。

image-20201229222706253

发现将input的值存入了v23+16的位置和v23+48的位置,两者的差值为32,并且在v23+80的位置存入了一个字符串,长度也为32位。

56行的代码如下

image-20201229222938423

会发现3个关键的地方,第一个是判定字符串的长度为32,这里的额this+16代表的就是之前的v23,之所以+16是因为刚才input的值是从v23+16的地方开始存储的。接着将v23+16位置的值依次进行异或和加法运算,下面同样如此。最后来看57行

image-20201229223210679

首先for循环中将this+80位置开始的值存入v1中,前面已经说过v23+80的值存入的是一个字符串。接着if语句来判断v23+16的值与v23+80中的值是否相等即可。

那么分析之后我们的流程和逻辑就清晰了,我们输入的字符串长度为32,每个字符会经过两次异或和加法运算,得到的结果与系统中的字符串匹配。脚本的结果输入到程序中拿flag。

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env python3
# coding=utf-8
import string
str = "327a6c4304ad5938eaf0efb6cc3e53dc"
flag = ''
for i in range(len(str)):
num = (ord(str[i]) - 11) ^ 0x13
num = (num - 23) ^ 0x50
flag += chr(num)
print(flag)
1
flag{tMx~qdstOs~crvtwb~aOba}qddtbrtcd}

运行脚本得到的值放到程序中跑一下就能得到flag。

Windows_Reverse1

逻辑及其清晰,但是很怪的题目,给自己提醒不要过分依赖于IDA的F5,多看汇编多用OD调试。

image-20201230162008366

将我们的输入放入sub_401000函数中但是最后的check处却是用的check_buf做比较,先进到函数中看看再说。

image-20201230162132734

input-v1的值赋给v4,在后面的do while循环中将byte_402FF8数组中的值以v1[v4]为下标赋给v1。

1.其中input是通过函数传参的方式,v1是通过压栈的方式传递的参数。

2.最迷惑的地方是v1[v4]这个地方,注意到v1[v4]其实也可以写成v1+v4,在刚开始的时候v1+v4等于input,随着v1的递增v1[v4]也会遍历input数组中的各个元素的地址。

3.而地址又怎么能作为数组的索引呢? 这里就是 IDA 背锅了, 换言之, 做题还是不能太依赖于反编译后的伪代码. 查看了反汇编代码后, 发现其实是将input字符串中的字符本身作为byte_402FF8的索引, 取值后放入v1数组中。

4.查看位于byte_402FF8的值。

image-20201230164554556

这一堆问号更是懵了,这里就要考验你思维的连贯性了. 要知道ASCII编码表里的可视字符就得是32往后了, 所以, byte_402FF8里凡是位于32以前的数统统都是迷惑项. 不会被索引到的. 往下看, 咦? 这一坨是什么. 再一算地址的差值. 没错, 这一坨就是与a1数组中的各个元素进行替换的字符 !

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
hexData=[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xAE, 0x1D, 0x3E, 0x07, 0x51, 0xE2, 0xC1,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00,
0x7E, 0x7D, 0x7C, 0x7B, 0x7A, 0x79, 0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70, 0x6F,
0x6E, 0x6D, 0x6C, 0x6B, 0x6A, 0x69, 0x68, 0x67, 0x66, 0x65, 0x64, 0x63, 0x62, 0x61, 0x60, 0x5F,
0x5E, 0x5D, 0x5C, 0x5B, 0x5A, 0x59, 0x58, 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4F,
0x4E, 0x4D, 0x4C, 0x4B, 0x4A, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3F,
0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2F,
0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x00,
0x01, 0x00, 0x00, 0x00, 0x90, 0x19, 0x9F, 0x00, 0xA8, 0x2C, 0x9F ]
str='DDCTF{reverseME}'
flag=''
for i in range(len(str)):
flag+=chr(hexData[ord(str[i])])
print('flag{'+flag+'}')

另外在看其他大佬的wp的时候发现可以用OD调试,我自己去调试之后发现根据自己输入的值会得到对应的值输入,比如:输入123456得到mlkjih,并且调换输入顺序之后映射跟着调换。查了一下ASCII表发现,这些对应的值的特点是相加之和均为158,也就是说我们输入的值被158减去之后就是输出值,输出值要与字符串DDCTF{reverseME}相匹配即可。

1
2
3
4
5
6
7
#!/usr/bin/env python3
# coding=utf-8
s3 = "DDCTF{reverseME}"
flag = ''
for i in range(len(s3)):
flag += chr(158 - ord(s3[i]))
print(flag)

这个代码就很简洁了,虽然过程并不严谨,仅供参考吧,放出一个大佬的WPhttps://www.52pojie.cn/thread-1180981-1-1.html

CATALOG
  1. 1. Guess-the-Number
  2. 2. Shuffle1
  3. 3. re-for-50-plz-50
  4. 4. dmd-50
  5. 5. parallel-comparator-200
  6. 6. secret-galaxy-300
  7. 7. srm-50
  8. 8. simple-check-100
  9. 9. Mysterious
  10. 10. Newbie_calculations
  11. 11. re1-100
  12. 12. answer_to_everything
  13. 13. elrond32
  14. 14. gametime
  15. 15. tt3441810
  16. 16. re2-cpp-is-awesome
  17. 17. re4-unvm
  18. 18. 流浪者
  19. 19. 666
  20. 20. SignIn
  21. 21. easy_Maze
  22. 22. ReverseMe-120
  23. 23. Reversing-x64Elf-100
  24. 24. IgniteMe
  25. 25. debug
  26. 26. hackme
  27. 27. babyRE
  28. 28. EASYHOOK
  29. 29. EasyRE
  30. 30. babymips
  31. 31. reverse-for-the-holy-grail-350
  32. 32. serial-150
  33. 33. secret-string-400
  34. 34. crazy
  35. 35. Windows_Reverse1