diff --git a/bindings/python/unicorn/unicorn.py b/bindings/python/unicorn/unicorn.py index 3289b5377c..dea11e483c 100644 --- a/bindings/python/unicorn/unicorn.py +++ b/bindings/python/unicorn/unicorn.py @@ -122,6 +122,7 @@ class _uc_mem_region(ctypes.Structure): _setup_prototype(_uc, "uc_close", ucerr, uc_engine) _setup_prototype(_uc, "uc_strerror", ctypes.c_char_p, ucerr) _setup_prototype(_uc, "uc_errno", ucerr, uc_engine) +_setup_prototype(_uc, "uc_set_slow_self_unpack", None, uc_engine, ctypes.c_bool) _setup_prototype(_uc, "uc_reg_read", ucerr, uc_engine, ctypes.c_int, ctypes.c_void_p) _setup_prototype(_uc, "uc_reg_write", ucerr, uc_engine, ctypes.c_int, ctypes.c_void_p) _setup_prototype(_uc, "uc_mem_read", ucerr, uc_engine, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t) @@ -320,6 +321,9 @@ def emu_stop(self): if status != uc.UC_ERR_OK: raise UcError(status) + def set_slow_self_unpack(self, state): + _uc.uc_set_slow_self_unpack(self._uch, state) + # return the value of a register def reg_read(self, reg_id, opt=None): if self._arch == uc.UC_ARCH_X86: diff --git a/include/uc_priv.h b/include/uc_priv.h index 22f494e4ff..db5e2df6ae 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -244,7 +244,9 @@ struct uc_struct { uint32_t target_page_size; uint32_t target_page_align; uint64_t next_pc; // save next PC for some special cases - bool hook_insert; // insert new hook at begin of the hook list (append by default) + bool hook_insert; // insert new hook at begin of the hook list (append by + // default) + bool slow_self_unpack; }; // Metadata stub for the variable-size cpu context used with uc_context_*() diff --git a/qemu/cpus.c b/qemu/cpus.c index 90e202ff1f..276527bda4 100644 --- a/qemu/cpus.c +++ b/qemu/cpus.c @@ -71,6 +71,8 @@ int resume_all_vcpus(struct uc_struct *uc) return -1; } + cpu_set_slow_self_unpack(cpu, uc->slow_self_unpack); + //qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true); cpu_resume(cpu); qemu_tcg_cpu_loop(uc); @@ -210,4 +212,3 @@ static void qemu_tcg_init_cpu_signals(void) } #endif /* _WIN32 */ #endif - diff --git a/qemu/include/qom/cpu.h b/qemu/include/qom/cpu.h index 016aa053f4..1d1371fe40 100644 --- a/qemu/include/qom/cpu.h +++ b/qemu/include/qom/cpu.h @@ -181,6 +181,8 @@ struct kvm_run; * @running: #true if CPU is currently running (usermode). * @created: Indicates whether the CPU thread has been successfully created. * @interrupt_request: Indicates a pending interrupt request. + * @slow_self_unpack: Indicates whether EOB should be generated after each + * instruction or not. * @halted: Nonzero if the CPU is in suspended state. * @stop: Indicates a pending stop request. * @stopped: Indicates the CPU has been artificially stopped. @@ -211,6 +213,8 @@ struct CPUState { int nr_threads; int numa_node; + bool slow_self_unpack; + struct QemuThread *thread; #ifdef _WIN32 HANDLE hThread; @@ -415,6 +419,34 @@ ObjectClass *cpu_class_by_name(struct uc_struct *uc, const char *typename, const */ CPUState *cpu_generic_init(struct uc_struct *uc, const char *typename, const char *cpu_model); +/** + * cpu_slow_self_unpack_enabled + * @cpu: The vCPU to check. + * + * Checks where the codegen for CPU should injects EOB after each instruction. + * Useful to emulate self-modifying shellcode. + * + * Returns: %true if the mode is enabled for the CPU, %false otherwise. + */ +static inline bool cpu_slow_self_unpack_enabled(CPUState *cpu) +{ + return cpu->slow_self_unpack; +} + +/** + * cpu_slow_self_unpack_enabled + * @cpu: The vCPU to set. + * @state: %true to enable mode, %false to disable. + * + * Set where the codegen for CPU should injects EOB after each instruction. + * Useful to emulate self-modifying shellcode. + */ +static inline void cpu_set_slow_self_unpack(CPUState *cpu, bool state) +{ + printf("set cpu_set_slow_self_unpack %p = %d\n", cpu, state); + cpu->slow_self_unpack = state; +} + /** * cpu_has_work: * @cpu: The vCPU to check. diff --git a/qemu/qom/cpu.c b/qemu/qom/cpu.c index 2c3a19353c..1dc17e9bea 100644 --- a/qemu/qom/cpu.c +++ b/qemu/qom/cpu.c @@ -157,6 +157,7 @@ static void cpu_common_reset(CPUState *cpu) log_cpu_state(cpu, cc->reset_dump_flags); } + cpu->slow_self_unpack = false; cpu->interrupt_request = 0; cpu->current_tb = NULL; cpu->halted = 0; diff --git a/qemu/target-i386/translate.c b/qemu/target-i386/translate.c index 36fae092cf..5a27e87c16 100644 --- a/qemu/target-i386/translate.c +++ b/qemu/target-i386/translate.c @@ -4973,7 +4973,7 @@ static void restore_eflags(DisasContext *s, TCGContext *tcg_ctx) TCGv_ptr cpu_env = tcg_ctx->cpu_env; tcg_gen_ld_tl(tcg_ctx, *cpu_T[0], cpu_env, offsetof(CPUX86State, eflags)); - gen_helper_write_eflags(tcg_ctx, cpu_env, *cpu_T[0], + gen_helper_write_eflags(tcg_ctx, cpu_env, *cpu_T[0], tcg_const_i32(tcg_ctx, (TF_MASK | AC_MASK | ID_MASK | NT_MASK) & 0xffff)); set_cc_op(s, CC_OP_EFLAGS); } @@ -5050,6 +5050,13 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, s->vex_l = 0; s->vex_v = 0; next_byte: + /* x86 has an upper limit of 15 bytes for an instruction. Since we + * do not want to decode and generate IR for an illegal + * instruction, the following check limits the instruction size to + * 25 bytes: 14 prefix + 1 opc + 6 (modrm+sib+ofs) + 4 imm */ + if (s->pc - pc_start > 14) { + goto illegal_op; + } b = cpu_ldub_code(env, s->pc); s->pc++; /* Collect prefixes. */ @@ -6363,7 +6370,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (mod != 3) { /* memory op */ gen_lea_modrm(env, s, modrm); - + if( (op >= 0x00 && op <= 0x07) || /* fxxxs */ (op >= 0x10 && op <= 0x17) || /* fixxxl */ (op >= 0x20 && op <= 0x27) || /* fxxxl */ @@ -8746,6 +8753,7 @@ static inline void gen_intermediate_code_internal(uint8_t *gen_opc_cc_op, /* stop translation if indicated */ if (dc->is_jmp) break; + /* if single step mode, we generate only one instruction and generate an exception */ /* if irq were inhibited with HF_INHIBIT_IRQ_MASK, we clear @@ -8766,6 +8774,13 @@ static inline void gen_intermediate_code_internal(uint8_t *gen_opc_cc_op, block_full = true; break; } + + if (cpu_slow_self_unpack_enabled(cs)) { + gen_jmp_im(dc, pc_ptr - dc->cs_base); + gen_eob(dc); + block_full = true; + break; + } } //if (tb->cflags & CF_LAST_IO) // gen_io_end(); diff --git a/uc.c b/uc.c index 11399017b6..691ee84438 100644 --- a/uc.c +++ b/uc.c @@ -351,10 +351,18 @@ uc_err uc_close(uc_engine *uc) // finally, free uc itself. memset(uc, 0, sizeof(*uc)); free(uc); - + return UC_ERR_OK; } +UNICORN_EXPORT +void uc_set_slow_self_unpack(uc_engine *uc, bool state) +{ + uc->slow_self_unpack = state; + if (uc->current_cpu) { + cpu_set_slow_self_unpack(uc, state); + } +} UNICORN_EXPORT uc_err uc_reg_read_batch(uc_engine *uc, int *ids, void **vals, int count) @@ -612,6 +620,8 @@ uc_err uc_emu_start(uc_engine* uc, uint64_t begin, uint64_t until, uint64_t time if (err != UC_ERR_OK) { return err; } + + uc_set_slow_self_unpack(uc, true); } uc->addr_end = until; @@ -830,7 +840,7 @@ static bool split_region(struct uc_struct *uc, MemoryRegion *mr, uint64_t addres // RAM_PREALLOC is not defined outside exec.c and I didn't feel like // moving it - prealloc = !!(block->flags & 1); + prealloc = !!(block->flags & 1); if (block->flags & 1) { backup = block->host;