[GKCTF2020]EzMachine

参考资料:

https://blog.csdn.net/weixin_43876357/article/details/108488762

https://www.cnblogs.com/EveningBreeze/p/13773930.html

一直想动笔写ezmachine这个,去网上看wp的时候发现是vm,还从来没写过,然后尝试着分析,便很快放弃,代码量还好,就是老跳转,把自己跳傻了,今天在来挑战的试试看。

由于在写这道题之前就看过wp,知道这是一个模仿机器指令的程序,所以先想办法推出程序流程。

 

 

在浏览汇编的时候突然看到一些标红的代码段,并且在里面发现了混淆指令,把401594这里nop掉就行了

 看到函数,由于之前我用x32dbg动调的时候,看到了这一部分的汇编代码,我知道这是主要的跳转函数

 先看第一个dd offset sub_4011B0,之前动调经常看到这个全局变量,并且我推测这个全局变量是eip,所以这个指令可能就是eip+1

 看第二个dd offset sub_401000,这里把byte_4449a1的值给了eax与ecx,eip+3,a2的值存在off_4427fc与eax的一个偏移里面

看第三个dd offset sub_401070,把byte_4449a1的值给cl,在把这个值给到dword__445bac+esi,esi看起来像个index,并且每次调用都会加1

 看第四个dd offset sub_401030,从off_4427fc中选一个数组里的元素出来,在放入另一个数组中,并且esi加1

 看第五个dd offset sub_4010A0,把dword_445BAC数组里的最后一个放入off_4427FC数组里的元素,并且dword_445BC8-1

第六个dd offset sub_4010E0,一个switch语句,估计是打印结果的

.text:004010E0                 mov     eax, dword_445BCC
.text:004010E5                 mov     ecx, dword_445BA8
.text:004010EB                 cmp     eax, 4          ; switch 5 cases
.text:004010EE                 ja      loc_401187      ; jumptable 004010F4 default case
.text:004010F4                 jmp     ds:off_401198[eax*4] ; switch jump
.text:004010FB ; ---------------------------------------------------------------------------
.text:004010FB
.text:004010FB loc_4010FB:                             ; CODE XREF: sub_4010E0+14↑j
.text:004010FB                                         ; DATA XREF: .text:off_401198↓o
.text:004010FB                 mov     eax, ds:dword_4427D8 ; jumptable 004010F4 case 0
.text:00401100                 mov     [ecx], eax
.text:00401102                 mov     ax, ds:word_4427DC
.text:00401108                 push    ecx
.text:00401109                 mov     [ecx+4], ax
.text:0040110D                 call    sub_407C00
.text:00401112                 add     esp, 4
.text:00401115                 add     dword_445BD8, 3
.text:0040111C                 retn
.text:0040111D ; ---------------------------------------------------------------------------
.text:0040111D
.text:0040111D loc_40111D:                             ; CODE XREF: sub_4010E0+14↑j
.text:0040111D                                         ; DATA XREF: .text:off_401198↓o
.text:0040111D                 mov     eax, ds:dword_4427E0 ; jumptable 004010F4 case 1
.text:00401122                 mov     [ecx], eax
.text:00401124                 mov     ax, ds:word_4427E4
.text:0040112A                 push    ecx
.text:0040112B                 mov     [ecx+4], ax
.text:0040112F                 call    sub_407C00
.text:00401134                 add     esp, 4
.text:00401137                 add     dword_445BD8, 3
.text:0040113E                 retn
.text:0040113F ; ---------------------------------------------------------------------------
.text:0040113F
.text:0040113F loc_40113F:                             ; CODE XREF: sub_4010E0+14↑j
.text:0040113F                                         ; DATA XREF: .text:off_401198↓o
.text:0040113F                 movq    xmm0, ds:qword_4427E8 ; jumptable 004010F4 case 3
.text:00401147                 movq    qword ptr [ecx], xmm0
.text:0040114B                 mov     ax, ds:word_4427F0
.text:00401151                 mov     [ecx+8], ax
.text:00401155                 mov     al, ds:byte_4427F2
.text:0040115A                 push    ecx
.text:0040115B                 mov     [ecx+0Ah], al
.text:0040115E                 call    sub_407C00
.text:00401163                 add     esp, 4
.text:00401166                 add     dword_445BD8, 3
.text:0040116D                 retn
.text:0040116E ; ---------------------------------------------------------------------------
.text:0040116E
.text:0040116E loc_40116E:                             ; CODE XREF: sub_4010E0+14↑j
.text:0040116E                                         ; DATA XREF: .text:off_401198↓o
.text:0040116E                 mov     eax, ds:dword_4427F4 ; jumptable 004010F4 case 4
.text:00401173                 mov     [ecx], eax
.text:00401175                 mov     ax, ds:word_4427F8
.text:0040117B                 mov     [ecx+4], ax
.text:0040117F                 mov     al, ds:byte_4427FA
.text:00401184                 mov     [ecx+6], al
.text:00401187
.text:00401187 loc_401187:                             ; CODE XREF: sub_4010E0+E↑j
.text:00401187                                         ; sub_4010E0+14↑j
.text:00401187                                         ; DATA XREF: ...
.text:00401187                 push    ecx             ; jumptable 004010F4 default case
.text:00401188                 call    sub_407C00
.text:0040118D                 add     dword_445BD8, 3
.text:00401194                 add     esp, 4
.text:00401197                 retn
.text:00401197 sub_4010E0      endp
.text:00401197
.text:00401197 ; ---------------------------------------------------------------------------
.text:00401198 off_401198      dd offset loc_4010FB    ; DATA XREF: sub_4010E0+14↑r
.text:00401198                 dd offset loc_40111D    ; jump table for switch statement
.text:00401198                 dd offset loc_401187
.text:00401198                 dd offset loc_40113F
.text:00401198                 dd offset loc_40116E
.text:004011AC                 align 10h

 第七个dd offset sub_4011D0 ,两个值相加

 第8个dd offset sub_401200,两个数相减

 第9个dd offset sub_401230,两个数相乘

 第10个dd offset sub_401270,两个数相除,dword_445bdc存放商,dword_445bc4存放余数

 第11个dd offset sub_4012B0,两个数做异或

 第12个dd offset sub_4012E0,不太清楚

第13个 dd offset sub_401300,两个数相减,并存放到dword_445bcc

 第14个dd offset sub_401340,这个也没看懂干嘛

 第15个dd offset sub_401370,感觉都跟上面一个样

 第16个dd offset sub_4013A0-17几乎都是跟上面一样

第18个dd offset sub_401400,计算字符串的长度

 第19个dd offset sub_401430,没看懂

 第20个dd offset sub_401470,一个值加上dword_445bd0在加上dword_445bac偏移给另一个值

第21个dd offset sub_4014B0,跟上面的差不都

 

 第22个dd offset sub_4011C0,不知道干啥

 把opcode和数据全部导出来,并写下脚本,把程序流程打印出来

#字节码
code =[
  0x01, 0x03, 0x03, 0x05, 0x00, 0x00, 0x11, 0x00, 0x00, 0x01, 
  0x01, 0x11, 0x0C, 0x00, 0x01, 0x0D, 0x0A, 0x00, 0x01, 0x03, 
  0x01, 0x05, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x01, 0x02, 0x00, 
  0x01, 0x00, 0x11, 0x0C, 0x00, 0x02, 0x0D, 0x2B, 0x00, 0x14, 
  0x00, 0x02, 0x01, 0x01, 0x61, 0x0C, 0x00, 0x01, 0x10, 0x1A, 
  0x00, 0x01, 0x01, 0x7A, 0x0C, 0x00, 0x01, 0x0F, 0x1A, 0x00, 
  0x01, 0x01, 0x47, 0x0A, 0x00, 0x01, 0x01, 0x01, 0x01, 0x06, 
  0x00, 0x01, 0x0B, 0x24, 0x00, 0x01, 0x01, 0x41, 0x0C, 0x00, 
  0x01, 0x10, 0x24, 0x00, 0x01, 0x01, 0x5A, 0x0C, 0x00, 0x01, 
  0x0F, 0x24, 0x00, 0x01, 0x01, 0x4B, 0x0A, 0x00, 0x01, 0x01, 
  0x01, 0x01, 0x07, 0x00, 0x01, 0x01, 0x01, 0x10, 0x09, 0x00, 
  0x01, 0x03, 0x01, 0x00, 0x03, 0x00, 0x00, 0x01, 0x01, 0x01, 
  0x06, 0x02, 0x01, 0x0B, 0x0B, 0x00, 0x02, 0x07, 0x00, 0x02, 
  0x0D, 0x00, 0x02, 0x00, 0x00, 0x02, 0x05, 0x00, 0x02, 0x01, 
  0x00, 0x02, 0x0C, 0x00, 0x02, 0x01, 0x00, 0x02, 0x00, 0x00, 
  0x02, 0x00, 0x00, 0x02, 0x0D, 0x00, 0x02, 0x05, 0x00, 0x02, 
  0x0F, 0x00, 0x02, 0x00, 0x00, 0x02, 0x09, 0x00, 0x02, 0x05, 
  0x00, 0x02, 0x0F, 0x00, 0x02, 0x03, 0x00, 0x02, 0x00, 0x00, 
  0x02, 0x02, 0x00, 0x02, 0x05, 0x00, 0x02, 0x03, 0x00, 0x02, 
  0x03, 0x00, 0x02, 0x01, 0x00, 0x02, 0x07, 0x00, 0x02, 0x07, 
  0x00, 0x02, 0x0B, 0x00, 0x02, 0x02, 0x00, 0x02, 0x01, 0x00, 
  0x02, 0x02, 0x00, 0x02, 0x07, 0x00, 0x02, 0x02, 0x00, 0x02, 
  0x0C, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 
  0x01, 0x13, 0x01, 0x02, 0x04, 0x00, 0x00, 0x0C, 0x00, 0x01, 
  0x0E, 0x5B, 0x00, 0x01, 0x01, 0x22, 0x0C, 0x02, 0x01, 0x0D, 
  0x59, 0x00, 0x01, 0x01, 0x01, 0x06, 0x02, 0x01, 0x0B, 0x4E, 
  0x00, 0x01, 0x03, 0x00, 0x05, 0x00, 0x00, 0xFF, 0x00, 0x00, 
  0x01, 0x03, 0x01, 0x05, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00
]
#指令集
opcodekey = {0:'pcadd',1:'mov',2:'push',3:'?',4:'pop',5:'caseprint',6:'add',7:'sub1',8:'mul',9:'div',10:'xor',11:'jmp',12:'subcmp',13:'jedx4',14:'jnedx4',15:'jedx4orlow',16:'jedx4orhight',17:'input',18:'?2',19:'LoadStack',20:'LoadString',0xFF:'end'}
 
#B为字节码的下标
B=0
#C为每一行的序号
C=1
print(str(C)+": ",end="")
#字节码三个一组,每三个的第一个是指令,后两个为操作数
#i为字节码的遍历
for i in code:
   #print(hex(i)+" ",end="")
    if B==0:
        #print(i)
        op=opcodekey[i]
        #opcodekey【每三个的第一个字节码】所对应的指令
        print(op,end=' ')
    else:
        print(i,end=" ")
    B=B+1
    #每三个转下一行,也就是下一个汇编
    if B==3:
        B=0
        C=C+1
        print()
        print(str(C)+": ",end="")

得到如下程序流程

1: mov 3 3
2: caseprint 0 0
3: input 0 0
4: mov 1 17
5: subcmp 0 1
6: jedx4 10 0
7: mov 3 1
8: caseprint 0 0
9: end 0 0
10: mov 2 0
11: mov 0 17
12: subcmp 0 2
13: jedx4 43 0
14: LoadString 0 2
15: mov 1 97
16: subcmp 0 1
17: jedx4orhight 26 0
18: mov 1 122
19: subcmp 0 1
20: jedx4orlow 26 0
21: mov 1 71
22: xor 0 1
23: mov 1 1
24: add 0 1
25: jmp 36 0
26: mov 1 65
27: subcmp 0 1
28: jedx4orhight 36 0
29: mov 1 90
30: subcmp 0 1
31: jedx4orlow 36 0
32: mov 1 75
33: xor 0 1
34: mov 1 1
35: sub1 0 1
36: mov 1 16
37: div 0 1
38: ? 1 0
39: ? 0 0
40: mov 1 1
41: add 2 1
42: jmp 11 0
43: push 7 0
44: push 13 0
45: push 0 0
46: push 5 0
47: push 1 0
48: push 12 0
49: push 1 0
50: push 0 0
51: push 0 0
52: push 13 0
53: push 5 0
54: push 15 0
55: push 0 0
56: push 9 0
57: push 5 0
58: push 15 0
59: push 3 0
60: push 0 0
61: push 2 0
62: push 5 0
63: push 3 0
64: push 3 0
65: push 1 0
66: push 7 0
67: push 7 0
68: push 11 0
69: push 2 0
70: push 1 0
71: push 2 0
72: push 7 0
73: push 2 0
74: push 12 0
75: push 2 0
76: push 2 0
77: mov 2 1
78: LoadStack 1 2
79: pop 0 0
80: subcmp 0 1
81: jnedx4 91 0
82: mov 1 34
83: subcmp 2 1
84: jedx4 89 0
85: mov 1 1
86: add 2 1
87: jmp 78 0
88: mov 3 0
89: caseprint 0 0
90: end 0 0
91: mov 3 1
92: caseprint 0 0
93: end 0 0
94: pcadd

在分析源程序

 整个程序的逻辑大概是这样的:(开了两个栈)
首先判断你输入的是不是17位,不是直接跳转报错,是的话进行判断。
如果是小写字母,那就xor71+1,最后除以16,将商和余数压栈。(即十位和个位)
如果是大写字母,那就xor75-1,最后除以16,将商和余数压栈。(即十位和个位)
其余的直接除16压栈。
然后压入对照数据,最后弹栈比较。

最终脚本

array = [0x7,0xd,0x0,0x5,0x1,0xc,0x1,0x0,0x0,0xd,0x5,0xf,0x0,0x9,0x5,0xf,0x3,0x0,0x2,0x5,0x3,0x3,0x1,0x7,0x7,0xb,0x2,0x1,0x2,0x7,0x2,0xc,0x2,0x2,]
#注意是压栈,先进后出,所以要[::-1]
array = array[::-1] 
#先输出的是个位然后是十位,两个一组
for i in range(0, len(array), 2): 
    c = array[i] + array[i+1]*16 
    tmp = (c-1) ^ 71 
    #如果是小写字母
    if tmp >= ord('a') and tmp <= ord('z'): 
        print(chr(tmp), end = "") 
        continue 
    tmp = (c+1) ^ 75 
    #如果是大写字母
    if tmp >= ord('A') and tmp <= ord('Z'): 
        print(chr(tmp), end = "") 
        continue
    #都不是 
    print(chr(c), end = "") 

学到的知识

vm就是虚拟的机器指令

cdq指令,把eax的符号位给edx的每一位

原文地址:https://www.cnblogs.com/pppyyyzzz/p/13850626.html