2023 羊城杯 vm_wo详解
这是一道Vm的题,第一次做这种题总结下,
VM框架
大概就是VM框架中会模拟正常的CPU去读指令 然后执行指令。
然后会有1个全局变量
然后会有一个dispatcher的程序 模拟CPU读取指令,然后去执行函数,就可以做到和真实的程序一样
writeUP
这道题的整体逻辑还是挺简单的
进入 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]
然后进入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
标签:25,arr,wo,0x,2X,vm,26,opcode,2023 From: https://www.cnblogs.com/immune53/p/17682500.html