From bf8fe61697529dc4479dbb638e4e690d0a68ab45 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Mon, 26 Aug 2024 20:28:26 +0200 Subject: [PATCH] Dynamically resolve syscall IDs --- src/windows_emulator/main.cpp | 35 +++-- src/windows_emulator/process_context.hpp | 12 +- src/windows_emulator/syscalls.cpp | 166 +++++++++++++++-------- src/windows_emulator/syscalls.hpp | 14 +- 4 files changed, 160 insertions(+), 67 deletions(-) diff --git a/src/windows_emulator/main.cpp b/src/windows_emulator/main.cpp index 4ea7e8fd..d7eeeed5 100644 --- a/src/windows_emulator/main.cpp +++ b/src/windows_emulator/main.cpp @@ -159,11 +159,13 @@ namespace for (DWORD i = 0; i < names_count; i++) { - const auto* function_name = reinterpret_cast(ptr + names[i]); - const auto function_rva = functions[ordinals[i]]; - const auto function_address = binary.image_base + function_rva; + exported_symbol symbol{}; + symbol.ordinal = ordinals[i]; + symbol.name = reinterpret_cast(ptr + names[i]); + symbol.rva = functions[symbol.ordinal]; + symbol.address = binary.image_base + symbol.rva; - binary.exports[function_name] = function_address; + binary.exports.push_back(std::move(symbol)); } return binary; @@ -514,6 +516,19 @@ namespace std::unordered_map hooks_{}; }; + uint64_t find_exported_function(const std::vector& exports, const std::string_view name) + { + for (auto& symbol : exports) + { + if (symbol.name == name) + { + return symbol.address; + } + } + + return 0; + } + void run() { const auto emu = unicorn::create_x64_emulator(); @@ -529,16 +544,16 @@ namespace context.ntdll = map_file(*emu, R"(C:\Windows\System32\ntdll.dll)"); - const auto entry1 = context.ntdll.exports.at("LdrInitializeThunk"); - const auto entry2 = context.ntdll.exports.at("RtlUserThreadStart"); + const auto entry1 = find_exported_function(context.ntdll.exports, "LdrInitializeThunk"); + const auto entry2 = find_exported_function(context.ntdll.exports, "RtlUserThreadStart"); (void)entry1; (void)entry2; std::unordered_map export_remap{}; - for (const auto& exp : context.ntdll.exports) + for (const auto& symbol : context.ntdll.exports) { - export_remap.try_emplace(exp.second, exp.first); + export_remap.try_emplace(symbol.address, symbol.name); } for (const auto& exp : export_remap) @@ -551,9 +566,11 @@ namespace }); } + syscall_dispatcher dispatcher{context.ntdll.exports}; + emu->hook_instruction(x64_hookable_instructions::syscall, [&] { - handle_syscall(*emu, context); + dispatcher.dispatch(*emu, context); }); watch_object(*emu, context.teb); diff --git a/src/windows_emulator/process_context.hpp b/src/windows_emulator/process_context.hpp index 97481215..434dac31 100644 --- a/src/windows_emulator/process_context.hpp +++ b/src/windows_emulator/process_context.hpp @@ -1,11 +1,21 @@ #pragma once #include "emulator_utils.hpp" +struct exported_symbol +{ + std::string name{}; + uint64_t ordinal{}; + uint64_t rva{}; + uint64_t address{}; +}; + +using exported_symbols = std::vector; + struct mapped_binary { uint64_t image_base{}; uint64_t size_of_image{}; - std::unordered_map exports{}; + exported_symbols exports{}; }; struct event diff --git a/src/windows_emulator/syscalls.cpp b/src/windows_emulator/syscalls.cpp index 5fd33832..771d1da2 100644 --- a/src/windows_emulator/syscalls.cpp +++ b/src/windows_emulator/syscalls.cpp @@ -1,14 +1,14 @@ #include "std_include.hpp" #include "syscalls.hpp" +struct syscall_context +{ + x64_emulator& emu; + process_context& proc; +}; + namespace { - struct syscall_context - { - x64_emulator& emu; - process_context& proc; - }; - constexpr uint64_t PSEUDO_BIT = 1ULL << 63ULL; constexpr uint64_t EVENT_BIT = 1ULL << 62ULL; constexpr uint64_t DIRECTORY_BIT = 1ULL << 61ULL; @@ -34,6 +34,53 @@ namespace } } + + bool is_uppercase(const char character) + { + return toupper(character) == character; + } + + bool is_syscall(const std::string_view name) + { + return name.starts_with("Nt") && name.size() > 3 && is_uppercase(name[2]); + } + + std::vector find_syscalls(const exported_symbols& exports) + { + std::map ordered_syscalls{}; + + for (const auto& symbol : exports) + { + if (is_syscall(symbol.name)) + { + ordered_syscalls[symbol.address] = symbol.name; + } + } + + std::vector syscalls{}; + syscalls.reserve(ordered_syscalls.size()); + + for (auto& syscall : ordered_syscalls) + { + syscalls.push_back(std::move(syscall.second)); + } + + return syscalls; + } + + uint64_t get_syscall_id(const std::vector& syscalls, const std::string_view name) + { + for (size_t i = 0; i < syscalls.size(); ++i) + { + if (syscalls[i] == name) + { + return i; + } + } + + throw std::runtime_error("Unable to determine syscall id: " + std::string(name)); + } + uint32_t store_os_handle(process_context& proc, const HANDLE handle) { uint32_t index = 1; @@ -184,8 +231,9 @@ namespace } NTSTATUS handle_NtSetInformationThread(const syscall_context& c, const uint64_t /*thread_handle*/, - const THREADINFOCLASS info_class, - const uint64_t /*thread_information*/, const uint32_t /*thread_information_length*/) + const THREADINFOCLASS info_class, + const uint64_t /*thread_information*/, + const uint32_t /*thread_information_length*/) { if (info_class == ThreadSchedulerSharedDataSlot) { @@ -495,7 +543,7 @@ namespace return STATUS_SUCCESS; } - NTSTATUS handle_NtQueryProcessInformation(const syscall_context& c, const uint64_t process_handle, + NTSTATUS handle_NtQueryInformationProcess(const syscall_context& c, const uint64_t process_handle, const uint32_t info_class, const uint64_t process_information, const uint32_t process_information_length, const emulator_object return_length) @@ -729,55 +777,51 @@ namespace throw std::runtime_error("Bad free type"); } - -#define handle(id, handler) \ - case id: \ - forward(c, handler);\ - break - - void dispatch_syscall(const syscall_context& c, const uint32_t syscall_id) - { - switch (syscall_id) - { - handle(0x00D, handle_NtSetInformationThread); - handle(0x00E, handle_NtSetEvent); - handle(0x00F, handle_NtClose); - handle(0x012, handle_NtOpenKey); - handle(0x018, handle_NtAllocateVirtualMemory); - handle(0x019, handle_NtQueryProcessInformation); - handle(0x01C, handle_NtSetInformationProcess); - handle(0x01E, handle_NtFreeVirtualMemory); - handle(0x023, handle_NtQueryVirtualMemory); - handle(0x024, handle_NtOpenThreadToken); - handle(0x031, handle_NtQueryPerformanceCounter); - handle(0x036, handle_NtQuerySystemInformation); - handle(0x048, handle_NtCreateEvent); - handle(0x050, handle_NtProtectVirtualMemory); - handle(0x058, handle_NtOpenDirectoryObject); - handle(0x05E, handle_NtTraceEvent); - handle(0x078, handle_NtAllocateVirtualMemoryEx); - handle(0x0B2, handle_NtCreateIoCompletion); - handle(0x0D2, handle_NtCreateWaitCompletionPacket); - handle(0x0D5, handle_NtCreateWorkerFactory); - handle(0x11A, handle_NtManageHotPatch); - handle(0x138, handle_NtOpenSymbolicLinkObject); - handle(0x16B, handle_NtQuerySymbolicLinkObject); - handle(0x16E, handle_NtQuerySystemInformationEx); - - default: - printf("Unhandled syscall: %X\n", syscall_id); - c.emu.reg(x64_register::rax, STATUS_NOT_SUPPORTED); - c.emu.stop(); - break; - } - } - - -#undef handle } +syscall_dispatcher::syscall_dispatcher(const exported_symbols& ntdll_exports) +{ +#define add_handler(syscall) do \ + { \ + const auto id = get_syscall_id(syscalls, #syscall); \ + auto handler = +[](const syscall_context& c) \ + { \ + forward(c, handle_ ## syscall); \ + }; \ + this->handlers_[id] = handler; \ + } while(0) -void handle_syscall(x64_emulator& emu, process_context& context) + const auto syscalls = find_syscalls(ntdll_exports); + + add_handler(NtSetInformationThread); + add_handler(NtSetEvent); + add_handler(NtClose); + add_handler(NtOpenKey); + add_handler(NtAllocateVirtualMemory); + add_handler(NtQueryInformationProcess); + add_handler(NtSetInformationProcess); + add_handler(NtFreeVirtualMemory); + add_handler(NtQueryVirtualMemory); + add_handler(NtOpenThreadToken); + add_handler(NtQueryPerformanceCounter); + add_handler(NtQuerySystemInformation); + add_handler(NtCreateEvent); + add_handler(NtProtectVirtualMemory); + add_handler(NtOpenDirectoryObject); + add_handler(NtTraceEvent); + add_handler(NtAllocateVirtualMemoryEx); + add_handler(NtCreateIoCompletion); + add_handler(NtCreateWaitCompletionPacket); + add_handler(NtCreateWorkerFactory); + add_handler(NtManageHotPatch); + add_handler(NtOpenSymbolicLinkObject); + add_handler(NtQuerySymbolicLinkObject); + add_handler(NtQuerySystemInformationEx); + +#undef add_handler +} + +void syscall_dispatcher::dispatch(x64_emulator& emu, process_context& context) { const auto address = emu.read_instruction_pointer(); const auto syscall_id = emu.reg(x64_register::eax); @@ -788,7 +832,17 @@ void handle_syscall(x64_emulator& emu, process_context& context) try { - dispatch_syscall(c, syscall_id); + const auto entry = this->handlers_.find(syscall_id); + if (entry == this->handlers_.end()) + { + printf("Unhandled syscall: %X\n", syscall_id); + c.emu.reg(x64_register::rax, STATUS_NOT_SUPPORTED); + c.emu.stop(); + } + else + { + entry->second(c); + } } catch (std::exception& e) { diff --git a/src/windows_emulator/syscalls.hpp b/src/windows_emulator/syscalls.hpp index 8990ffca..f2f81016 100644 --- a/src/windows_emulator/syscalls.hpp +++ b/src/windows_emulator/syscalls.hpp @@ -3,4 +3,16 @@ #include #include "process_context.hpp" -void handle_syscall(x64_emulator& emu, process_context& context); +struct syscall_context; +using syscall_handler = void(*)(const syscall_context& c); + +class syscall_dispatcher +{ +public: + syscall_dispatcher(const exported_symbols& ntdll_exports); + + void dispatch(x64_emulator& emu, process_context& context); + +private: + std::unordered_map handlers_{}; +};