/* radare - LGPL - Copyright 2008-2010 pancake */ #include "r_vm.h" #include "p/plugins.h" static ut64 r_vm_get_value(RVm *vm, const char *str) { ut64 ret = 0LL; for (;*str&&*str==' ';str=str+1); if (str[0]=='$' && str[1]=='$') { #if TODO struct aop_t aop; char w[32]; if (str[2]=='$') { // $$$ ret = r_vm_reg_get (vm, vm->cpu.pc); arch_aop (ret , config.block, &aop); return aop.length; } else return config.seek; // $$ #endif } if (str[0]=='0' && str[1]=='x') sscanf (str, "0x%"PFMT64x"", &ret); else if (str[0]>='0' && str[0]<='9') sscanf (str, "%"PFMT64d"", &ret); else ret = r_vm_reg_get (vm, str); return ret; } static ut64 r_vm_get_math(struct r_vm_t *vm, const char *str) { int len; char *p,*a; len = strlen (str)+1; p = alloca (len); memcpy (p, str, len); a = strchr (p,'+'); if (a) { *a='\0'; return r_vm_get_value(vm, p) + r_vm_get_value(vm, a+1); } a = strchr (p,'-'); if (a) { *a='\0'; return r_vm_get_value(vm, p) - r_vm_get_value(vm, a+1); } a = strchr (p,'*'); if (a) { *a='\0'; return r_vm_get_value(vm, p) * r_vm_get_value(vm, a+1); } a = strchr (p,'/'); if (a) { ut64 d = r_vm_get_value(vm, a+1); ut64 n = r_vm_get_value(vm, p); *a='\0'; return d?n/d:0; } a = strchr (p,'&'); if (a) { *a='\0'; return r_vm_get_value (vm, p) & r_vm_get_value(vm, a+1); } a = strchr (p,'|'); if (a) { *a='\0'; return r_vm_get_value (vm, p) | r_vm_get_value(vm, a+1); } a = strchr (p,'^'); if (a) { *a='\0'; return r_vm_get_value (vm, p) ^ r_vm_get_value(vm, a+1); } a = strchr (p,'%'); if (a) { ut64 d = r_vm_get_value(vm, a+1); ut64 n = r_vm_get_value(vm, p); *a='\0'; return d?n%d:0; } a = strchr (p,'>'); if (a) { *a='\0'; return r_vm_get_value(vm, p) >> r_vm_get_value(vm, a+1); } a = strchr(p,'<'); if (a) { *a='\0'; return r_vm_get_value (vm, p) << r_vm_get_value(vm, a+1); } return r_vm_get_value (vm, p); } R_API void r_vm_print(RVm *vm, int type) { struct list_head *pos; if (type == -2) printf("fs vm\n"); list_for_each (pos, &vm->regs) { struct r_vm_reg_t *r = list_entry(pos, struct r_vm_reg_t, list); if (type == -2) { eprintf("f vm.%s @ 0x%08"PFMT64x"\n", r->name, r->value); } else { if (type == -1 || type == r->type) eprintf(".%s\t%s = 0x%08"PFMT64x"\n", r_vm_reg_type(r->type), r->name, (r->get!=NULL)?r_vm_reg_get(vm, r->name):r->value); } } if (type == -2) printf ("fs *\n"); } R_API int r_vm_reg_add(struct r_vm_t *vm, const char *name, int type, ut64 value) { RVmReg *r = (RVmReg*)malloc (sizeof (RVmReg)); if (r == NULL) return 0; strncpy (r->name, name, 15); r->type = type; r->value = value; r->get = NULL; r->set = NULL; list_add_tail (&(r->list), &vm->regs); return 1; } // XXX: deprecate R_API int r_vm_import(struct r_vm_t *vm, int in_vm) { char name[64]; struct list_head *pos; //eprintf ("Importing register values\n"); list_for_each(pos, &vm->regs) { struct r_vm_reg_t *r = list_entry(pos, struct r_vm_reg_t, list); snprintf(name, 63, "vm.%s", r->name); if (in_vm) { r->value = r_num_get (NULL, name); // XXX doesnt work for eflags and so } else r->value = r_num_get (NULL, r->name); // XXX doesnt work for eflags and so printf ("f %s @ 0x%08llx\n", name, r->value); } return 0; } R_API void r_vm_cpu_call(struct r_vm_t *vm, ut64 addr) { /* x86 style */ r_vm_stack_push (vm, r_vm_reg_get (vm, vm->cpu.pc)); r_vm_reg_set (vm, vm->cpu.pc, addr); // XXX this should be the next instruction after pc (we need insn length here) } R_API RVm *r_vm_new() { RVm *vm = R_NEW (RVm); if (vm) r_vm_init (vm, 1); return vm; } R_API int r_vm_set_arch(RVm *vm, const char *name, int bits) { const char *profile = NULL; if (strstr (name, "x86")) { switch (bits) { case 16: profile = vmprofile_x86_16; break; case 32: profile = vmprofile_x86_32; break; case 64: profile = vmprofile_x86_64; break; } } else if (strstr (name, "arm")) { switch (bits) { case 16: profile = vmprofile_arm_32; // XXX 16; break; case 32: profile = vmprofile_arm_32; break; } } if (profile) { char *str = strdup (profile); r_vm_init (vm, 2); r_vm_cmd_eval (vm, str); free (str); } else eprintf ("r_vm: No profile found for '%s' on %d bits\n", name, bits); return 0; } // This is conceptually rotten R_API int r_vm_init(RVm *vm, int init) { if (init) { vm->log = 0; vm->use_mmu_cache = 0; INIT_LIST_HEAD (&vm->mmu_cache); INIT_LIST_HEAD (&vm->regs); INIT_LIST_HEAD (&vm->ops); memset (&vm->cpu, '\0', sizeof(RVmCpu)); if (init==2) return 0; } //vm_mmu_real(vm, config_get_i("vm.realio")); //vm_setup_call("[ebp-4]", "[ebp-8]", "[ebp-12]", "edx"); r_vm_setup_fastcall (vm, "eax", "ebx", "ecx", "edx"); //vm_setup_loop("ecx"); //vm_setup_copy("esi", "edi"); // TODO: do the same for fpregs and mmregs return 0; } R_API int r_vm_eval_cmp(RVm *vm, const char *str) { int len; char *p, *ptr; for (;*str==' ';str=str+1); len = strlen (str)+1; ptr = alloca (len); memcpy (ptr, str, len); p = strchr (ptr, ','); if (!p) p = strchr (ptr, ' '); if (p) { r_vm_reg_set (vm, vm->cpu.zf,(r_vm_get_math(vm, ptr)-r_vm_get_math(vm, p+1))); p = '\0'; return 0; } return 1; } R_API int r_vm_eval_eq(RVm *vm, const char *str, const char *val) { char *p; ut8 buf[64]; ut64 _int8 = 0; ut16 _int16 = 0; ut32 _int32 = 0; ut64 _int64 = 0; for(;*str==' ';str=str+1); for(;*val==' ';val=val+1); if (*str=='[') { // USE MMU // [foo] = 33, [reg] = 33 if (*val=='[') { // [0x804800] = [0x30480] ut64 off = r_vm_get_math(vm, val+1); p = strchr(val+1,':'); if (p) { int size = atoi(val+1); off = r_vm_get_math(vm, p+1); switch(size) { case 8: r_vm_mmu_read(vm, off, buf, 1); r_vm_mmu_write(vm, off, buf, 1); break; case 16: r_vm_mmu_read(vm, off, buf, 2); r_vm_mmu_write(vm, off, buf, 2); break; case 64: r_vm_mmu_read(vm, off, buf, 8); r_vm_mmu_write(vm, off, buf, 8); break; default: r_vm_mmu_read(vm, off, buf, 4); r_vm_mmu_write(vm, off, buf, 4); } } else { r_vm_mmu_read(vm, off, (ut8*)&_int32, 4); //off = r_vm_get_math(val); r_vm_mmu_write(vm, off, (void*)&_int32, 4); } } else { // [0x804800] = eax // use ssssskcvtgvmu ut64 off = r_vm_get_math(vm, str+1); // XXX support 64 bits here ut32 v = (ut32)r_vm_get_math(vm, val); // TODO control endian p = strchr (str+1,':'); if (vm->log) eprintf (" ; ==> [0x%08"PFMT64x"] = %x ((%s))\n", off, v, str+1); if (p) { int size = atoi (val+1); off = r_vm_get_math (vm, p+1); printf(" write size: %d\n", size); switch(size) { case 8: r_vm_mmu_write (vm, off, buf, 1); break; case 16: r_vm_mmu_write (vm, off, buf, 2); break; case 64: r_vm_mmu_write (vm, off, buf, 8); break; default: r_vm_mmu_write (vm, off, buf, 4); } } else { if (vm->log) eprintf (" ; write %x @ 0x%08"PFMT64x"\n", v, off); r_vm_mmu_write (vm, off, (ut8*)&v, 4); } } } else { // USE REG // reg = [foo] , reg = 33 if (*val=='[') { // use mmu ut64 off; ut32 _int32 = 0; p = strchr(val+1,':'); if (p) { int size = atoi(val+1); off = r_vm_get_math(vm, p+1); switch(size) { case 8: r_vm_mmu_read(vm, off, (ut8*)&_int8, 1); r_vm_reg_set(vm, str, (ut64)_int8); break; case 16: r_vm_mmu_read(vm, off, (ut8*)&_int16, 2); r_vm_reg_set(vm, str, (ut64)_int16); break; case 64: r_vm_mmu_read(vm, off, (ut8*)&_int64, 8); r_vm_reg_set(vm, str, (ut64)_int64); break; default: r_vm_mmu_read(vm, off, (ut8*)&_int32, 4); r_vm_reg_set(vm, str, (ut64)_int32); } } else { off = r_vm_get_math(vm, val+1); r_vm_mmu_read(vm, off, (ut8*)&_int32, 4); r_vm_reg_set(vm, str, (ut64)_int32); } } else r_vm_reg_set(vm, str, r_vm_get_math(vm, val)); } return 0; } R_API int r_vm_eval_single(RVm *vm, const char *str) { char *ptr, *eq; char buf[128]; int i, len; // if (log) //fprintf(stderr," ; %s\n", str); for(;str&&str[0]==' ';str=str+1); len = strlen(str)+1; ptr = alloca(len); memcpy(ptr, str, len); //eprintf("EVAL(%s)\n", str); /* TODO: sync with r1 */ eq = strchr(ptr, '='); if (eq) { eq[0]='\0'; switch(eq[-1]) { case '+': case '-': case '*': case '/': case '&': case '^': case '%': case '|': case '<': case '>': snprintf(buf, 127, "%s%c%s", ptr, eq[-1], eq+1); r_vm_eval_eq(vm, ptr, buf); //printf("EQ(%s)(%s)\n", ptr, buf); break; case ' ': i=-1; do { eq[i--]='\0'; } while(eq[i]==' '); default: //printf("EQ(%s)(%s)\n", ptr, eq+1); r_vm_eval_eq(vm, ptr, eq+1); } eq[0]='='; } else { //eprintf("Unknown opcode\n"); if (!memcmp(ptr, "if ", 3)) { if (r_vm_reg_get(vm, ptr+3)!=0) return -1; } else if (!memcmp(ptr, "ifnot ", 6)) { if (r_vm_reg_get(vm, ptr+6)==0) return -1; } else if (!memcmp(ptr, "cmp ", 4)) { r_vm_eval_cmp(vm, str+5); } else return 0; if (!memcmp(ptr, "syscall", 6)) { if (vm->log) eprintf("TODO: syscall interface not yet implemented\n"); } else if((!memcmp (ptr, "call ", 4)) || (!memcmp (ptr, "jmp ", 4))){ if (ptr[0]=='c') r_vm_stack_push(vm, r_vm_get_value(vm, vm->cpu.pc)); eprintf ("CALL(%s)\n", ptr+4); r_vm_reg_set(vm, vm->cpu.pc, r_vm_get_value(vm, ptr+4)); } else if (!memcmp (ptr, "jz ", 3)){ if (r_vm_reg_get (vm, ptr+3)==0) r_vm_reg_set(vm, vm->cpu.pc, r_vm_get_value(vm, ptr+3)); } else if (!memcmp (ptr, "jnz ", 4)){ if (r_vm_reg_get(vm, ptr+4)==0) r_vm_reg_set (vm, vm->cpu.pc, r_vm_get_value(vm, ptr+4)); } else if (!memcmp(ptr, "push ", 5)) { r_vm_stack_push (vm, r_vm_get_value(vm, str+5)); } else if (!memcmp(str, "pop ", 4)) { r_vm_stack_pop (vm, str+5); } else if (!memcmp(ptr, "ret", 3)) { r_vm_stack_pop(vm, vm->cpu.pc); if (vm->log) eprintf("RET (%x)\n", (ut32)vm->cpu.pc); } else if (vm->log) eprintf("r_vm: Unknown opcode\n"); } return 0; } R_API int r_vm_eval(RVm *vm, const char *str) { char *next, *ptr; int ret, len = strlen(str)+1; ptr = alloca (len); memcpy (ptr, str, len); #if 0 r_vm_mmu_real (vm, 0); r_vm_mmu_real(vm, config_get_i("vm.realio")); .int32 eax alias-get alias-set .alias eax get set #endif do { next = strchr(ptr, ','); if (next) { next[0]='\0'; ret = r_vm_eval_single(vm, ptr); if (ret == -1) return 0; next[0]=','; ptr = next +1; } } while (next); r_vm_eval_single (vm, ptr); return R_TRUE; } R_API int r_vm_eval_file(struct r_vm_t *vm, const char *str) { char buf[1024]; FILE *fd = fopen(str, "r"); if (fd) { while (!feof(fd)) { *buf='\0'; fgets(buf, 1023, fd); if (*buf) { buf[strlen(buf)-1]='\0'; //r_vm_eval(vm, buf); r_vm_op_eval (vm, buf); } } fclose (fd); return R_TRUE; } return R_FALSE; } /* XXX: this must go in core, not here! i.. or add&use RIOBind? emulate n opcodes */ R_API int r_vm_emulate(struct r_vm_t *vm, int n) { #if 0 ut64 pc; char str[128]; ut8 buf[128]; int opsize; int op = config_get_i("asm.pseudo"); struct aop_t aop; printf("Emulating %d opcodes\n", n); ///vm_init(1); vm_mmu_real(config_get_i("vm.realio")); vm_import(0); while(n--) { pc = vm_reg_get(vm->cpu.pc); udis_init(); udis_set_pc(pc); vm_mmu_read(pc, buf, 32); //fprintf(stderr,"(%02x %02x)\n", buf[0], buf[1]); radare_cmdf("pd 1 @ 0x%08"PFMT64x"", pc); pas_aop(config.arch, pc, buf, 32, &aop, str, 1); arch_aop(pc, buf, &aop); opsize = aop.length; //fprintf(stderr,"%"PFMT64x" + %d (%02x %02x)\n", pc, opsize, buf[0], buf[1]); //printf("=> 0x%08"PFMT64x" '%s' (%d)\n", vm_reg_get(vm->cpu.pc), str, opsize); vm_reg_set(vm->cpu.pc, vm_reg_get(vm->cpu.pc)+opsize); vm_op_eval(str); } config_set("asm.pseudo", op?"true":"false"); #if 0 fprintf(stderr,"TODO: vm_emulate\n"); vm_init(1); vm_print(); #endif // TODO: perform asm-to-pas-eval // TODO: evaluate string return n; #endif return -1; } R_API void r_vm_reset(RVm *vm) { struct list_head *pos; list_for_each(pos, &vm->regs) { RVmReg *r = list_entry (pos, struct r_vm_reg_t, list); r->value = 0LL; } } /* TODO : Allow to remove and so on */ R_API int r_vm_cmd_op(RVm *vm, const char *op) { char *cmd, *ptr; int len = strlen (op)+1; if (*op==' ') op = op + 1; cmd = alloca (len); memcpy (cmd, op, len); ptr = strchr (cmd, ' '); if (ptr) { ptr[0]='\0'; if(vm->log) eprintf ("vm: opcode '%s' added\n", cmd); r_vm_op_add (vm, cmd, ptr+1); } else r_vm_cmd_op_help (); return 0; }