一个汇编全是mov的程序,第一次见

0x1 失败尝试

搜索一下发现是通过movfuscator加密混淆过的,RITSEC CTF 2018中有一道类似的题目
发现有个解混淆工具 demovfuscator
下载按指示安装使用后再看汇编,还是一堆mov,解混淆不了一点,不知道是不是我的使用姿势不太对
img
img

0x2 分析

Shift+F12 查看字符串

发现一个提示输入字符串ok input your flag:和一个可疑字符串VIPeeUd\\eHPQ\\UgQW\\IgTc\\TbRVTTPISRRV
img

可以推测如果输入正确则会输出’yes!’
img

进入到可疑字符串区域

转成数组发现刚好是42个byte(flag长度),第一想法应该是做了什么位运算得到的 而且很直观的是 四个 \ 正好对应四个 -,说明是一一对应关系(很重要,后面会用到)
img

Shift+E Export Data

复制数据内容
img

对比分析

我们已知的flag结果为 开头的’flag{‘,结尾的’}’ 以及中间的四个’-‘
与数组中的对应位进行二进制比较: 发现还是有些规律的 bit0取反,bit1,2,3保持不变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
idx 0:   0110 0111   0x67
idx 0: 0110 0110 'f'

idx 1: 1001 1101 0x9d
idx 1: 0110 1100 'l'

idx 2: 0110 0000 0x60
idx 2: 0110 0001 'a'

idx 3: 0110 0110 0x66
idx 3: 0110 0111 'g'

idx 4: 1000 1010 0x8a
idx 4: 0111 1011 '{'

idx 13: 0101 1100 0x5c
idx 13: 0010 1101 '-'

idx 41: 1000 1100 0x8c
idx 41: 0111 1101 '}'

0x3 exp

推断所有可能

字节范围内遍历异或,查看所有16进制可见字符的可能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# flag的16进制字母表
alphabet = "0123456789abcdef"
# 从IDA获取的42长的byte数组
raw = [0x67, 0x9D, 0x60, 0x66, 0x8A, 0x56, 0x49, 0x50, 0x65, 0x65,
0x60, 0x55, 0x64, 0x5C, 0x65, 0x48, 0x50, 0x51, 0x5C, 0x55,
0x67, 0x51, 0x57, 0x5C, 0x49, 0x67, 0x54, 0x63, 0x5C, 0x54,
0x62, 0x52, 0x56, 0x54, 0x54, 0x50, 0x49, 0x53, 0x52, 0x52,
0x56, 0x8C]

# 遍历查看{括号中的可能内容}
for j in range(5, 41):
print(f"{j}, {hex(raw[j])}:", end=' ')
# 0x5c对应'-'
if raw[j] == 0x5c:
print('-', end=' ')
# 遍历0x00-0xff 只取其中的奇数作亦或
for i in range(1, 256, 2):
s = chr(raw[j] ^ i)
#在字母表内 而且运算先后 低1,2,3bit位相同
if s in alphabet and ord(s) & 0xe == raw[j] & 0xe:
print(s, end=' ')
print()

输出如下 可见可以暴力枚举 但也有2^23的数量 考虑进一步优化

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
5, 0x56: 7 
6, 0x49: 8
7, 0x50: a 1
8, 0x65: d 4
9, 0x65: d 4
10, 0x60: a 1
11, 0x55: d 4
12, 0x64: e 5
13, 0x5c: -
14, 0x65: d 4
15, 0x48: 9
16, 0x50: a 1
17, 0x51: 0
18, 0x5c: -
19, 0x55: d 4
20, 0x67: f 6
21, 0x51: 0
22, 0x57: f 6
23, 0x5c: -
24, 0x49: 8
25, 0x67: f 6
26, 0x54: e 5
27, 0x63: b 2
28, 0x5c: -
29, 0x54: e 5
30, 0x62: c 3
31, 0x52: c 3
32, 0x56: 7
33, 0x54: e 5
34, 0x54: e 5
35, 0x50: a 1
36, 0x49: 8
37, 0x53: b 2
38, 0x52: c 3
39, 0x52: c 3
40, 0x56: 7

缩小可能范围

前面推测得到两个数组直接应该是一一映射的关系,即特定的输入对应特定的输出
由于我们已经知道了 fa 对应的输入为0x67和0x9d, 那么0x57就应该对应61
同理 如果0x54对应e 那么所有0x54都对应e
实际上也就并不需要枚举2^23种可能 只需确定[b,2] [c,3] [d,4] [e,5]的对应关系即2^4=16种可能

爆破脚本:

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
from pwn import *

# flag的16进制字母表
alphabet = "0123456789abcdef"
# 从IDA获取的42长的byte数组
raw = [0x67, 0x9D, 0x60, 0x66, 0x8A, 0x56, 0x49, 0x50, 0x65, 0x65,
0x60, 0x55, 0x64, 0x5C, 0x65, 0x48, 0x50, 0x51, 0x5C, 0x55,
0x67, 0x51, 0x57, 0x5C, 0x49, 0x67, 0x54, 0x63, 0x5C, 0x54,
0x62, 0x52, 0x56, 0x54, 0x54, 0x50, 0x49, 0x53, 0x52, 0x52,
0x56, 0x8C]

d = {0x67: ["f"], 0x9d: ["l"], 0x60: ["a"],
0x66: ["g"], 0x5c: ["-"], 0x8a: ["{"], 0x8c: ["}"]}
# 遍历查看{括号中的可能内容}
for j in range(5, 41):
# 0x5c对应'-'
if raw[j] in d.keys():
continue
# 遍历0x00-0xff 只取其中的奇数作亦或
t = []
for i in range(1, 256, 2):
s = chr(raw[j] ^ i)
# 在字母表内 而且运算先后 低1,2,3bit位相同 且未出现过
if s in alphabet and ord(s) & 0xe == raw[j] & 0xe and s not in "flag{-}":
t.append(s)
d[raw[j]] = t

print(d)

for i in raw:
print(f"{hex(i)} : {d[i]}")

# 16种可能
ds = [
{0x63: 'b', 0x53: '2', 0x62: 'c', 0x52: '3', 0x65: 'd', 0x55: '4', 0x64: 'e', 0x54: '5'},
{0x63: 'b', 0x53: '2', 0x62: 'c', 0x52: '3', 0x65: 'd', 0x55: '4', 0x64: '5', 0x54: 'e'},

{0x63: 'b', 0x53: '2', 0x62: 'c', 0x52: '3', 0x65: '4', 0x55: 'd', 0x64: 'e', 0x54: '5'},
{0x63: 'b', 0x53: '2', 0x62: 'c', 0x52: '3', 0x65: '4', 0x55: 'd', 0x64: '5', 0x54: 'e'},


{0x63: 'b', 0x53: '2', 0x62: '3', 0x52: 'c', 0x65: 'd', 0x55: '4', 0x64: 'e', 0x54: '5'},
{0x63: 'b', 0x53: '2', 0x62: '3', 0x52: 'c', 0x65: 'd', 0x55: '4', 0x64: '5', 0x54: 'e'},

{0x63: 'b', 0x53: '2', 0x62: '3', 0x52: 'c', 0x65: '4', 0x55: 'd', 0x64: 'e', 0x54: '5'},
{0x63: 'b', 0x53: '2', 0x62: '3', 0x52: 'c', 0x65: '4', 0x55: 'd', 0x64: '5', 0x54: 'e'},



{0x63: '2', 0x53: 'b', 0x62: 'c', 0x52: '3', 0x65: 'd', 0x55: '4', 0x64: 'e', 0x54: '5'},
{0x63: '2', 0x53: 'b', 0x62: 'c', 0x52: '3', 0x65: 'd', 0x55: '4', 0x64: '5', 0x54: 'e'},

{0x63: '2', 0x53: 'b', 0x62: 'c', 0x52: '3', 0x65: '4', 0x55: 'd', 0x64: 'e', 0x54: '5'},
{0x63: '2', 0x53: 'b', 0x62: 'c', 0x52: '3', 0x65: '4', 0x55: 'd', 0x64: '5', 0x54: 'e'},


{0x63: '2', 0x53: 'b', 0x62: '3', 0x52: 'c', 0x65: 'd', 0x55: '4', 0x64: 'e', 0x54: '5'},
{0x63: '2', 0x53: 'b', 0x62: '3', 0x52: 'c', 0x65: 'd', 0x55: '4', 0x64: '5', 0x54: 'e'},

{0x63: '2', 0x53: 'b', 0x62: '3', 0x52: 'c', 0x65: '4', 0x55: 'd', 0x64: 'e', 0x54: '5'},
{0x63: '2', 0x53: 'b', 0x62: '3', 0x52: 'c', 0x65: '4', 0x55: 'd', 0x64: '5', 0x54: 'e'},

]

for i in range(16):
flag=''
for j in raw:
if len(d[j])==1:
flag+=d[j][0]
else:
flag+=ds[i][j]
print(flag)
proc=process('./moveAside')
proc.sendlineafter(b'flag:\n', flag.encode())
# 接收返回的输入字符串
proc.recvline()
# 超过0.02秒则说明没有回显,停止接收
res = proc.recv(timeout=0.02)
if b'yes!' in res:
success('\n')
success(flag)
success('\n')
exit(0)
proc.kill()

img

⬆︎TOP