buuctf [GKCTF2020]EzMachine
vm的题目欠了好久了~~
首先OD打开,跟进去,发现此处每次CALL的位置都会变化。这应该就是我们要找的。
跟进这个函数,大概分析下流程,能找到虚拟机的字节码。
同时,在IDA里对应。可以找到CALL调用的函数的地址。通过X交叉引用,就能找到虚拟机的指令集。
List里存有各个opcode对应的地址,有了指令集。结合IDA和OD动调,逐个分析其功能。在看各函数功能的时候,同时要分辨出各个参数的作用。
------------------------------------------------------------------------------------
这里是字节码,在调用中用了两个数组。每次调用都取两个操作数,command_0是第一个,_1是第二个。
这里存放四个寄存器。
剩下的是所总结的其他的参数作用。有些参数实在是看不懂。。。
----------------------------------------------------------
然后要分析指令集的功能。没有注释的就不上图了。??是看不懂的,也有很多标注不太标准。
0.eipadd
1.mov
2.VM_push_data
3.vm_push
4.vm_pop
5.caseprint
6.vm_add
7.vm_sub
8.vm_mul
9.vm_div
10.vm_xor
11.vm_jmp
12.vm_subcmp
13.vm_je
14.vm_jne
15.vm_big_jmp
16.vm_low_jmp
17.vm_input
18.???
19.vm_LoadStack
20.vm_LoadString
0xff: vm_end
---------------------------------------------------------------------------------------------------------------------------------------------------
然后就是写脚本翻译一下vm的字节码。
1 code =[ #字节码
2 0x01, 0x03, 0x03, 0x05, 0x00, 0x00, 0x11, 0x00, 0x00, 0x01,
3 0x01, 0x11, 0x0C, 0x00, 0x01, 0x0D, 0x0A, 0x00, 0x01, 0x03,
4 0x01, 0x05, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x01, 0x02, 0x00,
5 0x01, 0x00, 0x11, 0x0C, 0x00, 0x02, 0x0D, 0x2B, 0x00, 0x14,
6 0x00, 0x02, 0x01, 0x01, 0x61, 0x0C, 0x00, 0x01, 0x10, 0x1A,
7 0x00, 0x01, 0x01, 0x7A, 0x0C, 0x00, 0x01, 0x0F, 0x1A, 0x00,
8 0x01, 0x01, 0x47, 0x0A, 0x00, 0x01, 0x01, 0x01, 0x01, 0x06,
9 0x00, 0x01, 0x0B, 0x24, 0x00, 0x01, 0x01, 0x41, 0x0C, 0x00,
10 0x01, 0x10, 0x24, 0x00, 0x01, 0x01, 0x5A, 0x0C, 0x00, 0x01,
11 0x0F, 0x24, 0x00, 0x01, 0x01, 0x4B, 0x0A, 0x00, 0x01, 0x01,
12 0x01, 0x01, 0x07, 0x00, 0x01, 0x01, 0x01, 0x10, 0x09, 0x00,
13 0x01, 0x03, 0x01, 0x00, 0x03, 0x00, 0x00, 0x01, 0x01, 0x01,
14 0x06, 0x02, 0x01, 0x0B, 0x0B, 0x00, 0x02, 0x07, 0x00, 0x02,
15 0x0D, 0x00, 0x02, 0x00, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01,
16 0x00, 0x02, 0x0C, 0x00, 0x02, 0x01, 0x00, 0x02, 0x00, 0x00,
17 0x02, 0x00, 0x00, 0x02, 0x0D, 0x00, 0x02, 0x05, 0x00, 0x02,
18 0x0F, 0x00, 0x02, 0x00, 0x00, 0x02, 0x09, 0x00, 0x02, 0x05,
19 0x00, 0x02, 0x0F, 0x00, 0x02, 0x03, 0x00, 0x02, 0x00, 0x00,
20 0x02, 0x02, 0x00, 0x02, 0x05, 0x00, 0x02, 0x03, 0x00, 0x02,
21 0x03, 0x00, 0x02, 0x01, 0x00, 0x02, 0x07, 0x00, 0x02, 0x07,
22 0x00, 0x02, 0x0B, 0x00, 0x02, 0x02, 0x00, 0x02, 0x01, 0x00,
23 0x02, 0x02, 0x00, 0x02, 0x07, 0x00, 0x02, 0x02, 0x00, 0x02,
24 0x0C, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02,
25 0x01, 0x13, 0x01, 0x02, 0x04, 0x00, 0x00, 0x0C, 0x00, 0x01,
26 0x0E, 0x5B, 0x00, 0x01, 0x01, 0x22, 0x0C, 0x02, 0x01, 0x0D,
27 0x59, 0x00, 0x01, 0x01, 0x01, 0x06, 0x02, 0x01, 0x0B, 0x4E,
28 0x00, 0x01, 0x03, 0x00, 0x05, 0x00, 0x00, 0xFF, 0x00, 0x00,
29 0x01, 0x03, 0x01, 0x05, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00
30 ]
31 opcodekey = {0:'eipadd',1:'mov',2:'VM_push_data',3:'vm_push',4:'vm_pop',5:'caseprint',6:'vm_add',
32 7:'vm_sub',8:'vm_mul',9:'vm_div',10:'vm_xor',11:'vm_jmp',12:'vm_subcmp',
33 13:'vm_je',14:'vm_jne',15:'vm_big_jmp',16:'vm_low_jmp',17:'vm_input',18:'???',
34 19:'vm_LoadStack',20:'vm_LoadString',0XFF:'vm_end'}
35 num =0
36 line = 1;
37 for i in range(len(code)):
38 if(num==0):
39 print(str(line)+': '+opcodekey[code[i]],end=' ')
40 line = line +1
41 else:
42 print(code[i],end=' ')
43 if(num==2):
44 print()
45 num +=1
46 if(num==3):
47 num=0
分析一下所得到的伪代码。得到程序的基本流程。
1: mov 3 3
2: caseprint 0 0
3: vm_input 0 0 输入flag
4: mov 1 17 将17放到v1
5: vm_subcmp 0 1 v1-v0,比较长度
6: vm_je 10 0 相等则跳10
7: mov 3 1
8: caseprint 0 0
9: vm_end 0 0 结束
10: mov 2 0 0给v2
11: mov 0 17 17给v0
12: vm_subcmp 0 2 比较v0和v2
13: vm_je 43 0 相等则到43
============================================
14: vm_LoadString 0 2
15: mov 1 97 97给v1
16: vm_subcmp 0 1 v0和97比较
17: vm_low_jmp 26 0 小于则跳到26
18: mov 1 122 122给v1
19: vm_subcmp 0 1 比较
20: vm_big_jmp 26 0 大于则跳26
===========若是小写字母======================
21: mov 1 71 71给1
22: vm_xor 0 1 v0与v1异或,放在v0
23: mov 1 1 1给1
24: vm_add 0 1 v0++
25: vm_jmp 36 0 跳到36
=============================================
26: mov 1 65 65给v1
27: vm_subcmp 0 1 比较
28: vm_low_jmp 36 0 小于则跳36
29: mov 1 90 90给v1
30: vm_subcmp 0 1 比较
31: vm_big_jmp 36 0 大于则跳36
===========若是大写字母==========================
32: mov 1 75 75给1
33: vm_xor 0 1 异或,放在v0
34: mov 1 1
35: vm_sub 0 1 v0--
==================================================
36: mov 1 16 16给1
37: vm_div 0 1 v0/v1。就是加密后flag/16.结果在v0,余数在v1
38: vm_push 1 0 v1压栈,余数
39: vm_push 0 0 v0压栈,商
40: mov 1 1 1给v1
41: vm_add 2 1 v2+1,表示已经完成加密的Flag的个数
42: vm_jmp 11 0 跳到11
43: VM_push_data 7 0 数据压栈,第二位
44: VM_push_data 13 0
45: VM_push_data 0 0
46: VM_push_data 5 0
47: VM_push_data 1 0
48: VM_push_data 12 0
49: VM_push_data 1 0
50: VM_push_data 0 0
51: VM_push_data 0 0
52: VM_push_data 13 0
53: VM_push_data 5 0
54: VM_push_data 15 0
55: VM_push_data 0 0
56: VM_push_data 9 0
57: VM_push_data 5 0
58: VM_push_data 15 0
59: VM_push_data 3 0
60: VM_push_data 0 0
61: VM_push_data 2 0
62: VM_push_data 5 0
63: VM_push_data 3 0
64: VM_push_data 3 0
65: VM_push_data 1 0
66: VM_push_data 7 0
67: VM_push_data 7 0
68: VM_push_data 11 0
69: VM_push_data 2 0
70: VM_push_data 1 0
71: VM_push_data 2 0
72: VM_push_data 7 0
73: VM_push_data 2 0
74: VM_push_data 12 0
75: VM_push_data 2 0
76: VM_push_data 2 0
77: mov 2 1 1给v2(这里v2是数量)
78: vm_LoadStack 1 2
79: vm_pop 0 0 出栈,放到v0
80: vm_subcmp 0 1 v0和v1比较
81: vm_jne 91 0 不相等就到91
82: mov 1 34 34给v1
83: vm_subcmp 2 1 v2和v1比较
84: vm_je 89 0 相等就到89
85: mov 1 1 1给v1
86: vm_add 2 1 v2++
87: vm_jmp 78 0 到78
================成功==================
88: mov 3 0
89: caseprint 0 0
90: vm_end 0 0
================结束==================
91: mov 3 1
92: caseprint 0 0
93: vm_end 0 0
94: eipadd
大体的流程是,输入17位Flag。对每一位,如果是小写,则(flag+1)^71,如果是大写,则(flag-1)^75。然后对每一位,除以16.商和余数分别压栈。再与已经在另一个栈中的数据分别比较,相同则正确。
编写解密脚本。
1 a=[7 2 ,13 3 ,0 4 ,5 5 ,1 6 ,12 7 ,1 8 ,0 9 ,0 10 ,13 11 ,5 12 ,15 13 ,0 14 ,9 15 ,5 16 ,15 17 ,3 18 ,0 19 ,2 20 ,5 21 ,3 22 ,3 23 ,1 24 ,7 25 ,7 26 ,11 27 ,2 28 ,1 29 ,2 30 ,7 31 ,2 32 ,12 33 ,2 34 ,2] 35 b=[0]*17 36 j=0 37 for i in range(0,len(a),2): 38 b[j]=a[i]*16+a[i+1] 39 j += 1 40 print(b) 41 t = 0 42 flag = '' 43 for i in range(len(b)): 44 t = (b[i]+1)^75 45 if t>=ord('A') and t<=ord('Z'): 46 flag += chr(t) 47 continue 48 t = (b[i]-1)^71 49 if t>=ord('a') and t<=ord('z'): 50 flag += chr(t) 51 continue 52 flag += chr(b[i]) 53 flag=flag[::-1] 54 print(flag)
flag{Such_A_EZVM}