• 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}

     

  • 相关阅读:
    成功熬了四年还没死?一个IT屌丝创业者的深刻反思
    史氏语录
    WEB安全攻防学习内容
    从程序员的角度谈创业三年
    Windows2008 R2修改3389端口教程
    Win2008R2 zip格式mysql 安装与配置
    制作支持UEFI PC的Server2008 R2系统安装U盘
    郎科U208(主控 PS2251-50 HYNIX H27UCG8T2MYR)量产还原
    自用有线IP切换
    自动配置IP地址.bat
  • 原文地址:https://www.cnblogs.com/EveningBreeze/p/13773930.html
Copyright © 2020-2023  润新知