mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2025-01-03 15:45:35 +00:00
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
This commit is contained in:
parent
8acd6d47c9
commit
02e6c14e12
|
@ -202,6 +202,11 @@ class uc_x86_mmr(ctypes.Structure):
|
||||||
("flags", ctypes.c_uint32), # not used by GDTR and IDTR
|
("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):
|
class uc_x86_float80(ctypes.Structure):
|
||||||
"""Float80"""
|
"""Float80"""
|
||||||
|
@ -282,7 +287,7 @@ class Uc(object):
|
||||||
raise UcError(status)
|
raise UcError(status)
|
||||||
|
|
||||||
# return the value of a register
|
# 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 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]:
|
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()
|
reg = uc_x86_mmr()
|
||||||
|
@ -302,6 +307,15 @@ class Uc(object):
|
||||||
if status != uc.UC_ERR_OK:
|
if status != uc.UC_ERR_OK:
|
||||||
raise UcError(status)
|
raise UcError(status)
|
||||||
return reg.low_qword | (reg.high_qword << 64)
|
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
|
# read to 64bit number to be safe
|
||||||
reg = ctypes.c_uint64(0)
|
reg = ctypes.c_uint64(0)
|
||||||
|
@ -330,6 +344,10 @@ class Uc(object):
|
||||||
reg = uc_x86_xmm()
|
reg = uc_x86_xmm()
|
||||||
reg.low_qword = value & 0xffffffffffffffff
|
reg.low_qword = value & 0xffffffffffffffff
|
||||||
reg.high_qword = value >> 64
|
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:
|
if reg is None:
|
||||||
# convert to 64bit number to be safe
|
# convert to 64bit number to be safe
|
||||||
|
@ -339,6 +357,14 @@ class Uc(object):
|
||||||
if status != uc.UC_ERR_OK:
|
if status != uc.UC_ERR_OK:
|
||||||
raise UcError(status)
|
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
|
# read data from memory
|
||||||
def mem_read(self, address, size):
|
def mem_read(self, address, size):
|
||||||
data = ctypes.create_string_buffer(size)
|
data = ctypes.create_string_buffer(size)
|
||||||
|
|
|
@ -250,7 +250,8 @@ UC_X86_REG_LDTR = 244
|
||||||
UC_X86_REG_TR = 245
|
UC_X86_REG_TR = 245
|
||||||
UC_X86_REG_FPCW = 246
|
UC_X86_REG_FPCW = 246
|
||||||
UC_X86_REG_FPTAG = 247
|
UC_X86_REG_FPTAG = 247
|
||||||
UC_X86_REG_ENDING = 248
|
UC_X86_REG_MSR = 248
|
||||||
|
UC_X86_REG_ENDING = 249
|
||||||
|
|
||||||
# X86 instructions
|
# X86 instructions
|
||||||
|
|
||||||
|
|
|
@ -251,3 +251,4 @@ struct uc_context {
|
||||||
MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address);
|
MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
/* vim: set ts=4 noet: */
|
||||||
|
|
|
@ -19,6 +19,13 @@ typedef struct uc_x86_mmr {
|
||||||
uint32_t flags; /* not used by GDTR and IDTR */
|
uint32_t flags; /* not used by GDTR and IDTR */
|
||||||
} uc_x86_mmr;
|
} 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())
|
// Callback function for tracing SYSCALL/SYSENTER (for uc_hook_intr())
|
||||||
// @user_data: user data passed to tracing APIs.
|
// @user_data: user data passed to tracing APIs.
|
||||||
typedef void (*uc_cb_insn_syscall_t)(struct uc_struct *uc, void *user_data);
|
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_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_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_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_ENDING // <-- mark the end of the list of registers
|
||||||
} uc_x86_reg;
|
} uc_x86_reg;
|
||||||
|
|
|
@ -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);
|
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);
|
const int X86_REGS_STORAGE_SIZE = offsetof(CPUX86State, tlb_table);
|
||||||
|
|
||||||
static void x86_set_pc(struct uc_struct *uc, uint64_t address)
|
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)
|
int x86_reg_read(struct uc_struct *uc, unsigned int *regs, void **vals, int count)
|
||||||
{
|
{
|
||||||
CPUState *mycpu = uc->cpu;
|
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)->selector = (uint16_t)X86_CPU(uc, mycpu)->env.tr.selector;
|
||||||
((uc_x86_mmr *)value)->flags = X86_CPU(uc, mycpu)->env.tr.flags;
|
((uc_x86_mmr *)value)->flags = X86_CPU(uc, mycpu)->env.tr.flags;
|
||||||
break;
|
break;
|
||||||
|
case UC_X86_REG_MSR:
|
||||||
|
x86_msr_read(uc, (uc_x86_msr *)value);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
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)->selector = (uint16_t)X86_CPU(uc, mycpu)->env.tr.selector;
|
||||||
((uc_x86_mmr *)value)->flags = X86_CPU(uc, mycpu)->env.tr.flags;
|
((uc_x86_mmr *)value)->flags = X86_CPU(uc, mycpu)->env.tr.flags;
|
||||||
break;
|
break;
|
||||||
|
case UC_X86_REG_MSR:
|
||||||
|
x86_msr_read(uc, (uc_x86_msr *)value);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
#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.selector = (uint16_t)((uc_x86_mmr *)value)->selector;
|
||||||
X86_CPU(uc, mycpu)->env.tr.flags = ((uc_x86_mmr *)value)->flags;
|
X86_CPU(uc, mycpu)->env.tr.flags = ((uc_x86_mmr *)value)->flags;
|
||||||
break;
|
break;
|
||||||
|
case UC_X86_REG_MSR:
|
||||||
|
x86_msr_write(uc, (uc_x86_msr *)value);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
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.selector = (uint16_t)((uc_x86_mmr *)value)->selector;
|
||||||
X86_CPU(uc, mycpu)->env.tr.flags = ((uc_x86_mmr *)value)->flags;
|
X86_CPU(uc, mycpu)->env.tr.flags = ((uc_x86_mmr *)value)->flags;
|
||||||
break;
|
break;
|
||||||
|
case UC_X86_REG_MSR:
|
||||||
|
x86_msr_write(uc, (uc_x86_msr *)value);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
@ -1185,3 +1244,5 @@ void x86_uc_init(struct uc_struct* uc)
|
||||||
uc->stop_interrupt = x86_stop_interrupt;
|
uc->stop_interrupt = x86_stop_interrupt;
|
||||||
uc_common_init(uc);
|
uc_common_init(uc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* vim: set ts=4 sts=4 sw=4 et: */
|
||||||
|
|
2
uc.c
2
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);
|
return uc_reg_read_batch(uc, ®id, &value, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
UNICORN_EXPORT
|
UNICORN_EXPORT
|
||||||
uc_err uc_reg_write(uc_engine *uc, int regid, const void *value)
|
uc_err uc_reg_write(uc_engine *uc, int regid, const void *value)
|
||||||
{
|
{
|
||||||
return uc_reg_write_batch(uc, ®id, (void *const *)&value, 1);
|
return uc_reg_write_batch(uc, ®id, (void *const *)&value, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// check if a memory area is mapped
|
// check if a memory area is mapped
|
||||||
// this is complicated because an area can overlap adjacent blocks
|
// this is complicated because an area can overlap adjacent blocks
|
||||||
static bool check_mem_area(uc_engine *uc, uint64_t address, size_t size)
|
static bool check_mem_area(uc_engine *uc, uint64_t address, size_t size)
|
||||||
|
|
Loading…
Reference in a new issue