2019 西湖论剑 线上赛

西湖论剑的初赛题目,质量不是很高,但是由于这是我第一次接触花指令,再加上对MFC不熟悉,有点懵逼,第三题题没做出来。最终卡着线进了决赛。。。

比赛时间:4月7日

下载地址

Re

easyCpp

输入16个数a[16],用vector保存

动调中可以发现,依次用a[0]减去a[1]到a[15]的每一个数,然后反序,跟斐波那契数列的前16位对比。

1
2
3
4
5
6
7
8
9
a = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]
b = [0 for i in range(16)]
for i in range(16):
b[i] = a[15-i]
c = [0 for i in range(16)]
c[0] = b[0]
for i in range(1,16):
c[i] = b[i] - b[0]
print(c)

testre

输入一串长度16的字符串后,先跟常量异或再加。但是之后并没有用到,所以这一步没用。

还是原先的输入,将输入转成58进制(其实从%58再/58就能看出来了)存进一个数组,然后按照一个table转成字符,跟常量对比。期间穿插了一个base64的数组,并没什么用直接忽略。

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
_i = zero;
current_lenth = n - 1;
while ( _i < input_lenth )
{
decnumber = (unsigned __int8)input_[_i];
for ( j = n - 1; ; --j )
{
v10 = 1;
if ( j <= current_lenth )
v10 = decnumber != 0;
if ( !v10 )
break;
noused = base58bit[j] << 6;
decnumber += base58bit[j] << 8;
v9 = 64;
base58bit[j] = decnumber % 0x3A;
b64noused[j] = noused & 0x3F;
noused >>= 6;
decnumber /= 0x3A;
v27 /= v9;
if ( !j )
break;
}
++_i;
current_lenth = j;
}

直接转dec再转string就行了

1
2
3
4
5
6
7
8
9
10
s = 'D9cS9N9iHjMLTdA8YSMRMp'
t = []
table = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
for i in range(len(s)):
t.append(table.index(s[i]))
res = 0
for i in range(22):
res+=t[i]*(58**(21-i))
t = hex(res)[2:-1]
print(t.decode('hex'))

Junk Instruction

此题为MFC程序。由于不太擅长窗体程序,一开始跟踪了很久,也没找到点击事件函数。后来知道了有xspy这个工具,可以查看到点击事件函数在0x00AA2420

进入IDA静态分析,在sub_402420中可以看到有两个分支,里面明显有Correct和Error,跟进去可以看到对应的字符串。sub_402600就是check函数了,一进去也能看到一串常量。

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
if ( sub_402600(v4 + 16) )
{
sub_401BF0(v5);
v10 = 0;
v12 = 0;
sub_403DC0(129, 0);
v9 = &CCorrect::`vftable';
v19 = 0;
sub_40484E(v7, v8);
v9 = &CCorrect::`vftable';
v19 = 2;
sub_407B3B(&v13);
LOBYTE(v19) = 1;
v11 = &CBrush::`vftable';
sub_401580(&v11);
}
else
{
sub_401BF0(v5);
v15 = 0;
v17 = 0;
sub_403DC0(130, 0);
v14 = &CError::`vftable';
v19 = 3;
sub_40484E(v7, v8);
v14 = &CError::`vftable';
v19 = 5;
sub_407B3B(&v18);
LOBYTE(v19) = 4;
v16 = &CBrush::`vftable';
sub_401580(&v16);
}
v19 = -1;
return sub_40434D(v7, v8);

一进去发现反编译不动,往下看有几条可疑的指令:

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
.text:0040293A 4D8                 call    sub_402CA0
.text:0040293F 4D8 call $+5
.text:00402944
.text:00402944 loc_402944: ; DATA XREF: check+398↓r
.text:00402944 4DC pop eax
.text:00402945 4D8 mov [ebp+var_23C], eax
.text:0040294B 4D8 call loc_402953
.text:0040294B ; --------------------------------------------------------------------
.text:00402950 4D8 db 0EAh
.text:00402951 ; --------------------------------------------------------------------
.text:00402951 4D8 jmp short loc_40295C
.text:00402953 ; --------------------------------------------------------------------
.text:00402953
.text:00402953 loc_402953: ; CODE XREF: check+34B↑j
.text:00402953 4D8 pop ebx
.text:00402954 4D4 inc ebx
.text:00402955 4D4 push ebx
.text:00402956 4D8 mov eax, 11111111h
.text:0040295B 4D8 retn
.text:0040295C ; --------------------------------------------------------------------
.text:0040295C
.text:0040295C loc_40295C: ; CODE XREF: check+351↑j
.text:0040295C 4D8 call loc_402968
.text:00402961 4D8 mov ebx, 33333333h
.text:00402966 4D8 jmp short loc_402975
.text:00402968 ; --------------------------------------------------------------------
.text:00402968
.text:00402968 loc_402968: ; CODE XREF: check:loc_40295C↑p
.text:00402968 4D8 mov ebx, 11111111h
.text:0040296D 4D8 pop ebx
.text:0040296E 4D4 mov ebx, offset loc_402975
.text:00402973 4D4 push ebx
.text:00402974 4D8 retn
.text:00402975 ; --------------------------------------------------------------------
.text:00402975
.text:00402975 loc_402975: ; CODE XREF: check+366↑j
.text:00402975 ; DATA XREF: check+36E↑o
.text:00402975 4D8 mov ebx, 22222222h
.text:0040297A 4D8 lea ecx, [ebp+var_38]
.text:0040297D 4D8 mov [ebp+var_260], ecx
.text:00402983 4D8 mov edx, [ebp+var_260]

0040293F的call就是下一条指令,之后pop eax将返回地址出栈,之后0040294B的call调用到00402953后,返回地址出栈,加一,再入栈,这样返回地址指向的实际是00402951,下一个位于0040295C的call完成了相似的操作,将返回地址改为00402975,之后程序就正常进行了。

通过刚刚的分析可以看出,虽然经过了三个call,但并没有对数据进行实质的改变,所以中间的指令都没什么用,但是却阻碍了ida的分析,因此我们将第一个call直接改为jmp到00402975,这样ida就能成功分析了。在这些指令中看起来改变了一些寄存器eax,ebx的值,但这些值都没用到,期间保存到栈上的数据(00402945的指令)也没用到,比如eax到最后被幅值为了0x11111111h,再往后看可以发现这个数据没用。

剩下的部分就照常分析了,期间几个函数内也有相同的花指令,用相同的方式处理即可。

加密方式:

将输入除去"flag{}"后反序,再以“qwertyuiop”为密钥进行ARC4加密。

1
2
3
4
5
6
7
from Crypto.Cipher import ARC4
key = 'qwertyuiop'
arc4 = ARC4.new(key)
a = [0x5B, 0xD6, 0xD0, 0x26, 0xC8, 0xDD, 0x19, 0x7E, 0x6E, 0x3E, 0xCB, 0x16, 0x91, 0x7D, 0xFF, 0xAF, 0xDD, 0x76, 0x64, 0xB0, 0xF7, 0xE5, 0x89, 0x57, 0x82, 0x9F, 0xC, 0, 0x9E, 0xD0, 0x45, 0xFA]
c = ''.join(map(chr,a))
p = arc4.decrypt(c)
print(p[::-1])

第一次搞花指令,花的类型也比较简单,多多学习。

Misc

ttl

将ttl的头两位提取出来,每4个(8位)组成一个字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
with open("flag.txt","w") as flag:
with open("ttl.txt","r") as ttlfile:
j=0
temp=0
for i in ttlfile.readlines():
j+=1
this=i.split("=")[1]
this=(int(this)&0xc0)>>6
temp=(temp<<2)+this
if(j%4==0):
#print temp
flag.write(chr(temp))
# print chr(temp)
temp=0
j=0

直接转二进制写入文件,观察文件头可以看出是jpeg。打开后是一部分二维码。foremost分离后得六部分,拼接一下扫出来是:

key:AutomaticKey cipher:fftu{2028mb39927wn1f96o6e12z03j58002p}

用自动密钥解,明文开头肯定是flag,因此再密钥后面加个flag然后解维吉尼亚密码就出flag了。

路径

图的最短路径问题,每个条边权值一样。用Dijkstra很容易求出结果

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
# -*-coding:utf-8 -*-
class DijkstraPath():
def __init__(self, node_map):
self.node_map = node_map
self.node_length = len(node_map)
self.used_node_list = []
self.collected_node_dict = {}
def __call__(self, from_node, to_node):
self.from_node = from_node
self.to_node = to_node
self._init_dijkstra()
return self._format_path()
def _init_dijkstra(self):
self.used_node_list.append(self.from_node)
self.collected_node_dict[self.from_node] = [0, -1]
for index1, node1 in enumerate(self.node_map[self.from_node]):
if node1:
self.collected_node_dict[index1] = [node1, self.from_node]
self._foreach_dijkstra()
def _foreach_dijkstra(self):
if len(self.used_node_list) == self.node_length - 1:
return
for key, val in self.collected_node_dict.items(): # 遍历已有权值节点
if key not in self.used_node_list and key != to_node:
self.used_node_list.append(key)
else:
continue
for index1, node1 in enumerate(self.node_map[key]): # 对节点进行遍历
# 如果节点在权值节点中并且权值大于新权值
if node1 and index1 in self.collected_node_dict and self.collected_node_dict[index1][0] > node1 + val[0]:
self.collected_node_dict[index1][0] = node1 + val[0] # 更新权值
self.collected_node_dict[index1][1] = key
elif node1 and index1 not in self.collected_node_dict:
self.collected_node_dict[index1] = [node1 + val[0], key]
self._foreach_dijkstra()
def _format_path(self):
node_list = []
temp_node = self.to_node
node_list.append((temp_node, self.collected_node_dict[temp_node][0]))
while self.collected_node_dict[temp_node][1] != -1:
temp_node = self.collected_node_dict[temp_node][1]
node_list.append((temp_node, self.collected_node_dict[temp_node][0]))
node_list.reverse()
return node_list
def set_node_map(node_map, node, node_list):
for x, y, val in node_list:
node_map[node.index(x)][node.index(y)] = node_map[node.index(y)][node.index(x)] = val
if __name__ == "__main__":
node = []
node_list = [('FloraPrice','E11', 1),('FloraPrice','E9', 1),('FloraPrice','75D}', 1),('NoraFayette','E11', 1),('NoraFayette','E10', 1),('NoraFayette','E13', 1),('NoraFayette','E12', 1),('NoraFayette','E14', 1),('NoraFayette','E9', 1),('NoraFayette','E7', 1),('NoraFayette','E6', 1),('E10','SylviaAvondale', 1),('E10','MyraLiddel', 1),('E10','HelenLloyd', 1),('E10','KatherinaRogers', 1),('VerneSanderson','E7', 1),('VerneSanderson','E12', 1),('VerneSanderson','E9', 1),('VerneSanderson','E8', 1),('E12','HelenLloyd', 1),('E12','KatherinaRogers', 1),('E12','SylviaAvondale', 1),('E12','MyraLiddel', 1),('E14','SylviaAvondale', 1),('E14','75D}', 1),('E14','KatherinaRogers', 1),('FrancesAnderson','E5', 1),('FrancesAnderson','E6', 1),('FrancesAnderson','E8', 1),('FrancesAnderson','E3', 1),('DorothyMurchison','E9', 1),('DorothyMurchison','E8', 1),('EvelynJefferson','E9', 1),('EvelynJefferson','E8', 1),('EvelynJefferson','E5', 1),('EvelynJefferson','E4', 1),('EvelynJefferson','E6', 1),('EvelynJefferson','E1', 1),('EvelynJefferson','E3', 1),('EvelynJefferson','E2', 1),('RuthDeSand','E5', 1),('RuthDeSand','E7', 1),('RuthDeSand','E9', 1),('RuthDeSand','E8', 1),('HelenLloyd','E11', 1),('HelenLloyd','E7', 1),('HelenLloyd','E8', 1),('OliviaCarleton','E11', 1),('OliviaCarleton','E9', 1),('EleanorNye','E5', 1),('EleanorNye','E7', 1),('EleanorNye','E6', 1),('EleanorNye','E8', 1),('E9','TheresaAnderson', 1),('E9','PearlOglethorpe', 1),('E9','KatherinaRogers', 1),('E9','SylviaAvondale', 1),('E9','MyraLiddel', 1),('E8','TheresaAnderson', 1),('E8','PearlOglethorpe', 1),('E8','KatherinaRogers', 1),('E8','SylviaAvondale', 1),('E8','BrendaRogers', 1),('E8','LauraMandeville', 1),('E8','MyraLiddel', 1),('E5','TheresaAnderson', 1),('E5','BrendaRogers', 1),('E5','LauraMandeville', 1),('E5','CharlotteMcDowd', 1),('E4','CharlotteMcDowd', 1),('E4','TheresaAnderson', 1),('E4','BrendaRogers', 1),('E7','TheresaAnderson', 1),('E7','SylviaAvondale', 1),('E7','BrendaRogers', 1),('E7','LauraMandeville', 1),('E7','CharlotteMcDowd', 1),('E6','TheresaAnderson', 1),('E6','PearlOglethorpe', 1),('E6','BrendaRogers', 1),('E6','LauraMandeville', 1),('E1','LauraMandeville', 1),('E1','BrendaRogers', 1),('E3','TheresaAnderson', 1),('E3','BrendaRogers', 1),('E3','LauraMandeville', 1),('E3','CharlotteMcDowd', 1),('E3','flag{', 1),('E2','LauraMandeville', 1),('E2','TheresaAnderson', 1),('KatherinaRogers','E13', 1),('E13','SylviaAvondale', 1)]
for i in node_list:
for j in range(2):
if i[j] in node:
continue
else:
node.append(i[j])
node_map = [[0 for val in xrange(len(node))] for val in xrange(len(node))]
set_node_map(node_map, node, node_list)
# A -->; D
from_node = node.index('flag{')
to_node = node.index('75D}')
dijkstrapath = DijkstraPath(node_map)
path = dijkstrapath(from_node, to_node)
for i in path:
print(node[i[0]])
print path

Crypto

Haffman

哈夫曼编码,画一个哈夫曼树,用lg{}这些来确定构造哈夫曼树的规则

1
2
3
4
5
6
a = ['110','00111','000','00101','00100','10','10','110','01','10','110','10','111','110','111','01','01','01','111','01','111','111','000','01','000','110','01','01','10','10','111','10','01','10','111','000','10','00110']
key = {'00100':'{','00101':'g','00110':'}','00111':'l','000':'a','110':'f','111':'0','10':'d','01':'5'}
s = ''
for i in a:
s+=key[i]
print(s)