From e0dc1934eb3d04c4a730a7a7622d7039e32e1cdb Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 1 Nov 2024 16:40:22 +0100 Subject: [PATCH 01/19] Add hive parser --- .gitmodules | 3 +++ deps/CMakeLists.txt | 3 ++- deps/windows-hive-parser | 1 + deps/windows-hive-parser.cmake | 5 +++++ src/windows-emulator/CMakeLists.txt | 1 + 5 files changed, 12 insertions(+), 1 deletion(-) create mode 160000 deps/windows-hive-parser create mode 100644 deps/windows-hive-parser.cmake diff --git a/.gitmodules b/.gitmodules index c6814e54..7c2056d1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,3 +18,6 @@ [submodule "deps/googletest"] path = deps/googletest url = https://github.com/google/googletest.git +[submodule "deps/windows-hive-parser"] + path = deps/windows-hive-parser + url = ../windows-hive-parser.git diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 439293bd..5bdc31dc 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -13,4 +13,5 @@ target_include_directories(reflect INTERFACE ########################################## include(mini-gdbstub.cmake) -include(googletest.cmake) \ No newline at end of file +include(googletest.cmake) +include(windows-hive-parser.cmake) diff --git a/deps/windows-hive-parser b/deps/windows-hive-parser new file mode 160000 index 00000000..b1bd6ca4 --- /dev/null +++ b/deps/windows-hive-parser @@ -0,0 +1 @@ +Subproject commit b1bd6ca49e4a91f0def04100b30564ae25d411ed diff --git a/deps/windows-hive-parser.cmake b/deps/windows-hive-parser.cmake new file mode 100644 index 00000000..98304124 --- /dev/null +++ b/deps/windows-hive-parser.cmake @@ -0,0 +1,5 @@ +add_library(windows-hive-parser INTERFACE) + +target_include_directories(windows-hive-parser INTERFACE + "${CMAKE_CURRENT_LIST_DIR}/windows-hive-parser" +) diff --git a/src/windows-emulator/CMakeLists.txt b/src/windows-emulator/CMakeLists.txt index 7948efc7..ed81108e 100644 --- a/src/windows-emulator/CMakeLists.txt +++ b/src/windows-emulator/CMakeLists.txt @@ -16,6 +16,7 @@ target_link_libraries(windows-emulator PRIVATE common unicorn-emulator mini-gdbstub + windows-hive-parser ) target_link_libraries(windows-emulator PUBLIC From 92b9712e45f3b203f88a92261c8a87d3a66f0f52 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 2 Nov 2024 16:07:27 +0100 Subject: [PATCH 02/19] Prepare registry support --- deps/windows-hive-parser | 2 +- src/windows-emulator/handles.hpp | 1 + src/windows-emulator/process_context.hpp | 8 ++ src/windows-emulator/registry_manager.cpp | 123 ++++++++++++++++++++++ src/windows-emulator/registry_manager.hpp | 48 +++++++++ src/windows-emulator/syscalls.cpp | 13 ++- 6 files changed, 192 insertions(+), 3 deletions(-) create mode 100644 src/windows-emulator/registry_manager.cpp create mode 100644 src/windows-emulator/registry_manager.hpp diff --git a/deps/windows-hive-parser b/deps/windows-hive-parser index b1bd6ca4..fdeabc39 160000 --- a/deps/windows-hive-parser +++ b/deps/windows-hive-parser @@ -1 +1 @@ -Subproject commit b1bd6ca49e4a91f0def04100b30564ae25d411ed +Subproject commit fdeabc39b1ed79fb7472fd08288eba8e6649a812 diff --git a/src/windows-emulator/handles.hpp b/src/windows-emulator/handles.hpp index 49fd1d55..8ee8d1d0 100644 --- a/src/windows-emulator/handles.hpp +++ b/src/windows-emulator/handles.hpp @@ -13,6 +13,7 @@ struct handle_types semaphore, port, thread, + registry, }; }; diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index b2b104e0..3caf52ce 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -2,6 +2,7 @@ #include "emulator_utils.hpp" #include "handles.hpp" +#include "registry_manager.hpp" #include "module/module_manager.hpp" #include @@ -375,6 +376,8 @@ struct process_context { } + registry_manager registry{R"(C:\Users\mauri\Desktop\windows\win-x64\registry)"}; // TODO: Fix + uint64_t executed_instructions{0}; uint64_t current_ip{0}; uint64_t previous_ip{0}; @@ -404,6 +407,7 @@ struct process_context handle_store files{}; handle_store semaphores{}; handle_store ports{}; + handle_store registry_keys{}; std::map atoms{}; std::vector default_register_set{}; @@ -414,6 +418,7 @@ struct process_context void serialize(utils::buffer_serializer& buffer) const { + buffer.write(this->registry); buffer.write(this->executed_instructions); buffer.write(this->current_ip); buffer.write(this->previous_ip); @@ -438,6 +443,7 @@ struct process_context buffer.write(this->files); buffer.write(this->semaphores); buffer.write(this->ports); + buffer.write(this->registry_keys); buffer.write_map(this->atoms); buffer.write_vector(this->default_register_set); @@ -449,6 +455,7 @@ struct process_context void deserialize(utils::buffer_deserializer& buffer) { + buffer.read(this->registry); buffer.read(this->executed_instructions); buffer.read(this->current_ip); buffer.read(this->previous_ip); @@ -477,6 +484,7 @@ struct process_context buffer.read(this->files); buffer.read(this->semaphores); buffer.read(this->ports); + buffer.read(this->registry_keys); buffer.read_map(this->atoms); buffer.read_vector(this->default_register_set); diff --git a/src/windows-emulator/registry_manager.cpp b/src/windows-emulator/registry_manager.cpp new file mode 100644 index 00000000..7137df9b --- /dev/null +++ b/src/windows-emulator/registry_manager.cpp @@ -0,0 +1,123 @@ +#include "registry_manager.hpp" +#include + +namespace +{ + std::filesystem::path canonicalize_path(const std::filesystem::path& key) + { + auto path = key.lexically_normal().wstring(); + std::ranges::transform(path, path.begin(), std::towlower); + return {std::move(path)}; + } + + bool is_subpath(const std::filesystem::path& root, const std::filesystem::path& p) + { + auto root_it = root.begin(); + auto p_it = p.begin(); + + for (; root_it != root.end(); ++root_it, ++p_it) + { + if (p_it == p.end() || *root_it != *p_it) + { + return false; + } + } + + return true; + } + + void register_hive(registry_manager::hive_map& hives, + const std::filesystem::path& key, const std::filesystem::path& file) + { + hives[canonicalize_path(key)] = std::make_unique(file); + } +} + +registry_manager::~registry_manager() = default; + +registry_manager::registry_manager(const std::filesystem::path& hive_path) +{ + const std::filesystem::path root = R"(\registry)"; + const std::filesystem::path machine = root / "machine"; + + register_hive(this->hives_, machine / "system", hive_path / "SYSTEM"); + register_hive(this->hives_, machine / "security", hive_path / "SECURITY"); + register_hive(this->hives_, machine / "sam", hive_path / "SAM"); + register_hive(this->hives_, machine / "software", hive_path / "SOFTWARE"); + register_hive(this->hives_, machine / "system", hive_path / "SYSTEM"); + register_hive(this->hives_, machine / "hardware", hive_path / "HARDWARE"); + + register_hive(this->hives_, root / "user", hive_path / "NTUSER.dat"); + + this->add_path_mapping(machine / "system" / "CurrentControlSet", machine / "system" / "ControlSet001"); +} + +void registry_manager::serialize(utils::buffer_serializer& buffer) const +{ + (void)buffer; +} + +void registry_manager::deserialize(utils::buffer_deserializer& buffer) +{ + (void)buffer; +} + +std::filesystem::path registry_manager::normalize_path(const std::filesystem::path& path) const +{ + auto canonical_path = canonicalize_path(path); + + for (const auto& mapping : this->path_mapping_) + { + if (is_subpath(mapping.first, canonical_path)) + { + return mapping.second / canonical_path.lexically_relative(mapping.first); + } + } + + return canonical_path; +} + +void registry_manager::add_path_mapping(const std::filesystem::path& key, const std::filesystem::path& value) +{ + this->path_mapping_[canonicalize_path(key)] = canonicalize_path(value); +} + +std::optional registry_manager::get_key(const std::filesystem::path& key) +{ + const auto normal_key = this->normalize_path(key); + const auto iterator = this->find_hive(normal_key); + if (iterator == this->hives_.end()) + { + return {}; + } + + registry_key reg_key{}; + reg_key.hive = iterator->first; + reg_key.path = normal_key.lexically_relative(reg_key.hive); + + if (reg_key.path.empty()) + { + return {std::move(reg_key)}; + } + + const auto entry = iterator->second->get_subkey(reg_key.path.begin()->string(), reg_key.path.generic_string()); + if (!entry) + { + return {}; + } + + return {std::move(reg_key)}; +} + +registry_manager::hive_map::iterator registry_manager::find_hive(const std::filesystem::path& key) +{ + for (auto i = this->hives_.begin(); i != this->hives_.end(); ++i) + { + if (is_subpath(i->first, key)) + { + return i; + } + } + + return this->hives_.end(); +} diff --git a/src/windows-emulator/registry_manager.hpp b/src/windows-emulator/registry_manager.hpp new file mode 100644 index 00000000..3f96cbde --- /dev/null +++ b/src/windows-emulator/registry_manager.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include "std_include.hpp" +#include + +class hive_parser; + +struct registry_key +{ + std::filesystem::path hive{}; + std::filesystem::path path{}; + + void serialize(utils::buffer_serializer& buffer) const + { + buffer.write_string(this->hive.wstring()); + buffer.write_string(this->path.wstring()); + } + + void deserialize(utils::buffer_deserializer& buffer) + { + this->hive = buffer.read_string(); + this->path = buffer.read_string(); + } +}; + +class registry_manager +{ +public: + using hive_ptr = std::unique_ptr; + using hive_map = std::unordered_map; + + registry_manager(const std::filesystem::path& hive_path); + ~registry_manager(); + + void serialize(utils::buffer_serializer& buffer) const; + void deserialize(utils::buffer_deserializer& buffer); + + std::optional get_key(const std::filesystem::path& key); + +private: + hive_map hives_{}; + std::unordered_map path_mapping_{}; + + std::filesystem::path normalize_path(const std::filesystem::path& path) const; + void add_path_mapping(const std::filesystem::path& key, const std::filesystem::path& value); + + hive_map::iterator find_hive(const std::filesystem::path& key); +}; diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index a848be4b..9bd29fa7 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -53,7 +53,7 @@ namespace return STATUS_NOT_SUPPORTED; } - NTSTATUS handle_NtOpenKey(const syscall_context& c, const emulator_object /*key_handle*/, + NTSTATUS handle_NtOpenKey(const syscall_context& c, const emulator_object key_handle, const ACCESS_MASK /*desired_access*/, const emulator_object object_attributes) { @@ -62,7 +62,16 @@ namespace c.win_emu.logger.print(color::dark_gray, "--> Registry key: %S\n", key.c_str()); - return STATUS_OBJECT_NAME_NOT_FOUND; + auto entry = c.proc.registry.get_key(key); + if (!entry.has_value()) + { + return STATUS_OBJECT_NAME_NOT_FOUND; + } + + const auto handle = c.proc.registry_keys.store(std::move(entry.value())); + key_handle.write(handle.bits); + + return STATUS_SUCCESS; } NTSTATUS handle_NtOpenKeyEx() From 86609ff151208998a1690987792aaa04beb79bbf Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 2 Nov 2024 22:52:09 +0100 Subject: [PATCH 03/19] Almost working registry support --- deps/windows-hive-parser | 2 +- src/windows-emulator/registry_manager.cpp | 27 ++++++++++ src/windows-emulator/registry_manager.hpp | 7 +++ src/windows-emulator/syscalls.cpp | 63 ++++++++++++++++++++++- 4 files changed, 96 insertions(+), 3 deletions(-) diff --git a/deps/windows-hive-parser b/deps/windows-hive-parser index fdeabc39..df62310e 160000 --- a/deps/windows-hive-parser +++ b/deps/windows-hive-parser @@ -1 +1 @@ -Subproject commit fdeabc39b1ed79fb7472fd08288eba8e6649a812 +Subproject commit df62310ec375a47f9460f5c756346462033359b8 diff --git a/src/windows-emulator/registry_manager.cpp b/src/windows-emulator/registry_manager.cpp index 7137df9b..0008452c 100644 --- a/src/windows-emulator/registry_manager.cpp +++ b/src/windows-emulator/registry_manager.cpp @@ -109,6 +109,33 @@ std::optional registry_manager::get_key(const std::filesystem::pat return {std::move(reg_key)}; } +std::optional registry_manager::get_value(const registry_key& key, const std::string_view name) +{ + const auto iterator = this->hives_.find(key.hive); + if (iterator == this->hives_.end()) + { + return {}; + } + + auto entry = iterator->second->get_subkey(key.path.begin()->string(), key.path.generic_string()); + if (!entry) + { + return {}; + } + + const auto value = entry->get_key_value(name); + if (!value) + { + return {}; + } + + registry_value v{}; + v.type = value->first; + v.data = value->second; + + return v; +} + registry_manager::hive_map::iterator registry_manager::find_hive(const std::filesystem::path& key) { for (auto i = this->hives_.begin(); i != this->hives_.end(); ++i) diff --git a/src/windows-emulator/registry_manager.hpp b/src/windows-emulator/registry_manager.hpp index 3f96cbde..d8fb7ce9 100644 --- a/src/windows-emulator/registry_manager.hpp +++ b/src/windows-emulator/registry_manager.hpp @@ -23,6 +23,12 @@ struct registry_key } }; +struct registry_value +{ + uint32_t type; + std::string_view data; +}; + class registry_manager { public: @@ -36,6 +42,7 @@ public: void deserialize(utils::buffer_deserializer& buffer); std::optional get_key(const std::filesystem::path& key); + std::optional get_value(const registry_key& key, const std::string_view name); private: hive_map hives_{}; diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 9bd29fa7..2f22d2a1 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -74,9 +74,62 @@ namespace return STATUS_SUCCESS; } - NTSTATUS handle_NtOpenKeyEx() + NTSTATUS handle_NtOpenKeyEx(const syscall_context& c, const emulator_object key_handle, + const ACCESS_MASK desired_access, + const emulator_object object_attributes, + ULONG /*open_options*/) { - return STATUS_NOT_SUPPORTED; + return handle_NtOpenKey(c, key_handle, desired_access, object_attributes); + } + + NTSTATUS handle_NtQueryValueKey(const syscall_context& c, handle key_handle, + const emulator_object value_name, + KEY_VALUE_INFORMATION_CLASS key_value_information_class, + uint64_t key_value_information, + ULONG length, const emulator_object result_length) + { + if (key_value_information_class != KeyValuePartialInformation) + { + c.win_emu.logger.print(color::gray, "Unsupported registry class: %X\n", key_value_information_class); + c.emu.stop(); + return STATUS_NOT_SUPPORTED; + } + + const auto* key = c.proc.registry_keys.get(key_handle); + if (!key) + { + return STATUS_INVALID_HANDLE; + } + + const auto wide_name = read_unicode_string(c.emu, value_name); + const std::string name(wide_name.begin(), wide_name.end()); + + const auto value = c.proc.registry.get_value(*key, name); + if (!value) + { + return STATUS_OBJECT_NAME_NOT_FOUND; + } + + const auto required_size = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + value->data.size() - 1; + result_length.write(static_cast(required_size)); + + if (required_size > length) + { + return STATUS_BUFFER_TOO_SMALL; + } + + KEY_VALUE_PARTIAL_INFORMATION partial_info{}; + partial_info.TitleIndex = 0; + partial_info.Type = value->type; + partial_info.DataLength = static_cast(value->data.size()); + + const emulator_object partial_info_obj{c.emu, key_value_information}; + partial_info_obj.write(partial_info); + + c.emu.write_memory(key_value_information + offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data), value->data.data(), + value->data.size()); + + return STATUS_SUCCESS; } NTSTATUS handle_NtSetInformationThread(const syscall_context& c, const uint64_t thread_handle, @@ -165,6 +218,11 @@ namespace return STATUS_SUCCESS; } + if (value.type == handle_types::registry && c.proc.registry_keys.erase(handle)) + { + return STATUS_SUCCESS; + } + return STATUS_INVALID_HANDLE; } @@ -2276,6 +2334,7 @@ void syscall_dispatcher::add_handlers(std::map& ha add_handler(NtReadFile); add_handler(NtSetInformationFile); add_handler(NtUserRegisterWindowMessage); + add_handler(NtQueryValueKey); #undef add_handler } From 52f2a6835a8e29132a35161776aa74b06689249f Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 2 Nov 2024 22:55:29 +0100 Subject: [PATCH 04/19] Some dummy syscall --- src/windows-emulator/syscalls.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 2f22d2a1..5578e68e 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -1599,6 +1599,11 @@ namespace return STATUS_NOT_SUPPORTED; } + NTSTATUS handle_NtGetNlsSectionPtr() + { + return STATUS_NOT_SUPPORTED; + } + NTSTATUS handle_NtAlpcSendWaitReceivePort(const syscall_context& c, const uint64_t port_handle, const ULONG /*flags*/, const emulator_object /*send_message*/, @@ -2335,6 +2340,7 @@ void syscall_dispatcher::add_handlers(std::map& ha add_handler(NtSetInformationFile); add_handler(NtUserRegisterWindowMessage); add_handler(NtQueryValueKey); + add_handler(NtGetNlsSectionPtr); #undef add_handler } From 629f9998d7eefdce4c253496a9c778e46a90a389 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 3 Nov 2024 08:06:54 +0100 Subject: [PATCH 05/19] More registry progress --- src/windows-emulator/syscalls.cpp | 98 ++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 21 deletions(-) diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 5578e68e..19f7b58f 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -88,13 +88,6 @@ namespace uint64_t key_value_information, ULONG length, const emulator_object result_length) { - if (key_value_information_class != KeyValuePartialInformation) - { - c.win_emu.logger.print(color::gray, "Unsupported registry class: %X\n", key_value_information_class); - c.emu.stop(); - return STATUS_NOT_SUPPORTED; - } - const auto* key = c.proc.registry_keys.get(key_handle); if (!key) { @@ -110,26 +103,89 @@ namespace return STATUS_OBJECT_NAME_NOT_FOUND; } - const auto required_size = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + value->data.size() - 1; - result_length.write(static_cast(required_size)); - - if (required_size > length) + if (key_value_information_class == KeyValueBasicInformation) { - return STATUS_BUFFER_TOO_SMALL; + const auto required_size = sizeof(KEY_VALUE_BASIC_INFORMATION) + (wide_name.size() * 2) - 1; + result_length.write(static_cast(required_size)); + + if (required_size > length) + { + return STATUS_BUFFER_TOO_SMALL; + } + + KEY_VALUE_BASIC_INFORMATION info{}; + info.TitleIndex = 0; + info.Type = value->type; + info.NameLength = static_cast(wide_name.size() * 2); + + const emulator_object info_obj{ c.emu, key_value_information }; + info_obj.write(info); + + c.emu.write_memory(key_value_information + offsetof(KEY_VALUE_BASIC_INFORMATION, Name), + wide_name.data(), + info.NameLength); + + return STATUS_SUCCESS; } - KEY_VALUE_PARTIAL_INFORMATION partial_info{}; - partial_info.TitleIndex = 0; - partial_info.Type = value->type; - partial_info.DataLength = static_cast(value->data.size()); + if (key_value_information_class == KeyValuePartialInformation) + { + const auto required_size = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + value->data.size() - 1; + result_length.write(static_cast(required_size)); - const emulator_object partial_info_obj{c.emu, key_value_information}; - partial_info_obj.write(partial_info); + if (required_size > length) + { + return STATUS_BUFFER_TOO_SMALL; + } - c.emu.write_memory(key_value_information + offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data), value->data.data(), - value->data.size()); + KEY_VALUE_PARTIAL_INFORMATION info{}; + info.TitleIndex = 0; + info.Type = value->type; + info.DataLength = static_cast(value->data.size()); - return STATUS_SUCCESS; + const emulator_object info_obj{c.emu, key_value_information}; + info_obj.write(info); + + c.emu.write_memory(key_value_information + offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data), + value->data.data(), + value->data.size()); + + return STATUS_SUCCESS; + } + + if (key_value_information_class == KeyValueFullInformation) + { + const auto required_size = sizeof(KEY_VALUE_FULL_INFORMATION) + (wide_name.size() * 2) + value->data.size() - 1; + result_length.write(static_cast(required_size)); + + if (required_size > length) + { + return STATUS_BUFFER_TOO_SMALL; + } + + KEY_VALUE_FULL_INFORMATION info{}; + info.TitleIndex = 0; + info.Type = value->type; + info.DataLength = static_cast(value->data.size()); + info.NameLength = static_cast(wide_name.size() * 2); + + const emulator_object info_obj{ c.emu, key_value_information }; + info_obj.write(info); + + c.emu.write_memory(key_value_information + offsetof(KEY_VALUE_BASIC_INFORMATION, Name), + wide_name.data(), + info.NameLength); + + c.emu.write_memory(key_value_information + offsetof(KEY_VALUE_FULL_INFORMATION, Name) + info.NameLength, + value->data.data(), + value->data.size()); + + return STATUS_SUCCESS; + } + + c.win_emu.logger.print(color::gray, "Unsupported registry class: %X\n", key_value_information_class); + c.emu.stop(); + return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtSetInformationThread(const syscall_context& c, const uint64_t thread_handle, From e7148c91aee34f304fadcb62b4836de0b3c3f702 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 3 Nov 2024 08:26:29 +0100 Subject: [PATCH 06/19] Only register hive when successful --- src/windows-emulator/registry_manager.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/windows-emulator/registry_manager.cpp b/src/windows-emulator/registry_manager.cpp index 0008452c..c73f5f94 100644 --- a/src/windows-emulator/registry_manager.cpp +++ b/src/windows-emulator/registry_manager.cpp @@ -29,7 +29,11 @@ namespace void register_hive(registry_manager::hive_map& hives, const std::filesystem::path& key, const std::filesystem::path& file) { - hives[canonicalize_path(key)] = std::make_unique(file); + auto hive = std::make_unique(file); + if (hive && hive->success()) + { + hives[canonicalize_path(key)] = std::move(hive); + } } } From d710fb44035a4db469fad751f03724b498d4852a Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 3 Nov 2024 09:48:32 +0100 Subject: [PATCH 07/19] Fix serialization --- src/emulator/serialization.hpp | 12 ++++----- src/emulator/serialization_helper.hpp | 30 +++++++++++++++++++++++ src/windows-emulator/process_context.hpp | 15 +----------- src/windows-emulator/registry_manager.hpp | 10 ++++---- 4 files changed, 42 insertions(+), 25 deletions(-) create mode 100644 src/emulator/serialization_helper.hpp diff --git a/src/emulator/serialization.hpp b/src/emulator/serialization.hpp index 0cb844cb..342735aa 100644 --- a/src/emulator/serialization.hpp +++ b/src/emulator/serialization.hpp @@ -38,8 +38,8 @@ namespace utils }; template - struct has_serialize_function(), - std::declval()))>> + struct has_serialize_function(), + std::declval&>()))>> : std::true_type { }; @@ -50,8 +50,8 @@ namespace utils }; template - struct has_deserialize_function(), std::declval()))>> + struct has_deserialize_function(), std::declval&>()))>> : std::true_type { }; @@ -122,7 +122,7 @@ namespace utils } else if constexpr (detail::has_deserialize_function::value) { - deserialize(*this, object); + ::deserialize(*this, object); } else if constexpr (std::is_trivially_copyable_v) { @@ -338,7 +338,7 @@ namespace utils } else if constexpr (detail::has_serialize_function::value) { - serialize(*this, object); + ::serialize(*this, object); } else if constexpr (std::is_trivially_copyable_v) { diff --git a/src/emulator/serialization_helper.hpp b/src/emulator/serialization_helper.hpp new file mode 100644 index 00000000..fbf769cd --- /dev/null +++ b/src/emulator/serialization_helper.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "serialization.hpp" + +#include +#include + +inline void serialize(utils::buffer_serializer& buffer, const std::chrono::steady_clock::time_point& tp) +{ + buffer.write(tp.time_since_epoch().count()); +} + +inline void deserialize(utils::buffer_deserializer& buffer, std::chrono::steady_clock::time_point& tp) +{ + using time_point = std::chrono::steady_clock::time_point; + using duration = time_point::duration; + + const auto count = buffer.read(); + tp = time_point{duration{count}}; +} + +inline void serialize(utils::buffer_serializer& buffer, const std::filesystem::path& path) +{ + buffer.write_string(path.wstring()); +} + +inline void deserialize(utils::buffer_deserializer& buffer, std::filesystem::path& path) +{ + path = buffer.read_string(); +} diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index 3caf52ce..46ec0318 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -9,6 +9,7 @@ #include #include +#include #define PEB_SEGMENT_SIZE (20 << 20) // 20 MB @@ -24,20 +25,6 @@ #define GDT_LIMIT 0x1000 #define GDT_ENTRY_SIZE 0x8 -inline void serialize(utils::buffer_serializer& buffer, const std::chrono::steady_clock::time_point& tp) -{ - buffer.write(tp.time_since_epoch().count()); -} - -inline void deserialize(utils::buffer_deserializer& buffer, std::chrono::steady_clock::time_point& tp) -{ - using time_point = std::chrono::steady_clock::time_point; - using duration = time_point::duration; - - const auto count = buffer.read(); - tp = time_point{duration{count}}; -} - struct ref_counted_object { uint32_t ref_count{1}; diff --git a/src/windows-emulator/registry_manager.hpp b/src/windows-emulator/registry_manager.hpp index d8fb7ce9..bbdeabd8 100644 --- a/src/windows-emulator/registry_manager.hpp +++ b/src/windows-emulator/registry_manager.hpp @@ -1,7 +1,7 @@ #pragma once #include "std_include.hpp" -#include +#include class hive_parser; @@ -12,14 +12,14 @@ struct registry_key void serialize(utils::buffer_serializer& buffer) const { - buffer.write_string(this->hive.wstring()); - buffer.write_string(this->path.wstring()); + buffer.write(this->hive); + buffer.write(this->path); } void deserialize(utils::buffer_deserializer& buffer) { - this->hive = buffer.read_string(); - this->path = buffer.read_string(); + buffer.read(this->hive); + buffer.read(this->path); } }; From 5390d90993b678f25c15b280071a2f4238e868b9 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 3 Nov 2024 10:13:06 +0100 Subject: [PATCH 08/19] Serialize registry manager --- src/emulator/serialization.hpp | 6 +++-- src/windows-emulator/registry_manager.cpp | 32 ++++++++++++++++------- src/windows-emulator/registry_manager.hpp | 6 ++++- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/emulator/serialization.hpp b/src/emulator/serialization.hpp index 342735aa..0b6f916d 100644 --- a/src/emulator/serialization.hpp +++ b/src/emulator/serialization.hpp @@ -39,7 +39,8 @@ namespace utils template struct has_serialize_function(), - std::declval&>()))>> + std::declval&>()) + )>> : std::true_type { }; @@ -51,7 +52,8 @@ namespace utils template struct has_deserialize_function(), std::declval&>()))>> + std::declval(), + std::declval&>()))>> : std::true_type { }; diff --git a/src/windows-emulator/registry_manager.cpp b/src/windows-emulator/registry_manager.cpp index c73f5f94..be8bd01e 100644 --- a/src/windows-emulator/registry_manager.cpp +++ b/src/windows-emulator/registry_manager.cpp @@ -1,5 +1,7 @@ #include "registry_manager.hpp" + #include +#include namespace { @@ -39,31 +41,41 @@ namespace registry_manager::~registry_manager() = default; -registry_manager::registry_manager(const std::filesystem::path& hive_path) +registry_manager::registry_manager(std::filesystem::path hive_path) + : hive_path_(std::move(hive_path)) { + this->setup(); +} + +void registry_manager::setup() +{ + this->path_mapping_.clear(); + this->hives_.clear(); + const std::filesystem::path root = R"(\registry)"; const std::filesystem::path machine = root / "machine"; - register_hive(this->hives_, machine / "system", hive_path / "SYSTEM"); - register_hive(this->hives_, machine / "security", hive_path / "SECURITY"); - register_hive(this->hives_, machine / "sam", hive_path / "SAM"); - register_hive(this->hives_, machine / "software", hive_path / "SOFTWARE"); - register_hive(this->hives_, machine / "system", hive_path / "SYSTEM"); - register_hive(this->hives_, machine / "hardware", hive_path / "HARDWARE"); + register_hive(this->hives_, machine / "system", this->hive_path_ / "SYSTEM"); + register_hive(this->hives_, machine / "security", this->hive_path_ / "SECURITY"); + register_hive(this->hives_, machine / "sam", this->hive_path_ / "SAM"); + register_hive(this->hives_, machine / "software", this->hive_path_ / "SOFTWARE"); + register_hive(this->hives_, machine / "system", this->hive_path_ / "SYSTEM"); + register_hive(this->hives_, machine / "hardware", this->hive_path_ / "HARDWARE"); - register_hive(this->hives_, root / "user", hive_path / "NTUSER.dat"); + register_hive(this->hives_, root / "user", this->hive_path_ / "NTUSER.dat"); this->add_path_mapping(machine / "system" / "CurrentControlSet", machine / "system" / "ControlSet001"); } void registry_manager::serialize(utils::buffer_serializer& buffer) const { - (void)buffer; + buffer.write(this->hive_path_); } void registry_manager::deserialize(utils::buffer_deserializer& buffer) { - (void)buffer; + buffer.read(this->hive_path_); + this->setup(); } std::filesystem::path registry_manager::normalize_path(const std::filesystem::path& path) const diff --git a/src/windows-emulator/registry_manager.hpp b/src/windows-emulator/registry_manager.hpp index bbdeabd8..43a544f5 100644 --- a/src/windows-emulator/registry_manager.hpp +++ b/src/windows-emulator/registry_manager.hpp @@ -35,7 +35,8 @@ public: using hive_ptr = std::unique_ptr; using hive_map = std::unordered_map; - registry_manager(const std::filesystem::path& hive_path); + registry_manager() = default; + registry_manager(std::filesystem::path hive_path); ~registry_manager(); void serialize(utils::buffer_serializer& buffer) const; @@ -45,6 +46,7 @@ public: std::optional get_value(const registry_key& key, const std::string_view name); private: + std::filesystem::path hive_path_{}; hive_map hives_{}; std::unordered_map path_mapping_{}; @@ -52,4 +54,6 @@ private: void add_path_mapping(const std::filesystem::path& key, const std::filesystem::path& value); hive_map::iterator find_hive(const std::filesystem::path& key); + + void setup(); }; From b646ac882ab5f3bebbd2f7a31000547d44944c1d Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 3 Nov 2024 10:34:58 +0100 Subject: [PATCH 09/19] Move registry manager --- src/windows-emulator/process_context.hpp | 2 +- src/windows-emulator/{ => registry}/registry_manager.cpp | 0 src/windows-emulator/{ => registry}/registry_manager.hpp | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/windows-emulator/{ => registry}/registry_manager.cpp (100%) rename src/windows-emulator/{ => registry}/registry_manager.hpp (97%) diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index 46ec0318..8a572d54 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -2,7 +2,7 @@ #include "emulator_utils.hpp" #include "handles.hpp" -#include "registry_manager.hpp" +#include "registry/registry_manager.hpp" #include "module/module_manager.hpp" #include diff --git a/src/windows-emulator/registry_manager.cpp b/src/windows-emulator/registry/registry_manager.cpp similarity index 100% rename from src/windows-emulator/registry_manager.cpp rename to src/windows-emulator/registry/registry_manager.cpp diff --git a/src/windows-emulator/registry_manager.hpp b/src/windows-emulator/registry/registry_manager.hpp similarity index 97% rename from src/windows-emulator/registry_manager.hpp rename to src/windows-emulator/registry/registry_manager.hpp index 43a544f5..b5e79d88 100644 --- a/src/windows-emulator/registry_manager.hpp +++ b/src/windows-emulator/registry/registry_manager.hpp @@ -1,6 +1,6 @@ #pragma once -#include "std_include.hpp" +#include "../std_include.hpp" #include class hive_parser; From 59eba15308d18b8ec01ca4a9b5ba774603dca389 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 3 Nov 2024 10:39:08 +0100 Subject: [PATCH 10/19] Remove hive parser submodule --- .gitmodules | 3 - deps/CMakeLists.txt | 1 - deps/windows-hive-parser | 1 - deps/windows-hive-parser.cmake | 5 - src/windows-emulator/CMakeLists.txt | 1 - src/windows-emulator/registry/hive_parser.hpp | 340 ++++++++++++++++++ .../registry/registry_manager.cpp | 2 +- 7 files changed, 341 insertions(+), 12 deletions(-) delete mode 160000 deps/windows-hive-parser delete mode 100644 deps/windows-hive-parser.cmake create mode 100644 src/windows-emulator/registry/hive_parser.hpp diff --git a/.gitmodules b/.gitmodules index 7c2056d1..c6814e54 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,6 +18,3 @@ [submodule "deps/googletest"] path = deps/googletest url = https://github.com/google/googletest.git -[submodule "deps/windows-hive-parser"] - path = deps/windows-hive-parser - url = ../windows-hive-parser.git diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 5bdc31dc..9217618b 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -14,4 +14,3 @@ target_include_directories(reflect INTERFACE include(mini-gdbstub.cmake) include(googletest.cmake) -include(windows-hive-parser.cmake) diff --git a/deps/windows-hive-parser b/deps/windows-hive-parser deleted file mode 160000 index df62310e..00000000 --- a/deps/windows-hive-parser +++ /dev/null @@ -1 +0,0 @@ -Subproject commit df62310ec375a47f9460f5c756346462033359b8 diff --git a/deps/windows-hive-parser.cmake b/deps/windows-hive-parser.cmake deleted file mode 100644 index 98304124..00000000 --- a/deps/windows-hive-parser.cmake +++ /dev/null @@ -1,5 +0,0 @@ -add_library(windows-hive-parser INTERFACE) - -target_include_directories(windows-hive-parser INTERFACE - "${CMAKE_CURRENT_LIST_DIR}/windows-hive-parser" -) diff --git a/src/windows-emulator/CMakeLists.txt b/src/windows-emulator/CMakeLists.txt index ed81108e..7948efc7 100644 --- a/src/windows-emulator/CMakeLists.txt +++ b/src/windows-emulator/CMakeLists.txt @@ -16,7 +16,6 @@ target_link_libraries(windows-emulator PRIVATE common unicorn-emulator mini-gdbstub - windows-hive-parser ) target_link_libraries(windows-emulator PUBLIC diff --git a/src/windows-emulator/registry/hive_parser.hpp b/src/windows-emulator/registry/hive_parser.hpp new file mode 100644 index 00000000..e2ad7990 --- /dev/null +++ b/src/windows-emulator/registry/hive_parser.hpp @@ -0,0 +1,340 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct offsets_t +{ + long block_size; + char block_type[2]; + short count; + long first; + long hash; +}; + +struct key_block_t +{ + long block_size; + char block_type[2]; + char dummya[18]; + int subkey_count; + char dummyb[4]; + int subkeys; + char dummyc[4]; + int value_count; + int offsets; + char dummyd[28]; + short len; + short du; + char name[255]; +}; + +struct value_block_t +{ + long block_size; + char block_type[2]; + short name_len; + long size; + long offset; + long value_type; + short flags; + short dummy; + char name[255]; +}; + +namespace detail +{ + inline std::vector read_file(const std::filesystem::path& file_path) + { + std::ifstream file(file_path, std::ios::binary); + if (!file.is_open()) + { + return {}; + } + + return {std::istreambuf_iterator(file), std::istreambuf_iterator()}; + } + + struct string_hash + { + using is_transparent = void; + + size_t operator()(const std::string_view str) const + { + constexpr std::hash hasher{}; + return hasher(str); + } + }; + + template + using unordered_string_map = std::unordered_map>; +} + +class hive_key_t +{ + key_block_t* key_block; + uintptr_t main_root; + +public: + explicit hive_key_t(): key_block(nullptr), main_root(0) + { + } + + explicit hive_key_t(key_block_t* a, const uintptr_t b): key_block(a), main_root(b) + { + } + + [[nodiscard]] std::vector subkeys_list() const + { + const auto item = reinterpret_cast(this->main_root + key_block->subkeys); + if (item->block_type[1] != 'f' && item->block_type[1] != 'h') + return {}; + + std::vector out; + for (auto i = 0; i < key_block->subkey_count; i++) + { + const auto subkey = reinterpret_cast((&item->first)[i * 2] + this->main_root); + if (!subkey) + continue; + + out.emplace_back(subkey->name, subkey->len); + } + + return out; + } + + [[nodiscard]] std::vector keys_list() const + { + if (!key_block->value_count) + return {}; + + std::vector out; + for (auto i = 0; i < key_block->value_count; i++) + { + const auto value = reinterpret_cast(reinterpret_cast(key_block->offsets + this-> + main_root + 4)[i] + this->main_root); + if (!value) + continue; + + out.emplace_back(value->name, value->name_len); + } + + return out; + } + + using value = std::pair; + + std::optional get_key_value(const std::string_view& name) + { + for (auto i = 0; i < key_block->value_count; i++) + { + const auto value = reinterpret_cast(reinterpret_cast(key_block->offsets + this-> + main_root + 4)[i] + this->main_root); + if (!value || std::string_view(value->name, value->name_len) != name) + continue; + + auto data = reinterpret_cast(this->main_root + value->offset + 4); + if (value->size & 1 << 31) + data = reinterpret_cast(&value->offset); + + return std::make_pair(value->value_type, std::string_view(data, value->size & 0xffff)); + } + + return std::nullopt; + } + + template + std::optional get_key_value(const std::string_view& name) + { + const auto value = this->get_key_value(name); + if (!value) + { + return std::nullopt; + } + + const auto [type, data] = *value; + + if constexpr (std::is_same_v) + { + if (type != REG_SZ && type != REG_EXPAND_SZ) + return std::nullopt; + + return data; + } + else if constexpr (std::is_same_v>) + { + if (type != REG_MULTI_SZ) + return std::nullopt; + + std::string_view text; + std::vector out; + for (auto j = 0; j < data.size(); j++) + { + if (data[j] == '\0' && data[j + 1] == '\0' && data[j + 2] == '\0') + { + if (!text.empty()) + out.emplace_back(text); + text = {}; + } + else + { + text = std::string_view(data.data() + j - text.size(), text.size() + 1); + } + } + + return out; + } + else if constexpr (std::is_same_v) + { + if (type != REG_DWORD) + return std::nullopt; + + return *reinterpret_cast(data); + } + else if constexpr (std::is_same_v>) + { + if (type != REG_BINARY) + return std::nullopt; + + return {reinterpret_cast(data.data()), data.size()}; + } + + return std::nullopt; + } +}; + +class hive_parser +{ + struct hive_subpaths_t + { + std::string path; + hive_key_t data; + }; + + struct hive_cache_t + { + hive_key_t data; + std::vector subpaths; + }; + + key_block_t* main_key_block_data; + uintptr_t main_root; + std::vector file_data; + detail::unordered_string_map subkey_cache; + + void reclusive_search(const key_block_t* key_block_data, const std::string& current_path, + const bool is_reclusive = false) + { + if (!key_block_data) + return; + + const auto item = reinterpret_cast(main_root + key_block_data->subkeys); + if (item->block_type[1] != 'f' && item->block_type[1] != 'h') + return; + + for (auto i = 0; i < item->count; i++) + { + const auto subkey = reinterpret_cast((&item->first)[i * 2] + main_root); + if (!subkey) + continue; + + std::string_view subkey_name(subkey->name, subkey->len); + std::string full_path = current_path.empty() + ? std::string(subkey_name) + : std::string(current_path).append("/").append(subkey_name); + std::ranges::transform(full_path, full_path.begin(), ::tolower); + + if (!is_reclusive) + subkey_cache.try_emplace(full_path, hive_cache_t{ + hive_key_t{subkey, main_root}, std::vector{} + }); + + const auto extract_main_key = [ ](const std::string_view str) -> std::string_view + { + const size_t slash_pos = str.find('/'); + if (slash_pos == std::string::npos) + return str; + + return str.substr(0, slash_pos); + }; + + if (subkey->subkey_count > 0) + { + reclusive_search(subkey, full_path, true); + const auto entry = subkey_cache.find(extract_main_key(full_path)); + if (entry == subkey_cache.end()) + { + throw std::out_of_range("Invalid key"); + } + + entry->second.subpaths.emplace_back(hive_subpaths_t{ + full_path, hive_key_t{subkey, main_root} + }); + } + else + { + const auto entry = subkey_cache.find(extract_main_key(full_path)); + if (entry == subkey_cache.end()) + { + throw std::out_of_range("Invalid key"); + } + + entry->second.subpaths.emplace_back(full_path, hive_key_t{subkey, main_root}); + } + } + } + +public: + explicit hive_parser(const std::filesystem::path& file_path) + : hive_parser(detail::read_file(file_path)) + { + } + + explicit hive_parser(std::vector input_data) + : file_data(std::move(input_data)) + { + if (file_data.size() < 0x1020) + return; + + if (file_data.at(0) != 'r' && file_data.at(1) != 'e' && file_data.at(2) != 'g' && file_data.at(3) != 'f') + return; + + main_key_block_data = reinterpret_cast(reinterpret_cast(file_data.data() + 0x1020)); + main_root = reinterpret_cast(main_key_block_data) - 0x20; + + reclusive_search(main_key_block_data, ""); + } + + [[nodiscard]] bool success() const + { + return !subkey_cache.empty(); + } + + [[nodiscard]] std::optional get_subkey(const std::string_view key_name, + const std::string_view path) const + { + if (!subkey_cache.contains(key_name)) + return std::nullopt; + + const auto hive_block = subkey_cache.find(key_name); + if (hive_block == subkey_cache.end()) + { + throw std::out_of_range("Invalid key"); + } + + for (const auto& hive : hive_block->second.subpaths) + { + if (hive.path == path) + return hive.data; + } + + return std::nullopt; + } +}; diff --git a/src/windows-emulator/registry/registry_manager.cpp b/src/windows-emulator/registry/registry_manager.cpp index be8bd01e..e758a9d2 100644 --- a/src/windows-emulator/registry/registry_manager.cpp +++ b/src/windows-emulator/registry/registry_manager.cpp @@ -1,6 +1,6 @@ #include "registry_manager.hpp" -#include +#include "hive_parser.hpp" #include namespace From 53d9277d0c8c6ca156b7233037cc7d3183997ee6 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 3 Nov 2024 10:41:28 +0100 Subject: [PATCH 11/19] Add credits --- src/windows-emulator/registry/hive_parser.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/windows-emulator/registry/hive_parser.hpp b/src/windows-emulator/registry/hive_parser.hpp index e2ad7990..a2e8ece3 100644 --- a/src/windows-emulator/registry/hive_parser.hpp +++ b/src/windows-emulator/registry/hive_parser.hpp @@ -10,6 +10,8 @@ #include #include +// Based on this implementation: https://github.com/reahly/windows-hive-parser + struct offsets_t { long block_size; From 4b9efc825a1357d37e84ddc69a0771c80c56e649 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 3 Nov 2024 11:10:31 +0100 Subject: [PATCH 12/19] Extract container utils --- src/common/utils/container.hpp | 25 ++++++ src/windows-emulator/registry/hive_parser.hpp | 79 +------------------ 2 files changed, 27 insertions(+), 77 deletions(-) create mode 100644 src/common/utils/container.hpp diff --git a/src/common/utils/container.hpp b/src/common/utils/container.hpp new file mode 100644 index 00000000..eeafdf39 --- /dev/null +++ b/src/common/utils/container.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include +#include + +namespace utils +{ + struct string_hash + { + using is_transparent = void; + + size_t operator()(const std::string_view str) const + { + constexpr std::hash hasher{}; + return hasher(str); + } + }; + + template + using unordered_string_map = std::unordered_map>; + + using unordered_string_set = std::unordered_set>; +} diff --git a/src/windows-emulator/registry/hive_parser.hpp b/src/windows-emulator/registry/hive_parser.hpp index a2e8ece3..3a2e0327 100644 --- a/src/windows-emulator/registry/hive_parser.hpp +++ b/src/windows-emulator/registry/hive_parser.hpp @@ -6,9 +6,7 @@ #include #include #include -#include -#include -#include +#include // Based on this implementation: https://github.com/reahly/windows-hive-parser @@ -63,20 +61,6 @@ namespace detail return {std::istreambuf_iterator(file), std::istreambuf_iterator()}; } - - struct string_hash - { - using is_transparent = void; - - size_t operator()(const std::string_view str) const - { - constexpr std::hash hasher{}; - return hasher(str); - } - }; - - template - using unordered_string_map = std::unordered_map>; } class hive_key_t @@ -151,65 +135,6 @@ public: return std::nullopt; } - - template - std::optional get_key_value(const std::string_view& name) - { - const auto value = this->get_key_value(name); - if (!value) - { - return std::nullopt; - } - - const auto [type, data] = *value; - - if constexpr (std::is_same_v) - { - if (type != REG_SZ && type != REG_EXPAND_SZ) - return std::nullopt; - - return data; - } - else if constexpr (std::is_same_v>) - { - if (type != REG_MULTI_SZ) - return std::nullopt; - - std::string_view text; - std::vector out; - for (auto j = 0; j < data.size(); j++) - { - if (data[j] == '\0' && data[j + 1] == '\0' && data[j + 2] == '\0') - { - if (!text.empty()) - out.emplace_back(text); - text = {}; - } - else - { - text = std::string_view(data.data() + j - text.size(), text.size() + 1); - } - } - - return out; - } - else if constexpr (std::is_same_v) - { - if (type != REG_DWORD) - return std::nullopt; - - return *reinterpret_cast(data); - } - else if constexpr (std::is_same_v>) - { - if (type != REG_BINARY) - return std::nullopt; - - return {reinterpret_cast(data.data()), data.size()}; - } - - return std::nullopt; - } }; class hive_parser @@ -229,7 +154,7 @@ class hive_parser key_block_t* main_key_block_data; uintptr_t main_root; std::vector file_data; - detail::unordered_string_map subkey_cache; + utils::unordered_string_map subkey_cache; void reclusive_search(const key_block_t* key_block_data, const std::string& current_path, const bool is_reclusive = false) From 17db05a410d8d8ca67ba27a2447f8cd9eee667b5 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 3 Nov 2024 11:58:10 +0100 Subject: [PATCH 13/19] Prepare refactoring --- src/windows-emulator/registry/hive_parser.hpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/windows-emulator/registry/hive_parser.hpp b/src/windows-emulator/registry/hive_parser.hpp index 3a2e0327..50cada54 100644 --- a/src/windows-emulator/registry/hive_parser.hpp +++ b/src/windows-emulator/registry/hive_parser.hpp @@ -10,13 +10,18 @@ // Based on this implementation: https://github.com/reahly/windows-hive-parser +struct offset_entry_t +{ + long offset; + long hash; +}; + struct offsets_t { long block_size; char block_type[2]; short count; - long first; - long hash; + offset_entry_t entries[0]; }; struct key_block_t @@ -86,7 +91,7 @@ public: std::vector out; for (auto i = 0; i < key_block->subkey_count; i++) { - const auto subkey = reinterpret_cast((&item->first)[i * 2] + this->main_root); + const auto subkey = reinterpret_cast(item->entries[i].offset + this->main_root); if (!subkey) continue; @@ -168,7 +173,7 @@ class hive_parser for (auto i = 0; i < item->count; i++) { - const auto subkey = reinterpret_cast((&item->first)[i * 2] + main_root); + const auto subkey = reinterpret_cast(item->entries[i].offset + main_root); if (!subkey) continue; From 7c2132510ac67a73a33d9aa6115379408ce1b9bf Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 3 Nov 2024 14:24:18 +0100 Subject: [PATCH 14/19] Cleanup registry parsing --- src/windows-emulator/registry/hive_parser.cpp | 244 +++++++++++++++ src/windows-emulator/registry/hive_parser.hpp | 287 ++++-------------- .../registry/registry_manager.cpp | 31 +- .../registry/registry_manager.hpp | 3 +- src/windows-emulator/syscalls.cpp | 18 +- 5 files changed, 325 insertions(+), 258 deletions(-) create mode 100644 src/windows-emulator/registry/hive_parser.cpp diff --git a/src/windows-emulator/registry/hive_parser.cpp b/src/windows-emulator/registry/hive_parser.cpp new file mode 100644 index 00000000..bd88b492 --- /dev/null +++ b/src/windows-emulator/registry/hive_parser.cpp @@ -0,0 +1,244 @@ +#include "hive_parser.hpp" + +// Based on this implementation: https://github.com/reahly/windows-hive-parser + +namespace +{ + constexpr uint64_t MAIN_ROOT_OFFSET = 0x1000; + constexpr uint64_t MAIN_KEY_BLOCK_OFFSET = MAIN_ROOT_OFFSET + 0x20; + + struct offset_entry_t + { + long offset; + long hash; + }; + + struct offsets_t + { + long block_size; + char block_type[2]; + short count; + offset_entry_t entries[1]; + }; + + struct key_block_t + { + long block_size; + char block_type[2]; + char dummya[18]; + int subkey_count; + char dummyb[4]; + int subkeys; + char dummyc[4]; + int value_count; + int offsets; + char dummyd[28]; + short len; + short du; + char name[255]; + }; + + struct value_block_t + { + long block_size; + char block_type[2]; + short name_len; + long size; + long offset; + long value_type; + short flags; + short dummy; + char name[255]; + }; + + bool read_file_data(std::ifstream& file, const uint64_t offset, void* buffer, const size_t size) + { + if (file.bad()) + { + return false; + } + + file.clear(); + + if (!file.good()) + { + return false; + } + + file.seekg(static_cast(offset)); + + if (!file.good()) + { + return false; + } + + file.read(static_cast(buffer), static_cast(size)); + + return file.good(); + } + + std::vector read_file_data(std::ifstream& file, const uint64_t offset, const size_t size) + { + std::vector result{}; + result.resize(size); + + if (read_file_data(file, offset, result.data(), size)) + { + return result; + } + + return {}; + } + + std::string read_file_data_string(std::ifstream& file, const uint64_t offset, const size_t size) + { + std::string result{}; + result.resize(size); + + if (read_file_data(file, offset, result.data(), size)) + { + return result; + } + + return {}; + } + + template + requires(std::is_trivially_copyable_v) + std::optional read_file_object(std::ifstream& file, const uint64_t offset, const size_t array_index = 0) + { + T obj{}; + if (read_file_data(file, offset + (array_index * sizeof(T)), &obj, sizeof(T))) + { + return {std::move(obj)}; + } + + return std::nullopt; + } + + template + requires(std::is_trivially_copyable_v) + T read_file_object_or_throw(std::ifstream& file, const uint64_t offset, const size_t array_index = 0) + { + auto result = read_file_object(file, offset, array_index); + if (!result) + { + throw std::runtime_error("Failed to read file object"); + } + + return std::move(*result); + } + + hive_key parse_root_block(std::ifstream& file, const std::filesystem::path& file_path) + { + if (read_file_data_string(file, 0, 4) != "regf") + { + throw std::runtime_error("Bad hive file: " + file_path.string()); + } + + const auto key_block = read_file_object_or_throw(file, MAIN_KEY_BLOCK_OFFSET); + + return {key_block.subkeys, key_block.value_count, key_block.offsets}; + } + + char char_to_lower(const char val) + { + return static_cast(std::tolower(static_cast(val))); + } + + bool ichar_equals(const char a, const char b) + { + return char_to_lower(a) == char_to_lower(b); + } + + bool iequals(std::string_view lhs, std::string_view rhs) + { + return std::ranges::equal(lhs, rhs, ichar_equals); + } +} + +const hive_value* hive_key::get_value(std::ifstream& file, const std::string_view name) +{ + this->parse(file); + + const auto entry = this->values_.find(name); + if (entry == this->values_.end()) + { + return nullptr; + } + + auto& value = entry->second; + + if (value.parsed) + { + value.data = read_file_data(file, MAIN_ROOT_OFFSET + value.data_offset, value.data_length); + value.parsed = true; + } + + return &value; +} + +void hive_key::parse(std::ifstream& file) +{ + if (this->parsed_) + { + return; + } + + this->parsed_ = true; + + // Values + + for (auto i = 0; i < this->value_count_; i++) + { + const auto offset = read_file_object_or_throw(file, MAIN_ROOT_OFFSET + this->value_offsets_ + 4, i); + const auto value = read_file_object_or_throw(file, MAIN_ROOT_OFFSET + offset); + + std::string value_name(value.name, std::min(value.name_len, static_cast(sizeof(value.name)))); + + raw_hive_value raw_value{}; + raw_value.parsed = false; + raw_value.type = value.value_type; + raw_value.name = value_name; + raw_value.data_length = value.size & 0xffff; + raw_value.data_offset = value.offset + 4; + + if (value.size & 1 << 31) + { + raw_value.data_offset = offset + static_cast(offsetof(value_block_t, offset)); + } + + std::ranges::transform(value_name, value_name.begin(), char_to_lower); + this->values_[std::move(value_name)] = std::move(raw_value); + } + + // Subkeys + + const auto item = read_file_object_or_throw(file, MAIN_ROOT_OFFSET + this->subkey_block_offset_); + + if (item.block_type[1] != 'f' && item.block_type[1] != 'h') + { + return; + } + + const auto entry_offsets = this->subkey_block_offset_ + offsetof(offsets_t, entries); + + for (short i = 0; i < item.count; ++i) + { + const auto offset_entry = read_file_object_or_throw(file, MAIN_ROOT_OFFSET + entry_offsets, i); + + const auto subkey_block_offset = MAIN_ROOT_OFFSET + offset_entry.offset; + const auto subkey = read_file_object_or_throw(file, subkey_block_offset); + + std::string subkey_name(subkey.name, std::min(subkey.len, static_cast(sizeof(subkey.name)))); + std::ranges::transform(subkey_name, subkey_name.begin(), char_to_lower); + + this->sub_keys_.emplace(std::move(subkey_name), hive_key{subkey.subkeys, subkey.value_count, subkey.offsets}); + } +} + +hive_parser::hive_parser(const std::filesystem::path& file_path) + : file_(file_path, std::ios::binary) + , root_key_(parse_root_block(file_, file_path)) +{ +} diff --git a/src/windows-emulator/registry/hive_parser.hpp b/src/windows-emulator/registry/hive_parser.hpp index 50cada54..462557d9 100644 --- a/src/windows-emulator/registry/hive_parser.hpp +++ b/src/windows-emulator/registry/hive_parser.hpp @@ -1,272 +1,99 @@ #pragma once -#include #include -#include -#include -#include -#include #include -// Based on this implementation: https://github.com/reahly/windows-hive-parser - -struct offset_entry_t +struct hive_value { - long offset; - long hash; + uint32_t type{}; + std::string name{}; + std::vector data{}; }; -struct offsets_t +class hive_key { - long block_size; - char block_type[2]; - short count; - offset_entry_t entries[0]; -}; - -struct key_block_t -{ - long block_size; - char block_type[2]; - char dummya[18]; - int subkey_count; - char dummyb[4]; - int subkeys; - char dummyc[4]; - int value_count; - int offsets; - char dummyd[28]; - short len; - short du; - char name[255]; -}; - -struct value_block_t -{ - long block_size; - char block_type[2]; - short name_len; - long size; - long offset; - long value_type; - short flags; - short dummy; - char name[255]; -}; - -namespace detail -{ - inline std::vector read_file(const std::filesystem::path& file_path) - { - std::ifstream file(file_path, std::ios::binary); - if (!file.is_open()) - { - return {}; - } - - return {std::istreambuf_iterator(file), std::istreambuf_iterator()}; - } -} - -class hive_key_t -{ - key_block_t* key_block; - uintptr_t main_root; - public: - explicit hive_key_t(): key_block(nullptr), main_root(0) + hive_key(const int subkey_block_offset, const int value_count, const int value_offsets) + : subkey_block_offset_(subkey_block_offset) + , value_count_(value_count) + , value_offsets_(value_offsets) { } - explicit hive_key_t(key_block_t* a, const uintptr_t b): key_block(a), main_root(b) + utils::unordered_string_map& get_sub_keys(std::ifstream& file) { + this->parse(file); + return this->sub_keys_; } - [[nodiscard]] std::vector subkeys_list() const + hive_key* get_sub_key(std::ifstream& file, const std::string_view name) { - const auto item = reinterpret_cast(this->main_root + key_block->subkeys); - if (item->block_type[1] != 'f' && item->block_type[1] != 'h') - return {}; + auto& sub_keys = this->get_sub_keys(file); + const auto entry = sub_keys.find(name); - std::vector out; - for (auto i = 0; i < key_block->subkey_count; i++) + if (entry == sub_keys.end()) { - const auto subkey = reinterpret_cast(item->entries[i].offset + this->main_root); - if (!subkey) - continue; - - out.emplace_back(subkey->name, subkey->len); + return nullptr; } - return out; + return &entry->second; } - [[nodiscard]] std::vector keys_list() const + const hive_value* get_value(std::ifstream& file, const std::string_view name); + +private: + struct raw_hive_value : hive_value { - if (!key_block->value_count) - return {}; + bool parsed{false}; + int data_offset{}; + size_t data_length{}; + }; - std::vector out; - for (auto i = 0; i < key_block->value_count; i++) - { - const auto value = reinterpret_cast(reinterpret_cast(key_block->offsets + this-> - main_root + 4)[i] + this->main_root); - if (!value) - continue; + bool parsed_{false}; + utils::unordered_string_map sub_keys_{}; + utils::unordered_string_map values_{}; - out.emplace_back(value->name, value->name_len); - } + const int subkey_block_offset_{}; + const int value_count_{}; + const int value_offsets_{}; - return out; - } - - using value = std::pair; - - std::optional get_key_value(const std::string_view& name) - { - for (auto i = 0; i < key_block->value_count; i++) - { - const auto value = reinterpret_cast(reinterpret_cast(key_block->offsets + this-> - main_root + 4)[i] + this->main_root); - if (!value || std::string_view(value->name, value->name_len) != name) - continue; - - auto data = reinterpret_cast(this->main_root + value->offset + 4); - if (value->size & 1 << 31) - data = reinterpret_cast(&value->offset); - - return std::make_pair(value->value_type, std::string_view(data, value->size & 0xffff)); - } - - return std::nullopt; - } + void parse(std::ifstream& file); }; class hive_parser { - struct hive_subpaths_t - { - std::string path; - hive_key_t data; - }; - - struct hive_cache_t - { - hive_key_t data; - std::vector subpaths; - }; - - key_block_t* main_key_block_data; - uintptr_t main_root; - std::vector file_data; - utils::unordered_string_map subkey_cache; - - void reclusive_search(const key_block_t* key_block_data, const std::string& current_path, - const bool is_reclusive = false) - { - if (!key_block_data) - return; - - const auto item = reinterpret_cast(main_root + key_block_data->subkeys); - if (item->block_type[1] != 'f' && item->block_type[1] != 'h') - return; - - for (auto i = 0; i < item->count; i++) - { - const auto subkey = reinterpret_cast(item->entries[i].offset + main_root); - if (!subkey) - continue; - - std::string_view subkey_name(subkey->name, subkey->len); - std::string full_path = current_path.empty() - ? std::string(subkey_name) - : std::string(current_path).append("/").append(subkey_name); - std::ranges::transform(full_path, full_path.begin(), ::tolower); - - if (!is_reclusive) - subkey_cache.try_emplace(full_path, hive_cache_t{ - hive_key_t{subkey, main_root}, std::vector{} - }); - - const auto extract_main_key = [ ](const std::string_view str) -> std::string_view - { - const size_t slash_pos = str.find('/'); - if (slash_pos == std::string::npos) - return str; - - return str.substr(0, slash_pos); - }; - - if (subkey->subkey_count > 0) - { - reclusive_search(subkey, full_path, true); - const auto entry = subkey_cache.find(extract_main_key(full_path)); - if (entry == subkey_cache.end()) - { - throw std::out_of_range("Invalid key"); - } - - entry->second.subpaths.emplace_back(hive_subpaths_t{ - full_path, hive_key_t{subkey, main_root} - }); - } - else - { - const auto entry = subkey_cache.find(extract_main_key(full_path)); - if (entry == subkey_cache.end()) - { - throw std::out_of_range("Invalid key"); - } - - entry->second.subpaths.emplace_back(full_path, hive_key_t{subkey, main_root}); - } - } - } - public: - explicit hive_parser(const std::filesystem::path& file_path) - : hive_parser(detail::read_file(file_path)) + explicit hive_parser(const std::filesystem::path& file_path); + + [[nodiscard]] hive_key* get_sub_key(const std::filesystem::path& key) { - } + hive_key* current_key = &this->root_key_; - explicit hive_parser(std::vector input_data) - : file_data(std::move(input_data)) - { - if (file_data.size() < 0x1020) - return; - - if (file_data.at(0) != 'r' && file_data.at(1) != 'e' && file_data.at(2) != 'g' && file_data.at(3) != 'f') - return; - - main_key_block_data = reinterpret_cast(reinterpret_cast(file_data.data() + 0x1020)); - main_root = reinterpret_cast(main_key_block_data) - 0x20; - - reclusive_search(main_key_block_data, ""); - } - - [[nodiscard]] bool success() const - { - return !subkey_cache.empty(); - } - - [[nodiscard]] std::optional get_subkey(const std::string_view key_name, - const std::string_view path) const - { - if (!subkey_cache.contains(key_name)) - return std::nullopt; - - const auto hive_block = subkey_cache.find(key_name); - if (hive_block == subkey_cache.end()) + for (const auto& key_part : key) { - throw std::out_of_range("Invalid key"); + if (!current_key) + { + return nullptr; + } + + current_key = current_key->get_sub_key(this->file_, key_part.string()); } - for (const auto& hive : hive_block->second.subpaths) + return current_key; + } + + [[nodiscard]] const hive_value* get_value(const std::filesystem::path& key, const std::string_view name) + { + auto* sub_key = this->get_sub_key(key); + if (!sub_key) { - if (hive.path == path) - return hive.data; + return nullptr; } - return std::nullopt; + return sub_key->get_value(this->file_, name); } + +private: + std::ifstream file_{}; + hive_key root_key_; }; diff --git a/src/windows-emulator/registry/registry_manager.cpp b/src/windows-emulator/registry/registry_manager.cpp index e758a9d2..8ff8a610 100644 --- a/src/windows-emulator/registry/registry_manager.cpp +++ b/src/windows-emulator/registry/registry_manager.cpp @@ -1,8 +1,10 @@ #include "registry_manager.hpp" -#include "hive_parser.hpp" +#include #include +#include "hive_parser.hpp" + namespace { std::filesystem::path canonicalize_path(const std::filesystem::path& key) @@ -31,11 +33,7 @@ namespace void register_hive(registry_manager::hive_map& hives, const std::filesystem::path& key, const std::filesystem::path& file) { - auto hive = std::make_unique(file); - if (hive && hive->success()) - { - hives[canonicalize_path(key)] = std::move(hive); - } + hives[canonicalize_path(key)] = std::make_unique(file); } } @@ -116,10 +114,10 @@ std::optional registry_manager::get_key(const std::filesystem::pat return {std::move(reg_key)}; } - const auto entry = iterator->second->get_subkey(reg_key.path.begin()->string(), reg_key.path.generic_string()); + const auto entry = iterator->second->get_sub_key(reg_key.path); if (!entry) { - return {}; + return std::nullopt; } return {std::move(reg_key)}; @@ -130,24 +128,19 @@ std::optional registry_manager::get_value(const registry_key& ke const auto iterator = this->hives_.find(key.hive); if (iterator == this->hives_.end()) { - return {}; + return std::nullopt; } - auto entry = iterator->second->get_subkey(key.path.begin()->string(), key.path.generic_string()); + auto* entry = iterator->second->get_value(key.path, name); if (!entry) { - return {}; - } - - const auto value = entry->get_key_value(name); - if (!value) - { - return {}; + return std::nullopt; } registry_value v{}; - v.type = value->first; - v.data = value->second; + v.type = entry->type; + v.name = entry->name; + v.data = entry->data; return v; } diff --git a/src/windows-emulator/registry/registry_manager.hpp b/src/windows-emulator/registry/registry_manager.hpp index b5e79d88..d63b7f32 100644 --- a/src/windows-emulator/registry/registry_manager.hpp +++ b/src/windows-emulator/registry/registry_manager.hpp @@ -26,7 +26,8 @@ struct registry_key struct registry_value { uint32_t type; - std::string_view data; + std::string_view name; + std::span data; }; class registry_manager diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 19f7b58f..0c6a0ca5 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -94,8 +94,8 @@ namespace return STATUS_INVALID_HANDLE; } - const auto wide_name = read_unicode_string(c.emu, value_name); - const std::string name(wide_name.begin(), wide_name.end()); + const auto query_name = read_unicode_string(c.emu, value_name); + const std::string name(query_name.begin(), query_name.end()); const auto value = c.proc.registry.get_value(*key, name); if (!value) @@ -103,9 +103,11 @@ namespace return STATUS_OBJECT_NAME_NOT_FOUND; } + const std::wstring original_name(value->name.begin(), value->name.end()); + if (key_value_information_class == KeyValueBasicInformation) { - const auto required_size = sizeof(KEY_VALUE_BASIC_INFORMATION) + (wide_name.size() * 2) - 1; + const auto required_size = sizeof(KEY_VALUE_BASIC_INFORMATION) + (original_name.size() * 2) - 1; result_length.write(static_cast(required_size)); if (required_size > length) @@ -116,13 +118,13 @@ namespace KEY_VALUE_BASIC_INFORMATION info{}; info.TitleIndex = 0; info.Type = value->type; - info.NameLength = static_cast(wide_name.size() * 2); + info.NameLength = static_cast(original_name.size() * 2); const emulator_object info_obj{ c.emu, key_value_information }; info_obj.write(info); c.emu.write_memory(key_value_information + offsetof(KEY_VALUE_BASIC_INFORMATION, Name), - wide_name.data(), + original_name.data(), info.NameLength); return STATUS_SUCCESS; @@ -155,7 +157,7 @@ namespace if (key_value_information_class == KeyValueFullInformation) { - const auto required_size = sizeof(KEY_VALUE_FULL_INFORMATION) + (wide_name.size() * 2) + value->data.size() - 1; + const auto required_size = sizeof(KEY_VALUE_FULL_INFORMATION) + (original_name.size() * 2) + value->data.size() - 1; result_length.write(static_cast(required_size)); if (required_size > length) @@ -167,13 +169,13 @@ namespace info.TitleIndex = 0; info.Type = value->type; info.DataLength = static_cast(value->data.size()); - info.NameLength = static_cast(wide_name.size() * 2); + info.NameLength = static_cast(original_name.size() * 2); const emulator_object info_obj{ c.emu, key_value_information }; info_obj.write(info); c.emu.write_memory(key_value_information + offsetof(KEY_VALUE_BASIC_INFORMATION, Name), - wide_name.data(), + original_name.data(), info.NameLength); c.emu.write_memory(key_value_information + offsetof(KEY_VALUE_FULL_INFORMATION, Name) + info.NameLength, From a83588f18dc90d2a317d7d43d933a8fbc757d815 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 3 Nov 2024 15:16:36 +0100 Subject: [PATCH 15/19] Cleanup hive parser --- src/windows-emulator/registry/hive_parser.cpp | 71 ++++++------------- .../registry/registry_manager.cpp | 9 ++- 2 files changed, 30 insertions(+), 50 deletions(-) diff --git a/src/windows-emulator/registry/hive_parser.cpp b/src/windows-emulator/registry/hive_parser.cpp index bd88b492..425d1415 100644 --- a/src/windows-emulator/registry/hive_parser.cpp +++ b/src/windows-emulator/registry/hive_parser.cpp @@ -51,7 +51,7 @@ namespace char name[255]; }; - bool read_file_data(std::ifstream& file, const uint64_t offset, void* buffer, const size_t size) + bool read_file_data_safe(std::ifstream& file, const uint64_t offset, void* buffer, const size_t size) { if (file.bad()) { @@ -77,17 +77,21 @@ namespace return file.good(); } + void read_file_data(std::ifstream& file, const uint64_t offset, void* buffer, const size_t size) + { + if (!read_file_data_safe(file, offset, buffer, size)) + { + throw std::runtime_error("Failed to read file data"); + } + } + std::vector read_file_data(std::ifstream& file, const uint64_t offset, const size_t size) { std::vector result{}; result.resize(size); - if (read_file_data(file, offset, result.data(), size)) - { - return result; - } - - return {}; + read_file_data(file, offset, result.data(), size); + return result; } std::string read_file_data_string(std::ifstream& file, const uint64_t offset, const size_t size) @@ -95,38 +99,17 @@ namespace std::string result{}; result.resize(size); - if (read_file_data(file, offset, result.data(), size)) - { - return result; - } - - return {}; + read_file_data(file, offset, result.data(), size); + return result; } template requires(std::is_trivially_copyable_v) - std::optional read_file_object(std::ifstream& file, const uint64_t offset, const size_t array_index = 0) + T read_file_object(std::ifstream& file, const uint64_t offset, const size_t array_index = 0) { T obj{}; - if (read_file_data(file, offset + (array_index * sizeof(T)), &obj, sizeof(T))) - { - return {std::move(obj)}; - } - - return std::nullopt; - } - - template - requires(std::is_trivially_copyable_v) - T read_file_object_or_throw(std::ifstream& file, const uint64_t offset, const size_t array_index = 0) - { - auto result = read_file_object(file, offset, array_index); - if (!result) - { - throw std::runtime_error("Failed to read file object"); - } - - return std::move(*result); + read_file_data(file, offset + (array_index * sizeof(T)), &obj, sizeof(T)); + return obj; } hive_key parse_root_block(std::ifstream& file, const std::filesystem::path& file_path) @@ -136,7 +119,7 @@ namespace throw std::runtime_error("Bad hive file: " + file_path.string()); } - const auto key_block = read_file_object_or_throw(file, MAIN_KEY_BLOCK_OFFSET); + const auto key_block = read_file_object(file, MAIN_KEY_BLOCK_OFFSET); return {key_block.subkeys, key_block.value_count, key_block.offsets}; } @@ -145,16 +128,6 @@ namespace { return static_cast(std::tolower(static_cast(val))); } - - bool ichar_equals(const char a, const char b) - { - return char_to_lower(a) == char_to_lower(b); - } - - bool iequals(std::string_view lhs, std::string_view rhs) - { - return std::ranges::equal(lhs, rhs, ichar_equals); - } } const hive_value* hive_key::get_value(std::ifstream& file, const std::string_view name) @@ -191,8 +164,8 @@ void hive_key::parse(std::ifstream& file) for (auto i = 0; i < this->value_count_; i++) { - const auto offset = read_file_object_or_throw(file, MAIN_ROOT_OFFSET + this->value_offsets_ + 4, i); - const auto value = read_file_object_or_throw(file, MAIN_ROOT_OFFSET + offset); + const auto offset = read_file_object(file, MAIN_ROOT_OFFSET + this->value_offsets_ + 4, i); + const auto value = read_file_object(file, MAIN_ROOT_OFFSET + offset); std::string value_name(value.name, std::min(value.name_len, static_cast(sizeof(value.name)))); @@ -214,7 +187,7 @@ void hive_key::parse(std::ifstream& file) // Subkeys - const auto item = read_file_object_or_throw(file, MAIN_ROOT_OFFSET + this->subkey_block_offset_); + const auto item = read_file_object(file, MAIN_ROOT_OFFSET + this->subkey_block_offset_); if (item.block_type[1] != 'f' && item.block_type[1] != 'h') { @@ -225,10 +198,10 @@ void hive_key::parse(std::ifstream& file) for (short i = 0; i < item.count; ++i) { - const auto offset_entry = read_file_object_or_throw(file, MAIN_ROOT_OFFSET + entry_offsets, i); + const auto offset_entry = read_file_object(file, MAIN_ROOT_OFFSET + entry_offsets, i); const auto subkey_block_offset = MAIN_ROOT_OFFSET + offset_entry.offset; - const auto subkey = read_file_object_or_throw(file, subkey_block_offset); + const auto subkey = read_file_object(file, subkey_block_offset); std::string subkey_name(subkey.name, std::min(subkey.len, static_cast(sizeof(subkey.name)))); std::ranges::transform(subkey_name, subkey_name.begin(), char_to_lower); diff --git a/src/windows-emulator/registry/registry_manager.cpp b/src/windows-emulator/registry/registry_manager.cpp index 8ff8a610..a5440b97 100644 --- a/src/windows-emulator/registry/registry_manager.cpp +++ b/src/windows-emulator/registry/registry_manager.cpp @@ -33,7 +33,14 @@ namespace void register_hive(registry_manager::hive_map& hives, const std::filesystem::path& key, const std::filesystem::path& file) { - hives[canonicalize_path(key)] = std::make_unique(file); + try + { + hives[canonicalize_path(key)] = std::make_unique(file); + } + catch (const std::exception& e) + { + + } } } From 94410721968a0ac1a52d15badc3e56c0cae500d7 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 3 Nov 2024 15:26:38 +0100 Subject: [PATCH 16/19] Fix formatting --- src/windows-emulator/syscalls.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 0c6a0ca5..30f177bd 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -120,12 +120,12 @@ namespace info.Type = value->type; info.NameLength = static_cast(original_name.size() * 2); - const emulator_object info_obj{ c.emu, key_value_information }; + const emulator_object info_obj{c.emu, key_value_information}; info_obj.write(info); c.emu.write_memory(key_value_information + offsetof(KEY_VALUE_BASIC_INFORMATION, Name), - original_name.data(), - info.NameLength); + original_name.data(), + info.NameLength); return STATUS_SUCCESS; } @@ -157,7 +157,8 @@ namespace if (key_value_information_class == KeyValueFullInformation) { - const auto required_size = sizeof(KEY_VALUE_FULL_INFORMATION) + (original_name.size() * 2) + value->data.size() - 1; + const auto required_size = sizeof(KEY_VALUE_FULL_INFORMATION) + (original_name.size() * 2) + value->data. + size() - 1; result_length.write(static_cast(required_size)); if (required_size > length) @@ -171,16 +172,16 @@ namespace info.DataLength = static_cast(value->data.size()); info.NameLength = static_cast(original_name.size() * 2); - const emulator_object info_obj{ c.emu, key_value_information }; + const emulator_object info_obj{c.emu, key_value_information}; info_obj.write(info); c.emu.write_memory(key_value_information + offsetof(KEY_VALUE_BASIC_INFORMATION, Name), - original_name.data(), - info.NameLength); + original_name.data(), + info.NameLength); c.emu.write_memory(key_value_information + offsetof(KEY_VALUE_FULL_INFORMATION, Name) + info.NameLength, - value->data.data(), - value->data.size()); + value->data.data(), + value->data.size()); return STATUS_SUCCESS; } From e32624ba1f0268ebee73bd6e46b7953521920155 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 3 Nov 2024 15:26:51 +0100 Subject: [PATCH 17/19] Handle root keys --- src/windows-emulator/syscalls.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 30f177bd..7aa165dd 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -58,7 +58,18 @@ namespace const emulator_object object_attributes) { const auto attributes = object_attributes.read(); - const auto key = read_unicode_string(c.emu, attributes.ObjectName); + auto key = read_unicode_string(c.emu, attributes.ObjectName); + + if (attributes.RootDirectory) + { + const auto* parent_handle = c.proc.registry_keys.get(reinterpret_cast(attributes.RootDirectory)); + if (!parent_handle) + { + return STATUS_INVALID_HANDLE; + } + + key = parent_handle->hive / parent_handle->path / key; + } c.win_emu.logger.print(color::dark_gray, "--> Registry key: %S\n", key.c_str()); From 0a81280796b8f022fc49cd66d0b3effc2dff9226 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 3 Nov 2024 15:57:52 +0100 Subject: [PATCH 18/19] Prepare configurable registry path --- src/windows-emulator/process_context.hpp | 2 +- src/windows-emulator/registry/hive_parser.cpp | 19 +++++++++++++------ .../registry/registry_manager.cpp | 16 ++++++---------- .../registry/registry_manager.hpp | 11 +++++++++-- src/windows-emulator/syscalls.cpp | 9 ++++----- src/windows-emulator/windows_emulator.cpp | 2 ++ src/windows-emulator/windows_emulator.hpp | 7 ++++--- 7 files changed, 39 insertions(+), 27 deletions(-) diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index 8a572d54..3a1a780c 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -363,7 +363,7 @@ struct process_context { } - registry_manager registry{R"(C:\Users\mauri\Desktop\windows\win-x64\registry)"}; // TODO: Fix + registry_manager registry{}; uint64_t executed_instructions{0}; uint64_t current_ip{0}; diff --git a/src/windows-emulator/registry/hive_parser.cpp b/src/windows-emulator/registry/hive_parser.cpp index 425d1415..03190cb4 100644 --- a/src/windows-emulator/registry/hive_parser.cpp +++ b/src/windows-emulator/registry/hive_parser.cpp @@ -114,14 +114,21 @@ namespace hive_key parse_root_block(std::ifstream& file, const std::filesystem::path& file_path) { - if (read_file_data_string(file, 0, 4) != "regf") + try { - throw std::runtime_error("Bad hive file: " + file_path.string()); + if (read_file_data_string(file, 0, 4) != "regf") + { + throw std::runtime_error("Invalid signature"); + } + + const auto key_block = read_file_object(file, MAIN_KEY_BLOCK_OFFSET); + + return {key_block.subkeys, key_block.value_count, key_block.offsets}; + } + catch (const std::exception& e) + { + throw std::runtime_error("Bad hive file '" + file_path.string() + "': " + e.what()); } - - const auto key_block = read_file_object(file, MAIN_KEY_BLOCK_OFFSET); - - return {key_block.subkeys, key_block.value_count, key_block.offsets}; } char char_to_lower(const char val) diff --git a/src/windows-emulator/registry/registry_manager.cpp b/src/windows-emulator/registry/registry_manager.cpp index a5440b97..ad88cb29 100644 --- a/src/windows-emulator/registry/registry_manager.cpp +++ b/src/windows-emulator/registry/registry_manager.cpp @@ -33,21 +33,17 @@ namespace void register_hive(registry_manager::hive_map& hives, const std::filesystem::path& key, const std::filesystem::path& file) { - try - { - hives[canonicalize_path(key)] = std::make_unique(file); - } - catch (const std::exception& e) - { - - } + hives[canonicalize_path(key)] = std::make_unique(file); } } +registry_manager::registry_manager() = default; registry_manager::~registry_manager() = default; +registry_manager::registry_manager(registry_manager&&) noexcept = default; +registry_manager& registry_manager::operator=(registry_manager&&) noexcept = default; -registry_manager::registry_manager(std::filesystem::path hive_path) - : hive_path_(std::move(hive_path)) +registry_manager::registry_manager(const std::filesystem::path& hive_path) + : hive_path_(absolute(hive_path)) { this->setup(); } diff --git a/src/windows-emulator/registry/registry_manager.hpp b/src/windows-emulator/registry/registry_manager.hpp index d63b7f32..71153379 100644 --- a/src/windows-emulator/registry/registry_manager.hpp +++ b/src/windows-emulator/registry/registry_manager.hpp @@ -36,10 +36,17 @@ public: using hive_ptr = std::unique_ptr; using hive_map = std::unordered_map; - registry_manager() = default; - registry_manager(std::filesystem::path hive_path); + registry_manager(); + registry_manager(const std::filesystem::path& hive_path); ~registry_manager(); + registry_manager(registry_manager&&) noexcept; + registry_manager& operator=(registry_manager&&) noexcept; + + registry_manager(const registry_manager&) = delete; + registry_manager& operator=(const registry_manager&) = delete; + + void serialize(utils::buffer_serializer& buffer) const; void deserialize(utils::buffer_deserializer& buffer); diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 7aa165dd..e0d810f5 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -1,12 +1,10 @@ #include "std_include.hpp" #include "syscall_dispatcher.hpp" - -#include - #include "context_frame.hpp" #include "emulator_utils.hpp" #include "syscall_utils.hpp" +#include #include namespace @@ -168,8 +166,9 @@ namespace if (key_value_information_class == KeyValueFullInformation) { - const auto required_size = sizeof(KEY_VALUE_FULL_INFORMATION) + (original_name.size() * 2) + value->data. - size() - 1; + const auto name_size = original_name.size() * 2; + const auto value_size = value->data.size(); + const auto required_size = sizeof(KEY_VALUE_FULL_INFORMATION) + name_size + value_size + -1; result_length.write(static_cast(required_size)); if (required_size > length) diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 393520bd..8e4c0eb0 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -255,6 +255,8 @@ namespace { setup_gdt(emu); + context.registry = registry_manager(settings.registry_directory); + context.kusd = setup_kusd(emu); context.base_allocator = create_allocator(emu, PEB_SEGMENT_SIZE); diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index 7ae773ec..5e858ca4 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -11,9 +11,10 @@ std::unique_ptr create_default_x64_emulator(); struct emulator_settings { - std::filesystem::path application; - std::filesystem::path working_directory; - std::vector arguments; + std::filesystem::path application{}; + std::filesystem::path working_directory{}; + std::filesystem::path registry_directory{"./registry"}; + std::vector arguments{}; bool disable_logging{false}; }; From d33faab1d85d966c3abf9febfeda6caba71615fd Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 3 Nov 2024 16:03:54 +0100 Subject: [PATCH 19/19] Dump registry --- .github/workflows/build.yml | 3 +++ src/grab-registry.bat | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/grab-registry.bat diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4dbb1173..d544331b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,5 +48,8 @@ jobs: path: | build/${{matrix.preset}}/artifacts/* + - name: Dump Registry + run: cd build/${{matrix.preset}}/artifacts && ../../../src/grab-registry.bat + - name: CMake Test run: cd build/${{matrix.preset}} && ctest --verbose diff --git a/src/grab-registry.bat b/src/grab-registry.bat new file mode 100644 index 00000000..09c3b3a3 --- /dev/null +++ b/src/grab-registry.bat @@ -0,0 +1,17 @@ +@echo off + +NET SESSIONS > NUL 2>&1 +IF %ERRORLEVEL% NEQ 0 ( + ECHO Error: This script requires administrative privileges. + EXIT /B 1 +) + +SET REGDIR="registry" +MKDIR %REGDIR% + +REG SAVE HKLM\SYSTEM %REGDIR%\SYSTEM /Y +REG SAVE HKLM\SECURITY %REGDIR%\SECURITY /Y +REG SAVE HKLM\SOFTWARE %REGDIR%\SOFTWARE /Y +REG SAVE HKLM\HARDWARE %REGDIR%\HARDWARE /Y +REG SAVE HKLM\SAM %REGDIR%\SAM /Y +COPY /B /Y C:\Users\Default\NTUSER.DAT "%REGDIR%\NTUSER.DAT"