mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-18 19:23:56 +00:00
Improvements to registry syscalls
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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<>>;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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_{};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user