mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-18 19:23:56 +00:00
Cleanup snapshot generation
This commit is contained in:
@@ -4,9 +4,8 @@
|
||||
#include <win_x64_gdb_stub_handler.hpp>
|
||||
|
||||
#include "object_watching.hpp"
|
||||
#include "snapshot.hpp"
|
||||
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/compression.hpp>
|
||||
#include <utils/interupt_handler.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
@@ -26,67 +25,6 @@ namespace
|
||||
std::unordered_map<windows_path, std::filesystem::path> path_mappings{};
|
||||
};
|
||||
|
||||
std::vector<uint8_t> build_dump_data(const windows_emulator& win_emu)
|
||||
{
|
||||
utils::buffer_serializer serializer{};
|
||||
win_emu.serialize(serializer);
|
||||
|
||||
auto compressed_data = utils::compression::zlib::compress(serializer.get_buffer());
|
||||
|
||||
// TODO: Add version
|
||||
compressed_data.insert(compressed_data.begin(), {'E', 'D', 'M', 'P'});
|
||||
|
||||
return compressed_data;
|
||||
}
|
||||
|
||||
std::string get_main_executable_name(const windows_emulator& win_emu)
|
||||
{
|
||||
const auto* exe = win_emu.mod_manager.executable;
|
||||
if (exe)
|
||||
{
|
||||
return std::filesystem::path(exe->name).stem().string();
|
||||
}
|
||||
|
||||
return "process";
|
||||
}
|
||||
|
||||
void generate_dump(const windows_emulator& win_emu)
|
||||
{
|
||||
std::filesystem::path dump = get_main_executable_name(win_emu) + "-" + std::to_string(time(nullptr)) + ".edmp";
|
||||
win_emu.log.log("Writing to %s...\n", dump.string().c_str());
|
||||
|
||||
const auto data = build_dump_data(win_emu);
|
||||
utils::io::write_file(dump, data);
|
||||
}
|
||||
|
||||
void load_dump_data(windows_emulator& win_emu, const std::span<const uint8_t> data)
|
||||
{
|
||||
if (data.size() < 4 || memcmp(data.data(), "EDMP", 4) != 0)
|
||||
{
|
||||
throw std::runtime_error("Invalid dump");
|
||||
}
|
||||
|
||||
const auto plain_data = utils::compression::zlib::decompress(data.subspan(4));
|
||||
if (plain_data.empty())
|
||||
{
|
||||
throw std::runtime_error("Failed to decompress dump");
|
||||
}
|
||||
|
||||
utils::buffer_deserializer deserializer{plain_data, true};
|
||||
win_emu.deserialize(deserializer);
|
||||
}
|
||||
|
||||
void load_dump(windows_emulator& win_emu, const std::filesystem::path& dump)
|
||||
{
|
||||
std::vector<uint8_t> data{};
|
||||
if (!utils::io::read_file(dump, &data))
|
||||
{
|
||||
throw std::runtime_error("Failed to read dump file: " + dump.string());
|
||||
}
|
||||
|
||||
load_dump_data(win_emu, data);
|
||||
}
|
||||
|
||||
void watch_system_objects(windows_emulator& win_emu, const std::set<std::string, std::less<>>& modules,
|
||||
const bool cache_logging)
|
||||
{
|
||||
@@ -119,6 +57,23 @@ namespace
|
||||
#endif
|
||||
}
|
||||
|
||||
bool read_yes_no_answer()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
const auto chr = static_cast<char>(getchar());
|
||||
if (chr == 'y')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (chr == 'n')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool run_emulation(windows_emulator& win_emu, const analysis_options& options)
|
||||
{
|
||||
std::atomic_uint32_t signals_received{0};
|
||||
@@ -153,20 +108,12 @@ namespace
|
||||
|
||||
if (signals_received > 0)
|
||||
{
|
||||
win_emu.log.log("Do you want to create a dump? (y/n)\n");
|
||||
win_emu.log.log("Do you want to create a snapshot? (y/n)\n");
|
||||
const auto write_snapshot = read_yes_no_answer();
|
||||
|
||||
bool write_dump = false;
|
||||
|
||||
char res{};
|
||||
while (res != 'n' && res != 'y')
|
||||
if (write_snapshot)
|
||||
{
|
||||
res = static_cast<char>(getchar());
|
||||
write_dump = res == 'y';
|
||||
}
|
||||
|
||||
if (write_dump)
|
||||
{
|
||||
generate_dump(win_emu);
|
||||
snapshot::write_emulator_snapshot(win_emu);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,7 +201,7 @@ namespace
|
||||
}
|
||||
|
||||
auto win_emu = create_empty_emulator(options);
|
||||
load_dump(*win_emu, options.dump);
|
||||
snapshot::load_emulator_snapshot(*win_emu, options.dump);
|
||||
return win_emu;
|
||||
}
|
||||
|
||||
|
||||
123
src/analyzer/snapshot.cpp
Normal file
123
src/analyzer/snapshot.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#include "snapshot.hpp"
|
||||
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/compression.hpp>
|
||||
|
||||
namespace snapshot
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct snapshot_header
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
char magic[4] = {'S', 'N', 'A', 'P'};
|
||||
uint32_t version{1};
|
||||
};
|
||||
|
||||
static_assert(sizeof(snapshot_header) == 8);
|
||||
|
||||
std::span<const std::byte> validate_header(const std::span<const std::byte> snapshot)
|
||||
{
|
||||
snapshot_header default_header;
|
||||
snapshot_header header{};
|
||||
|
||||
if (snapshot.size() < sizeof(header))
|
||||
{
|
||||
throw std::runtime_error("Snapshot is too small");
|
||||
}
|
||||
|
||||
memcpy(&header, snapshot.data(), sizeof(header));
|
||||
|
||||
if (memcmp(default_header.magic, header.magic, sizeof(header.magic)) != 0)
|
||||
{
|
||||
throw std::runtime_error("Invalid snapshot");
|
||||
}
|
||||
|
||||
if (default_header.version != header.version)
|
||||
{
|
||||
throw std::runtime_error("Unsupported snapshot version: " + std::to_string(header.version) +
|
||||
"(needed: " + std::to_string(default_header.version) + ")");
|
||||
}
|
||||
|
||||
return snapshot.subspan(sizeof(header));
|
||||
}
|
||||
|
||||
std::vector<std::byte> get_compressed_emulator_state(const windows_emulator& win_emu)
|
||||
{
|
||||
utils::buffer_serializer serializer{};
|
||||
win_emu.serialize(serializer);
|
||||
|
||||
return utils::compression::zlib::compress(serializer.get_buffer());
|
||||
}
|
||||
|
||||
std::vector<std::byte> get_decompressed_emulator_state(const std::span<const std::byte> snapshot)
|
||||
{
|
||||
const auto data = validate_header(snapshot);
|
||||
return utils::compression::zlib::decompress(data);
|
||||
}
|
||||
|
||||
std::string get_main_executable_name(const windows_emulator& win_emu)
|
||||
{
|
||||
const auto* exe = win_emu.mod_manager.executable;
|
||||
if (exe)
|
||||
{
|
||||
return std::filesystem::path(exe->name).stem().string();
|
||||
}
|
||||
|
||||
return "process";
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::byte> create_emulator_snapshot(const windows_emulator& win_emu)
|
||||
{
|
||||
const auto state = get_compressed_emulator_state(win_emu);
|
||||
|
||||
snapshot_header header{};
|
||||
std::span header_span(reinterpret_cast<const std::byte*>(&header), sizeof(header));
|
||||
|
||||
std::vector<std::byte> snapshot{};
|
||||
snapshot.reserve(header_span.size() + state.size());
|
||||
snapshot.assign(header_span.begin(), header_span.end());
|
||||
snapshot.insert(snapshot.end(), state.begin(), state.end());
|
||||
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
std::filesystem::path write_emulator_snapshot(const windows_emulator& win_emu, const bool log)
|
||||
{
|
||||
std::filesystem::path snapshot_file =
|
||||
get_main_executable_name(win_emu) + "-" + std::to_string(time(nullptr)) + ".snap";
|
||||
|
||||
if (log)
|
||||
{
|
||||
win_emu.log.log("Writing snapshot to %s...\n", snapshot_file.string().c_str());
|
||||
}
|
||||
|
||||
const auto snapshot = create_emulator_snapshot(win_emu);
|
||||
if (!utils::io::write_file(snapshot_file, snapshot))
|
||||
{
|
||||
throw std::runtime_error("Failed to write snapshot!");
|
||||
}
|
||||
|
||||
return snapshot_file;
|
||||
}
|
||||
|
||||
void load_emulator_snapshot(windows_emulator& win_emu, const std::span<const std::byte> snapshot)
|
||||
{
|
||||
const auto data = get_decompressed_emulator_state(snapshot);
|
||||
|
||||
utils::buffer_deserializer deserializer{data};
|
||||
win_emu.deserialize(deserializer);
|
||||
}
|
||||
|
||||
void load_emulator_snapshot(windows_emulator& win_emu, const std::filesystem::path& snapshot_file)
|
||||
{
|
||||
std::vector<std::byte> data{};
|
||||
if (!utils::io::read_file(snapshot_file, &data))
|
||||
{
|
||||
throw std::runtime_error("Failed to read snapshot file: " + snapshot_file.string());
|
||||
}
|
||||
|
||||
load_emulator_snapshot(win_emu, data);
|
||||
}
|
||||
}
|
||||
12
src/analyzer/snapshot.hpp
Normal file
12
src/analyzer/snapshot.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows_emulator.hpp>
|
||||
|
||||
namespace snapshot
|
||||
{
|
||||
std::vector<std::byte> create_emulator_snapshot(const windows_emulator& win_emu);
|
||||
std::filesystem::path write_emulator_snapshot(const windows_emulator& win_emu, bool log = true);
|
||||
|
||||
void load_emulator_snapshot(windows_emulator& win_emu, std::span<const std::byte> snapshot);
|
||||
void load_emulator_snapshot(windows_emulator& win_emu, const std::filesystem::path& snapshot_file);
|
||||
}
|
||||
@@ -5,12 +5,13 @@
|
||||
|
||||
namespace utils
|
||||
{
|
||||
template <typename T, typename S = const uint8_t>
|
||||
requires(std::is_trivially_copyable_v<T> && std::is_same_v<uint8_t, std::remove_cv_t<S>>)
|
||||
template <typename Type, typename SpanElement = const std::byte>
|
||||
requires(std::is_trivially_copyable_v<Type> && (std::is_same_v<uint8_t, std::remove_cv_t<SpanElement>> ||
|
||||
std::is_same_v<std::byte, std::remove_cv_t<SpanElement>>))
|
||||
class safe_object_accessor
|
||||
{
|
||||
public:
|
||||
safe_object_accessor(const std::span<S> buffer, const size_t offset)
|
||||
safe_object_accessor(const std::span<SpanElement> buffer, const size_t offset)
|
||||
: buffer_(buffer),
|
||||
offset_(offset)
|
||||
{
|
||||
@@ -21,25 +22,25 @@ namespace utils
|
||||
* are respected
|
||||
****************************************************************************/
|
||||
|
||||
T get(const size_t element_index = 0) const
|
||||
Type get(const size_t element_index = 0) const
|
||||
{
|
||||
T value{};
|
||||
Type value{};
|
||||
memcpy(&value, get_valid_pointer(element_index), size);
|
||||
return value;
|
||||
}
|
||||
|
||||
void set(const T value, const size_t element_index = 0) const
|
||||
void set(const Type value, const size_t element_index = 0) const
|
||||
{
|
||||
memcpy(get_valid_pointer(element_index), &value, size);
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr auto size = sizeof(T);
|
||||
static constexpr auto size = sizeof(Type);
|
||||
|
||||
std::span<S> buffer_{};
|
||||
std::span<SpanElement> buffer_{};
|
||||
size_t offset_{};
|
||||
|
||||
S* get_valid_pointer(const size_t element_index) const
|
||||
SpanElement* get_valid_pointer(const size_t element_index) const
|
||||
{
|
||||
const auto start_offset = offset_ + (size * element_index);
|
||||
const auto end_offset = start_offset + size;
|
||||
@@ -52,29 +53,31 @@ namespace utils
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
requires(std::is_same_v<uint8_t, std::remove_cv_t<T>>)
|
||||
template <typename SpanElement>
|
||||
requires(std::is_same_v<uint8_t, std::remove_cv_t<SpanElement>> ||
|
||||
std::is_same_v<std::byte, std::remove_cv_t<SpanElement>>)
|
||||
class safe_buffer_accessor
|
||||
{
|
||||
public:
|
||||
safe_buffer_accessor(const std::span<T> buffer)
|
||||
safe_buffer_accessor(const std::span<SpanElement> buffer)
|
||||
: buffer_(buffer)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
safe_buffer_accessor(const safe_buffer_accessor<S>& obj)
|
||||
template <typename OtherSpanElement>
|
||||
requires(std::is_same_v<std::remove_cv_t<SpanElement>, std::remove_cv_t<OtherSpanElement>>)
|
||||
safe_buffer_accessor(const safe_buffer_accessor<OtherSpanElement>& obj)
|
||||
: buffer_(obj.get_buffer())
|
||||
{
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
safe_object_accessor<S, T> as(const size_t offset) const
|
||||
template <typename Type>
|
||||
safe_object_accessor<Type, SpanElement> as(const size_t offset) const
|
||||
{
|
||||
return {this->buffer_, offset};
|
||||
}
|
||||
|
||||
T* get_pointer_for_range(const size_t offset, const size_t size) const
|
||||
SpanElement* get_pointer_for_range(const size_t offset, const size_t size) const
|
||||
{
|
||||
this->validate(offset, size);
|
||||
return this->buffer_.data() + offset;
|
||||
@@ -89,11 +92,11 @@ namespace utils
|
||||
}
|
||||
}
|
||||
|
||||
template <typename S = char>
|
||||
std::basic_string<S> as_string(const size_t offset) const
|
||||
template <typename Char = char>
|
||||
std::basic_string<Char> as_string(const size_t offset) const
|
||||
{
|
||||
safe_object_accessor<S> string_accessor{this->buffer_, offset};
|
||||
std::basic_string<S> result{};
|
||||
safe_object_accessor<Char> string_accessor{this->buffer_, offset};
|
||||
std::basic_string<Char> result{};
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -107,12 +110,12 @@ namespace utils
|
||||
}
|
||||
}
|
||||
|
||||
std::span<T> get_buffer() const
|
||||
std::span<SpanElement> get_buffer() const
|
||||
{
|
||||
return this->buffer_;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::span<T> buffer_{};
|
||||
const std::span<SpanElement> buffer_{};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <zlib.h>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
namespace utils::compression
|
||||
@@ -48,16 +49,16 @@ namespace utils::compression
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> decompress(const std::span<const std::uint8_t> data)
|
||||
std::vector<std::byte> decompress(const std::span<const std::byte> data)
|
||||
{
|
||||
std::vector<std::uint8_t> buffer{};
|
||||
std::vector<std::byte> buffer{};
|
||||
zlib_stream stream_container{};
|
||||
if (!stream_container.is_valid())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
static thread_local std::array<uint8_t, ZCHUNK_SIZE> dest{};
|
||||
static thread_local std::array<std::byte, ZCHUNK_SIZE> dest{};
|
||||
auto& stream = stream_container.get();
|
||||
|
||||
stream.avail_in = static_cast<uInt>(data.size());
|
||||
@@ -66,7 +67,7 @@ namespace utils::compression
|
||||
do
|
||||
{
|
||||
stream.avail_out = static_cast<uInt>(dest.size());
|
||||
stream.next_out = dest.data();
|
||||
stream.next_out = reinterpret_cast<uint8_t*>(dest.data());
|
||||
|
||||
const auto ret = inflate(&stream, Z_FINISH);
|
||||
if (ret != Z_OK && ret != Z_BUF_ERROR && ret != Z_STREAM_END)
|
||||
@@ -80,14 +81,9 @@ namespace utils::compression
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> decompress(const std::span<const std::byte>& data)
|
||||
std::vector<std::byte> compress(const std::span<const std::byte> data)
|
||||
{
|
||||
return decompress(std::span(reinterpret_cast<const uint8_t*>(data.data()), data.size()));
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> compress(const std::span<const std::uint8_t> data)
|
||||
{
|
||||
std::vector<std::uint8_t> result{};
|
||||
std::vector<std::byte> result{};
|
||||
auto length = compressBound(static_cast<uLong>(data.size()));
|
||||
result.resize(length);
|
||||
|
||||
@@ -100,10 +96,5 @@ namespace utils::compression
|
||||
result.resize(length);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> compress(const std::span<const std::byte> data)
|
||||
{
|
||||
return compress(std::span(reinterpret_cast<const uint8_t*>(data.data()), data.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,13 @@
|
||||
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace utils::compression
|
||||
{
|
||||
namespace zlib
|
||||
{
|
||||
constexpr unsigned int ZCHUNK_SIZE = 16384u;
|
||||
std::vector<std::uint8_t> compress(std::span<const std::uint8_t> data);
|
||||
std::vector<std::uint8_t> compress(std::span<const std::byte> data);
|
||||
std::vector<std::uint8_t> decompress(std::span<const std::uint8_t> data);
|
||||
std::vector<std::uint8_t> decompress(std::span<const std::byte> data);
|
||||
std::vector<std::byte> compress(std::span<const std::byte> data);
|
||||
std::vector<std::byte> decompress(std::span<const std::byte> data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace utils::io
|
||||
return std::ifstream(file).good();
|
||||
}
|
||||
|
||||
bool write_file(const std::filesystem::path& file, const std::span<const uint8_t> data, const bool append)
|
||||
bool write_file(const std::filesystem::path& file, const std::span<const std::byte> data, const bool append)
|
||||
{
|
||||
if (file.has_parent_path())
|
||||
{
|
||||
@@ -41,34 +41,42 @@ namespace utils::io
|
||||
return false;
|
||||
}
|
||||
|
||||
bool write_file(const std::filesystem::path& file, const std::span<const std::byte> data, const bool append)
|
||||
std::vector<std::byte> read_file(const std::filesystem::path& file)
|
||||
{
|
||||
return write_file(file, std::span(reinterpret_cast<const uint8_t*>(data.data()), data.size()), append);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> read_file(const std::filesystem::path& file)
|
||||
{
|
||||
std::vector<uint8_t> data;
|
||||
std::vector<std::byte> data{};
|
||||
read_file(file, &data);
|
||||
return data;
|
||||
}
|
||||
|
||||
bool read_file(const std::filesystem::path& file, std::vector<uint8_t>* data)
|
||||
bool read_file(const std::filesystem::path& file, std::vector<std::byte>* data)
|
||||
{
|
||||
if (!data)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
data->clear();
|
||||
*data = {};
|
||||
|
||||
std::ifstream stream(file, std::ios::binary);
|
||||
if (!stream)
|
||||
std::ifstream file_stream(file, std::ios::binary);
|
||||
if (!file_stream)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*data = std::vector<uint8_t>{(std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>()};
|
||||
std::vector<char> temp_buffer(0x1000);
|
||||
|
||||
while (file_stream)
|
||||
{
|
||||
file_stream.read(temp_buffer.data(), static_cast<std::streamsize>(temp_buffer.size()));
|
||||
const auto bytes_read = file_stream.gcount();
|
||||
|
||||
if (bytes_read > 0)
|
||||
{
|
||||
const auto* buffer = reinterpret_cast<const std::byte*>(temp_buffer.data());
|
||||
data->insert(data->end(), buffer, buffer + bytes_read);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,9 @@ namespace utils::io
|
||||
bool remove_file(const std::filesystem::path& file);
|
||||
bool move_file(const std::filesystem::path& src, const std::filesystem::path& target);
|
||||
bool file_exists(const std::filesystem::path& file);
|
||||
bool write_file(const std::filesystem::path& file, std::span<const uint8_t> data, bool append = false);
|
||||
bool write_file(const std::filesystem::path& file, std::span<const std::byte> data, bool append = false);
|
||||
bool read_file(const std::filesystem::path& file, std::vector<uint8_t>* data);
|
||||
std::vector<uint8_t> read_file(const std::filesystem::path& file);
|
||||
bool read_file(const std::filesystem::path& file, std::vector<std::byte>* data);
|
||||
std::vector<std::byte> read_file(const std::filesystem::path& file);
|
||||
size_t file_size(const std::filesystem::path& file);
|
||||
bool create_directory(const std::filesystem::path& directory);
|
||||
bool directory_exists(const std::filesystem::path& directory);
|
||||
|
||||
@@ -72,23 +72,20 @@ namespace utils
|
||||
|
||||
void write(const void* buffer, const size_t length)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
const uint64_t old_size = this->buffer_.size();
|
||||
#endif
|
||||
const auto old_size_remainder = static_cast<uint8_t>(length);
|
||||
constexpr auto check_size = sizeof(old_size_remainder);
|
||||
|
||||
if (this->break_offset_ && this->buffer_.size() <= *this->break_offset_ &&
|
||||
this->buffer_.size() + length > *this->break_offset_)
|
||||
this->buffer_.size() + length + check_size > *this->break_offset_)
|
||||
{
|
||||
throw std::runtime_error("Break offset reached!");
|
||||
}
|
||||
|
||||
const auto* security_buffer = reinterpret_cast<const std::byte*>(&old_size_remainder);
|
||||
this->buffer_.insert(this->buffer_.end(), security_buffer, security_buffer + check_size);
|
||||
|
||||
const auto* byte_buffer = static_cast<const std::byte*>(buffer);
|
||||
this->buffer_.insert(this->buffer_.end(), byte_buffer, byte_buffer + length);
|
||||
|
||||
#ifndef NDEBUG
|
||||
const auto* security_buffer = reinterpret_cast<const std::byte*>(&old_size);
|
||||
this->buffer_.insert(this->buffer_.end(), security_buffer, security_buffer + sizeof(old_size));
|
||||
#endif
|
||||
}
|
||||
|
||||
void write(const buffer_serializer& object)
|
||||
@@ -256,60 +253,43 @@ namespace utils
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
buffer_deserializer(const std::span<T> buffer, const bool no_debugging = false)
|
||||
: no_debugging_(no_debugging),
|
||||
buffer_(reinterpret_cast<const std::byte*>(buffer.data()), buffer.size() * sizeof(T))
|
||||
buffer_deserializer(const std::span<T> buffer)
|
||||
: buffer_(reinterpret_cast<const std::byte*>(buffer.data()), buffer.size() * sizeof(T))
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Type must be trivially copyable");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
buffer_deserializer(const std::vector<T>& buffer, const bool no_debugging = false)
|
||||
: buffer_deserializer(std::span(buffer), no_debugging)
|
||||
buffer_deserializer(const std::vector<T>& buffer)
|
||||
: buffer_deserializer(std::span(buffer))
|
||||
{
|
||||
}
|
||||
|
||||
buffer_deserializer(const buffer_serializer& serializer, const bool no_debugging = false)
|
||||
: buffer_deserializer(serializer.get_buffer(), no_debugging)
|
||||
buffer_deserializer(const buffer_serializer& serializer)
|
||||
: buffer_deserializer(serializer.get_buffer())
|
||||
{
|
||||
}
|
||||
|
||||
std::span<const std::byte> read_data(const size_t length)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
const uint64_t real_old_size = this->offset_;
|
||||
(void)real_old_size;
|
||||
#endif
|
||||
const auto length_rest = static_cast<uint8_t>(length);
|
||||
constexpr auto check_size = sizeof(length_rest);
|
||||
|
||||
if (this->offset_ + length > this->buffer_.size())
|
||||
if (this->offset_ + (length + check_size) > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
|
||||
if (static_cast<uint8_t>(this->buffer_[this->offset_]) != length_rest)
|
||||
{
|
||||
throw std::runtime_error("Reading from serialized buffer mismatches written data!");
|
||||
}
|
||||
|
||||
this->offset_ += check_size;
|
||||
|
||||
const std::span result(this->buffer_.data() + this->offset_, length);
|
||||
this->offset_ += length;
|
||||
|
||||
(void)this->no_debugging_;
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (!this->no_debugging_)
|
||||
{
|
||||
uint64_t old_size{};
|
||||
if (this->offset_ + sizeof(old_size) > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
|
||||
memcpy(&old_size, this->buffer_.data() + this->offset_, sizeof(old_size));
|
||||
if (old_size != real_old_size)
|
||||
{
|
||||
throw std::runtime_error("Reading from serialized buffer mismatches written data!");
|
||||
}
|
||||
|
||||
this->offset_ += sizeof(old_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -506,7 +486,6 @@ namespace utils
|
||||
}
|
||||
|
||||
private:
|
||||
bool no_debugging_{false};
|
||||
size_t offset_{0};
|
||||
std::span<const std::byte> buffer_{};
|
||||
std::unordered_map<std::type_index, std::function<void*()>> factories_{};
|
||||
|
||||
@@ -35,8 +35,8 @@ int main()
|
||||
// print_apiset(apiSetMap);
|
||||
|
||||
// Compress the API-SET binary blob
|
||||
const auto* data_ptr = reinterpret_cast<const uint8_t*>(api_set_map);
|
||||
const std::vector<uint8_t> buffer(data_ptr, data_ptr + api_set_map->Size);
|
||||
const auto* data_ptr = reinterpret_cast<const std::byte*>(api_set_map);
|
||||
const std::span buffer(data_ptr, data_ptr + api_set_map->Size);
|
||||
const auto compressed = utils::compression::zlib::compress(buffer);
|
||||
if (compressed.empty())
|
||||
{
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace apiset
|
||||
return static_cast<ULONG>(address - result_base);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> decompress_apiset(const std::vector<uint8_t>& apiset)
|
||||
std::vector<std::byte> decompress_apiset(const std::vector<std::byte>& apiset)
|
||||
{
|
||||
auto buffer = utils::compression::zlib::decompress(apiset);
|
||||
if (buffer.empty())
|
||||
@@ -53,7 +53,7 @@ namespace apiset
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> obtain_data(const location location, const std::filesystem::path& root)
|
||||
std::vector<std::byte> obtain_data(const location location, const std::filesystem::path& root)
|
||||
{
|
||||
switch (location)
|
||||
{
|
||||
@@ -61,7 +61,7 @@ namespace apiset
|
||||
case location::host: {
|
||||
const auto apiSetMap =
|
||||
reinterpret_cast<const API_SET_NAMESPACE*>(NtCurrentTeb64()->ProcessEnvironmentBlock->ApiSetMap);
|
||||
const auto* dataPtr = reinterpret_cast<const uint8_t*>(apiSetMap);
|
||||
const auto* dataPtr = reinterpret_cast<const std::byte*>(apiSetMap);
|
||||
return {dataPtr, dataPtr + apiSetMap->Size};
|
||||
}
|
||||
#else
|
||||
@@ -78,11 +78,13 @@ namespace apiset
|
||||
return decompress_apiset(apiset);
|
||||
}
|
||||
case location::default_windows_10: {
|
||||
const std::vector<uint8_t> apiset{apiset_w10, apiset_w10 + sizeof(apiset_w10)};
|
||||
const auto* byte_apiset = reinterpret_cast<const std::byte*>(apiset_w10);
|
||||
const std::vector<std::byte> apiset{byte_apiset, byte_apiset + sizeof(apiset_w10)};
|
||||
return decompress_apiset(apiset);
|
||||
}
|
||||
case location::default_windows_11: {
|
||||
const std::vector<uint8_t> apiset{apiset_w11, apiset_w11 + sizeof(apiset_w11)};
|
||||
const auto* byte_apiset = reinterpret_cast<const std::byte*>(apiset_w11);
|
||||
const std::vector<std::byte> apiset{byte_apiset, byte_apiset + sizeof(apiset_w11)};
|
||||
return decompress_apiset(apiset);
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace apiset
|
||||
|
||||
struct container
|
||||
{
|
||||
std::vector<uint8_t> data{};
|
||||
std::vector<std::byte> data{};
|
||||
|
||||
const API_SET_NAMESPACE& get() const
|
||||
{
|
||||
|
||||
@@ -20,16 +20,16 @@ namespace
|
||||
return nt_headers_offset + (first_section_absolute - absolute_base);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> read_mapped_memory(const memory_manager& memory, const mapped_module& binary)
|
||||
std::vector<std::byte> read_mapped_memory(const memory_manager& memory, const mapped_module& binary)
|
||||
{
|
||||
std::vector<uint8_t> mem{};
|
||||
std::vector<std::byte> mem{};
|
||||
mem.resize(binary.size_of_image);
|
||||
memory.read_memory(binary.image_base, mem.data(), mem.size());
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
void collect_exports(mapped_module& binary, const utils::safe_buffer_accessor<const uint8_t> buffer,
|
||||
void collect_exports(mapped_module& binary, const utils::safe_buffer_accessor<const std::byte> buffer,
|
||||
const PEOptionalHeader_t<std::uint64_t>& optional_header)
|
||||
{
|
||||
const auto& export_directory_entry = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
@@ -70,7 +70,7 @@ namespace
|
||||
|
||||
template <typename T>
|
||||
requires(std::is_integral_v<T>)
|
||||
void apply_relocation(const utils::safe_buffer_accessor<uint8_t> buffer, const uint64_t offset,
|
||||
void apply_relocation(const utils::safe_buffer_accessor<std::byte> buffer, const uint64_t offset,
|
||||
const uint64_t delta)
|
||||
{
|
||||
const auto obj = buffer.as<T>(offset);
|
||||
@@ -79,7 +79,7 @@ namespace
|
||||
obj.set(new_value);
|
||||
}
|
||||
|
||||
void apply_relocations(const mapped_module& binary, const utils::safe_buffer_accessor<uint8_t> buffer,
|
||||
void apply_relocations(const mapped_module& binary, const utils::safe_buffer_accessor<std::byte> buffer,
|
||||
const PEOptionalHeader_t<std::uint64_t>& optional_header)
|
||||
{
|
||||
const auto delta = binary.image_base - optional_header.ImageBase;
|
||||
@@ -142,7 +142,7 @@ namespace
|
||||
}
|
||||
|
||||
void map_sections(memory_manager& memory, mapped_module& binary,
|
||||
const utils::safe_buffer_accessor<const uint8_t> buffer,
|
||||
const utils::safe_buffer_accessor<const std::byte> buffer,
|
||||
const PENTHeaders_t<std::uint64_t>& nt_headers, const uint64_t nt_headers_offset)
|
||||
{
|
||||
const auto first_section_offset = get_first_section_offset(nt_headers, nt_headers_offset);
|
||||
@@ -196,7 +196,7 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
mapped_module map_module_from_data(memory_manager& memory, const std::span<const uint8_t> data,
|
||||
mapped_module map_module_from_data(memory_manager& memory, const std::span<const std::byte> data,
|
||||
std::filesystem::path file)
|
||||
{
|
||||
mapped_module binary{};
|
||||
@@ -241,7 +241,7 @@ mapped_module map_module_from_data(memory_manager& memory, const std::span<const
|
||||
map_sections(memory, binary, buffer, nt_headers, nt_headers_offset);
|
||||
|
||||
auto mapped_memory = read_mapped_memory(memory, binary);
|
||||
utils::safe_buffer_accessor<uint8_t> mapped_buffer{mapped_memory};
|
||||
utils::safe_buffer_accessor<std::byte> mapped_buffer{mapped_memory};
|
||||
|
||||
apply_relocations(binary, mapped_buffer, optional_header);
|
||||
collect_exports(binary, mapped_buffer, optional_header);
|
||||
|
||||
@@ -791,7 +791,7 @@ namespace
|
||||
}
|
||||
|
||||
uint64_t size = section_entry->maximum_size;
|
||||
std::vector<uint8_t> file_data{};
|
||||
std::vector<std::byte> file_data{};
|
||||
|
||||
if (!section_entry->file_name.empty())
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user