diff --git a/.gitmodules b/.gitmodules index 7c2056d..c6814e5 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 5bdc31d..9217618 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 df62310..0000000 --- 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 9830412..0000000 --- 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 ed81108..7948efc 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 0000000..e2ad799 --- /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 be8bd01..e758a9d 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