2023 羊城杯 vm_wo

阿威在潜水 / 2023-09-06 / 原文

2023 羊城杯 vm_wo详解

 

这是一道Vm的题,第一次做这种题总结下,

VM框架

图片

大概就是VM框架中会模拟正常的CPU去读指令 然后执行指令。

然后会有1个全局变量

图片

然后会有一个dispatcher的程序 模拟CPU读取指令,然后去执行函数,就可以做到和真实的程序一样

 

writeUP

这道题的整体逻辑还是挺简单的

image-20230906145938906

 

进入 myoperate函数 ,这里是用两段大整数合成一段opcode,第一个数的最后一个字节被第二个数的第一字节覆盖,然后把input和Vm_body[0]插到opcode中,经过interpretBytecode后,就生成了新的input,所以第一步,先把这个opcode提取出来,按照这个程序的逻辑去生成,input随便弄一个字符

def get_opcode(v1, v2, x):
   l = list(v1.to_bytes(8, 'little'))
   l = l[:-1] + list(v2.to_bytes(8, 'little'))
   l[2] = x
   return l

opcode = get_opcode(0x20D01011903001A, 0x300010201180702, 0xFF)
opcode += get_opcode(0x20D02011903001A, 0x400010201180602, 0xFF)
opcode += get_opcode(0x20D03011903001A, 0x500010201180502, 0xFF)
opcode += get_opcode(0x20D04011903001A, 0x600010201180402, 0xFF)
print(opcode)
#[26, 0, 255, 25, 1, 1, 13, 2, 7, 24, 1, 2, 1, 0, 3, 26, 0, 255, 25, 1, 2, 13, 2, 6, 24, 1, 2, 1, 0, 4, 26, 0, 255, 25, 1, 3, 13, 2, 5, 24, 1, 2, 1, 0, 5, 26, 0, 255, 25, 1, 4, 13, 2, 4, 24, 1, 2, 1, 0, 6]

image-20230906150735457

然后进入interpretBytecode函数,这里有26个case处理,这里需要手动将其还原成对应的汇编语句,这里对之前的opcode进行一次去重得到[0, 1, 2, 3, 4, 5, 6, 7, 13, 24, 25, 26, 255] 所以只还原这几句就行

case0:

swap arr[{0}], arr[{1}]

case1:

xor arr[{0}], arr[{1}]

case 2:

add arr[] opcode2

case3:

add arr[] arr

case4:

sub arr arr

case 5:

sub arr arr

case 6:

mul arr opcode2

case 7:

mul arr arr

case 13:

shl arr[opcode1] arr[0] opcode2

case 24:

or arr[0] arr[1], arr[2]

case 25:

arr[opcode1]=arr[0]>>opcode2

case 26:

mov arr[opcode1]=opcode2

 

大概还原到自己可以看懂,主要是参数的个数不能弄混 会影响后面的还原

 

opcode=[26, 0, 255, 25, 1, 1, 13, 2, 7, 24, 1, 2, 1, 0, 3, 26, 0, 255, 25, 1, 2, 13, 2, 6, 24, 1, 2, 1, 0, 4, 26, 0, 255, 25, 1, 3, 13, 2, 5, 24, 1, 2, 1, 0, 5, 26, 0, 255, 25, 1, 4, 13, 2, 4, 24, 1, 2, 1, 0, 6]
ins_set = { 0: [3, 2, "swap arr[{0}], arr[{1}]"],
           1: [3, 2, "xor arr[{0}], arr[{1}]"],
           2: [3, 2, "add arr[{0}], 0x{1:0>2X}"],
           3: [3, 2, "add arr[{0}], arr[{1}]"],
           4: [3, 2, "sub arr[{0}], 0x{1:0>2X}"],
           5: [3, 2, "sub arr[{0}], arr[{1}]"],
           6: [3, 2, "mul arr[{0}], 0x{1:0>2X}"],
           7: [3, 2, "mul arr[{0}], arr[{1}]"],
           8: [3, 2, "div arr[{0}], 0x{1:0>2X}"],
           9: [3, 2, "div arr[{0}], arr[{1}]"],
          10: [3, 2, "mod arr[{0}], 0x{1:0>2X}"],
          11: [3, 2, "mod arr[{0}], arr[{1}]"],
          12: [3, 2, "shl arr[{0}], 0x{1:0>2X}"],
          13: [3, 2, "shl arr[{0}], arr[0], 0x{1:0>2X}"],
          14: [3, 1, "push arr[{0}]"],
          15: [3, 1, "print arr[{0}]"],
          16: [3, 0, "print [rsp]"],
          17: [3, 2, "if !arr[{0}]: jmp 0x{1:0>2X}"],
          18: [3, 2, "if arr[{0}]: jmp 0x{1:0>2X}"],
          19: [3, 1, "jmp 0x{0:0>2X}"],
          20: [3, 1, "push r[arr[{0}]]"],
          21: [3, 0, "pop arr[0]"],
          22: [3, 1, "push 0x{0:0>2X}"],
          23: [3, 0, "exit"],
          24: [3, 0, "or arr[0], arr[1], arr[2]"],
          25: [3, 2, "shr arr[{0}], arr[0], 0x{1:0>2X}"],
          26: [3, 2, "mov arr[{0}], 0x{1:0>2X}"]}

pc = 0
output = open('assembly.txt', 'w')
output.write("Addr   Code\n")
addrfmt = "{0:0>3}     "

func_start = [0]
lstFunc = 0
while pc < len(opcode):
   i = pc
   pc += ins_set[opcode[i]][0]
   output.write(addrfmt.format(i-lstFunc))
   if opcode[i] not in ins_set.keys():
       print("\033[0;31m[-] UknOpcode 0x{0:X} in addr 0x{1:0>8X}.\033[0m".format(opcode[i], i))
       break
   else:
       args=[]
       for j in range(ins_set[opcode[i]][1]):
           args.append(opcode[i+1+j])
       output.write(ins_set[opcode[i]][2].format(*args) + '\n')
   if pc in func_start:
       output.write('\n')
       lstFunc = pc

output.close()

就可以得到汇编语句了

Addr    Code
000     mov arr[0], 0xFF
003     shr arr[1], arr[0], 0x01
006     shl arr[2], arr[0], 0x07
009     or arr[0], arr[1], arr[2]
012     xor arr[0], arr[3]
015     mov arr[0], 0xFF
018     shr arr[1], arr[0], 0x02
021     shl arr[2], arr[0], 0x06
024     or arr[0], arr[1], arr[2]
027     xor arr[0], arr[4]
030     mov arr[0], 0xFF
033     shr arr[1], arr[0], 0x03
036     shl arr[2], arr[0], 0x05
039     or arr[0], arr[1], arr[2]
042     xor arr[0], arr[5]
045     mov arr[0], 0xFF
048     shr arr[1], arr[0], 0x04
051     shl arr[2], arr[0], 0x04
054     or arr[0], arr[1], arr[2]
057     xor arr[0], arr[6]

然后按照逻辑编写逆脚本


enc=[0xDF, 0xD5, 0xF1, 0xD1, 0xFF, 0xDB, 0xA1, 0xA5, 0x89, 0xBD, 0xE9, 0x95, 0xB3, 0x9D, 0xE9, 0xB3, 0x85, 0x99, 0x87, 0xBF, 0xE9, 0xB1, 0x89, 0xE9, 0x91, 0x89, 0x89, 0x8F, 0xAD]
arr = 0xBEEDBEEF.to_bytes(4, 'little')
def vm(s):
   s=(s >>1 |  s<<7) & 0xFF
   s=s^ arr[0]
   s = (s >> 2 | s << 6) & 0xFF
   s = s ^ arr[1]
   s = (s >> 3 | s << 5) & 0xFF
   s = s ^ arr[2]
   s = (s >> 4 | s << 4) & 0xFF
   s = s ^ arr[3]
   return s

def unvm(s):
   s=s^ arr[3]
   s = (s << 4 | s >> 4) & 0xFF
   s = s ^ arr[2]
   s = (s << 3 | s >> 5) & 0xFF
   s = s ^ arr[1]
   s = (s << 2 | s >> 6) & 0xFF
   s = s ^ arr[0]
   s = (s << 1 | s >> 7) & 0xFF
   return s

flag=[]
for i in enc:
   i= ((i >>3)  | (i<<5)&0xFF)
   flag.append(unvm(i))
print(bytes(flag))
# b'DASCTF{you_are_right_so_cool}'

 

# 参考文章

https://mp.weixin.qq.com/s/CI8_JmwS-IUsJJQdsY1lFw

https://mp.weixin.qq.com/s/e3KinwG89srM6_dEC1e5-w#at