From 763b8fc760d65eaca989ca95b1531ca6995f8e79 Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Sun, 20 Apr 2025 16:09:04 -0300 Subject: [PATCH] Improvements to registry syscalls --- src/common/platform/registry.hpp | 18 ++ src/common/utils/container.hpp | 31 +++ src/windows-emulator/registry/hive_parser.cpp | 27 ++- src/windows-emulator/registry/hive_parser.hpp | 48 ++++- .../registry/registry_manager.cpp | 35 +++- .../registry/registry_manager.hpp | 3 + src/windows-emulator/syscalls.cpp | 9 +- src/windows-emulator/syscalls/registry.cpp | 189 +++++++++++++++++- 8 files changed, 349 insertions(+), 11 deletions(-) diff --git a/src/common/platform/registry.hpp b/src/common/platform/registry.hpp index 7f76d0ae..a44e8b2e 100644 --- a/src/common/platform/registry.hpp +++ b/src/common/platform/registry.hpp @@ -34,6 +34,24 @@ struct KEY_NAME_INFORMATION char16_t Name[1]; }; +typedef struct _KEY_BASIC_INFORMATION +{ + LARGE_INTEGER LastWriteTime; + ULONG TitleIndex; + ULONG NameLength; + char16_t Name[1]; +} KEY_BASIC_INFORMATION, *PKEY_BASIC_INFORMATION; + +typedef struct _KEY_NODE_INFORMATION +{ + LARGE_INTEGER LastWriteTime; + ULONG TitleIndex; + ULONG ClassOffset; + ULONG ClassLength; + ULONG NameLength; + char16_t Name[1]; +} KEY_NODE_INFORMATION, *PKEY_NODE_INFORMATION; + typedef struct _KEY_FULL_INFORMATION { LARGE_INTEGER LastWriteTime; diff --git a/src/common/utils/container.hpp b/src/common/utils/container.hpp index 2a7cb2f5..0877728c 100644 --- a/src/common/utils/container.hpp +++ b/src/common/utils/container.hpp @@ -18,8 +18,39 @@ namespace utils } }; + struct insensitive_string_hash + { + using is_transparent = void; + + size_t operator()(const std::string_view str) const + { + size_t hash = 0; + constexpr std::hash hasher{}; + for (unsigned char c : str) + { + hash ^= hasher(std::tolower(c)) + 0x9e3779b9 + (hash << 6) + (hash >> 2); + } + return hash; + } + }; + + struct insensitive_string_equal + { + using is_transparent = void; + + bool operator()(const std::string_view lhs, const std::string_view rhs) const + { + return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), + [](unsigned char c1, unsigned char c2) { return std::tolower(c1) == std::tolower(c2); }); + } + }; + template using unordered_string_map = std::unordered_map>; + template + using unordered_insensitive_string_map = + std::unordered_map; + using unordered_string_set = std::unordered_set>; } diff --git a/src/windows-emulator/registry/hive_parser.cpp b/src/windows-emulator/registry/hive_parser.cpp index c6892433..a328b2c9 100644 --- a/src/windows-emulator/registry/hive_parser.cpp +++ b/src/windows-emulator/registry/hive_parser.cpp @@ -159,6 +159,18 @@ const hive_value* hive_key::get_value(std::ifstream& file, const std::string_vie return &value; } +const hive_value* hive_key::get_value(std::ifstream& file, size_t index) +{ + this->parse(file); + + if (index < 0 || index >= values_by_index_.size()) + { + return nullptr; + } + + return get_value(file, values_by_index_[index]); +} + void hive_key::parse(std::ifstream& file) { if (this->parsed_) @@ -189,8 +201,11 @@ void hive_key::parse(std::ifstream& file) raw_value.data_offset = offset + static_cast(offsetof(value_block_t, offset)); } - utils::string::to_lower_inplace(value_name); - this->values_[std::move(value_name)] = std::move(raw_value); + const auto [it, inserted] = this->values_.emplace(std::move(value_name), std::move(raw_value)); + if (inserted) + { + this->values_by_index_.emplace_back(it->first); + } } // Subkeys @@ -212,9 +227,13 @@ void hive_key::parse(std::ifstream& file) 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)))); - utils::string::to_lower_inplace(subkey_name); - this->sub_keys_.emplace(std::move(subkey_name), hive_key{subkey.subkeys, subkey.value_count, subkey.offsets}); + const auto [it, inserted] = this->sub_keys_.emplace( + std::move(subkey_name), hive_key{subkey.subkeys, subkey.value_count, subkey.offsets}); + if (inserted) + { + this->sub_keys_by_index_.emplace_back(it->first); + } } } diff --git a/src/windows-emulator/registry/hive_parser.hpp b/src/windows-emulator/registry/hive_parser.hpp index 3677f821..011d0d7b 100644 --- a/src/windows-emulator/registry/hive_parser.hpp +++ b/src/windows-emulator/registry/hive_parser.hpp @@ -23,12 +23,24 @@ class hive_key { } - utils::unordered_string_map& get_sub_keys(std::ifstream& file) + utils::unordered_insensitive_string_map& get_sub_keys(std::ifstream& file) { this->parse(file); return this->sub_keys_; } + const std::string_view* get_sub_key_name(std::ifstream& file, size_t index) + { + this->parse(file); + + if (index < 0 || index >= sub_keys_by_index_.size()) + { + return nullptr; + } + + return &sub_keys_by_index_[index]; + } + hive_key* get_sub_key(std::ifstream& file, const std::string_view name) { auto& sub_keys = this->get_sub_keys(file); @@ -42,7 +54,13 @@ class hive_key return &entry->second; } + hive_key* get_sub_key(std::ifstream& file, size_t index) + { + return get_sub_key(file, *this->get_sub_key_name(file, index)); + } + const hive_value* get_value(std::ifstream& file, std::string_view name); + const hive_value* get_value(std::ifstream& file, size_t index); private: struct raw_hive_value : hive_value @@ -53,8 +71,10 @@ class hive_key }; bool parsed_{false}; - utils::unordered_string_map sub_keys_{}; - utils::unordered_string_map values_{}; + utils::unordered_insensitive_string_map sub_keys_{}; + std::vector sub_keys_by_index_{}; + utils::unordered_insensitive_string_map values_{}; + std::vector values_by_index_{}; const int subkey_block_offset_{}; const int value_count_{}; @@ -85,6 +105,17 @@ class hive_parser return current_key; } + [[nodiscard]] const std::string_view* get_sub_key_name(const std::filesystem::path& key, size_t index) + { + auto* target_key = this->get_sub_key(key); + if (!target_key) + { + return nullptr; + } + + return target_key->get_sub_key_name(this->file_, index); + } + [[nodiscard]] const hive_value* get_value(const std::filesystem::path& key, const std::string_view name) { auto* sub_key = this->get_sub_key(key); @@ -96,6 +127,17 @@ class hive_parser return sub_key->get_value(this->file_, name); } + [[nodiscard]] const hive_value* get_value(const std::filesystem::path& key, size_t index) + { + auto* sub_key = this->get_sub_key(key); + if (!sub_key) + { + return nullptr; + } + + return sub_key->get_value(this->file_, index); + } + 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 9a822569..f09199df 100644 --- a/src/windows-emulator/registry/registry_manager.cpp +++ b/src/windows-emulator/registry/registry_manager.cpp @@ -116,8 +116,6 @@ std::optional registry_manager::get_key(const utils::path_key& key std::optional registry_manager::get_value(const registry_key& key, std::string name) { - utils::string::to_lower_inplace(name); - const auto iterator = this->hives_.find(key.hive); if (iterator == this->hives_.end()) { @@ -138,6 +136,28 @@ std::optional registry_manager::get_value(const registry_key& ke return v; } +std::optional registry_manager::get_value(const registry_key& key, size_t index) +{ + const auto iterator = this->hives_.find(key.hive); + if (iterator == this->hives_.end()) + { + return std::nullopt; + } + + const auto* entry = iterator->second->get_value(key.path.get(), index); + if (!entry) + { + return std::nullopt; + } + + registry_value v{}; + v.type = entry->type; + v.name = entry->name; + v.data = entry->data; + + return v; +} + registry_manager::hive_map::iterator registry_manager::find_hive(const utils::path_key& key) { for (auto i = this->hives_.begin(); i != this->hives_.end(); ++i) @@ -150,3 +170,14 @@ registry_manager::hive_map::iterator registry_manager::find_hive(const utils::pa return this->hives_.end(); } + +std::optional registry_manager::get_sub_key_name(const registry_key& key, size_t index) +{ + const auto iterator = this->hives_.find(key.hive); + if (iterator == this->hives_.end()) + { + return std::nullopt; + } + + return *iterator->second->get_sub_key_name(key.path.get(), index); +} \ No newline at end of file diff --git a/src/windows-emulator/registry/registry_manager.hpp b/src/windows-emulator/registry/registry_manager.hpp index c041d2f2..cd62c7d7 100644 --- a/src/windows-emulator/registry/registry_manager.hpp +++ b/src/windows-emulator/registry/registry_manager.hpp @@ -48,6 +48,9 @@ class registry_manager std::optional get_key(const utils::path_key& key); std::optional get_value(const registry_key& key, std::string name); + std::optional get_value(const registry_key& key, size_t index); + + std::optional get_sub_key_name(const registry_key& key, size_t index); private: std::filesystem::path hive_path_{}; diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 605f000e..d62447cd 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -220,7 +220,13 @@ namespace syscalls NTSTATUS handle_NtCreateKey(); NTSTATUS handle_NtNotifyChangeKey(); NTSTATUS handle_NtSetInformationKey(); - NTSTATUS handle_NtEnumerateKey(); + NTSTATUS handle_NtEnumerateKey(const syscall_context& c, handle key_handle, ULONG index, + KEY_INFORMATION_CLASS key_information_class, uint64_t key_information, ULONG length, + emulator_object result_length); + NTSTATUS handle_NtEnumerateValueKey(const syscall_context& c, handle key_handle, ULONG index, + KEY_VALUE_INFORMATION_CLASS key_value_information_class, + uint64_t key_value_information, ULONG length, + emulator_object result_length); // syscalls/section.cpp: NTSTATUS handle_NtCreateSection(const syscall_context& c, emulator_object section_handle, @@ -778,6 +784,7 @@ void syscall_dispatcher::add_handlers(std::map& ha add_handler(NtUserGetDpiForCurrentProcess); add_handler(NtReleaseSemaphore); add_handler(NtEnumerateKey); + add_handler(NtEnumerateValueKey); add_handler(NtAlpcConnectPort); add_handler(NtGetNextThread); add_handler(NtSetInformationObject); diff --git a/src/windows-emulator/syscalls/registry.cpp b/src/windows-emulator/syscalls/registry.cpp index d383335a..be62249f 100644 --- a/src/windows-emulator/syscalls/registry.cpp +++ b/src/windows-emulator/syscalls/registry.cpp @@ -242,8 +242,195 @@ namespace syscalls return STATUS_NOT_SUPPORTED; } - NTSTATUS handle_NtEnumerateKey() + NTSTATUS handle_NtEnumerateKey(const syscall_context& c, const handle key_handle, const ULONG index, + const KEY_INFORMATION_CLASS key_information_class, const uint64_t key_information, + const ULONG length, const emulator_object result_length) { + const auto* key = c.proc.registry_keys.get(key_handle); + if (!key) + { + return STATUS_INVALID_HANDLE; + } + + const auto subkey_name = c.win_emu.registry.get_sub_key_name(*key, index); + if (!subkey_name) + { + return STATUS_NO_MORE_ENTRIES; + } + + const std::u16string subkey_name_u16(subkey_name->begin(), subkey_name->end()); + + if (key_information_class == KeyBasicInformation) + { + constexpr auto base_size = offsetof(KEY_BASIC_INFORMATION, Name); + const auto name_size = subkey_name_u16.size() * 2; + const auto required_size = base_size + name_size; + result_length.write(static_cast(required_size)); + + KEY_BASIC_INFORMATION info{}; + info.LastWriteTime.QuadPart = 0; + info.TitleIndex = 0; + info.NameLength = static_cast(name_size); + + if (base_size <= length) + { + c.emu.write_memory(key_information, &info, base_size); + } + + if (required_size > length) + { + return STATUS_BUFFER_OVERFLOW; + } + + c.emu.write_memory(key_information + base_size, subkey_name_u16.data(), name_size); + + return STATUS_SUCCESS; + } + + if (key_information_class == KeyNodeInformation) + { + constexpr auto base_size = offsetof(KEY_NODE_INFORMATION, Name); + const auto name_size = subkey_name_u16.size() * 2; + constexpr auto class_size = 0; // TODO: Class Name + const auto required_size = base_size + name_size + class_size; + result_length.write(static_cast(required_size)); + + KEY_NODE_INFORMATION info{}; + info.LastWriteTime.QuadPart = 0; + info.TitleIndex = 0; + info.ClassOffset = static_cast(base_size + name_size); + info.ClassLength = static_cast(class_size); + info.NameLength = static_cast(name_size); + + if (base_size <= length) + { + c.emu.write_memory(key_information, &info, base_size); + } + + if (required_size > length) + { + return STATUS_BUFFER_OVERFLOW; + } + + c.emu.write_memory(key_information + base_size, subkey_name_u16.data(), name_size); + // TODO: Write Class Name + + return STATUS_SUCCESS; + } + + c.win_emu.log.print(color::gray, "Unsupported registry enumeration class: %X\n", key_information_class); + return STATUS_NOT_SUPPORTED; + } + + NTSTATUS handle_NtEnumerateValueKey(const syscall_context& c, const handle key_handle, const ULONG index, + const KEY_VALUE_INFORMATION_CLASS key_value_information_class, + const uint64_t key_value_information, const ULONG length, + const emulator_object result_length) + { + const auto* key = c.proc.registry_keys.get(key_handle); + if (!key) + { + return STATUS_INVALID_HANDLE; + } + + const auto value = c.win_emu.registry.get_value(*key, index); + if (!value) + { + return STATUS_NO_MORE_ENTRIES; + } + + const std::u16string value_name_u16(value->name.begin(), value->name.end()); + + if (key_value_information_class == KeyValueBasicInformation) + { + constexpr auto base_size = offsetof(KEY_VALUE_BASIC_INFORMATION, Name); + const auto name_size = value_name_u16.size() * 2; + const auto required_size = base_size + name_size; + result_length.write(static_cast(required_size)); + + KEY_VALUE_BASIC_INFORMATION info{}; + info.TitleIndex = 0; + info.Type = value->type; + info.NameLength = static_cast(name_size); + + if (base_size <= length) + { + c.emu.write_memory(key_value_information, &info, base_size); + } + + if (required_size > length) + { + return STATUS_BUFFER_OVERFLOW; + } + + c.emu.write_memory(key_value_information + base_size, value_name_u16.data(), name_size); + + return STATUS_SUCCESS; + } + + if (key_value_information_class == KeyValuePartialInformation) + { + constexpr auto base_size = offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data); + const auto data_size = value->data.size(); + const auto required_size = base_size + data_size; + result_length.write(static_cast(required_size)); + + KEY_VALUE_PARTIAL_INFORMATION info{}; + info.TitleIndex = 0; + info.Type = value->type; + info.DataLength = static_cast(data_size); + + if (base_size <= length) + { + c.emu.write_memory(key_value_information, &info, base_size); + } + + if (required_size > length) + { + return STATUS_BUFFER_OVERFLOW; + } + + c.emu.write_memory(key_value_information + base_size, value->data.data(), data_size); + + return STATUS_SUCCESS; + } + + if (key_value_information_class == KeyValueFullInformation) + { + constexpr auto base_size = offsetof(KEY_VALUE_FULL_INFORMATION, Name); + const auto name_size = value_name_u16.size() * 2; + const auto data_size = value->data.size(); + const ULONG data_offset = static_cast(base_size + name_size); + const auto required_size = data_offset + data_size; + + result_length.write(static_cast(required_size)); + + KEY_VALUE_FULL_INFORMATION info{}; + info.TitleIndex = 0; + info.Type = value->type; + info.DataOffset = data_offset; + info.DataLength = static_cast(data_size); + info.NameLength = static_cast(name_size); + + if (base_size <= length) + { + c.emu.write_memory(key_value_information, &info, base_size); + } + + if (required_size > length) + { + return STATUS_BUFFER_OVERFLOW; + } + + c.emu.write_memory(key_value_information + base_size, value_name_u16.data(), name_size); + + c.emu.write_memory(key_value_information + data_offset, value->data.data(), data_size); + + return STATUS_SUCCESS; + } + + c.win_emu.log.print(color::gray, "Unsupported registry value enumeration class: %X\n", + key_value_information_class); return STATUS_NOT_SUPPORTED; } }