diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4dbb117..d544331 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/deps/CMakeLists.txt b/deps/CMakeLists.txt index 439293b..9217618 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -13,4 +13,4 @@ target_include_directories(reflect INTERFACE ########################################## include(mini-gdbstub.cmake) -include(googletest.cmake) \ No newline at end of file +include(googletest.cmake) diff --git a/src/common/utils/container.hpp b/src/common/utils/container.hpp new file mode 100644 index 0000000..eeafdf3 --- /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/emulator/serialization.hpp b/src/emulator/serialization.hpp index 0cb844c..0b6f916 100644 --- a/src/emulator/serialization.hpp +++ b/src/emulator/serialization.hpp @@ -38,8 +38,9 @@ namespace utils }; template - struct has_serialize_function(), - std::declval()))>> + struct has_serialize_function(), + std::declval&>()) + )>> : std::true_type { }; @@ -50,8 +51,9 @@ namespace utils }; template - struct has_deserialize_function(), std::declval()))>> + struct has_deserialize_function(), + std::declval&>()))>> : std::true_type { }; @@ -122,7 +124,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 +340,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 0000000..fbf769c --- /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/grab-registry.bat b/src/grab-registry.bat new file mode 100644 index 0000000..09c3b3a --- /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" diff --git a/src/windows-emulator/handles.hpp b/src/windows-emulator/handles.hpp index 49fd1d5..8ee8d1d 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 b2b104e..3a1a780 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -2,12 +2,14 @@ #include "emulator_utils.hpp" #include "handles.hpp" +#include "registry/registry_manager.hpp" #include "module/module_manager.hpp" #include #include #include +#include #define PEB_SEGMENT_SIZE (20 << 20) // 20 MB @@ -23,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}; @@ -375,6 +363,8 @@ struct process_context { } + registry_manager registry{}; + uint64_t executed_instructions{0}; uint64_t current_ip{0}; uint64_t previous_ip{0}; @@ -404,6 +394,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 +405,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 +430,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 +442,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 +471,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/hive_parser.cpp b/src/windows-emulator/registry/hive_parser.cpp new file mode 100644 index 0000000..03190cb --- /dev/null +++ b/src/windows-emulator/registry/hive_parser.cpp @@ -0,0 +1,224 @@ +#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_safe(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(); + } + + 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); + + 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) + { + std::string result{}; + result.resize(size); + + read_file_data(file, offset, result.data(), size); + return result; + } + + template + requires(std::is_trivially_copyable_v) + T read_file_object(std::ifstream& file, const uint64_t offset, const size_t array_index = 0) + { + T obj{}; + 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) + { + try + { + 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()); + } + } + + char char_to_lower(const char val) + { + return static_cast(std::tolower(static_cast(val))); + } +} + +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(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)))); + + 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(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(file, MAIN_ROOT_OFFSET + entry_offsets, i); + + const auto subkey_block_offset = MAIN_ROOT_OFFSET + offset_entry.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); + + 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 new file mode 100644 index 0000000..462557d --- /dev/null +++ b/src/windows-emulator/registry/hive_parser.hpp @@ -0,0 +1,99 @@ +#pragma once + +#include +#include + +struct hive_value +{ + uint32_t type{}; + std::string name{}; + std::vector data{}; +}; + +class hive_key +{ +public: + 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) + { + } + + utils::unordered_string_map& get_sub_keys(std::ifstream& file) + { + this->parse(file); + return this->sub_keys_; + } + + hive_key* get_sub_key(std::ifstream& file, const std::string_view name) + { + auto& sub_keys = this->get_sub_keys(file); + const auto entry = sub_keys.find(name); + + if (entry == sub_keys.end()) + { + return nullptr; + } + + return &entry->second; + } + + const hive_value* get_value(std::ifstream& file, const std::string_view name); + +private: + struct raw_hive_value : hive_value + { + bool parsed{false}; + int data_offset{}; + size_t data_length{}; + }; + + bool parsed_{false}; + utils::unordered_string_map sub_keys_{}; + utils::unordered_string_map values_{}; + + const int subkey_block_offset_{}; + const int value_count_{}; + const int value_offsets_{}; + + void parse(std::ifstream& file); +}; + +class hive_parser +{ +public: + 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_; + + for (const auto& key_part : key) + { + if (!current_key) + { + return nullptr; + } + + current_key = current_key->get_sub_key(this->file_, key_part.string()); + } + + 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) + { + return nullptr; + } + + 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 new file mode 100644 index 0000000..ad88cb2 --- /dev/null +++ b/src/windows-emulator/registry/registry_manager.cpp @@ -0,0 +1,162 @@ +#include "registry_manager.hpp" + +#include +#include + +#include "hive_parser.hpp" + +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() = default; +registry_manager::registry_manager(registry_manager&&) noexcept = default; +registry_manager& registry_manager::operator=(registry_manager&&) noexcept = default; + +registry_manager::registry_manager(const std::filesystem::path& hive_path) + : hive_path_(absolute(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", 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", this->hive_path_ / "NTUSER.dat"); + + this->add_path_mapping(machine / "system" / "CurrentControlSet", machine / "system" / "ControlSet001"); +} + +void registry_manager::serialize(utils::buffer_serializer& buffer) const +{ + buffer.write(this->hive_path_); +} + +void registry_manager::deserialize(utils::buffer_deserializer& buffer) +{ + buffer.read(this->hive_path_); + this->setup(); +} + +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_sub_key(reg_key.path); + if (!entry) + { + return std::nullopt; + } + + 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 std::nullopt; + } + + auto* entry = iterator->second->get_value(key.path, name); + 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 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/registry_manager.hpp b/src/windows-emulator/registry/registry_manager.hpp new file mode 100644 index 0000000..7115337 --- /dev/null +++ b/src/windows-emulator/registry/registry_manager.hpp @@ -0,0 +1,67 @@ +#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(this->hive); + buffer.write(this->path); + } + + void deserialize(utils::buffer_deserializer& buffer) + { + buffer.read(this->hive); + buffer.read(this->path); + } +}; + +struct registry_value +{ + uint32_t type; + std::string_view name; + std::span data; +}; + +class registry_manager +{ +public: + using hive_ptr = std::unique_ptr; + using hive_map = std::unordered_map; + + 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); + + std::optional get_key(const std::filesystem::path& key); + 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_{}; + + 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); + + void setup(); +}; diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index a848be4..e0d810f 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 @@ -53,20 +51,153 @@ 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) { 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()); - 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(const syscall_context& c, const emulator_object key_handle, + const ACCESS_MASK desired_access, + const emulator_object object_attributes, + ULONG /*open_options*/) + { + return handle_NtOpenKey(c, key_handle, desired_access, object_attributes); } - NTSTATUS handle_NtOpenKeyEx() + 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) { + const auto* key = c.proc.registry_keys.get(key_handle); + if (!key) + { + return STATUS_INVALID_HANDLE; + } + + 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) + { + 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) + (original_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(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), + original_name.data(), + info.NameLength); + + return STATUS_SUCCESS; + } + + 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)); + + if (required_size > length) + { + return STATUS_BUFFER_TOO_SMALL; + } + + KEY_VALUE_PARTIAL_INFORMATION info{}; + info.TitleIndex = 0; + info.Type = value->type; + info.DataLength = static_cast(value->data.size()); + + 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 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) + { + 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(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), + 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()); + + 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; } @@ -156,6 +287,11 @@ namespace return STATUS_SUCCESS; } + if (value.type == handle_types::registry && c.proc.registry_keys.erase(handle)) + { + return STATUS_SUCCESS; + } + return STATUS_INVALID_HANDLE; } @@ -1532,6 +1668,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*/, @@ -2267,6 +2408,8 @@ void syscall_dispatcher::add_handlers(std::map& ha add_handler(NtReadFile); add_handler(NtSetInformationFile); add_handler(NtUserRegisterWindowMessage); + add_handler(NtQueryValueKey); + add_handler(NtGetNlsSectionPtr); #undef add_handler } diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 393520b..8e4c0eb 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 7ae773e..5e858ca 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}; };