#include "../std_include.hpp" #include "hive_parser.hpp" #include // 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; // NOLINTBEGIN(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) struct offset_entry_t { int32_t offset; int32_t hash; }; struct offsets_t { int32_t block_size; char block_type[2]; int16_t count; offset_entry_t entries[1]; }; struct key_block_t { int32_t block_size; char block_type[2]; uint8_t dummya[18]; int32_t subkey_count; uint8_t dummyb[4]; int32_t subkeys; uint8_t dummyc[4]; int32_t value_count; int32_t offsets; uint8_t dummyd[28]; int16_t len; int16_t du; char name[255]; }; struct value_block_t { int32_t block_size; char block_type[2]; int16_t name_len; int32_t size; int32_t offset; int32_t value_type; int16_t flags; int16_t dummy; char name[255]; }; // NOLINTEND(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) 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()); } } } 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; } const hive_value* hive_key::get_value(std::ifstream& file, size_t index) { this->parse(file); if (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_) { 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)); } 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 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 (int16_t 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)))); 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); } } } hive_parser::hive_parser(const std::filesystem::path& file_path) : file_(file_path, std::ios::binary), root_key_(parse_root_block(file_, file_path)) { }