VM
简介
一个虚拟机指令集,相当于自己写了个虚拟机,执行自己写的指令,来实现程序的运行,我觉得有点套中套的味道了
要求
分析代码能力,扎实的汇编基础,chatgpt的熟练使用
例子
简单学习一下vm,以WcyVM(nctf2018)为例

这就是一个经典的vm题目,下面的一堆case就分别代表不同的指令集,上面的v4,v5就代表不同的寄存器,用来存储和操作数据,那么我们首先需要做什么呢?
opcode

opcode也就是操作码,用来判断这个程序是根据什么顺序运行的,有些题的opcode是只有操作的,但是有些题的opcode还会有带上参数什么的,这题就是
寄存器

操作分析
case 0x8


传入R和数据,a2值传入a1,相当于mov指令
case 0x9
1  | sub_40096D(v4[*(_DWORD *)(v5[0] + 4) - 1], &v4[4], v5);  | 
1  | _QWORD *__fastcall sub_40096D(_DWORD *a1, _DWORD **a2, _QWORD *a3)  | 
传入R和栈,a2传入a1,a2++,相当于pop指令
case 0xa
1  | sub_400927(v4[*(_DWORD *)(v5[0] + 4) - 1], &v4[4], v5);  | 
1  | _QWORD *__fastcall sub_400927(_DWORD *a1, _QWORD *a2, _QWORD *a3)  | 
传入R和栈,a2-4,a1传入a2,相当于push指令
case 0xb,0xc
1  | sub_4009B3(v4[0], v5);  | 
1  | _QWORD *__fastcall sub_4009B3(int *a1, _QWORD *a2)  | 
1  | _QWORD *__fastcall sub_4009E5(int *a1, _QWORD *a2)  | 
传入R0,R0=getchar(),R0=putchar()操作
case 0xd
1  | sub_400B5D(&v1, v4[*(_DWORD *)(v5[0] + 4) - 1], v4[*(_DWORD *)(v5[0] + 8) - 1], v5);  | 
1  | _QWORD *__fastcall sub_400B5D(_DWORD *a1, _DWORD *a2, _DWORD *a3, _QWORD *a4)  | 
比较难的部分,但不是很关键的部分
传入了两个R和v1,进行了比较,只有相等了才能使v1=80然后接下来下面的操作
case 0xe
1  | sub_400A34(v5, *(unsigned int *)(v5[0] + 4), dest);  | 
1  | _QWORD *__fastcall sub_400A34(_QWORD *a1, int a2, __int64 a3)  | 
传入了数据和dest,使v5发生改变,相当于是一个跳转指令,jmp
case 0xf,0x10
1  | sub_400AAF(v1, v5, *(unsigned int *)(v5[0] + 4), dest);  | 
1  | __int64 *__fastcall sub_400AAF(char a1, __int64 *a2, unsigned int a3, __int64 a4)  | 
1  | __int64 *__fastcall sub_400A61(char a1, __int64 *a2, unsigned int a3, __int64 a4)  | 
传入数据和dest和v1,两个代码的if和else反一下,其实就是一个是jz,一个是jnz
case 0x11,0x12
1  | sub_400AFD(v4[*(_DWORD *)(v5[0] + 4) - 1], v5);  | 
1  | _QWORD *__fastcall sub_400AFD(_DWORD *a1, _QWORD *a2)  | 
1  | _QWORD *__fastcall sub_400B2D(_DWORD *a1, _QWORD *a2)  | 
只传入了R,很简单的+1,-1操作,inc,dec
case 0x13,0x14,0x15,0x16,0x17,0x1D
1  | sub_400C0E((_DWORD *)v4[*(_DWORD *)(v5[0] + 4) - 1], *(_DWORD *)(v5[0] + 8), v5);  | 
1  | sub_400C43((_DWORD *)v4[*(_DWORD *)(v5[0] + 4) - 1], (_DWORD *)v4[*(_DWORD *)(v5[0] + 8) - 1], v5);  | 
很简单的+,-,&,|,*操作,看好是传入数据还是R就行
case 0x19,0x1A,0x1B,0x1C
1  | sub_400889((_DWORD *)v4[*(_DWORD *)(v5[0] + 4) - 1], v4[*(_DWORD *)(v5[0] + 8) - 1], v5);  | 
各种类型的mov操作,地址啥的
opcode——汇编还原
1  | opcode = [  | 
1  | _00: mov R0 0  | 
因为比较长,看不懂的话直接交给gpt处理,当然其实重要的代码就这么点
分析
1  | R0 = 0 // 初始化 R0 为 0  | 
最重要的加密就在label3里面
找到密文就可以写解密脚本了
1  | 
  |