Improvements to registry syscalls

This commit is contained in:
Igor Pissolati
2025-04-20 16:09:04 -03:00
parent 759bd0e9fb
commit 763b8fc760
8 changed files with 349 additions and 11 deletions

View File

@@ -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;

View File

@@ -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<int> 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 <typename T>
using unordered_string_map = std::unordered_map<std::string, T, string_hash, std::equal_to<>>;
template <typename T>
using unordered_insensitive_string_map =
std::unordered_map<std::string, T, insensitive_string_hash, insensitive_string_equal>;
using unordered_string_set = std::unordered_set<std::string, string_hash, std::equal_to<>>;
}

View File

@@ -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<int>(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<key_block_t>(file, subkey_block_offset);
std::string subkey_name(subkey.name, std::min(subkey.len, static_cast<int16_t>(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);
}
}
}

View File

@@ -23,12 +23,24 @@ class hive_key
{
}
utils::unordered_string_map<hive_key>& get_sub_keys(std::ifstream& file)
utils::unordered_insensitive_string_map<hive_key>& 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<hive_key> sub_keys_{};
utils::unordered_string_map<raw_hive_value> values_{};
utils::unordered_insensitive_string_map<hive_key> sub_keys_{};
std::vector<std::string_view> sub_keys_by_index_{};
utils::unordered_insensitive_string_map<raw_hive_value> values_{};
std::vector<std::string_view> 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_;

View File

@@ -116,8 +116,6 @@ std::optional<registry_key> registry_manager::get_key(const utils::path_key& key
std::optional<registry_value> 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_value> registry_manager::get_value(const registry_key& ke
return v;
}
std::optional<registry_value> 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<std::string_view> 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);
}

View File

@@ -48,6 +48,9 @@ class registry_manager
std::optional<registry_key> get_key(const utils::path_key& key);
std::optional<registry_value> get_value(const registry_key& key, std::string name);
std::optional<registry_value> get_value(const registry_key& key, size_t index);
std::optional<std::string_view> get_sub_key_name(const registry_key& key, size_t index);
private:
std::filesystem::path hive_path_{};

View File

@@ -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<ULONG> 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<ULONG> result_length);
// syscalls/section.cpp:
NTSTATUS handle_NtCreateSection(const syscall_context& c, emulator_object<handle> section_handle,
@@ -778,6 +784,7 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
add_handler(NtUserGetDpiForCurrentProcess);
add_handler(NtReleaseSemaphore);
add_handler(NtEnumerateKey);
add_handler(NtEnumerateValueKey);
add_handler(NtAlpcConnectPort);
add_handler(NtGetNextThread);
add_handler(NtSetInformationObject);

View File

@@ -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<ULONG> 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<ULONG>(required_size));
KEY_BASIC_INFORMATION info{};
info.LastWriteTime.QuadPart = 0;
info.TitleIndex = 0;
info.NameLength = static_cast<ULONG>(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<ULONG>(required_size));
KEY_NODE_INFORMATION info{};
info.LastWriteTime.QuadPart = 0;
info.TitleIndex = 0;
info.ClassOffset = static_cast<ULONG>(base_size + name_size);
info.ClassLength = static_cast<ULONG>(class_size);
info.NameLength = static_cast<ULONG>(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<ULONG> 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<ULONG>(required_size));
KEY_VALUE_BASIC_INFORMATION info{};
info.TitleIndex = 0;
info.Type = value->type;
info.NameLength = static_cast<ULONG>(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<ULONG>(required_size));
KEY_VALUE_PARTIAL_INFORMATION info{};
info.TitleIndex = 0;
info.Type = value->type;
info.DataLength = static_cast<ULONG>(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<ULONG>(base_size + name_size);
const auto required_size = data_offset + data_size;
result_length.write(static_cast<ULONG>(required_size));
KEY_VALUE_FULL_INFORMATION info{};
info.TitleIndex = 0;
info.Type = value->type;
info.DataOffset = data_offset;
info.DataLength = static_cast<ULONG>(data_size);
info.NameLength = static_cast<ULONG>(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;
}
}