diff --git a/src/emulator/emulator.hpp b/src/emulator/emulator.hpp index ba4edd18..58e5d84e 100644 --- a/src/emulator/emulator.hpp +++ b/src/emulator/emulator.hpp @@ -40,8 +40,8 @@ using edge_generation_hook_callback = std::function; using interrupt_hook_callback = std::function; -using simple_memory_hook_callback = std::function; -using complex_memory_hook_callback = std::function; +using simple_memory_hook_callback = std::function; +using complex_memory_hook_callback = std::function; using memory_violation_hook_callback = std::function; @@ -133,10 +133,10 @@ private: { assert((static_cast(operation) & (static_cast(operation) - 1)) == 0); return this->hook_memory_access(address, size, operation, - [c = std::move(callback)](const uint64_t a, const size_t s, + [c = std::move(callback)](const uint64_t a, const size_t s, const uint64_t value, memory_operation) { - c(a, s); + c(a, s, value); }); } diff --git a/src/unicorn_emulator/unicorn_x64_emulator.cpp b/src/unicorn_emulator/unicorn_x64_emulator.cpp index 44faea20..e7faaa77 100644 --- a/src/unicorn_emulator/unicorn_x64_emulator.cpp +++ b/src/unicorn_emulator/unicorn_x64_emulator.cpp @@ -159,7 +159,7 @@ namespace unicorn const auto operation = map_memory_operation(type); if (operation != memory_permission::none) { - (*callback)(address, static_cast(size), operation); + (*callback)(address, static_cast(size), 0, operation); } }); @@ -176,12 +176,12 @@ namespace unicorn { function_wrapper wrapper( [callback](uc_engine*, const uc_mem_type type, const uint64_t address, const int size, - const int64_t) + const uint64_t value) { const auto operation = map_memory_operation(type); if (operation != memory_permission::none) { - (*callback)(address, static_cast(size), operation); + (*callback)(address, static_cast(size), value, operation); } }); @@ -199,7 +199,7 @@ namespace unicorn function_wrapper wrapper( [callback](uc_engine*, const uint64_t address, const uint32_t size) { - (*callback)(address, size, memory_permission::exec); + (*callback)(address, size, 0, memory_permission::exec); }); unicorn_hook hook{uc}; diff --git a/src/windows_emulator/debugging/x64_gdb_stub_handler.hpp b/src/windows_emulator/debugging/x64_gdb_stub_handler.hpp index 9279bf33..8b29877b 100644 --- a/src/windows_emulator/debugging/x64_gdb_stub_handler.hpp +++ b/src/windows_emulator/debugging/x64_gdb_stub_handler.hpp @@ -171,7 +171,7 @@ public: { this->hooks_[{addr, size, type}] = scoped_hook(*this->emu_, this->emu_->hook_memory_access( addr, size, map_breakpoint_type(type), - [this](uint64_t, size_t, memory_operation) + [this](uint64_t, size_t, uint64_t, memory_operation) { this->on_interrupt(); })); diff --git a/src/windows_emulator/emulator_utils.hpp b/src/windows_emulator/emulator_utils.hpp index a368b567..c5dd9ea1 100644 --- a/src/windows_emulator/emulator_utils.hpp +++ b/src/windows_emulator/emulator_utils.hpp @@ -128,10 +128,13 @@ public: constexpr auto required_alignment = alignof(decltype(str[0])); const auto total_length = str.size() * element_size; - const auto string_buffer = this->reserve(total_length, required_alignment); + const auto string_buffer = this->reserve(total_length + element_size, required_alignment); this->emu_->write_memory(string_buffer, str.data(), total_length); + constexpr std::array nullbyte{}; + this->emu_->write_memory(string_buffer + total_length, nullbyte.data(), nullbyte.size()); + result.Buffer = reinterpret_cast(string_buffer); result.Length = static_cast(total_length); result.MaximumLength = result.Length; @@ -159,6 +162,11 @@ public: return this->size_; } + uint64_t get_next_address() const + { + return this->active_address_; + } + void serialize(utils::buffer_serializer& buffer) const { buffer.write(this->address_); @@ -179,3 +187,30 @@ private: uint64_t size_{}; uint64_t active_address_{0}; }; + +inline std::wstring read_unicode_string(const emulator& emu, const UNICODE_STRING ucs) +{ + static_assert(offsetof(UNICODE_STRING, Length) == 0); + static_assert(offsetof(UNICODE_STRING, MaximumLength) == 2); + static_assert(offsetof(UNICODE_STRING, Buffer) == 8); + static_assert(sizeof(UNICODE_STRING) == 16); + + std::wstring result{}; + result.resize(ucs.Length / 2); + + emu.read_memory(reinterpret_cast(ucs.Buffer), result.data(), ucs.Length); + + return result; +} + + +inline std::wstring read_unicode_string(const emulator& emu, const emulator_object uc_string) +{ + const auto ucs = uc_string.read(); + return read_unicode_string(emu, ucs); +} + +inline std::wstring read_unicode_string(emulator& emu, const UNICODE_STRING* uc_string) +{ + return read_unicode_string(emu, emulator_object{emu, uc_string}); +} diff --git a/src/windows_emulator/main.cpp b/src/windows_emulator/main.cpp index b68ff45a..080ed938 100644 --- a/src/windows_emulator/main.cpp +++ b/src/windows_emulator/main.cpp @@ -71,30 +71,52 @@ namespace }; template - void watch_object(x64_emulator& emu, emulator_object object) + emulator_hook* watch_object(windows_emulator& emu, emulator_object object) { const type_info info{}; - emu.hook_memory_read(object.value(), object.size(), - [i = std::move(info), object](const uint64_t address, size_t) - { - const auto offset = address - object.value(); - printf("%s: %llX (%s)\n", i.get_type_name().c_str(), offset, - i.get_member_name(offset).c_str()); - }); + return emu.emu().hook_memory_read(object.value(), object.size(), + [i = std::move(info), object, &emu](const uint64_t address, size_t, uint64_t) + { + const auto rip = emu.emu().read_instruction_pointer(); + const auto* binary = emu.process().module_manager.find_by_address(rip); + + const auto offset = address - object.value(); + printf("%s: %llX (%s) at %llX (%s)\n", i.get_type_name().c_str(), offset, + i.get_member_name(offset).c_str(), rip, + binary ? binary->name.c_str() : ""); + }); } void run() { const std::filesystem::path application = - R"(C:\Program Files (x86)\Steam\steamapps\common\Hogwarts Legacy\Phoenix\Binaries\Win64\HogwartsLegacy.exe)"; + R"(C:\Users\mauri\source\repos\ConsoleApplication6\x64\Release\ConsoleApplication6.exe)"; + //R"(C:\Program Files (x86)\Steam\steamapps\common\Hogwarts Legacy\Phoenix\Binaries\Win64\HogwartsLegacy.exe)"; - windows_emulator win_emu{application}; + windows_emulator win_emu{application, {L"Hello", L"World"}}; - watch_object(win_emu.emu(), win_emu.process().teb); - watch_object(win_emu.emu(), win_emu.process().peb); - watch_object(win_emu.emu(), win_emu.process().process_params); - watch_object(win_emu.emu(), win_emu.process().kusd); + watch_object(win_emu, win_emu.process().teb); + watch_object(win_emu, win_emu.process().peb); + watch_object(win_emu, win_emu.process().kusd); + auto* params_hook = watch_object(win_emu, win_emu.process().process_params); + + win_emu.emu().hook_memory_write(win_emu.process().peb.value() + offsetof(PEB, ProcessParameters), 0x8, + [&](const uint64_t address, size_t, const uint64_t value) + { + const auto target_address = win_emu.process().peb.value() + offsetof( + PEB, ProcessParameters); + + if (address == target_address) + { + const emulator_object obj{ + win_emu.emu(), value + }; + + win_emu.emu().delete_hook(params_hook); + params_hook = watch_object(win_emu, obj); + } + }); win_emu.set_verbose(false); diff --git a/src/windows_emulator/syscalls.cpp b/src/windows_emulator/syscalls.cpp index 6f7a1169..b3b48f10 100644 --- a/src/windows_emulator/syscalls.cpp +++ b/src/windows_emulator/syscalls.cpp @@ -1,6 +1,7 @@ #include "std_include.hpp" #include "syscalls.hpp" #include "context_frame.hpp" +#include "emulator_utils.hpp" #include @@ -111,28 +112,6 @@ namespace throw std::runtime_error("Unable to determine syscall id: " + std::string(name)); } - std::wstring read_unicode_string(emulator& emu, const emulator_object uc_string) - { - static_assert(offsetof(UNICODE_STRING, Length) == 0); - static_assert(offsetof(UNICODE_STRING, MaximumLength) == 2); - static_assert(offsetof(UNICODE_STRING, Buffer) == 8); - static_assert(sizeof(UNICODE_STRING) == 16); - - const auto ucs = uc_string.read(); - - std::wstring result{}; - result.resize(ucs.Length / 2); - - emu.read_memory(reinterpret_cast(ucs.Buffer), result.data(), ucs.Length); - - return result; - } - - std::wstring read_unicode_string(emulator& emu, const PUNICODE_STRING uc_string) - { - return read_unicode_string(emu, emulator_object{emu, uc_string}); - } - template requires(std::is_integral_v || std::is_enum_v) T resolve_argument(x64_emulator& emu, const size_t index) diff --git a/src/windows_emulator/windows_emulator.cpp b/src/windows_emulator/windows_emulator.cpp index c6e9e042..af51fc8e 100644 --- a/src/windows_emulator/windows_emulator.cpp +++ b/src/windows_emulator/windows_emulator.cpp @@ -204,7 +204,8 @@ namespace emu.reg(x64_register::ss, 0x2B); } - void setup_context(process_context& context, x64_emulator& emu, const std::filesystem::path& file) + void setup_context(process_context& context, x64_emulator& emu, const std::filesystem::path& file, + const std::vector& arguments) { setup_stack(emu, STACK_ADDRESS, STACK_SIZE); setup_gdt(emu); @@ -218,7 +219,6 @@ namespace context.teb = gs.reserve(); context.peb = gs.reserve(); - context.process_params = gs.reserve(); context.teb.access([&](TEB& teb) { @@ -230,9 +230,25 @@ namespace teb.ProcessEnvironmentBlock = context.peb.ptr(); }); + /* Values of the following fields must be + * allocated relative to the process_params themselves. + * and included in the length: + * + * CurrentDirectory + * DllPath + * ImagePathName + * CommandLine + * WindowTitle + * DesktopInfo + * ShellInfo + * RuntimeData + * RedirectionDllName + */ + + context.process_params = gs.reserve(); + context.process_params.access([&](RTL_USER_PROCESS_PARAMETERS& proc_params) { - proc_params.Length = sizeof(proc_params); proc_params.Flags = 0x6001 | 0x80000000; // Prevent CsrClientConnectToServer proc_params.ConsoleHandle = CONSOLE_HANDLE.h; @@ -240,9 +256,23 @@ namespace proc_params.StandardInput = STDIN_HANDLE.h; proc_params.StandardError = proc_params.StandardOutput; + + std::wstring command_line = L"\"" + file.wstring() + L"\""; + + for (const auto& arg : arguments) + { + command_line.push_back(L' '); + command_line.append(arg); + } + + gs.make_unicode_string(proc_params.CommandLine, command_line); gs.make_unicode_string(proc_params.CurrentDirectory.DosPath, file.parent_path().wstring()); gs.make_unicode_string(proc_params.ImagePathName, file.wstring()); - gs.make_unicode_string(proc_params.CommandLine, file.wstring()); + + const auto total_length = gs.get_next_address() - context.process_params.value(); + + proc_params.Length = std::max(sizeof(proc_params), total_length); + proc_params.MaximumLength = proc_params.Length; }); context.peb.access([&](PEB& peb) @@ -435,10 +465,11 @@ namespace } } -windows_emulator::windows_emulator(const std::filesystem::path& application, std::unique_ptr emu) +windows_emulator::windows_emulator(const std::filesystem::path& application, const std::vector& arguments, + std::unique_ptr emu) : windows_emulator(std::move(emu)) { - this->setup_process(application); + this->setup_process(application, arguments); } windows_emulator::windows_emulator(std::unique_ptr emu) @@ -448,14 +479,15 @@ windows_emulator::windows_emulator(std::unique_ptr emu) this->setup_hooks(); } -void windows_emulator::setup_process(const std::filesystem::path& application) +void windows_emulator::setup_process(const std::filesystem::path& application, + const std::vector& arguments) { auto& emu = this->emu(); auto& context = this->process(); context.module_manager = module_manager(emu); // TODO: Cleanup module manager - setup_context(context, emu, application); + setup_context(context, emu, application, arguments); context.executable = context.module_manager.map_module(application); @@ -532,40 +564,46 @@ void windows_emulator::setup_hooks() return memory_violation_continuation::resume; }); - this->emu().hook_memory_execution(0, std::numeric_limits::max(), [&](const uint64_t address, const size_t) - { - ++this->process().executed_instructions; + this->emu().hook_memory_execution(0, std::numeric_limits::max(), + [&](const uint64_t address, const size_t, const uint64_t) + { + ++this->process().executed_instructions; - const auto* binary = this->process().module_manager.find_by_address(address); + const auto* binary = this->process().module_manager.find_by_address(address); - if (binary) - { - const auto export_entry = binary->address_names.find(address); - if (export_entry != binary->address_names.end()) - { - printf("Executing function: %s - %s (%llX)\n", binary->name.c_str(), export_entry->second.c_str(), - address); - } - else if (address == binary->entry_point) - { - printf("Executing entry point: %s (%llX)\n", binary->name.c_str(), address); - } - } + if (binary) + { + const auto export_entry = binary->address_names.find(address); + if (export_entry != binary->address_names.end()) + { + printf("Executing function: %s - %s (%llX)\n", binary->name.c_str(), + export_entry->second.c_str(), + address); + } + else if (address == binary->entry_point) + { + printf("Executing entry point: %s (%llX)\n", binary->name.c_str(), + address); + } + } - if (!this->verbose_) - { - return; - } + if (!this->verbose_) + { + return; + } - auto& emu = this->emu(); + auto& emu = this->emu(); - printf( - "Inst: %16llX - RAX: %16llX - RBX: %16llX - RCX: %16llX - RDX: %16llX - R8: %16llX - R9: %16llX - RDI: %16llX - RSI: %16llX - %s\n", - address, - emu.reg(x64_register::rax), emu.reg(x64_register::rbx), emu.reg(x64_register::rcx), - emu.reg(x64_register::rdx), emu.reg(x64_register::r8), emu.reg(x64_register::r9), - emu.reg(x64_register::rdi), emu.reg(x64_register::rsi), binary ? binary->name.c_str() : ""); - }); + printf( + "Inst: %16llX - RAX: %16llX - RBX: %16llX - RCX: %16llX - RDX: %16llX - R8: %16llX - R9: %16llX - RDI: %16llX - RSI: %16llX - %s\n", + address, + emu.reg(x64_register::rax), emu.reg(x64_register::rbx), + emu.reg(x64_register::rcx), + emu.reg(x64_register::rdx), emu.reg(x64_register::r8), + emu.reg(x64_register::r9), + emu.reg(x64_register::rdi), emu.reg(x64_register::rsi), + binary ? binary->name.c_str() : ""); + }); } void windows_emulator::serialize(utils::buffer_serializer& buffer) const diff --git a/src/windows_emulator/windows_emulator.hpp b/src/windows_emulator/windows_emulator.hpp index 47624457..ffcb68e9 100644 --- a/src/windows_emulator/windows_emulator.hpp +++ b/src/windows_emulator/windows_emulator.hpp @@ -9,7 +9,7 @@ class windows_emulator { public: windows_emulator(std::unique_ptr emu = unicorn::create_x64_emulator()); - windows_emulator(const std::filesystem::path& application, + windows_emulator(const std::filesystem::path& application, const std::vector& arguments = {}, std::unique_ptr emu = unicorn::create_x64_emulator()); windows_emulator(windows_emulator&&) = delete; @@ -59,5 +59,5 @@ private: //std::optional process_snapshot_{}; void setup_hooks(); - void setup_process(const std::filesystem::path& application); + void setup_process(const std::filesystem::path& application, const std::vector& arguments); };