2020网鼎杯青龙组_re_signal

本文最后更新于:2 年前

2020网鼎杯-青龙组-reverse-signal 的两种解法

程序分析

ida 载入, 符号保留了

image-20211108120827068

image-20211108120925009

我们可以看出是个 vm

因为这里 vm的逻辑特别简单, 所以我们可以利用 angr 自动化逆向, 直接跑出答案, 也可以手动分析vm指令, 得到答案

解法1: angr自动化

我在学习 angr 的时候遇到了一点问题, 在此记录下来

编写过程

1
2
3
4
5
6
7
8
9
10
import angr

p = angr.Project("signal.exe")

init_state = p.factory.entry_state()
simgr = p.factory.simulation_manager(init_state)

simgr.explore(find=0x0040179E)

print(simgr)

这样写会 报一个错误

image-20211108121247372

然后程序直接死了, 说明我们的angr可能没有实现 msvcrt.dll 这个库吧, 然后我们在 Project 出添加 option

1
p = angr.Project("signal.exe", auto_load_libs=False)

就好使了

跑了大约 11s, 感觉有点慢, 我们学着 优化下

优化

hook scanf 函数

这里通过 查看 <<ctf 从0 到1>> 我们知道 使用 hook_symbol 方法 可以直接hook 系统函数

1
2
3
4
5
6
7
8
9
import angr
class my_scanf(angr.SimProcedure):
def run(self, fmt, buff):
simgfd = self.state.posix.get_fd(0)
data, real_size = simgfd.read_data(15)
self.state.memory.store(buff, data)
return 15

p.hook_symbol("scanf", my_scanf(), replace=True)

然后发现 快了 1s

然后书里面有个细节 可以直接让 angr 不判断结果是否能到指定路径,而直接跑

1
simgr.one_active.options.add(angr.options.LAZY_SOLVES)

加上这个之后, 时间已经控制在 7s 左右了

完成

最终的脚本

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
#!/usr/bin/python3
# coding = utf-8


import angr
import time
class my_scanf(angr.SimProcedure):
def run(self, fmt, buff):
simgfd = self.state.posix.get_fd(0)
data, real_size = simgfd.read_data(15)
self.state.memory.store(buff, data)
return 15



p = angr.Project("signal.exe", auto_load_libs=False)


p.hook_symbol("scanf", my_scanf(), replace=True)
start_time = time.time()
init_state = p.factory.entry_state()
simgr = p.factory.simulation_manager(init_state)
simgr.one_active.options.add(angr.options.LAZY_SOLVES)


simgr.explore(find=0x0040179E)

print(simgr)


print(simgr.found[0].posix.dumps(0))


end_time = time.time()

print("共使用了:", end_time - start_time)

image-20211108123205516

解法2: 指令识别并逆向

vm分析

提取 指令数据 和 指令对应的转换逻辑, 编写成 python脚本识别

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
#!/usr/bin/python3
# coding = utf-8

l = [0x0000000a,0x00000004,0x00000010,0x00000008,0x00000003,0x00000005,0x00000001,0x00000004,0x00000020,0x00000008,0x00000005,0x00000003,0x00000001,0x00000003,0x00000002,0x00000008,0x0000000b,0x00000001,0x0000000c,0x00000008,0x00000004,0x00000004,0x00000001,0x00000005,0x00000003,0x00000008,0x00000003,0x00000021,0x00000001,0x0000000b,0x00000008,0x0000000b,0x00000001,0x00000004,0x00000009,0x00000008,0x00000003,0x00000020,0x00000001,0x00000002,0x00000051,0x00000008,0x00000004,0x00000024,0x00000001,0x0000000c,0x00000008,0x0000000b,0x00000001,0x00000005,0x00000002,0x00000008,0x00000002,0x00000025,0x00000001,0x00000002,0x00000036,0x00000008,0x00000004,0x00000041,0x00000001,0x00000002,0x00000020,0x00000008,0x00000005,0x00000001,0x00000001,0x00000005,0x00000003,0x00000008,0x00000002,0x00000025,0x00000001,0x00000004,0x00000009,0x00000008,0x00000003,0x00000020,0x00000001,0x00000002,0x00000041,0x00000008,0x0000000c,0x00000001,0x00000007,0x00000022,0x00000007,0x0000003f,0x00000007,0x00000034,0x00000007,0x00000032,0x00000007,0x00000072,0x00000007,0x00000033,0x00000007,0x00000018,0x00000007,0xffffffa7,0x00000007,0x00000031,0x00000007,0xfffffff1,0x00000007,0x00000028,0x00000007,0xffffff84,0x00000007,0xffffffc1,0x00000007,0x0000001e,0x00000007,0x0000007a]

cur_eip = 0
v5 = 0
v6 = 0
v7 = 0
v8 = 0

while cur_eip < len(l):

if l[cur_eip] == 1:
print(f"flag[{v6}] = temp\n")
cur_eip += 1
v6 += 1
v8 += 1
elif l[cur_eip] == 2:
print(f"temp = buff[{v8}] + {l[cur_eip+1].to_bytes(4, 'little')[0]}")
cur_eip += 2

elif l[cur_eip] == 3:
print(f"temp = buff[{v8}] - {l[cur_eip+1].to_bytes(4, 'little')[0]}")
cur_eip += 2
elif l[cur_eip] == 4:
print(f"temp = buff[{v8}] ^ {l[cur_eip+1].to_bytes(4, 'little')[0]}")
cur_eip += 2
elif l[cur_eip] == 5:
print(f"temp = buff[{v8}] * {l[cur_eip+1].to_bytes(4, 'little')[0]}")
cur_eip += 2
elif l[cur_eip] == 6:
print("nop")
cur_eip += 1
elif l[cur_eip] == 7:
print(f"flag[{v7}] = {hex(l[cur_eip+1].to_bytes(4, 'little')[0])}")
cur_eip += 2
v7 += 1
elif l[cur_eip] == 8:
print(f"buff[{v5}] = temp")
cur_eip += 1
v5 += 1
elif l[cur_eip] == 10:
print("read flag")
cur_eip += 1
elif l[cur_eip] == 11:
print(f"temp = buff[{v8}]-1")
cur_eip += 1
elif l[cur_eip] == 12:
print(f"temp = buff[{v8}]+1")
cur_eip += 1
else:
print("解析失败")

这里解析指令有一个细节

image-20211108123321698

数据操作是 关于最低字节的

所以 最后对比的话只需要提取 最低位有效字节即可

1
l[cur_eip+1].to_bytes(4, 'little')[0]

得到:

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
read flag
temp = buff[0] ^ 16
buff[0] = temp
temp = buff[0] - 5
flag[0] = temp

temp = buff[1] ^ 32
buff[1] = temp
temp = buff[1] * 3
flag[1] = temp

temp = buff[2] - 2
buff[2] = temp
temp = buff[2]-1
flag[2] = temp

temp = buff[3]+1
buff[3] = temp
temp = buff[3] ^ 4
flag[3] = temp

temp = buff[4] * 3
buff[4] = temp
temp = buff[4] - 33
flag[4] = temp

temp = buff[5]-1
buff[5] = temp
temp = buff[5]-1
flag[5] = temp

temp = buff[6] ^ 9
buff[6] = temp
temp = buff[6] - 32
flag[6] = temp

temp = buff[7] + 81
buff[7] = temp
temp = buff[7] ^ 36
flag[7] = temp

temp = buff[8]+1
buff[8] = temp
temp = buff[8]-1
flag[8] = temp

temp = buff[9] * 2
buff[9] = temp
temp = buff[9] + 37
flag[9] = temp

temp = buff[10] + 54
buff[10] = temp
temp = buff[10] ^ 65
flag[10] = temp

temp = buff[11] + 32
buff[11] = temp
temp = buff[11] * 1
flag[11] = temp

temp = buff[12] * 3
buff[12] = temp
temp = buff[12] + 37
flag[12] = temp

temp = buff[13] ^ 9
buff[13] = temp
temp = buff[13] - 32
flag[13] = temp

temp = buff[14] + 65
buff[14] = temp
temp = buff[14]+1
flag[14] = temp

flag[0] = 0x22
flag[1] = 0x3f
flag[2] = 0x34
flag[3] = 0x32
flag[4] = 0x72
flag[5] = 0x33
flag[6] = 0x18
flag[7] = 0xa7
flag[8] = 0x31
flag[9] = 0xf1
flag[10] = 0x28
flag[11] = 0x84
flag[12] = 0xc1
flag[13] = 0x1e
flag[14] = 0x7a

solver

对照着指令, 写求解代码

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
#!/usr/bin/python3
# coding = utf-8


flag = [i for i in range(15)]

flag[0] = 0x22
flag[1] = 0x3f
flag[2] = 0x34
flag[3] = 0x32
flag[4] = 0x72
flag[5] = 0x33
flag[6] = 0x18
flag[7] = 0xa7
flag[8] = 0x31
flag[9] = 0xf1
flag[10] = 0x28
flag[11] = 0x84
flag[12] = 0xc1
flag[13] = 0x1e
flag[14] = 0x7a

flag[0] = (flag[0]+5) ^ 16
flag[1] = (flag[1] // 3) ^ 32
flag[2] = flag[2] + 3
flag[3] = (flag[3] ^ 4) - 1
flag[4] = (flag[4] + 33) // 3
flag[5] = flag[5] + 2
flag[6] = (flag[6] + 32) ^ 9
flag[7] = (flag[7] ^ 36) - 81
flag[8] = flag[8]
flag[9] = (flag[9]-37) // 2
flag[10] = (flag[10] ^ 65) - 54
flag[11] = flag[11] - 32
flag[12] = (flag[12] - 37) // 3
flag[13] = (flag[13] + 32) ^ 9
flag[14] = flag[14] - 66

print(flag)
print(''.join(map(chr, flag)))
1
2
[55, 53, 55, 53, 49, 53, 49, 50, 49, 102, 51, 100, 52, 55, 56]
757515121f3d478

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!