From 7d6a01cc2999c3ad69394f0ca34e86b1b2166bdb Mon Sep 17 00:00:00 2001 From: offtkp Date: Mon, 16 Sep 2024 23:10:19 +0300 Subject: [PATCH] Patch CPUID --- src/core/cpu_patches.cpp | 46 ++++++++++++++++++++++++++++++++++++++++ src/core/cpu_patches.h | 3 +++ src/core/module.cpp | 1 + src/core/signals.cpp | 36 +++++++++++++++++++++++++------ 4 files changed, 79 insertions(+), 7 deletions(-) diff --git a/src/core/cpu_patches.cpp b/src/core/cpu_patches.cpp index 1b159d32b..ced7aaa09 100644 --- a/src/core/cpu_patches.cpp +++ b/src/core/cpu_patches.cpp @@ -798,4 +798,50 @@ void PrePatchInstructions(u64 segment_addr, u64 segment_size) { #endif } +// We patch CPUID to handle it specially and simulate an AMD environment. +// Because CPUID is at minimum 2 bytes, a trampoline jump is not possible. +// Instead, we replace the instruction with an illegal instruction and handle it in the illegal instruction handler. +void PatchCPUID(u64 segment_addr, u64 segment_size) { + u8* code = reinterpret_cast(segment_addr); + u8* end = code + segment_size; + bool previous_instruction_ok = false; + + while (code < end) { + ZydisDecodedInstruction instruction; + ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT]; + const auto status = ZydisDecoderDecodeFull(&instr_decoder, code, end - code, &instruction, operands); + + if (!ZYAN_SUCCESS(status)) { + code++; + previous_instruction_ok = false; + continue; + } + + if (instruction.mnemonic == ZYDIS_MNEMONIC_CPUID) { + if (!previous_instruction_ok) { + // It should be highly unlikely that the CPUID instruction is not preceded by a valid instruction. + // Instead of patching it we're going to skip it and generate a warning, because we could be in some + // data section that must not be changed. + LOG_ERROR(Core, "CPUID instruction is not preceded by a valid instruction at: {} while patching", fmt::ptr(code)); + code++; + continue; + } + + LOG_CRITICAL(Core, "FOUND CPUID INSTRUCTION AT: {}", fmt::ptr(code)); + + // Replace any potential prefixes and the 0F byte with single byte NOPs + u8 instruction_length = instruction.length; + for (u8 i = 0; i < instruction_length - 1; i++) { + code[i] = 0x90; + } + + // Replace the CPUID instruction with an illegal instruction + code[instruction_length - 1] = 0x37; // 0x37 is AAA, illegal in x86-64 + } + + code += instruction.length; + previous_instruction_ok = true; + } +} + } // namespace Core diff --git a/src/core/cpu_patches.h b/src/core/cpu_patches.h index f9f7fe646..8c1c9251b 100644 --- a/src/core/cpu_patches.h +++ b/src/core/cpu_patches.h @@ -18,4 +18,7 @@ void RegisterPatchModule(void* module_ptr, u64 module_size, void* trampoline_are /// Applies CPU patches that need to be done before beginning executions. void PrePatchInstructions(u64 segment_addr, u64 segment_size); +// Patches CPUID instructions with an illegal instruction to handle them specially in the signal handler +void PatchCPUID(u64 segment_addr, u64 segment_size); + } // namespace Core diff --git a/src/core/module.cpp b/src/core/module.cpp index ce2f9d3ab..5c36e0f03 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -137,6 +137,7 @@ void Module::LoadModuleToMemory(u32& max_tls_index) { #ifdef ARCH_X86_64 if (elf_pheader[i].p_flags & PF_EXEC) { PrePatchInstructions(segment_addr, segment_file_size); + PatchCPUID(segment_addr, segment_file_size); } #endif break; diff --git a/src/core/signals.cpp b/src/core/signals.cpp index a16c150e9..fc9f65573 100644 --- a/src/core/signals.cpp +++ b/src/core/signals.cpp @@ -7,8 +7,10 @@ #ifdef _WIN32 #include +#include #else #include +#include #ifdef ARCH_X86_64 #include #include @@ -31,9 +33,16 @@ static LONG WINAPI SignalHandler(EXCEPTION_POINTERS* pExp) noexcept { code_address, reinterpret_cast(pExp->ExceptionRecord->ExceptionInformation[1]), pExp->ExceptionRecord->ExceptionInformation[0] == 1); break; - case EXCEPTION_ILLEGAL_INSTRUCTION: - handled = signals->DispatchIllegalInstruction(code_address); + case EXCEPTION_ILLEGAL_INSTRUCTION: { + u8 opcode = *reinterpret_cast(code_address); + if (opcode == 0x37) { // 0x37 is AAA instruction, we use that to patch CPUID + handled = EXCEPTION_CONTINUE_EXECUTION; + pExp->ContextRecord->Rip += 1; // skip the illegal instruction + } else { + handled = signals->DispatchIllegalInstruction(code_address); + } break; + } default: break; } @@ -87,7 +96,7 @@ static std::string DisassembleInstruction(void* code_address) { } static void SignalHandler(int sig, siginfo_t* info, void* raw_context) { - const auto* ctx = static_cast(raw_context); + auto* ctx = static_cast(raw_context); const auto* signals = Signals::Instance(); auto* code_address = CODE_ADDRESS(ctx); @@ -102,12 +111,25 @@ static void SignalHandler(int sig, siginfo_t* info, void* raw_context) { fmt::ptr(info->si_addr)); } break; - case SIGILL: - if (!signals->DispatchIllegalInstruction(code_address)) { - UNREACHABLE_MSG("Unhandled illegal instruction at code address {}: {}", - fmt::ptr(code_address), DisassembleInstruction(code_address)); + case SIGILL: { + u8 opcode = *reinterpret_cast(code_address); + if (opcode == 0x37) { // 0x37 is AAA instruction, we use that to patch CPUID + u64 results[4]; + u64 eax = ctx->uc_mcontext.gregs[REG_RAX]; + u64 ecx = ctx->uc_mcontext.gregs[REG_RCX]; + LOG_WARNING(Core, "CPUID: eax=%lx ecx=%lx\n", eax, ecx); + __cpuid_count(eax, ecx, results[0], results[1], results[2], results[3]); + LOG_WARNING(Core, "RESULTS: %lx %lx %lx %lx\n", results[0], results[1], results[2], results[3]); + ctx->uc_mcontext.gregs[REG_RIP] += 1; // skip the illegal instruction + break; + } else { + if (!signals->DispatchIllegalInstruction(code_address)) { + UNREACHABLE_MSG("Unhandled illegal instruction at code address {}: {}", + fmt::ptr(code_address), DisassembleInstruction(code_address)); + } } break; + } default: break; }