diff --git a/native/cpu/native_cpu.cpp b/native/cpu/native_cpu.cpp index 7d0a4ca..9b55a2c 100644 --- a/native/cpu/native_cpu.cpp +++ b/native/cpu/native_cpu.cpp @@ -1633,7 +1633,11 @@ void NativeProcess::add_api_trap(uint64_t address, uint32_t callee_cleanup) return at - page < state->trap_page.size() ? state->trap_page[at - page] : read_memory(at, 1)[0]; }; if (!state->covers(address, 1, PROT_EXEC) || byte_at(address) != 0xcc) - throw std::runtime_error("native API trap must be an executable INT3"); + { + char text[96]; + std::snprintf(text, sizeof text, "native API trap must be an executable INT3: %llx", static_cast(address)); + throw std::runtime_error(text); + } if (state->mode == NativeMode::x86 && (address >= state->limit() || callee_cleanup % 4)) throw std::runtime_error("native x86 API trap requires a 32-bit address and dword-aligned cleanup"); state->api_traps[address] = callee_cleanup; diff --git a/native/graphics/vulkan_bridge_test.cpp b/native/graphics/vulkan_bridge_test.cpp index 8916663..58de5a1 100644 --- a/native/graphics/vulkan_bridge_test.cpp +++ b/native/graphics/vulkan_bridge_test.cpp @@ -166,8 +166,7 @@ int main(int argc, char **argv) raven::NativeProcess process(images, argv[1], std::chrono::seconds(2)); raven::VulkanBridge bridge(process, modules); for (const auto &symbol : modules.all().at("vulkan-1.dll")->exports) - process.add_api_trap(modules.resolve({"vulkan-1.dll", symbol.names.front(), 0, false, 0}) + - raven::fast_stub_trap_offset("vulkan-1.dll", symbol.names.front())); + process.add_api_trap(raven::api_trap_address(*modules.all().at("vulkan-1.dll"), "vulkan-1.dll", symbol.names.front(), symbol.address)); const auto memory = process.map_memory(4096, PROT_READ | PROT_WRITE); const auto rejected = [&](const std::string &name, std::span arguments) { const auto address = modules.resolve({"vulkan-1.dll", name, 0, false, 0}); diff --git a/native/nt/process_parameters_test.cpp b/native/nt/process_parameters_test.cpp index 8ded5b5..8e472af 100644 --- a/native/nt/process_parameters_test.cpp +++ b/native/nt/process_parameters_test.cpp @@ -74,7 +74,8 @@ int main(int argc,char **argv) check(rejected && !number(process,environment.peb()+0x20,8),"invalid parameters published a process block"); } const auto proxy=modules.resolve({"kernel32.dll","GetCommandLineW",0,false,0}); - process.add_api_trap(proxy); + const auto &kernel=*modules.all().at("kernel32.dll"); + process.add_api_trap(raven::api_trap_address(kernel,"kernel32.dll","GetCommandLineW",uint32_t(proxy-kernel.loaded.base()))); check(process.call(proxy,{},std::chrono::seconds(2)).stop==raven::NativeStop::api,"command line test did not stop at its API"); bool rejected=false; try { environment.prepare_parameters({u"image",u"command"}); } catch (const std::runtime_error &) { rejected=true; } diff --git a/native/nt/providers.cpp b/native/nt/providers.cpp index 05f9cdd..9e9b05f 100644 --- a/native/nt/providers.cpp +++ b/native/nt/providers.cpp @@ -1175,7 +1175,7 @@ static uint32_t served_stub_rva(std::span image, std::string_view image[rva] == 0x4c && image[rva + 1] == 0x8b && image[rva + 2] == 0xd1 && image[rva + 3] == 0xb8) return rva; // A plain provider has no guest-side prelude in front of its trap. - if (rva < image.size() && (image[rva] == 0xcc || (module == "user32.dll" && image[rva] == 0x90))) + if (rva < image.size() && (image[rva] == 0xcc || (image[rva] == 0x90 && size_t(rva) + 15 < image.size() && image[rva + 14] == 0xcc))) return rva; if (const auto *prelude = block_prelude(module, name)) { int32_t relative = 0; @@ -1216,7 +1216,11 @@ uint64_t api_trap_address(const PEModule &provider, std::string_view module, std const auto image = provider.loaded.data(); if (address < image.size() && image[address] == 0xcc) return provider.loaded.base() + address; - return provider.loaded.base() + served_stub_rva(image, module, name, address) + fast_stub_trap_offset(module, name); + const uint32_t stub = served_stub_rva(image, module, name, address); + // A plain x64 slot keeps its breakpoint past the space hooking libraries overwrite. + if (!served_api(module, name) && size_t(stub) + 15 < image.size() && image[stub] == 0x90 && image[stub + 14] == 0xcc) + return provider.loaded.base() + stub + 14; + return provider.loaded.base() + stub + fast_stub_trap_offset(module, name); } // Offset of the slow-path breakpoint inside a fast or served stub. @@ -1392,8 +1396,8 @@ std::vector runtime_provider(std::string_view name, uint64_t base, const uint32_t service = x86 ? 0 : nt_service_number(name, exports[index]); bytes[code] = 0xcc; bytes[code + 1] = 0xc3; - // UI hooks copy an x64 entry preamble into a trampoline; keep the breakpoint beyond their 14-byte jump space. - if (!x86 && name == "user32.dll") { + // Hooking libraries copy an x64 entry preamble into a trampoline; keep the breakpoint beyond their 14-byte jump space. + if (!x86 && (fast_paths || name == "user32.dll")) { std::fill_n(bytes.begin() + ptrdiff_t(code), 14, 0x90); bytes[code + 14] = 0xcc; bytes[code + 15] = 0xc3; diff --git a/native/nt/startup_test.cpp b/native/nt/startup_test.cpp index bc66afb..35dad10 100644 --- a/native/nt/startup_test.cpp +++ b/native/nt/startup_test.cpp @@ -52,7 +52,9 @@ int main(int argc,char **argv) raven::NativeProcess process(images,argv[1],std::chrono::seconds(5)); raven::NativeEnvironment environment(process,entry.loaded.base(),1,1); environment.prepare_parameters({u"client.exe",u"command"}); - const auto proxy=modules.resolve({"kernel32.dll","GetStartupInfoW",0,false,0});process.add_api_trap(proxy); + const auto proxy=modules.resolve({"kernel32.dll","GetStartupInfoW",0,false,0}); + const auto &kernel=*modules.all().at("kernel32.dll"); + process.add_api_trap(raven::api_trap_address(kernel,"kernel32.dll","GetStartupInfoW",uint32_t(proxy-kernel.loaded.base()))); const auto invoke=[&](const char *name,uint64_t output) { const uint64_t args[]={output};check(process.call(proxy,args,std::chrono::seconds(2)).stop==raven::NativeStop::api,"startup proxy did not stop"); check(environment.dispatch(name),"startup handler did not dispatch");return process.resume(std::chrono::seconds(2));