From 02e6c14e125ae26d02a37a820124d26f4e79a1ac Mon Sep 17 00:00:00 2001 From: Ahmed Samy Date: Fri, 24 Feb 2017 15:37:19 +0200 Subject: [PATCH] x86: add MSR API via reg API (#755) Writing / reading to model specific registers should be as easy as calling a function, it's a bit stupid to write shell code and run them just to write/read to a MSR, and even worse, you need more than just a shellcode to read... So, add a special register ID called UC_X86_REG_MSR, which should be passed to uc_reg_write()/uc_reg_read() as the register ID, and then a data structure which is uc_x86_msr (12 bytes), as the value (always), where: Byte Value Size 0 MSR ID 4 4 MSR val 8 --- bindings/python/unicorn/unicorn.py | 28 ++++++++++++- bindings/python/unicorn/x86_const.py | 3 +- include/uc_priv.h | 1 + include/unicorn/x86.h | 9 +++- qemu/target-i386/unicorn.c | 61 ++++++++++++++++++++++++++++ uc.c | 2 - 6 files changed, 99 insertions(+), 5 deletions(-) diff --git a/bindings/python/unicorn/unicorn.py b/bindings/python/unicorn/unicorn.py index 18751ae4..7bd297b6 100644 --- a/bindings/python/unicorn/unicorn.py +++ b/bindings/python/unicorn/unicorn.py @@ -202,6 +202,11 @@ class uc_x86_mmr(ctypes.Structure): ("flags", ctypes.c_uint32), # not used by GDTR and IDTR ] +class uc_x86_msr(ctypes.Structure): + _fields_ = [ + ("rid", ctypes.c_uint32), + ("value", ctypes.c_uint64), + ] class uc_x86_float80(ctypes.Structure): """Float80""" @@ -282,7 +287,7 @@ class Uc(object): raise UcError(status) # return the value of a register - def reg_read(self, reg_id): + def reg_read(self, reg_id, opt=None): if self._arch == uc.UC_ARCH_X86: if reg_id in [x86_const.UC_X86_REG_IDTR, x86_const.UC_X86_REG_GDTR, x86_const.UC_X86_REG_LDTR, x86_const.UC_X86_REG_TR]: reg = uc_x86_mmr() @@ -302,6 +307,15 @@ class Uc(object): if status != uc.UC_ERR_OK: raise UcError(status) return reg.low_qword | (reg.high_qword << 64) + if reg_id is x86_const.UC_X86_REG_MSR: + if opt is None: + raise UcError(uc.UC_ERR_ARG) + reg = uc_x86_msr() + reg.rid = opt + status = _uc.uc_reg_read(self._uch, reg_id, ctypes.byref(reg)) + if status != uc.UC_ERR_OK: + raise UcError(status) + return reg.value # read to 64bit number to be safe reg = ctypes.c_uint64(0) @@ -330,6 +344,10 @@ class Uc(object): reg = uc_x86_xmm() reg.low_qword = value & 0xffffffffffffffff reg.high_qword = value >> 64 + if reg_id is x86_const.UC_X86_REG_MSR: + reg = uc_x86_msr() + reg.rid = value[0] + reg.value = value[1] if reg is None: # convert to 64bit number to be safe @@ -339,6 +357,14 @@ class Uc(object): if status != uc.UC_ERR_OK: raise UcError(status) + # read from MSR + def msr_read(self, msr_id): + return self.reg_read(x86_const.UC_X86_REG_MSR, msr_id) + + # write to MSR + def msr_write(self, msr_id, value): + return self.reg_write(x86_const.UC_X86_REG_MSR, (msr_id, value)) + # read data from memory def mem_read(self, address, size): data = ctypes.create_string_buffer(size) diff --git a/bindings/python/unicorn/x86_const.py b/bindings/python/unicorn/x86_const.py index ee5ba38f..5642c543 100644 --- a/bindings/python/unicorn/x86_const.py +++ b/bindings/python/unicorn/x86_const.py @@ -250,7 +250,8 @@ UC_X86_REG_LDTR = 244 UC_X86_REG_TR = 245 UC_X86_REG_FPCW = 246 UC_X86_REG_FPTAG = 247 -UC_X86_REG_ENDING = 248 +UC_X86_REG_MSR = 248 +UC_X86_REG_ENDING = 249 # X86 instructions diff --git a/include/uc_priv.h b/include/uc_priv.h index 4fcb7531..009c2e58 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -251,3 +251,4 @@ struct uc_context { MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address); #endif +/* vim: set ts=4 noet: */ diff --git a/include/unicorn/x86.h b/include/unicorn/x86.h index b0e02d8d..59fd17e5 100644 --- a/include/unicorn/x86.h +++ b/include/unicorn/x86.h @@ -19,6 +19,13 @@ typedef struct uc_x86_mmr { uint32_t flags; /* not used by GDTR and IDTR */ } uc_x86_mmr; +// Model-Specific Register structure, use this with UC_X86_REG_MSR (as the register ID) in +// call to uc_reg_write/uc_reg_read() to manipulate MSRs. +typedef struct uc_x86_msr { + uint32_t rid; + uint64_t value; +} uc_x86_msr; + // Callback function for tracing SYSCALL/SYSENTER (for uc_hook_intr()) // @user_data: user data passed to tracing APIs. typedef void (*uc_cb_insn_syscall_t)(struct uc_struct *uc, void *user_data); @@ -76,7 +83,7 @@ typedef enum uc_x86_reg { UC_X86_REG_R14D, UC_X86_REG_R15D, UC_X86_REG_R8W, UC_X86_REG_R9W, UC_X86_REG_R10W, UC_X86_REG_R11W, UC_X86_REG_R12W, UC_X86_REG_R13W, UC_X86_REG_R14W, UC_X86_REG_R15W, UC_X86_REG_IDTR, UC_X86_REG_GDTR, UC_X86_REG_LDTR, UC_X86_REG_TR, UC_X86_REG_FPCW, - UC_X86_REG_FPTAG, + UC_X86_REG_FPTAG, UC_X86_REG_MSR, UC_X86_REG_ENDING // <-- mark the end of the list of registers } uc_x86_reg; diff --git a/qemu/target-i386/unicorn.c b/qemu/target-i386/unicorn.c index 817799db..5f26617d 100644 --- a/qemu/target-i386/unicorn.c +++ b/qemu/target-i386/unicorn.c @@ -17,6 +17,10 @@ static void load_seg_16_helper(CPUX86State *env, int seg, uint32_t selector) cpu_x86_load_seg_cache(env, seg, selector, (selector << 4), 0xffff, X86_NON_CS_FLAGS); } + +extern void helper_wrmsr(CPUX86State *env); +extern void helper_rdmsr(CPUX86State *env); + const int X86_REGS_STORAGE_SIZE = offsetof(CPUX86State, tlb_table); static void x86_set_pc(struct uc_struct *uc, uint64_t address) @@ -156,6 +160,49 @@ void x86_reg_reset(struct uc_struct *uc) } } +static int x86_msr_read(struct uc_struct *uc, uc_x86_msr *msr) +{ + CPUX86State *env = (CPUX86State *)uc->cpu->env_ptr; + uint64_t ecx = env->regs[R_ECX]; + uint64_t eax = env->regs[R_EAX]; + uint64_t edx = env->regs[R_EDX]; + + env->regs[R_ECX] = msr->rid; + helper_rdmsr(env); + + msr->value = ((uint32_t)env->regs[R_EAX]) | + ((uint64_t)((uint32_t)env->regs[R_EDX]) << 32); + + env->regs[R_EAX] = eax; + env->regs[R_ECX] = ecx; + env->regs[R_EDX] = edx; + + /* The implementation doesn't throw exception or return an error if there is one, so + * we will return 0. */ + return 0; +} + +static int x86_msr_write(struct uc_struct *uc, uc_x86_msr *msr) +{ + CPUX86State *env = (CPUX86State *)uc->cpu->env_ptr; + uint64_t ecx = env->regs[R_ECX]; + uint64_t eax = env->regs[R_EAX]; + uint64_t edx = env->regs[R_EDX]; + + env->regs[R_ECX] = msr->rid; + env->regs[R_EAX] = (unsigned int)msr->value; + env->regs[R_EDX] = (unsigned int)(msr->value >> 32); + helper_wrmsr(env); + + env->regs[R_ECX] = ecx; + env->regs[R_EAX] = eax; + env->regs[R_EDX] = edx; + + /* The implementation doesn't throw exception or return an error if there is one, so + * we will return 0. */ + return 0; +} + int x86_reg_read(struct uc_struct *uc, unsigned int *regs, void **vals, int count) { CPUState *mycpu = uc->cpu; @@ -376,6 +423,9 @@ int x86_reg_read(struct uc_struct *uc, unsigned int *regs, void **vals, int coun ((uc_x86_mmr *)value)->selector = (uint16_t)X86_CPU(uc, mycpu)->env.tr.selector; ((uc_x86_mmr *)value)->flags = X86_CPU(uc, mycpu)->env.tr.flags; break; + case UC_X86_REG_MSR: + x86_msr_read(uc, (uc_x86_msr *)value); + break; } break; @@ -644,6 +694,9 @@ int x86_reg_read(struct uc_struct *uc, unsigned int *regs, void **vals, int coun ((uc_x86_mmr *)value)->selector = (uint16_t)X86_CPU(uc, mycpu)->env.tr.selector; ((uc_x86_mmr *)value)->flags = X86_CPU(uc, mycpu)->env.tr.flags; break; + case UC_X86_REG_MSR: + x86_msr_read(uc, (uc_x86_msr *)value); + break; } break; #endif @@ -863,6 +916,9 @@ int x86_reg_write(struct uc_struct *uc, unsigned int *regs, void *const *vals, i X86_CPU(uc, mycpu)->env.tr.selector = (uint16_t)((uc_x86_mmr *)value)->selector; X86_CPU(uc, mycpu)->env.tr.flags = ((uc_x86_mmr *)value)->flags; break; + case UC_X86_REG_MSR: + x86_msr_write(uc, (uc_x86_msr *)value); + break; } break; @@ -1141,6 +1197,9 @@ int x86_reg_write(struct uc_struct *uc, unsigned int *regs, void *const *vals, i X86_CPU(uc, mycpu)->env.tr.selector = (uint16_t)((uc_x86_mmr *)value)->selector; X86_CPU(uc, mycpu)->env.tr.flags = ((uc_x86_mmr *)value)->flags; break; + case UC_X86_REG_MSR: + x86_msr_write(uc, (uc_x86_msr *)value); + break; } break; #endif @@ -1185,3 +1244,5 @@ void x86_uc_init(struct uc_struct* uc) uc->stop_interrupt = x86_stop_interrupt; uc_common_init(uc); } + +/* vim: set ts=4 sts=4 sw=4 et: */ diff --git a/uc.c b/uc.c index 744aa589..eabf7cb3 100644 --- a/uc.c +++ b/uc.c @@ -380,14 +380,12 @@ uc_err uc_reg_read(uc_engine *uc, int regid, void *value) return uc_reg_read_batch(uc, ®id, &value, 1); } - UNICORN_EXPORT uc_err uc_reg_write(uc_engine *uc, int regid, const void *value) { return uc_reg_write_batch(uc, ®id, (void *const *)&value, 1); } - // check if a memory area is mapped // this is complicated because an area can overlap adjacent blocks static bool check_mem_area(uc_engine *uc, uint64_t address, size_t size)