Skip to content

Commit

Permalink
Remove hive parser submodule
Browse files Browse the repository at this point in the history
  • Loading branch information
momo5502 committed Nov 3, 2024
1 parent b646ac8 commit 59eba15
Show file tree
Hide file tree
Showing 7 changed files with 341 additions and 12 deletions.
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 0 additions & 1 deletion deps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,3 @@ target_include_directories(reflect INTERFACE

include(mini-gdbstub.cmake)
include(googletest.cmake)
include(windows-hive-parser.cmake)
1 change: 0 additions & 1 deletion deps/windows-hive-parser
Submodule windows-hive-parser deleted from df6231
5 changes: 0 additions & 5 deletions deps/windows-hive-parser.cmake

This file was deleted.

1 change: 0 additions & 1 deletion src/windows-emulator/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ target_link_libraries(windows-emulator PRIVATE
common
unicorn-emulator
mini-gdbstub
windows-hive-parser
)

target_link_libraries(windows-emulator PUBLIC
Expand Down
340 changes: 340 additions & 0 deletions src/windows-emulator/registry/hive_parser.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,340 @@
#pragma once

#include <string>
#include <fstream>
#include <vector>
#include <ranges>
#include <cwctype>
#include <optional>
#include <filesystem>
#include <string_view>
#include <unordered_map>

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<char> 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<char>()};
}

struct string_hash
{
using is_transparent = void;

size_t operator()(const std::string_view str) const
{
constexpr std::hash<std::string_view> hasher{};
return hasher(str);
}
};

template <typename T>
using unordered_string_map = std::unordered_map<std::string, T, string_hash, std::equal_to<>>;
}

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<std::string_view> subkeys_list() const
{
const auto item = reinterpret_cast<offsets_t*>(this->main_root + key_block->subkeys);
if (item->block_type[1] != 'f' && item->block_type[1] != 'h')
return {};

std::vector<std::string_view> out;
for (auto i = 0; i < key_block->subkey_count; i++)
{
const auto subkey = reinterpret_cast<key_block_t*>((&item->first)[i * 2] + this->main_root);
if (!subkey)
continue;

out.emplace_back(subkey->name, subkey->len);
}

return out;
}

[[nodiscard]] std::vector<std::string_view> keys_list() const
{
if (!key_block->value_count)
return {};

std::vector<std::string_view> out;
for (auto i = 0; i < key_block->value_count; i++)
{
const auto value = reinterpret_cast<value_block_t*>(reinterpret_cast<int*>(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<long, std::string_view>;

std::optional<value> get_key_value(const std::string_view& name)
{
for (auto i = 0; i < key_block->value_count; i++)
{
const auto value = reinterpret_cast<value_block_t*>(reinterpret_cast<int*>(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<char*>(this->main_root + value->offset + 4);
if (value->size & 1 << 31)
data = reinterpret_cast<char*>(&value->offset);

return std::make_pair(value->value_type, std::string_view(data, value->size & 0xffff));
}

return std::nullopt;
}

template <class T>
std::optional<T> 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<T, std::string_view>)
{
if (type != REG_SZ && type != REG_EXPAND_SZ)
return std::nullopt;

return data;
}
else if constexpr (std::is_same_v<T, std::vector<std::string_view>>)
{
if (type != REG_MULTI_SZ)
return std::nullopt;

std::string_view text;
std::vector<std::string_view> 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<T, int>)
{
if (type != REG_DWORD)
return std::nullopt;

return *reinterpret_cast<T*>(data);
}
else if constexpr (std::is_same_v<T, std::basic_string_view<uint8_t>>)
{
if (type != REG_BINARY)
return std::nullopt;

return {reinterpret_cast<const uint8_t*>(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<hive_subpaths_t> subpaths;
};

key_block_t* main_key_block_data;
uintptr_t main_root;
std::vector<char> file_data;
detail::unordered_string_map<hive_cache_t> 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<offsets_t*>(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<key_block_t*>((&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<hive_subpaths_t>{}
});

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<char> 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<key_block_t*>(reinterpret_cast<uintptr_t>(file_data.data() + 0x1020));
main_root = reinterpret_cast<uintptr_t>(main_key_block_data) - 0x20;

reclusive_search(main_key_block_data, "");
}

[[nodiscard]] bool success() const
{
return !subkey_cache.empty();
}

[[nodiscard]] std::optional<hive_key_t> 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;
}
};
2 changes: 1 addition & 1 deletion src/windows-emulator/registry/registry_manager.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include "registry_manager.hpp"

#include <hive_parser.hh>
#include "hive_parser.hpp"
#include <serialization_helper.hpp>

namespace
Expand Down

0 comments on commit 59eba15

Please sign in to comment.