From 012eaaa234be6c49fe867b61df03c5441464a68a Mon Sep 17 00:00:00 2001 From: moleium Date: Sun, 22 Feb 2026 01:34:23 +0300 Subject: [PATCH] x86: hook support for rdmsr/wrmsr (#2297) --- qemu/target/i386/misc_helper.c | 61 ++++++++++++++++++++++++++- qemu/target/i386/unicorn.c | 6 +-- tests/unit/test_x86.c | 76 ++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 5 deletions(-) diff --git a/qemu/target/i386/misc_helper.c b/qemu/target/i386/misc_helper.c index ccc12c8192..c47177b22c 100644 --- a/qemu/target/i386/misc_helper.c +++ b/qemu/target/i386/misc_helper.c @@ -231,7 +231,7 @@ void helper_rdtsc(CPUX86State *env) continue; if (!HOOK_BOUND_CHECK(hook, env->eip)) continue; - + // Multiple rdtsc callbacks returning different values is undefined. // true -> skip the rdtsc instruction if (hook->insn == UC_X86_INS_RDTSC) { @@ -275,7 +275,7 @@ void helper_rdtscp(CPUX86State *env) continue; if (!HOOK_BOUND_CHECK(hook, env->eip)) continue; - + // Multiple rdtscp callbacks returning different values is undefined. // true -> skip the rdtscp instruction if (hook->insn == UC_X86_INS_RDTSCP) { @@ -316,9 +316,37 @@ void helper_rdpmc(CPUX86State *env) void helper_wrmsr(CPUX86State *env) { uint64_t val; + uc_engine *uc = env->uc; + struct hook *hook; + int skip_wrmsr = 0; + bool synced = false; cpu_svm_check_intercept_param(env, SVM_EXIT_MSR, 1, GETPC()); + HOOK_FOREACH_VAR_DECLARE; + HOOK_FOREACH(env->uc, hook, UC_HOOK_INSN) + { + if (hook->to_delete) + continue; + if (!HOOK_BOUND_CHECK(hook, env->eip)) + continue; + if (hook->insn == UC_X86_INS_WRMSR) { + uintptr_t pc = GETPC(); + if (!synced && !uc->skip_sync_pc_on_exit && pc) { + cpu_restore_state(uc->cpu, pc, false); + synced = true; + } + JIT_CALLBACK_GUARD_VAR( + skip_wrmsr, + ((uc_cb_insn_cpuid_t)hook->callback)(env->uc, hook->user_data)); + } + if (env->uc->stop_request) + break; + } + + if (skip_wrmsr) + return; + val = ((uint32_t)env->regs[R_EAX]) | ((uint64_t)((uint32_t)env->regs[R_EDX]) << 32); @@ -477,9 +505,37 @@ void helper_rdmsr(CPUX86State *env) { X86CPU *x86_cpu = env_archcpu(env); uint64_t val; + uc_engine *uc = env->uc; + struct hook *hook; + int skip_rdmsr = 0; + bool synced = false; cpu_svm_check_intercept_param(env, SVM_EXIT_MSR, 0, GETPC()); + HOOK_FOREACH_VAR_DECLARE; + HOOK_FOREACH(env->uc, hook, UC_HOOK_INSN) + { + if (hook->to_delete) + continue; + if (!HOOK_BOUND_CHECK(hook, env->eip)) + continue; + if (hook->insn == UC_X86_INS_RDMSR) { + uintptr_t pc = GETPC(); + if (!synced && !uc->skip_sync_pc_on_exit && pc) { + cpu_restore_state(uc->cpu, pc, false); + synced = true; + } + JIT_CALLBACK_GUARD_VAR( + skip_rdmsr, + ((uc_cb_insn_cpuid_t)hook->callback)(env->uc, hook->user_data)); + } + if (env->uc->stop_request) + break; + } + + if (skip_rdmsr) + return; + switch ((uint32_t)env->regs[R_ECX]) { case MSR_IA32_SYSENTER_CS: val = env->sysenter_cs; @@ -628,6 +684,7 @@ void helper_rdmsr(CPUX86State *env) env->regs[R_EDX] = (uint32_t)(val >> 32); } + static void do_pause(X86CPU *cpu) { CPUState *cs = CPU(cpu); diff --git a/qemu/target/i386/unicorn.c b/qemu/target/i386/unicorn.c index 1e79b948cb..5ae6a2c228 100644 --- a/qemu/target/i386/unicorn.c +++ b/qemu/target/i386/unicorn.c @@ -2023,12 +2023,12 @@ static bool x86_stop_interrupt(struct uc_struct *uc, int intno) static bool x86_insn_hook_validate(uint32_t insn_enum) { - // for x86 we can only hook IN, OUT, SYSCALL, SYSENTER, CPUID, RDTSC, and - // RDTSCP + // for x86 we can only hook IN, OUT, SYSCALL, SYSENTER, CPUID, RDTSC, RDTSCP, RDMSR and WRMSR if (insn_enum != UC_X86_INS_IN && insn_enum != UC_X86_INS_OUT && insn_enum != UC_X86_INS_SYSCALL && insn_enum != UC_X86_INS_SYSENTER && insn_enum != UC_X86_INS_CPUID && insn_enum != UC_X86_INS_RDTSC && - insn_enum != UC_X86_INS_RDTSCP) { + insn_enum != UC_X86_INS_RDTSCP && insn_enum != UC_X86_INS_RDMSR && + insn_enum != UC_X86_INS_WRMSR) { return false; } return true; diff --git a/tests/unit/test_x86.c b/tests/unit/test_x86.c index 8eedc31ee2..40a5bac4fe 100644 --- a/tests/unit/test_x86.c +++ b/tests/unit/test_x86.c @@ -2128,6 +2128,80 @@ static void test_x86_hook_insn_rdtscp(void) OK(uc_close(uc)); } +static int test_x86_hook_insn_wrmsr_cb(uc_engine *uc, void *user_data) +{ + *(int *)user_data = 1; + return 0; +} + +static void test_x86_hook_insn_wrmsr(void) +{ + /* WRMSR (0f 30) */ + char code[] = "\x0F\x30"; + int fired = 0; + + uc_engine *uc; + uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_64, code, sizeof code - 1); + + uint64_t rcx = 0x174; /* MSR_IA32_SYSENTER_CS */ + uint64_t rax = 0x10; + uint64_t rdx = 0; + OK(uc_reg_write(uc, UC_X86_REG_RCX, &rcx)); + OK(uc_reg_write(uc, UC_X86_REG_RAX, &rax)); + OK(uc_reg_write(uc, UC_X86_REG_RDX, &rdx)); + + uc_hook hook; + OK(uc_hook_add(uc, &hook, UC_HOOK_INSN, test_x86_hook_insn_wrmsr_cb, + &fired, 1, 0, UC_X86_INS_WRMSR)); + + OK(uc_emu_start(uc, code_start, code_start + sizeof code - 1, 0, 0)); + + OK(uc_hook_del(uc, hook)); + TEST_CHECK(fired == 1); + OK(uc_close(uc)); +} + +static int test_x86_hook_insn_rdmsr_cb(uc_engine *uc, void *user_data) +{ + uint64_t eax = 0xDEAD; + uint64_t edx = 0xBEEF; + OK(uc_reg_write(uc, UC_X86_REG_RAX, &eax)); + OK(uc_reg_write(uc, UC_X86_REG_RDX, &edx)); + *(int *)user_data = 1; + return 1; /* skip underlying rdmsr */ +} + +static void test_x86_hook_insn_rdmsr(void) +{ + /* RDMSR (0f 32) */ + char code[] = "\x0F\x32"; + int fired = 0; + + uc_engine *uc; + uc_common_setup(&uc, UC_ARCH_X86, UC_MODE_64, code, sizeof code - 1); + + uint64_t rcx = 0x174; /* MSR_IA32_SYSENTER_CS */ + OK(uc_reg_write(uc, UC_X86_REG_RCX, &rcx)); + + uc_hook hook; + OK(uc_hook_add(uc, &hook, UC_HOOK_INSN, test_x86_hook_insn_rdmsr_cb, + &fired, 1, 0, UC_X86_INS_RDMSR)); + + OK(uc_emu_start(uc, code_start, code_start + sizeof code - 1, 0, 0)); + + OK(uc_hook_del(uc, hook)); + TEST_CHECK(fired == 1); + + uint64_t eax = 0; + uint64_t edx = 0; + OK(uc_reg_read(uc, UC_X86_REG_RAX, &eax)); + OK(uc_reg_read(uc, UC_X86_REG_RDX, &edx)); + TEST_CHECK(eax == 0xDEAD); + TEST_CHECK(edx == 0xBEEF); + + OK(uc_close(uc)); +} + static void test_x86_dr7() { uc_engine *uc; @@ -2262,6 +2336,8 @@ TEST_LIST = { {"test_x86_ro_segfault", test_x86_ro_segfault}, {"test_x86_hook_insn_rdtsc", test_x86_hook_insn_rdtsc}, {"test_x86_hook_insn_rdtscp", test_x86_hook_insn_rdtscp}, + {"test_x86_hook_insn_wrmsr", test_x86_hook_insn_wrmsr}, + {"test_x86_hook_insn_rdmsr", test_x86_hook_insn_rdmsr}, {"test_x86_dr7", test_x86_dr7}, {"test_x86_hook_block", test_x86_hook_block}, {"test_x86_mem_hooks_pc_guarantee", test_x86_mem_hooks_pc_guarantee},