原理
程序运行时通过解释操作码,选择对应的函数(handle)执行
本质上是作者自己实现了一套指令集
比如x86汇编中的0xFF是jmp,而0xE0, 0x48 mov
在虚拟机中既可以自定义指令对应的操作码
还可以自定义数组实现cpu的功能

原始代码->前端编译器->虚拟指令(vmcode)->虚拟cpu解释执行->计算结果
0x01从零到一实现虚拟机
先从正向了解虚拟机
1.常量定义
1 2 3 4 5 6 7 8
| #define REG_COUNT 8 #define MEM_SIZE 256 #define STACK_SIZE 64 #define INSN_SIZE 4
#define FLAG_EQ 0 #define FLAG_LT 1 #define FLAG_GT 2
|
2.指令集定定义(Opcode枚举)
最基础的操作码,ida常见于汇编
将在后面给出每个指令的具体实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| typedef enum { OP_NOP = 0x00, /* 什么也不做,常用作填充 */ OP_MOV = 0x01, /* r[dst] = r[src1] 寄存器间赋值 */ OP_LDI = 0x02, /* r[dst] = (src1<<8)|src2 加载16位立即数 */ OP_LOAD = 0x03, /* r[dst] = mem[r[src1]] ,mem是memory(内存) 从内存读取数据 */ OP_STORE = 0x04, /* mem[r[dst]] = r[src1] 写入内存 */ OP_ADD = 0x05, /* r[dst] = r[src1] + r[src2] */ OP_SUB = 0x06, /* r[dst] = r[src1] - r[src2] */ OP_XOR = 0x07, /* r[dst] = r[src1] ^ r[src2] */ OP_AND = 0x08, /* r[dst] = r[src1] & r[src2] */ OP_OR = 0x09, /* r[dst] = r[src1] | r[src2] */ OP_SHL = 0x0A, /* r[dst] = r[src1] << src2 左移,src2是立即数 */ OP_SHR = 0x0B, /* r[dst] = r[src1] >> src2 右移 */ OP_CMP = 0x0C, /* flag = cmp(r[dst], r[src1]) 比较,设置标志位 */ OP_JMP = 0x0D, /* pc = (src1<<8)|src2 无条件跳转(绝对地址) */ OP_JEQ = 0x0E, /* if flag==FLAG_EQ: pc = (src1<<8)|src2 */ OP_JNE = 0x0F, /* if flag!=FLAG_EQ: pc = (src1<<8)|src2 */ OP_PUSH = 0x10, /* stack[sp++] = r[dst] */ OP_POP = 0x11, /* r[dst] = stack[--sp] */ OP_CALL = 0x12, /* push(pc+INSN_SIZE); pc = addr 子程序调用 */ OP_RET = 0x13, /* pc = pop() 从子程序返回 */ OP_IN = 0x14, /* r[dst] = getchar() 读一个字节输入 */ OP_OUT = 0x15, /* putchar(r[dst]) 输出一个字节 */ OP_HALT = 0xFF, /* 停机 */ } Opcode;
|
flag是标志寄存器,用于存储CMP的比较结果
1 2 3 4
| // OP_CMP: r[dst] 与 r[src1] 比较 if (reg[dst] == reg[src1]) flag = FLAG_EQ; // 0 else if (reg[dst] < reg[src1]) flag = FLAG_LT; // 1 else flag = FLAG_GT; // 2
|
pc是程序计数器:指向下一条指令的地址
| 正常执行 |
pc += INSN_SIZE(即 +4,取下一条指令) |
| 跳转 |
pc = target(强制改道) |
| CALL |
先压栈返回地址,再 pc = target |
| RET |
从栈弹出返回地址,pc = 返回地址 |
| HALT |
停止,pc 不再前进 |
3.状态结构体
1 2 3 4 5 6 7 8 9
| typedef struct{ uint32_t regs[REG_COUNT]; uint32_t pc; uint8_t flag uint8_t mem[MEM_SIZE]; uint32_t stack[STACK_SIZE]; int sp; int halted; }VM;
|
看到状态结构体就想到AES的状态矩阵
4.核心的执行函数:vm_step()
先取指:从内存中读取数据
reference
CTF 逆向 VM 题型:从零到一认识与逆向虚拟机 - wes1’s blog