分享

动手实现代码虚拟机 | WooYun知识库

 astrotycoon 2015-11-24
#!c #include "xvm.h" void target_func() { __asm__ __volatile__(".byte 0xa0, 0x10, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x11, 0x12, 0x00, 0x00, 0x00, 0xa4, 0xa0, 0x14, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x11, 0x29, 0x00, 0x00, 0x00, 0xa1, 0xa2, 0x20, 0xa6, 0x5b, 0xa0, 0x14, 0x01, 0x00, 0x00, 0x00, 0xa1, 0xa2, 0x21, 0xa6, 0x50, 0xa0, 0x14, 0x02, 0x00, 0x00, 0x00, 0xa1, 0xa2, 0x22, 0xa6, 0x45, 0xa0, 0x14, 0x03, 0x00, 0x00, 0x00, 0xa1, 0xa2, 0x23, 0xa6, 0x3a, 0xa0, 0x14, 0x04, 0x00, 0x00, 0x00, 0xa1, 0xa2, 0x24, 0xa6, 0x2f, 0xa0, 0x14, 0x05, 0x00, 0x00, 0x00, 0xa1, 0xa2, 0x25, 0xa6, 0x24, 0xa0, 0x14, 0x06, 0x00, 0x00, 0x00, 0xa1, 0xa2, 0x26, 0xa6, 0x19, 0xa0, 0x14, 0x07, 0x00, 0x00, 0x00, 0xa1, 0xa2, 0x27, 0xa6, 0x0f, 0xa0, 0x10, 0x30, 0x00, 0x00, 0x00, 0xa0, 0x11, 0x09, 0x00, 0x00, 0x00, 0xa5, 0xa3, 0xa0, 0x10, 0x40, 0x00, 0x00, 0x00, 0xa0, 0x11, 0x07, 0x00, 0x00, 0x00, 0xa5, 0xa3"); /* mov r1, 0x00000000 mov r2, 0x12 call vm_read ; 输入 mov r1, input[0] mov r2, 0x29 xor r1, r2 ; 异或 cmp r1, flag[0] ; 比较 jnz ERROR ; 如果不相同就跳转到输出错误的代码 ; 同上 mov r1, input[1] xor r1, r2 cmp r1, flag[1] jnz ERROR mov r1, input[2] xor r1, r2 cmp r1, flag[2] jnz ERROR mov r1, input[3] xor r1, r2 cmp r1, flag[3] jnz ERROR mov r1, input[4] xor r1, r2 cmp r1, flag[4] jnz ERROR mov r1, input[5] xor r1, r2 cmp r1, flag[5] jnz ERROR mov r1, input[6] xor r1, r2 cmp r1, flag[6] jnz ERROR mov r1, input[7] xor r1, r2 cmp r1, flag[7] jnz ERROR */ } /* * xor 指令解释函数 */ void vm_xor(vm_processor *proc) { // 异或的两个数据分别存放在r1,r2寄存器中 int arg1 = proc->r1; int arg2 = proc->r2; // 异或结果存在r1中 proc->r1 = arg1 ^ arg2; // xor指令只占一个字节,所以解释后,eip向后移动一个字节 proc->eip += 1; } /* * cmp 指令解释函数 */ void vm_cmp(vm_processor *proc) { // 比较的两个数据分别存放在r1和buffer中 int arg1 = proc->r1;、 // 字节码中包含了buffer的偏移 char *arg2 = *(proc->eip + 1) + heap_buf; // 比较并对flag寄存器置位,1为相等,0为不等 if (arg1 == *arg2) { proc->flag = 1; } else { proc->flag = 0; } // cmp指令占两个字节,eip向后移动2个字节 proc->eip += 2; } /* * jnz 指令解释函数 */ void vm_jnz(vm_processor *proc) { // 获取字节码中需要的地址相距eip当前地址的偏移 unsigned char arg1 = *(proc->eip + 1); // 通过比较flag的值来判断之前指令的结果,如果flag为零说明之前指令不想等,jnz跳转实现 if (proc->flag == 0) { // 跳转可以直接修改eip,偏移就是上面获取到的偏移 proc->eip += arg1; } else { proc->flag = 0; } // jnz 指令占2个字节,所以eip向后移动两个字节 proc->eip += 2; } /* * ret 指令解释函数 */ void vm_ret(vm_processor *proc) { } /* * read 系统调用解释函数 */ void vm_read(vm_processor *proc) { // read系统调用有两个参数,分别存放在r1,r2寄存器中,r1中是保存读入数据的buf的偏移,r2为希望读入的长度 char *arg2 = heap_buf + proc->r1; int arg3 = proc->r2; // 直接调用read read(0, arg2, arg3); // read系统调用占1个字节,所以eip向后移动1个字节 proc->eip += 1; } /* * write 系统调用解释函数 */ void vm_write(vm_processor *proc) { // 与read系统调用相同,r1中是保存写出数据的buf的偏移,r2为希望写出的长度 char *arg2 = heap_buf + proc->r1; int arg3 = proc->r2; // 直接调用write write(1, arg2, arg3); // write系统调用占1个字节,所以eip向后移动1个字节 proc->eip += 1; } /* * mov 指令解释函数 */ void vm_mov(vm_processor *proc) { // mov 指令两个参数都隐含在字节码中了,指令标识后的第一个字节是寄存器的标识,指令标识后的第二到第五个字节是要mov的立即数,目前只实现了mov一个立即数到一个寄存器中和mov一个buffer中的内容到一个r1寄存器 unsigned char *dest = proc->eip + 1; int *src = (int *) (proc->eip + 2); // 前4个case分别对应r1~r4,最后一个case中,*src保存的是buffer的一个偏移,实现了把buffer中的一个字节赋值给r1 switch (*dest) { case 0x10: proc->r1 = *src; break; case 0x11: proc->r2 = *src; break; case 0x12: proc->r3 = *src; break; case 0x13: proc->r4 = *src; break; case 0x14: proc->r1 = *(heap_buf + *src); break; } // mov指令占6个字节,所以eip向后移动6个字节 proc->eip += 6; } /* * 执行字节码 */ void exec_opcode(vm_processor *proc) { int flag = 0; int i = 0; // 查找eip指向的正在解释的字节码对应的处理函数 while (!flag && i < OPCODE_NUM) { if (*proc->eip == proc->op_table[i].opcode) { flag = 1; // 查找到之后,调用本条指令的处理函数,由处理函数来解释 proc->op_table[i].func((void *) proc); } else { i++; } } } /* * 虚拟机的解释器 */ void vm_interp(vm_processor *proc) { /* eip指向被保护代码的第一个字节 * target_func + 4是为了跳过编译器生成的函数入口的代码 */ proc->eip = (unsigned char *) target_func + 4; // 循环判断eip指向的字节码是否为返回指令,如果不是就调用exec_opcode来解释执行 while (*proc->eip != RET) { exec_opcode(proc); } } /* * 初始化虚拟机处理器 */ void init_vm_proc(vm_processor *proc) { proc->r1 = 0; proc->r2 = 0; proc->r3 = 0; proc->r4 = 0; proc->flag = 0; // 把指令字节码与解释函数关联起来 proc->op_table[0].opcode = MOV; proc->op_table[0].func = (void (*)(void *)) vm_mov; proc->op_table[1].opcode = XOR; proc->op_table[1].func = (void (*)(void *)) vm_xor; proc->op_table[2].opcode = CMP; proc->op_table[2].func = (void (*)(void *)) vm_cmp; proc->op_table[3].opcode = SYS_READ; proc->op_table[3].func = (void (*)(void *)) vm_read; proc->op_table[4].opcode = SYS_WRITE; proc->op_table[4].func = (void (*)(void *)) vm_write; proc->op_table[5].opcode = RET; proc->op_table[5].func = (void (*)(void *)) vm_ret; proc->op_table[6].opcode = JNZ; proc->op_table[6].func = (void (*)(void *)) vm_jnz; // 创建buffer heap_buf = (char *) malloc(HEAP_SIZE_MAX); // 初始化buffer memcpy(heap_buf + 0x20, "syclover", 8); memcpy(heap_buf + 0x30, "success!\n", 9); memcpy(heap_buf + 0x40, "error!\n", 7); } // flag: ZPJEF_L[ int main() { vm_processor proc = {0}; // initial vm processor init_vm_proc(&proc); // execute target func vm_interp(&proc); return 0; } 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约