Skip to content

Commit

Permalink
Add slow_self_unpack mode (x86)
Browse files Browse the repository at this point in the history
This mode forces EOB generation after each instruction.
This is work-around to emulate self-modifying shell-code, maybe also
unicorn-engine#820
  • Loading branch information
alxchk committed Jul 29, 2019
1 parent 7a72abe commit 5027421
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 6 deletions.
4 changes: 4 additions & 0 deletions bindings/python/unicorn/unicorn.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Expand Down
4 changes: 3 additions & 1 deletion include/uc_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_*()
Expand Down
3 changes: 2 additions & 1 deletion qemu/cpus.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -210,4 +212,3 @@ static void qemu_tcg_init_cpu_signals(void)
}
#endif /* _WIN32 */
#endif

32 changes: 32 additions & 0 deletions qemu/include/qom/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -211,6 +213,8 @@ struct CPUState {
int nr_threads;
int numa_node;

bool slow_self_unpack;

struct QemuThread *thread;
#ifdef _WIN32
HANDLE hThread;
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions qemu/qom/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
19 changes: 17 additions & 2 deletions qemu/target-i386/translate.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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
Expand All @@ -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();
Expand Down
14 changes: 12 additions & 2 deletions uc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 5027421

Please sign in to comment.