/** * This is the part that launches the code in the Unicorn emulator. **/ int em_code(u8 *code, u32 bytelength, u32 startat, u8 *seed_res, uc_arch arch){ // bit of lazy coding here: update a global arch var, so that // we can easily read the arch from the single step hook. // neither elegant nor dangerous. if (global_arch != arch) global_arch = arch; /* The start address must be aligned to 4KB, or uc_mem_map will * throw a UC_ERR_ARG error. */ u32 round_start = startat & (u32) (~0xFFF); u32 offset = startat - round_start; int errcode = 0; int roundlength = roundup(bytelength); uc_engine *uc; uc_err err; uc_hook hook1; int sys_abi_len; uc_mode mode; int ret_inst; int *sys_abi_vec; /* if (DEBUG){ */ /* printf("IN EMULATOR\n"); */ /* fdump(stdout, code, bytelength); */ /* } */ switch (arch) { case UC_ARCH_X86 : sys_abi_vec = x86_64_syscall_abi; // careful sys_abi_len = x86_64_syscall_abi_len; mode = UC_MODE_64; ret_inst = UC_X86_INS_RET; break; case UC_ARCH_ARM : if (DEBUG) printf("Emulating ARM architecture...\n"); sys_abi_vec = arm_32_syscall_abi; sys_abi_len = arm_32_syscall_abi_len; mode = UC_MODE_ARM; break; default : fprintf(stderr,"Unknown architecture requested of em_code.\nExiting.\n"); exit(EXIT_FAILURE); } union seedvals { u32 words[sys_abi_len]; // wrinkle here: the word size should be dynamic u8 bytes[sys_abi_len * sizeof(word)]; } seedvals; /* fprintf(stderr, "bytelength = %d\nroundlength = %d\nsizeof(seedvals.bytes) = %d\nsizeof(seed_res) = %d\nsizeof(word) * sys_abi_len = %d\n",bytelength, roundlength, sizeof(seedvals.bytes), sizeof(seed_res), (sys_abi_len * sizeof(word))); */ if (!memcpy(seedvals.bytes, seed_res, (sys_abi_len * sizeof(word)))){ fprintf(stderr, "Error in memcpy, in em_code.\n"); } /** * from the unicorn src: "This part of the API is less... clean... * because Unicorn supports arbitrary register types. So the least * intrusive solution is passing individual pointers. On the plus * side, you only need to make this pointer array once." */ void *ptrs[sys_abi_len]; int i; for (i = 0; i < sys_abi_len; i++) { ptrs[i] = &(seedvals.words[i]); } if ((err = uc_open(arch, mode, &uc))) { uc_perror("uc_open", err); return -1; } // seed the registers if ((err = uc_reg_write_batch(uc, sys_abi_vec, ptrs, sys_abi_len))){ uc_perror("uc_reg_write_batch", err); return -1; } /* Add a single-stepping hook if debugging */ if (DEBUG){ if ((err = uc_hook_add(uc, &hook1, UC_HOOK_CODE, hook_step, NULL, 1, 0, 0))) { uc_perror("uc_hook_add", err); return 1; } } // don't leave 0x1000 a magic number if ((err = uc_mem_map(uc, round_start, 0x1000, UC_PROT_ALL))) { // does PROT_ALL mean 777? might want to set to XN for ROP... uc_perror("uc_mem_map", err); return -1; } if ((err = uc_mem_write(uc, startat, (void *) code, bytelength-1))) { uc_perror("uc_mem_write", err); return -1; } // why does the unicorn example suggest sizeof(CODE) -1 // where I have bytelength (sizeof(CODE))? probably because // it's implemented as a string, so it ends with a null byte if ((err = uc_emu_start(uc, startat, startat + bytelength -1, 0, TTL))){ if (DEBUG){ uc_perror("uc_emu_start", err); if (err == UC_ERR_FETCH_UNMAPPED) ret_msg(uc, err, arch); } errcode = -2; } uc_reg_read_batch(uc, sys_abi_vec, ptrs, sys_abi_len); /** for testing **/ if (DEBUG) { printf("syscall vec: {"); for (i = 0; i < sys_abi_len; i++) { if (i != 0) printf(", "); printf(WORDFMT, seedvals.words[i]); } printf("}\n"); } /******************/ memcpy(seed_res, seedvals.bytes, (sys_abi_len * sizeof(word))); uc_close(uc); return errcode; }
int main() { int i; uc_hook sys_hook; uc_err err; uc_engine *uc; // set up register pointers for (i = 0; i < 7; i++) { ptrs[i] = &vals[i]; } if ((err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc))) { uc_perror("uc_open", err); return 1; } // reg_write_batch printf("reg_write_batch({200, 10, 11, 12, 13, 14, 15})\n"); if ((err = uc_reg_write_batch(uc, syscall_abi, ptrs, 7))) { uc_perror("uc_reg_write_batch", err); return 1; } // reg_read_batch memset(vals, 0, sizeof(vals)); if ((err = uc_reg_read_batch(uc, syscall_abi, ptrs, 7))) { uc_perror("uc_reg_read_batch", err); return 1; } printf("reg_read_batch = {"); for (i = 0; i < 7; i++) { if (i != 0) printf(", "); printf("%llu", vals[i]); } printf("}\n"); // syscall printf("\n"); printf("running syscall shellcode\n"); if ((err = uc_hook_add(uc, &sys_hook, UC_HOOK_INSN, hook_syscall, NULL, 1, 0, UC_X86_INS_SYSCALL))) { uc_perror("uc_hook_add", err); return 1; } if ((err = uc_mem_map(uc, BASE, 0x1000, UC_PROT_ALL))) { uc_perror("uc_mem_map", err); return 1; } if ((err = uc_mem_write(uc, BASE, CODE, sizeof(CODE) - 1))) { uc_perror("uc_mem_write", err); return 1; } if ((err = uc_emu_start(uc, BASE, BASE + sizeof(CODE) - 1, 0, 0))) { uc_perror("uc_emu_start", err); return 1; } return 0; }