ACTF 2021 reverse复现

Posted by inhann on 2021-03-20
Page views

[toc]

辣鸡复现一下ACTF 的逆向题,学一波逆向的基本操作

0x00 How2b64decode

基本信息

image-20210219101620924

  • pe
  • 64bit
  • 好像没有壳

跑跑看

image-20210219102015521

应该也是要输入个东西,那东西就是flag

给了提示,应该有用到base64

IDA看看

没有直接给main函数,看来要自己找

image-20210219102246366

应该是这个

改个名字

输入的东西经过一个 sub_401550 函数处理,成了 QUNURntCYXNlNjRfaXNfYV90cmFkaW9uYWxfY29kaW5nX2FsZ29yaXRobX0= 这样才算输入成功

很明显这段字符时base64加密了

解码看看

image-20210219102724425

直接得到了flag

那就不需要继续分析了

1
ACTF{Base64_is_a_tradional_coding_algorithm}

0x01 How2Reverse

基本信息

image-20210219102924496

  • pe
  • 64bit
  • 好像还是没有壳

跑跑看

image-20210219103014373

应该真的是入门吧

IDA看看

image-20210219103122918

直接找到flag

1
ACTF{Welcome_to_Reversing_World}

0x02 How2debug

基本信息

image-20210219103302536

  • pe
  • 64bit
  • 没有壳

跑一跑

image-20210219103351116

IDA看看

main 还是得自己找,改个名

image-20210219103537303

用到了函数指针

调用了 unk_403020 指向的函数,如果返回的不是0,那就成功了

函数的参数 v1,就是输入的东西

调试看看,看看那个函数里面到底干了什么,然后就能确定要输入怎样的v1了

image-20210315202330869

按一下 F5,得到 反编译的 c 代码

image-20210315202353335

双击 unk_403020

image-20210315202430588

只有 函数才能被转成 c 代码

按一下 p ,以下的指令就被转成函数的形式

image-20210315205425680

从而可以 F5 看C 了

image-20210315205445061

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
__int64 __fastcall sub_403020(__int64 a1)
{
char v2[80]; // [rsp+0h] [rbp-90h]
char v3[52]; // [rsp+50h] [rbp-40h]
int k; // [rsp+84h] [rbp-Ch]
int j; // [rsp+88h] [rbp-8h]
int i; // [rsp+8Ch] [rbp-4h]

v3[0] = 32;
v3[1] = 34;
v3[2] = 53;
v3[3] = 39;
v3[4] = 26;
v3[5] = 50;
v3[6] = 44;
v3[7] = 34;
v3[8] = 62;
v3[9] = 49;
v3[10] = 19;
v3[11] = 14;
v3[12] = 21;
v3[13] = 4;
v3[14] = 2;
v3[15] = 21;
v3[16] = 62;
v3[17] = 47;
v3[18] = 4;
v3[19] = 4;
v3[20] = 5;
v3[21] = 62;
v3[22] = 56;
v3[23] = 14;
v3[24] = 20;
v3[25] = 19;
v3[26] = 62;
v3[27] = 37;
v3[28] = 4;
v3[29] = 3;
v3[30] = 20;
v3[31] = 6;
v3[32] = 62;
v3[33] = 50;
v3[34] = 10;
v3[35] = 8;
v3[36] = 13;
v3[37] = 13;
v3[38] = 28;
for ( i = 0; i <= 79; ++i )
v2[i] = 0;
for ( j = 0; j <= 79; ++j )
v2[j] = *(_BYTE *)(a1 + j);
for ( k = 0; (unsigned __int64)k <= 0x26; ++k )
{
v2[k] ^= 0x61u;
if ( v2[k] != v3[k] )
return 0i64;
}
return 1i64;
}

分析一波

输入的东西,全给了 v2,而 v2[k] ^= 0x61 后,要和 v3[k] 相等

写个脚本,就能把 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
v3 = [i for i in range(39)]
v3[0] = 32
v3[1] = 34
v3[2] = 53
v3[3] = 39
v3[4] = 26
v3[5] = 50
v3[6] = 44
v3[7] = 34
v3[8] = 62
v3[9] = 49
v3[10] = 19
v3[11] = 14
v3[12] = 21
v3[13] = 4
v3[14] = 2
v3[15] = 21
v3[16] = 62
v3[17] = 47
v3[18] = 4
v3[19] = 4
v3[20] = 5
v3[21] = 62
v3[22] = 56
v3[23] = 14
v3[24] = 20
v3[25] = 19
v3[26] = 62
v3[27] = 37
v3[28] = 4
v3[29] = 3
v3[30] = 20
v3[31] = 6
v3[32] = 62
v3[33] = 50
v3[34] = 10
v3[35] = 8
v3[36] = 13
v3[37] = 13
v3[38] = 28

for i in range(39):
v3[i]=chr(v3[i]^0x61)

"".join(v3)
1
ACTF{SMC_Protect_Need_Your_Debug_Skill}

还可以用 idapython

image-20210316002234813

从 0x40302F 开始的指令的第二个 operand 就是需要的 v3[x]

1
2
3
4
5
6
7
8
9
10
11
from idc import *

start = 0x40302F
end = 0x4030CB
r = []
i = start
while i <= end:
r.append(get_operand_value(i, 1))
i = find_code(i,SEARCH_DOWN)

print("".join([chr(i^0x61) for i in r]))

image-20210316002635648

1
ACTF{SMC_Protect_Need_Your_Debug_Skill}

0x03 How2idaMacroCode

基本信息

image-20210317110310502

  • 64bit

跑跑看

image-20210317110508947

老样子,直接输入 flag

IDA看看

看看main

image-20210317110703875

然后看到这里

image-20210317141018486

貌似是代码混淆

稍微整理一下代码

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
__int64 __fastcall sub_4015F9(const char *a1)
{
size_t v1; // rax
size_t v2; // rax
size_t v3; // rax
int v5[14]; // [rsp+20h] [rbp-50h]
int v7; // [rsp+5Ch] [rbp-14h]
void *v8; // [rsp+60h] [rbp-10h]
unsigned __int8 v9; // [rsp+6Bh] [rbp-5h]
unsigned int i; // [rsp+6Ch] [rbp-4h]

v9 = 1;
v5[0] = 725299258;
v5[1] = 395038;
v5[2] = 70649600;
v5[3] = 4609;
v5[4] = 103890455;
v5[5] = 169607175;
v5[6] = 118699544;
v5[7] = 809372672;
v5[8] = 219882537;
v5[9] = 67857;
v5[10] = 570956565;
v5[11] = 1684757334;
v1 = strlen(a1);
v8 = malloc(v1 + 200);
v7 = (strlen(a1) + 3) >> 2;
v2 = strlen(a1);
memset(v8, 0, v2 + 4);
v3 = strlen(a1);
memcpy(v8, a1, v3);
for ( i = 0; v7 - 1 > i; ++i )
*((_DWORD *)v8 + (int)i) ^= *((_DWORD *)v8 + (int)i + 1);
*((_DWORD *)v8 + (int)i) ^= 0x19260817u;
for ( i = 0; (unsigned __int64)(int)i <= 0xB; ++i )
{
if ( *((_DWORD *)v8 + (int)i) != v5[i] )
return 0i64;
}
return v9;
}

直接写脚本解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
v5 = [i for i in range(13)]
v5[0] = 725299258
v5[1] = 395038
v5[2] = 70649600
v5[3] = 4609
v5[4] = 103890455
v5[5] = 169607175
v5[6] = 118699544
v5[7] = 809372672
v5[8] = 219882537
v5[9] = 67857
v5[10] = 570956565
v5[11] = 1684757334
v5[12] = 0x19260817
for i in range(11,-1,-1):
v5[i] = v5[i] ^ v5[i+1]

v5 = v5[0:-1]
v5 = map(hex,v5)
v5 = map(lambda x:x[2:],v5)
v5 = list(v5)
v5.reverse()
bytes.fromhex("".join(v5)).decode()[::-1]

image-20210317153307653

1
ACTF{sometimes_ida_is_not_reliable_READ_THE_ASM}

0x03 How2idaXref

基本信息

image-20210317153527105

  • 64bit

跑跑

image-20210317153626572

直接输入个东西

IDA看看

找到 有关的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int check()
{
int result; // eax
char Str[520]; // [rsp+20h] [rbp-60h] BYREF
int v2; // [rsp+228h] [rbp+1A8h]
int i; // [rsp+22Ch] [rbp+1ACh]

puts("Hello,Please Input Your Passwd");
scanf("%200s", Str);
v2 = strlen(Str);
for ( i = 0; i < v2; ++i )
Str[i] ^= i;
if ( !memcmp(Str, &unk_403020, 0x3Aui64) )
result = puts("success");
else
result = puts("failed");
return result;
}

输入的东西 Str 要和 unk_403020 中的内容相等

image-20210317154508662

用 idapython 写个脚本

1
2
3
4
5
6
import idc
start = 0x403020
for i in range(0x403058-start+1):
d = idc.GetDisasm(start + i)
d = int(d[4:6],16) ^ i
print(chr(d),end="")
1
ACTF{how_do_you_find_this_attribute_constructor_function}

0x04 How2idapython

基本信息

image-20210317161033157

  • 64bit

跑跑看

image-20210317161108315

也是输入flag

IDA看看

main 函数

image-20210317161203899

分析一波函数 sub_4015D4

Buf1 里面的东西要和 unk_405020 里的东西相等

image-20210317171733196

看看汇编,从图中位置开始,就一直有规律地重复同一模式的动作

直接写个 idapython 脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import idc
data = list(idc.get_bytes(0x405020,0x40504E-0x405020+1))

start_addr = 0x40162a
end_addr = 0x40354b
i = start_addr

while i<=end_addr:
x = idc.get_operand_value(i,1) if "+" in idc.print_operand(i,1) else 0
i = idc.find_code(i,idc.SEARCH_DOWN)
n = idc.get_operand_value(i,1)
data[x] = (data[x] - n) & 0xff
i = idc.find_code(i,idc.SEARCH_DOWN)
i = idc.find_code(i,idc.SEARCH_DOWN)

r = "".join(list(map(chr,data)))
print(r)
1
ACTF{idapython_is_a_efftive_tool_for_reversing}

0x05 Threads

基本信息

image-20210317202931663

  • 64bit

跑一跑

image-20210317203106664

提示是多线程的程序

IDA看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
int __cdecl main(int argc, const char **argv, const char **envp)
{
void *v3; // rbx
__int64 v4; // rbx
std::thread *v5; // rbx
usr_input *v6; // rbx
std::thread *Block; // [rsp+28h] [rbp-58h]

_main(argc, argv, envp);
v3 = (void *)operator new(0x30ui64);
memset(v3, 0, 0x30ui64);
usr_input::usr_input((usr_input *)v3);
usr = (usr_input *)v3;
usr_input::lock((usr_input *)v3);
v4 = operator new(8ui64);
std::thread::thread<void (&)(void)>(v4, reader_thread_func);
Block = (std::thread *)v4;
v5 = (std::thread *)operator new(8ui64);
std::thread::thread<void (&)(void)>(v5, checker_thread_func);
std::operator<<<std::char_traits<char>>(
refptr__ZSt4cout,
"MultThread Program~\nCould you reverse it?\nPlease Input:\n");
usr_input::unlock(usr);
std::thread::join(Block);
std::thread::join(v5);
if ( Block )
{
std::thread::~thread(Block);
operator delete(Block);
}
if ( v5 )
{
std::thread::~thread(v5);
operator delete(v5);
}
usr_input::lock(usr);
if ( (unsigned __int8)usr_input::get_mark(usr) )
std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "success!\n");
else
std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "fail\n");
system("timeout 5");
usr_input::unlock(usr);
v6 = usr;
if ( usr )
{
usr_input::~usr_input(usr);
operator delete(v6);
}
return 0;
}

不出所料,c++ 写的

image-20210317225433607

找到一个 reader_thread_func 和一个 checker_thread_func

看看 checker

比较重要的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
v1 = (const char *)usr_input::get_c_str(usr);
strcpy(Destination, v1);
for ( i = 0; i < *v7; ++i )
{
Destination[i] ^= 45 * i + 9;
Destination[i] = ~Destination[i];
}
for ( j = 0; j < *v7; ++j )
{
if ( Destination[j] != v2[j] )
usr_input::set_mark(usr, 0);
}
return usr_input::unlock(usr);

而main 函数中

1
2
if ( (unsigned __int8)usr_input::get_mark(usr) )
std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "success!\n");

所以至少不能触发 usr_input::set_mark(usr, 0);

直接写idapython脚本

1
2
3
4
5
6
7
8
9
10
11
12
import idc
start_addr = 0x401625
end_addr = 0x4016B5
i = start_addr
j = 0
while i<=end_addr:
n = idc.get_operand_value(i,1)
n = ~n & 0xff
n = (n ^ (45 * j + 9)) & 0xff
print(chr(n),end="")
i = idc.find_code(i,idc.SEARCH_DOWN)
j += 1
1
ACTF{mult_thread_is_hard_to_control}

0x06 pyde

基本信息

image-20210317233603308

  • 64bit

跑跑看

image-20210317233704262

直接让输入东西,啥提示也没有

直接 IDA打开,看了看 strings ,应该是python 写的

现在就反编译

先从 main.exe 中提取模块

1
python pyinstxtractor.py main.exe

找找看关键模块

找到了 struct 模块,这是内置模块

找到了个 main 模块

用 winhex 看看

这是 struct 的

image-20210318221447334

1
2
420D0D0A000000007079693010010000
E3000000000000000000000000080000

看看main 的

image-20210318221640209

里面有 flag 这样的字样,可以确定这个文件来自 main.py

但是这个文件缺少了一行内容

在文件开头添上

1
420D0D0A000000007079693010010000

image-20210318221926165

现在可以了,保存下来,文件名改为 main.pyc

1
uncompyle6.exe -o main.py .\main.pyc

得到源码

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
# uncompyle6 version 3.7.4
# Python bytecode 3.7 (3394)
# Decompiled from: Python 3.8.1 (default, Jan 8 2020, 15:55:49) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: main.py
# Compiled at: 1995-09-28 00:18:56
# Size of source mod 2**32: 272 bytes
from Crypto.Cipher import AES
import base64

def add_16(par):
if type(par) == str:
par = par.encode()
while len(par) % 16 != 0:
par += '\x00'

return par


flag = input()
result = (lambda data, key, iv: str((base64.b64encode(AES.new(add_16(key), AES.MODE_CBC, add_16(iv)).encrypt(lambda s: s + (AES.block_size - len(s) % AES.block_size) * chr(AES.block_size - len(s) % AES.block_size)(data).encode('utf-8')))), encoding='utf-8'))(flag, 'Are_you_Ok?hahah', '0102030405060708')
en_flag = 'NZRk3DxfmxGHwiQkDJQlqrTvUb2icaiUSIdnFn+WMb0='
if en_flag == result:
print('Success')
else:
print('No')

用到了一个复杂的加密算法

稍微改写一下

直接先将 en_flag 进行 base64decode

然后进行 AES decrypt

image-20210319194030564

得到 flag

1
actf{Thi5_7s_A3S_CBC}

0x07 Bytecode

竟然是个 txt 文件

内容是 python 的 bytecode 的反汇编结果

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
  1           0 LOAD_CONST               0 (0)
2 LOAD_CONST 1 (None)
4 IMPORT_NAME 0 (hashlib)
6 STORE_NAME 0 (hashlib)

2 8 LOAD_CONST 0 (0)
10 LOAD_CONST 1 (None)
12 IMPORT_NAME 1 (itertools)
14 STORE_NAME 1 (itertools)

3 16 LOAD_CONST 2
18 STORE_NAME 2 (part1)

4 20 LOAD_CONST 3
22 STORE_NAME 3 (part2)

5 24 LOAD_CONST 4 (32)
26 LOAD_CONST 5 (33)
28 LOAD_CONST 6 (10)
30 LOAD_CONST 7 (12)
32 LOAD_CONST 8 (7)
34 LOAD_CONST 9 (15)
36 BUILD_LIST 6
38 STORE_NAME 4 (key)

6 40 LOAD_NAME 5 (input)
42 LOAD_CONST 10 ('Part2 of flag:')
44 CALL_FUNCTION 1
46 STORE_NAME 6 (tmp)

7 48 LOAD_NAME 7 (len)
50 LOAD_NAME 6 (tmp)
52 CALL_FUNCTION 1
54 LOAD_CONST 8 (7)
56 COMPARE_OP 4 (>)
58 POP_JUMP_IF_FALSE 76

8 60 LOAD_NAME 8 (print)
62 LOAD_CONST 11 ('Part2 no more than 7 characters!')
64 CALL_FUNCTION 1
66 POP_TOP

9 68 LOAD_NAME 9 (exit)
70 LOAD_CONST 0 (0)
72 CALL_FUNCTION 1
74 POP_TOP

10 >> 76 BUILD_LIST 0
78 STORE_NAME 10 (input1)

11 80 SETUP_LOOP 44 (to 126)
82 LOAD_NAME 6 (tmp)
84 LOAD_CONST 0 (0)
86 LOAD_CONST 12 (6)
88 BUILD_SLICE 2
90 BINARY_SUBSCR
92 GET_ITER
>> 94 FOR_ITER 28 (to 124)
96 STORE_NAME 11 (i)

12 98 LOAD_NAME 12 (str)
100 LOAD_NAME 13 (int)
102 LOAD_NAME 11 (i)
104 LOAD_CONST 6 (10)
106 CALL_FUNCTION 2
108 CALL_FUNCTION 1
110 STORE_NAME 14 (t)

13 112 LOAD_NAME 10 (input1)
114 LOAD_ATTR 15 (append)
116 LOAD_NAME 14 (t)
118 CALL_FUNCTION 1
120 POP_TOP
122 JUMP_ABSOLUTE 94
>> 124 POP_BLOCK

14 >> 126 LOAD_NAME 5 (input)
128 LOAD_CONST 13 ('Part1 of flag:')
130 CALL_FUNCTION 1
132 STORE_NAME 16 (input2)

15 134 LOAD_NAME 16 (input2)
136 LOAD_CONST 14 ('')
138 LOAD_ATTR 17 (join)
140 LOAD_NAME 10 (input1)
142 CALL_FUNCTION 1
144 BINARY_ADD
146 LOAD_CONST 15 ('}')
148 BINARY_ADD
150 STORE_NAME 18 (temp)

16 152 LOAD_NAME 2 (part1)
154 LOAD_NAME 3 (part2)
156 BINARY_ADD
158 STORE_NAME 19 (flag)

17 160 LOAD_NAME 0 (hashlib)
162 LOAD_ATTR 20 (sha1)
164 LOAD_NAME 19 (flag)
166 LOAD_ATTR 21 (encode)
168 LOAD_CONST 16 ('utf-8')
170 CALL_FUNCTION 1
172 CALL_FUNCTION 1
174 STORE_NAME 22 (hash1)

18 176 LOAD_NAME 0 (hashlib)
178 LOAD_ATTR 20 (sha1)
180 LOAD_NAME 18 (temp)
182 LOAD_ATTR 21 (encode)
184 LOAD_CONST 16 ('utf-8')
186 CALL_FUNCTION 1
188 CALL_FUNCTION 1
190 STORE_NAME 23 (hash2)

19 192 LOAD_NAME 22 (hash1)
194 LOAD_ATTR 24 (hexdigest)
196 CALL_FUNCTION 0
198 LOAD_NAME 23 (hash2)
200 LOAD_ATTR 24 (hexdigest)
202 CALL_FUNCTION 0
204 COMPARE_OP 2 (==)
206 POP_JUMP_IF_FALSE 218

20 208 LOAD_NAME 8 (print)
210 LOAD_CONST 17 ('You are right!')
212 CALL_FUNCTION 1
214 POP_TOP
216 JUMP_FORWARD 16 (to 234)

22 >> 218 LOAD_NAME 8 (print)
220 LOAD_CONST 18 ('Try again!')
222 CALL_FUNCTION 1
224 POP_TOP

23 226 LOAD_NAME 9 (exit)
228 LOAD_CONST 0 (0)
230 CALL_FUNCTION 1
232 POP_TOP

24 >> 234 LOAD_NAME 22 (hash1)
236 LOAD_ATTR 24 (hexdigest)
238 CALL_FUNCTION 0
240 LOAD_ATTR 21 (encode)
242 CALL_FUNCTION 0
244 STORE_NAME 22 (hash1)

25 246 LOAD_CONST 5 (33)
248 BUILD_LIST 1
250 STORE_NAME 25 (enc2)

26 252 SETUP_LOOP 46 (to 300)
254 LOAD_NAME 26 (range)
256 LOAD_CONST 0 (0)
258 LOAD_NAME 7 (len)
260 LOAD_NAME 22 (hash1)
262 CALL_FUNCTION 1
264 CALL_FUNCTION 2
266 GET_ITER
>> 268 FOR_ITER 28 (to 298)
270 STORE_NAME 11 (i)

27 272 LOAD_NAME 25 (enc2)
274 LOAD_ATTR 15 (append)
276 LOAD_NAME 22 (hash1)
278 LOAD_NAME 11 (i)
280 BINARY_SUBSCR
282 LOAD_NAME 25 (enc2)
284 LOAD_NAME 11 (i)
286 BINARY_SUBSCR
288 BINARY_XOR
290 CALL_FUNCTION 1
292 POP_TOP
294 EXTENDED_ARG 1
296 JUMP_ABSOLUTE 268
>> 298 POP_BLOCK

28 >> 300 LOAD_NAME 8 (print)
302 LOAD_CONST 19 ('enc2:')
304 LOAD_NAME 25 (enc2)
306 CALL_FUNCTION 2
308 POP_TOP

29 310 LOAD_CONST 20 (<code object <listcomp> at 0x000001CB39F58ED0, file "test.py", line 29>)
312 LOAD_CONST 21 ('<listcomp>')
314 MAKE_FUNCTION 0
316 LOAD_NAME 27 (zip)
318 LOAD_NAME 2 (part1)
320 LOAD_NAME 1 (itertools)
322 LOAD_ATTR 28 (cycle)
324 LOAD_NAME 4 (key)
326 CALL_FUNCTION 1
328 CALL_FUNCTION 2
330 GET_ITER
332 CALL_FUNCTION 1
334 STORE_NAME 29 (enc1)

30 336 LOAD_NAME 8 (print)
338 LOAD_CONST 22 ('enc1:')
340 LOAD_NAME 29 (enc1)
342 CALL_FUNCTION 2
344 POP_TOP
346 LOAD_CONST 1 (None)
348 RETURN_VALUE

#output:
#You are right!
#enc2: [33, 64, 37, 29, 45, 24, 47, 29, 43, 25, 44, 28, 125, 31, 44, 78, 122, 79, 120, 72, 122, 24, 46, 31, 121, 24, 122, 31, 123, 74, 115, 22, 38, 20, 38, 69, 118, 68, 125, 79, 124]
#enc1: [65, 66, 126, 106, 124, 95, 89, 85, 98, 60, 105, 80, 66, 88, 126, 63, 100, 63, 68, 68, 85]

直接硬逆,还原成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
import hashlib
import itertools
part1 = "" #应该不为空的,是出题人删了
part2 = "" #应该不为空的,是出题人删了
key = [32,33,10,12,7,15]
tmp = input('Part2 of flag:')
if len(tmp) > 7:
print('Part2 no more than 7 characters!')
exit(0)
else:
input1 = []
for i in tmp:
t = str(int(i,10))
input1.append(t)
input2 = input('Part1 of flag:')
temp = input2 + ''.join(input1) + "}"
flag = part1 + part2
hash1 = hashlib.sha1(flag.encode('utf-8'))
hash2 = hashlib.sha1(temp.encode('utf-8'))
if hash1.hexdigest() == hash2.hexdigest():
print("You are right!")
else:
print("Try again!")
exit(0)
hash1 = hash1.hexdigest().encode()
enc2 = [33]
for i in range(0,len(hash1)):
enc2.append(hash1[i] ^ enc2[i])
print("enc2:",enc2)
enc1 = [ord(x)^y for x,y in zip(part1,itertools.cycle(key))]
print('enc1:',enc1)

可以看到,这是一个生成 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
import hashlib
import itertools

enc2 = [33, 64, 37, 29, 45, 24, 47, 29, 43, 25, 44, 28, 125, 31, 44, 78, 122, 79, 120, 72, 122, 24, 46, 31, 121, 24,
122, 31, 123, 74, 115, 22, 38, 20, 38, 69, 118, 68, 125, 79, 124]
enc1 = [65, 66, 126, 106, 124, 95, 89, 85, 98, 60, 105, 80, 66, 88, 126, 63, 100, 63, 68, 68, 85]
key = [32,33,10,12,7,15]
part1 = [chr(m ^ k) for m, k in zip(enc1, itertools.cycle(key))]

hash1 = [i for i in range(1000000)]
for i in range(len(enc2) - 2 , -1 , -1):
hash1[i] = enc2[i + 1] ^ enc2[i]
hash1 = [chr(i) for i in hash1[:len(enc2) - 1]]
hash1 = "".join(hash1)
print(hash1)
for part2 in range(0,999999):
flag = "".join(part1) + str(part2) + "}"
hash2 = hashlib.sha1(flag.encode('utf-8')).hexdigest().encode()
hash2 = bytes.decode(hash2)
if part2%1000 == 0:
print(part2)
if hash2 == hash1:
print("done!! >>> ",flag)
break

part1 是一堆字符串,part2 是个数字,范围为 0~999999

最终跑出flag

image-20210319231719013

1
actf{Pyth0n_byt3c0de_114514}

0x08 Daisy

基本信息

image-20210319232006782

  • 64bit

跑一跑

image-20210319232049389

有一个输入点

IDA看看

看看main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int __cdecl main(int argc, const char **argv, const char **envp)
{
char Str1[5]; // [rsp+20h] [rbp-60h] BYREF
_BYTE v6[3]; // [rsp+25h] [rbp-5Bh] BYREF

sub_4018E0(argc, argv, envp);
puts("这个遥遥漫长的旅途的真正意义:一切都是为了延续希望");
puts(asc_404102);
scanf("%s", Str1);
if ( !strncmp(Str1, "ACTF{", 5ui64) && (unsigned __int8)sub_4016E9(v6) && Str1[strlen(Str1) - 1] == 125 )
puts("Success!");
else
puts("岛外会有什么事物呢?");
system("pause");
return 0;
}

没法反编译 sub_4016E9

看看汇编

image-20210319235352780

可以看到,从 0x40172 开始的指令都是游离在外的,并非哪个函数的指令,而那个

jmp short near ptr loc_401707+5 最终只是让 rsp 指向的地址里面的值增一了,十分离谱,怀疑这段代码是花指令,啥用没有

直接改了

1
2
3
4
5
6
7
8
.text:000000000040171F                 jmp     short near ptr loc_401707+5
.text:000000000040171F ; ---------------------------------------------------------------------------
.text:0000000000401721 db 5
.text:0000000000401722 ; ---------------------------------------------------------------------------
.text:0000000000401722
.text:0000000000401722 loc_401722: ; CODE XREF: sub_4016E9+31↑p
.text:0000000000401722 inc [rsp+40h+var_40]
.text:0000000000401726 retn

改成

1
2
3
4
5
6
7
8
9
10
11
12
13
.text:000000000040171A                 nop
.text:000000000040171B nop
.text:000000000040171C nop
.text:000000000040171D nop
.text:000000000040171E nop
.text:000000000040171F nop
.text:0000000000401720 nop
.text:0000000000401721 nop
.text:0000000000401722 nop
.text:0000000000401723 nop
.text:0000000000401724 nop
.text:0000000000401725 nop
.text:0000000000401726 nop

然后就能成功反编译了

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
__int64 __fastcall sub_4016E9(const char *a1)
{
size_t v1; // rax
int v3; // eax
void *v4; // [rsp+28h] [rbp-18h]
int v5; // [rsp+30h] [rbp-10h]
unsigned int v6; // [rsp+34h] [rbp-Ch]
unsigned int v7; // [rsp+38h] [rbp-8h]
unsigned int v8; // [rsp+3Ch] [rbp-4h]

v4 = malloc(0x200ui64);
memset(v4, 0, 0x200ui64);
v1 = strlen(a1);
memcpy(v4, a1, v1);
v8 = 0;
v7 = 0;
v6 = 0;
v5 = 0;
while ( *((_BYTE *)v4 + v5) )
{
switch ( *((_BYTE *)v4 + v5) )
{
case 'a':
--v8;
goto LABEL_11;
case 'd':
++v8;
goto LABEL_11;
case 'l':
--v6;
goto LABEL_11;
case 's':
++v7;
goto LABEL_11;
case 'u':
++v6;
goto LABEL_11;
case 'w':
--v7;
LABEL_11:
v3 = sub_40161E(v8, v7, v6);
if ( v3 == 1 )
return 1i64;
if ( v3 != 2 && !v3 )
return 0i64;
++v5;
break;
default:
return 0i64;
}
}
return 0i64;
}
1
2
3
4
5
6
7
8
__int64 __fastcall sub_40161E(int a1, int a2, int a3)
{
if ( a1 < 0 || a2 < 0 || a3 < 0 || a1 > 9 || a2 > 9 || a3 > 1 || asc_404000[100 * a3 + 10 * a2 + a1] == 42 )
return 0i64;
if ( asc_404000[100 * a3 + 10 * a2 + a1] == 68 )
return 1i64;
return 2i64;
}

猜测是个迷宫问题

1
2
3
4
5
6
a --> v8--
d --> v8++
s --> v7++
w --> v7--
u --> v6++
l --> v6--

去把地图拿出来分析

image-20210320001127115

1
#***#*#**######*#**#******#**########**********###**********########**************#****#**###****##*****#*####****#*****#####****##********#*******#*#*******#**#******#**#*********#*######**#*******#D

显然这是个三维的迷宫

image-20210320001801883

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
array([[['#', '*', '*', '*', '#', '*', '#', '*', '*', '#'],
['#', '#', '#', '#', '#', '*', '#', '*', '*', '#'],
['*', '*', '*', '*', '*', '*', '#', '*', '*', '#'],
['#', '#', '#', '#', '#', '#', '#', '*', '*', '*'],
['*', '*', '*', '*', '*', '*', '*', '#', '#', '#'],
['*', '*', '*', '*', '*', '*', '*', '*', '*', '*'],
['#', '#', '#', '#', '#', '#', '#', '#', '*', '*'],
['*', '*', '*', '*', '*', '*', '*', '*', '*', '*'],
['*', '*', '#', '*', '*', '*', '*', '#', '*', '*'],
['#', '#', '#', '*', '*', '*', '*', '#', '#', '*']],

[['*', '*', '*', '*', '#', '*', '#', '#', '#', '#'],
['*', '*', '*', '*', '#', '*', '*', '*', '*', '*'],
['#', '#', '#', '#', '#', '*', '*', '*', '*', '#'],
['#', '*', '*', '*', '*', '*', '*', '*', '*', '#'],
['*', '*', '*', '*', '*', '*', '*', '#', '*', '#'],
['*', '*', '*', '*', '*', '*', '*', '#', '*', '*'],
['#', '*', '*', '*', '*', '*', '*', '#', '*', '*'],
['#', '*', '*', '*', '*', '*', '*', '*', '*', '*'],
['#', '*', '#', '#', '#', '#', '#', '#', '*', '*'],
['#', '*', '*', '*', '*', '*', '*', '*', '#', 'D']]], dtype='|S1')

行走序列

1
下右右右右上飞下下左左左左下遁右右右右右右上上上飞右右右右遁下下飞下下遁左左左飞下下遁左左左左左左左飞下下下遁右右上飞右右右右右遁下右飞右

写脚本

1
2
3
4
5
6
7
l = u"下右右右右上飞下下左左左左下遁右右右右右右上上上飞右右右右遁下下飞下下遁左左左飞下下遁左左左左左左左飞下下下遁右右上飞右右右右右遁下右飞右"
m = {"遁":"u","飞":"l","下":"s","上":"w","左":"a","右":"d"}
r = ""
for i in range(0,len(l)):
r += m[l[i]]
# print(i,l[i])
print("ACTF{"+r+"}")

得到flag

image-20210320004117860

1
ACTF{sddddwlssaaaasuddddddwwwlddddusslssuaaalssuaaaaaaalsssuddwldddddusdld}