mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-11 16:46:16 +00:00
Format all the code
This commit is contained in:
@@ -7,254 +7,247 @@
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
struct analysis_options
|
struct analysis_options
|
||||||
{
|
{
|
||||||
bool use_gdb{false};
|
bool use_gdb{false};
|
||||||
bool concise_logging{false};
|
bool concise_logging{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
void watch_system_objects(windows_emulator& win_emu, const bool cache_logging)
|
void watch_system_objects(windows_emulator& win_emu, const bool cache_logging)
|
||||||
{
|
{
|
||||||
(void)win_emu;
|
(void)win_emu;
|
||||||
(void)cache_logging;
|
(void)cache_logging;
|
||||||
|
|
||||||
#ifdef OS_WINDOWS
|
#ifdef OS_WINDOWS
|
||||||
watch_object(win_emu, *win_emu.current_thread().teb, cache_logging);
|
watch_object(win_emu, *win_emu.current_thread().teb, cache_logging);
|
||||||
watch_object(win_emu, win_emu.process().peb, cache_logging);
|
watch_object(win_emu, win_emu.process().peb, cache_logging);
|
||||||
watch_object(win_emu, emulator_object<KUSER_SHARED_DATA64>{win_emu.emu(), kusd_mmio::address()}, cache_logging);
|
watch_object(win_emu, emulator_object<KUSER_SHARED_DATA64>{win_emu.emu(), kusd_mmio::address()}, cache_logging);
|
||||||
|
|
||||||
auto* params_hook = watch_object(win_emu, win_emu.process().process_params, cache_logging);
|
auto* params_hook = watch_object(win_emu, win_emu.process().process_params, cache_logging);
|
||||||
|
|
||||||
win_emu.emu().hook_memory_write(win_emu.process().peb.value() + offsetof(PEB64, ProcessParameters), 0x8,
|
win_emu.emu().hook_memory_write(
|
||||||
[&, cache_logging](const uint64_t address, size_t, const uint64_t value)
|
win_emu.process().peb.value() + offsetof(PEB64, ProcessParameters), 0x8,
|
||||||
{
|
[&, cache_logging](const uint64_t address, size_t, const uint64_t value) {
|
||||||
const auto target_address = win_emu.process().peb.value() + offsetof(
|
const auto target_address = win_emu.process().peb.value() + offsetof(PEB64, ProcessParameters);
|
||||||
PEB64, ProcessParameters);
|
|
||||||
|
|
||||||
if (address == target_address)
|
if (address == target_address)
|
||||||
{
|
{
|
||||||
const emulator_object<RTL_USER_PROCESS_PARAMETERS64> obj{
|
const emulator_object<RTL_USER_PROCESS_PARAMETERS64> obj{win_emu.emu(), value};
|
||||||
win_emu.emu(), value
|
|
||||||
};
|
|
||||||
|
|
||||||
win_emu.emu().delete_hook(params_hook);
|
win_emu.emu().delete_hook(params_hook);
|
||||||
params_hook = watch_object(win_emu, obj, cache_logging);
|
params_hook = watch_object(win_emu, obj, cache_logging);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void run_emulation(windows_emulator& win_emu, const analysis_options& options)
|
void run_emulation(windows_emulator& win_emu, const analysis_options& options)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (options.use_gdb)
|
if (options.use_gdb)
|
||||||
{
|
{
|
||||||
const auto* address = "127.0.0.1:28960";
|
const auto* address = "127.0.0.1:28960";
|
||||||
win_emu.log.print(color::pink, "Waiting for GDB connection on %s...\n", address);
|
win_emu.log.print(color::pink, "Waiting for GDB connection on %s...\n", address);
|
||||||
|
|
||||||
win_x64_gdb_stub_handler handler{win_emu};
|
win_x64_gdb_stub_handler handler{win_emu};
|
||||||
run_gdb_stub(handler, "i386:x86-64", gdb_registers.size(), address);
|
run_gdb_stub(handler, "i386:x86-64", gdb_registers.size(), address);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
win_emu.start();
|
win_emu.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 " - %s\n",
|
win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 " - %s\n",
|
||||||
win_emu.emu().read_instruction_pointer(), e.what());
|
win_emu.emu().read_instruction_pointer(), e.what());
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 "\n",
|
win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 "\n",
|
||||||
win_emu.emu().read_instruction_pointer());
|
win_emu.emu().read_instruction_pointer());
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto exit_status = win_emu.process().exit_status;
|
const auto exit_status = win_emu.process().exit_status;
|
||||||
if (exit_status.has_value())
|
if (exit_status.has_value())
|
||||||
{
|
{
|
||||||
win_emu.log.print(color::red, "Emulation terminated with status: %X\n", *exit_status);
|
win_emu.log.print(color::red, "Emulation terminated with status: %X\n", *exit_status);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
win_emu.log.print(color::red, "Emulation terminated without status!\n");
|
win_emu.log.print(color::red, "Emulation terminated without status!\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::u16string> parse_arguments(const std::span<const std::string_view> args)
|
std::vector<std::u16string> parse_arguments(const std::span<const std::string_view> args)
|
||||||
{
|
{
|
||||||
std::vector<std::u16string> wide_args{};
|
std::vector<std::u16string> wide_args{};
|
||||||
wide_args.reserve(args.size() - 1);
|
wide_args.reserve(args.size() - 1);
|
||||||
|
|
||||||
for (size_t i = 1; i < args.size(); ++i)
|
for (size_t i = 1; i < args.size(); ++i)
|
||||||
{
|
{
|
||||||
const auto& arg = args[i];
|
const auto& arg = args[i];
|
||||||
wide_args.emplace_back(arg.begin(), arg.end());
|
wide_args.emplace_back(arg.begin(), arg.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
return wide_args;
|
return wide_args;
|
||||||
}
|
}
|
||||||
|
|
||||||
void run(const analysis_options& options, const std::span<const std::string_view> args)
|
void run(const analysis_options& options, const std::span<const std::string_view> args)
|
||||||
{
|
{
|
||||||
if (args.empty())
|
if (args.empty())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
emulator_settings settings{
|
emulator_settings settings{
|
||||||
.application = args[0],
|
.application = args[0],
|
||||||
.arguments = parse_arguments(args),
|
.arguments = parse_arguments(args),
|
||||||
.silent_until_main = options.concise_logging,
|
.silent_until_main = options.concise_logging,
|
||||||
};
|
};
|
||||||
|
|
||||||
windows_emulator win_emu{std::move(settings)};
|
windows_emulator win_emu{std::move(settings)};
|
||||||
|
|
||||||
(void)&watch_system_objects;
|
(void)&watch_system_objects;
|
||||||
watch_system_objects(win_emu, options.concise_logging);
|
watch_system_objects(win_emu, options.concise_logging);
|
||||||
win_emu.buffer_stdout = true;
|
win_emu.buffer_stdout = true;
|
||||||
//win_emu.verbose_calls = true;
|
// win_emu.verbose_calls = true;
|
||||||
|
|
||||||
const auto& exe = *win_emu.process().executable;
|
const auto& exe = *win_emu.process().executable;
|
||||||
|
|
||||||
const auto concise_logging = options.concise_logging;
|
const auto concise_logging = options.concise_logging;
|
||||||
|
|
||||||
for (const auto& section : exe.sections)
|
for (const auto& section : exe.sections)
|
||||||
{
|
{
|
||||||
if ((section.region.permissions & memory_permission::exec) != memory_permission::exec)
|
if ((section.region.permissions & memory_permission::exec) != memory_permission::exec)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto read_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t)
|
auto read_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t) {
|
||||||
{
|
const auto rip = win_emu.emu().read_instruction_pointer();
|
||||||
const auto rip = win_emu.emu().read_instruction_pointer();
|
if (win_emu.process().mod_manager.find_by_address(rip) != win_emu.process().executable)
|
||||||
if (win_emu.process().mod_manager.find_by_address(rip) != win_emu.process().executable)
|
{
|
||||||
{
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (concise_logging)
|
if (concise_logging)
|
||||||
{
|
{
|
||||||
static uint64_t count{0};
|
static uint64_t count{0};
|
||||||
++count;
|
++count;
|
||||||
if (count > 100 && count % 10000 != 0) return;
|
if (count > 100 && count % 10000 != 0)
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
win_emu.log.print(
|
win_emu.log.print(color::green,
|
||||||
color::green,
|
"Reading from executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n",
|
||||||
"Reading from executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n",
|
section.name.c_str(), address, rip);
|
||||||
section.name.c_str(), address, rip);
|
};
|
||||||
};
|
|
||||||
|
|
||||||
const auto write_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t)
|
const auto write_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t) {
|
||||||
{
|
const auto rip = win_emu.emu().read_instruction_pointer();
|
||||||
const auto rip = win_emu.emu().read_instruction_pointer();
|
if (win_emu.process().mod_manager.find_by_address(rip) != win_emu.process().executable)
|
||||||
if (win_emu.process().mod_manager.find_by_address(rip) != win_emu.process().executable)
|
{
|
||||||
{
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (concise_logging)
|
if (concise_logging)
|
||||||
{
|
{
|
||||||
static uint64_t count{0};
|
static uint64_t count{0};
|
||||||
++count;
|
++count;
|
||||||
if (count > 100 && count % 10000 != 0) return;
|
if (count > 100 && count % 10000 != 0)
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
win_emu.log.print(
|
win_emu.log.print(color::blue, "Writing to executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n",
|
||||||
color::blue,
|
section.name.c_str(), address, rip);
|
||||||
"Writing to executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n",
|
};
|
||||||
section.name.c_str(), address, rip);
|
|
||||||
};
|
|
||||||
|
|
||||||
win_emu.emu().hook_memory_read(section.region.start, section.region.length, std::move(read_handler));
|
win_emu.emu().hook_memory_read(section.region.start, section.region.length, std::move(read_handler));
|
||||||
win_emu.emu().hook_memory_write(section.region.start, section.region.length, std::move(write_handler));
|
win_emu.emu().hook_memory_write(section.region.start, section.region.length, std::move(write_handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
run_emulation(win_emu, options);
|
run_emulation(win_emu, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string_view> bundle_arguments(const int argc, char** argv)
|
std::vector<std::string_view> bundle_arguments(const int argc, char** argv)
|
||||||
{
|
{
|
||||||
std::vector<std::string_view> args{};
|
std::vector<std::string_view> args{};
|
||||||
|
|
||||||
for (int i = 1; i < argc; ++i)
|
for (int i = 1; i < argc; ++i)
|
||||||
{
|
{
|
||||||
args.push_back(argv[i]);
|
args.push_back(argv[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
analysis_options parse_options(std::vector<std::string_view>& args)
|
analysis_options parse_options(std::vector<std::string_view>& args)
|
||||||
{
|
{
|
||||||
analysis_options options{};
|
analysis_options options{};
|
||||||
|
|
||||||
while (!args.empty())
|
while (!args.empty())
|
||||||
{
|
{
|
||||||
auto arg_it = args.begin();
|
auto arg_it = args.begin();
|
||||||
const auto& arg = *arg_it;
|
const auto& arg = *arg_it;
|
||||||
|
|
||||||
if (arg == "-d")
|
if (arg == "-d")
|
||||||
{
|
{
|
||||||
options.use_gdb = true;
|
options.use_gdb = true;
|
||||||
}
|
}
|
||||||
else if (arg == "-c")
|
else if (arg == "-c")
|
||||||
{
|
{
|
||||||
options.concise_logging = true;
|
options.concise_logging = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
args.erase(arg_it);
|
args.erase(arg_it);
|
||||||
}
|
}
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(const int argc, char** argv)
|
int main(const int argc, char** argv)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto args = bundle_arguments(argc, argv);
|
auto args = bundle_arguments(argc, argv);
|
||||||
const auto options = parse_options(args);
|
const auto options = parse_options(args);
|
||||||
|
|
||||||
if (args.empty())
|
if (args.empty())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Application not specified!");
|
throw std::runtime_error("Application not specified!");
|
||||||
}
|
}
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
run(options, args);
|
run(options, args);
|
||||||
}
|
} while (options.use_gdb);
|
||||||
while (options.use_gdb);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
{
|
{
|
||||||
puts(e.what());
|
puts(e.what());
|
||||||
|
|
||||||
#if defined(_WIN32) && 0
|
#if defined(_WIN32) && 0
|
||||||
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
|
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int)
|
int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int)
|
||||||
{
|
{
|
||||||
return main(__argc, __argv);
|
return main(__argc, __argv);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,36 +5,32 @@
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
emulator_hook* watch_object(windows_emulator& emu, emulator_object<T> object, const bool cache_logging = false)
|
emulator_hook* watch_object(windows_emulator& emu, emulator_object<T> object, const bool cache_logging = false)
|
||||||
{
|
{
|
||||||
const reflect_type_info<T> info{};
|
const reflect_type_info<T> info{};
|
||||||
|
|
||||||
return emu.emu().hook_memory_read(object.value(), object.size(),
|
return emu.emu().hook_memory_read(
|
||||||
[i = std::move(info), object, &emu, cache_logging](
|
object.value(), object.size(),
|
||||||
const uint64_t address, size_t, uint64_t)
|
[i = std::move(info), object, &emu, cache_logging](const uint64_t address, size_t, uint64_t) {
|
||||||
{
|
const auto rip = emu.emu().read_instruction_pointer();
|
||||||
const auto rip = emu.emu().read_instruction_pointer();
|
const auto* mod = emu.process().mod_manager.find_by_address(rip);
|
||||||
const auto* mod = emu.process().mod_manager.find_by_address(rip);
|
const auto is_main_access = mod == emu.process().executable;
|
||||||
const auto is_main_access = mod == emu.process().executable;
|
|
||||||
|
|
||||||
if (!emu.verbose_calls && !is_main_access)
|
if (!emu.verbose_calls && !is_main_access)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cache_logging)
|
if (cache_logging)
|
||||||
{
|
{
|
||||||
static std::unordered_set<uint64_t> logged_addresses{};
|
static std::unordered_set<uint64_t> logged_addresses{};
|
||||||
if (is_main_access && !logged_addresses.insert(address).second)
|
if (is_main_access && !logged_addresses.insert(address).second)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto offset = address - object.value();
|
const auto offset = address - object.value();
|
||||||
emu.log.print(is_main_access ? color::green : color::dark_gray,
|
emu.log.print(is_main_access ? color::green : color::dark_gray,
|
||||||
"Object access: %s - 0x%llX (%s) at 0x%llX (%s)\n",
|
"Object access: %s - 0x%llX (%s) at 0x%llX (%s)\n", i.get_type_name().c_str(), offset,
|
||||||
i.get_type_name().c_str(),
|
i.get_member_name(offset).c_str(), rip, mod ? mod->name.c_str() : "<N/A>");
|
||||||
offset,
|
});
|
||||||
i.get_member_name(offset).c_str(), rip,
|
|
||||||
mod ? mod->name.c_str() : "<N/A>");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -15,51 +15,50 @@
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
class reflect_type_info
|
class reflect_type_info
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
reflect_type_info()
|
reflect_type_info()
|
||||||
{
|
{
|
||||||
this->type_name_ = reflect::type_name<T>();
|
this->type_name_ = reflect::type_name<T>();
|
||||||
|
|
||||||
reflect::for_each<T>([this](auto I)
|
reflect::for_each<T>([this](auto I) {
|
||||||
{
|
const auto member_name = reflect::member_name<I, T>();
|
||||||
const auto member_name = reflect::member_name<I, T>();
|
const auto member_offset = reflect::offset_of<I, T>();
|
||||||
const auto member_offset = reflect::offset_of<I, T>();
|
|
||||||
|
|
||||||
this->members_[member_offset] = member_name;
|
this->members_[member_offset] = member_name;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_member_name(const size_t offset) const
|
std::string get_member_name(const size_t offset) const
|
||||||
{
|
{
|
||||||
size_t last_offset{};
|
size_t last_offset{};
|
||||||
std::string_view last_member{};
|
std::string_view last_member{};
|
||||||
|
|
||||||
for (const auto& member : this->members_)
|
for (const auto& member : this->members_)
|
||||||
{
|
{
|
||||||
if (offset == member.first)
|
if (offset == member.first)
|
||||||
{
|
{
|
||||||
return member.second;
|
return member.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offset < member.first)
|
if (offset < member.first)
|
||||||
{
|
{
|
||||||
const auto diff = offset - last_offset;
|
const auto diff = offset - last_offset;
|
||||||
return std::string(last_member) + "+" + std::to_string(diff);
|
return std::string(last_member) + "+" + std::to_string(diff);
|
||||||
}
|
}
|
||||||
|
|
||||||
last_offset = member.first;
|
last_offset = member.first;
|
||||||
last_member = member.second;
|
last_member = member.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "<N/A>";
|
return "<N/A>";
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& get_type_name() const
|
const std::string& get_type_name() const
|
||||||
{
|
{
|
||||||
return this->type_name_;
|
return this->type_name_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string type_name_{};
|
std::string type_name_{};
|
||||||
std::map<size_t, std::string> members_{};
|
std::map<size_t, std::string> members_{};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,61 +5,59 @@
|
|||||||
|
|
||||||
#define THE_SIZE 30
|
#define THE_SIZE 30
|
||||||
|
|
||||||
extern "C" NO_INLINE EXPORT_SYMBOL
|
extern "C" NO_INLINE EXPORT_SYMBOL void vulnerable(const uint8_t* data, const size_t size)
|
||||||
void vulnerable(const uint8_t* data, const size_t size)
|
|
||||||
{
|
{
|
||||||
if (size < 10)
|
if (size < 10)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data[9] != 'A')
|
if (data[9] != 'A')
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data[8] != 'B')
|
if (data[8] != 'B')
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data[7] != 'C')
|
if (data[7] != 'C')
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data[2] != 'V')
|
if (data[2] != 'V')
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data[4] != 'H')
|
if (data[4] != 'H')
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size < 100)
|
if (size < 100)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
*(int*)1 = 1;
|
*(int*)1 = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t buffer[THE_SIZE] = {};
|
uint8_t buffer[THE_SIZE] = {};
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, const char* argv[])
|
int main(int argc, const char* argv[])
|
||||||
{
|
{
|
||||||
const void* input = buffer;
|
const void* input = buffer;
|
||||||
auto size = sizeof(buffer);
|
auto size = sizeof(buffer);
|
||||||
|
|
||||||
if (argc > 1)
|
if (argc > 1)
|
||||||
{
|
{
|
||||||
input = argv[1];
|
input = argv[1];
|
||||||
size = strlen(argv[1]);
|
size = strlen(argv[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
vulnerable((uint8_t*)input, size);
|
vulnerable((uint8_t*)input, size);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,376 +8,365 @@ using namespace std::literals;
|
|||||||
|
|
||||||
namespace network
|
namespace network
|
||||||
{
|
{
|
||||||
void initialize_wsa()
|
void initialize_wsa()
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static struct wsa_initializer
|
static struct wsa_initializer
|
||||||
{
|
{
|
||||||
public:
|
wsa_initializer()
|
||||||
wsa_initializer()
|
{
|
||||||
{
|
WSADATA wsa_data;
|
||||||
WSADATA wsa_data;
|
if (WSAStartup(MAKEWORD(2, 2), &wsa_data))
|
||||||
if (WSAStartup(MAKEWORD(2, 2), &wsa_data))
|
{
|
||||||
{
|
throw std::runtime_error("Unable to initialize WSA");
|
||||||
throw std::runtime_error("Unable to initialize WSA");
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
~wsa_initializer()
|
~wsa_initializer()
|
||||||
{
|
{
|
||||||
WSACleanup();
|
WSACleanup();
|
||||||
}
|
}
|
||||||
} _;
|
} _;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
address::address()
|
address::address()
|
||||||
{
|
{
|
||||||
initialize_wsa();
|
initialize_wsa();
|
||||||
ZeroMemory(&this->storage_, this->get_max_size());
|
ZeroMemory(&this->storage_, this->get_max_size());
|
||||||
|
|
||||||
this->address_.sa_family = AF_UNSPEC;
|
this->address_.sa_family = AF_UNSPEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
address::address(const std::string& addr, const std::optional<int>& family)
|
address::address(const std::string& addr, const std::optional<int>& family)
|
||||||
: address()
|
: address()
|
||||||
{
|
{
|
||||||
this->parse(addr, family);
|
this->parse(addr, family);
|
||||||
}
|
}
|
||||||
|
|
||||||
address::address(const sockaddr_in6& addr)
|
address::address(const sockaddr_in6& addr)
|
||||||
: address()
|
: address()
|
||||||
{
|
{
|
||||||
this->address6_ = addr;
|
this->address6_ = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
address::address(const sockaddr_in& addr)
|
address::address(const sockaddr_in& addr)
|
||||||
: address()
|
: address()
|
||||||
{
|
{
|
||||||
this->address4_ = addr;
|
this->address4_ = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
address::address(const sockaddr* addr, const socklen_t length)
|
address::address(const sockaddr* addr, const socklen_t length)
|
||||||
: address()
|
: address()
|
||||||
{
|
{
|
||||||
this->set_address(addr, length);
|
this->set_address(addr, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void address::set_ipv4(const uint32_t ip)
|
void address::set_ipv4(const uint32_t ip)
|
||||||
{
|
{
|
||||||
in_addr addr{};
|
in_addr addr{};
|
||||||
addr.s_addr = ip;
|
addr.s_addr = ip;
|
||||||
this->set_ipv4(addr);
|
this->set_ipv4(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool address::operator==(const address& obj) const
|
bool address::operator==(const address& obj) const
|
||||||
{
|
{
|
||||||
if (this->address_.sa_family != obj.address_.sa_family)
|
if (this->address_.sa_family != obj.address_.sa_family)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->get_port() != obj.get_port())
|
if (this->get_port() != obj.get_port())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->address_.sa_family == AF_INET)
|
if (this->address_.sa_family == AF_INET)
|
||||||
{
|
{
|
||||||
return this->address4_.sin_addr.s_addr == obj.address4_.sin_addr.s_addr;
|
return this->address4_.sin_addr.s_addr == obj.address4_.sin_addr.s_addr;
|
||||||
}
|
}
|
||||||
else if (this->address_.sa_family == AF_INET6)
|
else if (this->address_.sa_family == AF_INET6)
|
||||||
{
|
{
|
||||||
return !memcmp(this->address6_.sin6_addr.s6_addr, obj.address6_.sin6_addr.s6_addr,
|
return !memcmp(this->address6_.sin6_addr.s6_addr, obj.address6_.sin6_addr.s6_addr,
|
||||||
sizeof(obj.address6_.sin6_addr.s6_addr));
|
sizeof(obj.address6_.sin6_addr.s6_addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void address::set_ipv4(const in_addr& addr)
|
void address::set_ipv4(const in_addr& addr)
|
||||||
{
|
{
|
||||||
ZeroMemory(&this->address4_, sizeof(this->address4_));
|
ZeroMemory(&this->address4_, sizeof(this->address4_));
|
||||||
this->address4_.sin_family = AF_INET;
|
this->address4_.sin_family = AF_INET;
|
||||||
this->address4_.sin_addr = addr;
|
this->address4_.sin_addr = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void address::set_ipv6(const in6_addr& addr)
|
void address::set_ipv6(const in6_addr& addr)
|
||||||
{
|
{
|
||||||
ZeroMemory(&this->address6_, sizeof(this->address6_));
|
ZeroMemory(&this->address6_, sizeof(this->address6_));
|
||||||
this->address6_.sin6_family = AF_INET6;
|
this->address6_.sin6_family = AF_INET6;
|
||||||
this->address6_.sin6_addr = addr;
|
this->address6_.sin6_addr = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void address::set_address(const sockaddr* addr, const socklen_t length)
|
void address::set_address(const sockaddr* addr, const socklen_t length)
|
||||||
{
|
{
|
||||||
if (static_cast<size_t>(length) >= sizeof(sockaddr_in) && addr->sa_family == AF_INET)
|
if (static_cast<size_t>(length) >= sizeof(sockaddr_in) && addr->sa_family == AF_INET)
|
||||||
{
|
{
|
||||||
this->address4_ = *reinterpret_cast<const sockaddr_in*>(addr);
|
this->address4_ = *reinterpret_cast<const sockaddr_in*>(addr);
|
||||||
}
|
}
|
||||||
else if (static_cast<size_t>(length) == sizeof(sockaddr_in6) && addr->sa_family == AF_INET6)
|
else if (static_cast<size_t>(length) == sizeof(sockaddr_in6) && addr->sa_family == AF_INET6)
|
||||||
{
|
{
|
||||||
this->address6_ = *reinterpret_cast<const sockaddr_in6*>(addr);
|
this->address6_ = *reinterpret_cast<const sockaddr_in6*>(addr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Invalid network address");
|
throw std::runtime_error("Invalid network address");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void address::set_port(const unsigned short port)
|
void address::set_port(const unsigned short port)
|
||||||
{
|
{
|
||||||
switch (this->address_.sa_family)
|
switch (this->address_.sa_family)
|
||||||
{
|
{
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
this->address4_.sin_port = htons(port);
|
this->address4_.sin_port = htons(port);
|
||||||
break;
|
break;
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
this->address6_.sin6_port = htons(port);
|
this->address6_.sin6_port = htons(port);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Invalid address family");
|
throw std::runtime_error("Invalid address family");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned short address::get_port() const
|
unsigned short address::get_port() const
|
||||||
{
|
{
|
||||||
switch (this->address_.sa_family)
|
switch (this->address_.sa_family)
|
||||||
{
|
{
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
return ntohs(this->address4_.sin_port);
|
return ntohs(this->address4_.sin_port);
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
return ntohs(this->address6_.sin6_port);
|
return ntohs(this->address6_.sin6_port);
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string address::to_string() const
|
std::string address::to_string() const
|
||||||
{
|
{
|
||||||
char buffer[1000] = {0};
|
char buffer[1000] = {0};
|
||||||
std::string addr;
|
std::string addr;
|
||||||
|
|
||||||
switch (this->address_.sa_family)
|
switch (this->address_.sa_family)
|
||||||
{
|
{
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
inet_ntop(this->address_.sa_family, &this->address4_.sin_addr, buffer, sizeof(buffer));
|
inet_ntop(this->address_.sa_family, &this->address4_.sin_addr, buffer, sizeof(buffer));
|
||||||
addr = std::string(buffer);
|
addr = std::string(buffer);
|
||||||
break;
|
break;
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
inet_ntop(this->address_.sa_family, &this->address6_.sin6_addr, buffer, sizeof(buffer));
|
inet_ntop(this->address_.sa_family, &this->address6_.sin6_addr, buffer, sizeof(buffer));
|
||||||
addr = "[" + std::string(buffer) + "]";
|
addr = "[" + std::string(buffer) + "]";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
buffer[0] = '?';
|
buffer[0] = '?';
|
||||||
buffer[1] = 0;
|
buffer[1] = 0;
|
||||||
addr = std::string(buffer);
|
addr = std::string(buffer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return addr + ":"s + std::to_string(this->get_port());
|
return addr + ":"s + std::to_string(this->get_port());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool address::is_local() const
|
bool address::is_local() const
|
||||||
{
|
{
|
||||||
if (this->address_.sa_family != AF_INET)
|
if (this->address_.sa_family != AF_INET)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// According to: https://en.wikipedia.org/wiki/Private_network
|
// According to: https://en.wikipedia.org/wiki/Private_network
|
||||||
|
|
||||||
uint8_t bytes[4];
|
uint8_t bytes[4];
|
||||||
*reinterpret_cast<uint32_t*>(&bytes) = this->address4_.sin_addr.s_addr;
|
*reinterpret_cast<uint32_t*>(&bytes) = this->address4_.sin_addr.s_addr;
|
||||||
|
|
||||||
// 10.X.X.X
|
// 10.X.X.X
|
||||||
if (bytes[0] == 10)
|
if (bytes[0] == 10)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 192.168.X.X
|
// 192.168.X.X
|
||||||
if (bytes[0] == 192
|
if (bytes[0] == 192 && bytes[1] == 168)
|
||||||
&& bytes[1] == 168)
|
{
|
||||||
{
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 172.16.X.X - 172.31.X.X
|
// 172.16.X.X - 172.31.X.X
|
||||||
if (bytes[0] == 172
|
if (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] < 32)
|
||||||
&& bytes[1] >= 16
|
{
|
||||||
&& bytes[1] < 32)
|
return true;
|
||||||
{
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 127.0.0.1
|
// 127.0.0.1
|
||||||
if (this->address4_.sin_addr.s_addr == 0x0100007F)
|
if (this->address4_.sin_addr.s_addr == 0x0100007F)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
sockaddr& address::get_addr()
|
sockaddr& address::get_addr()
|
||||||
{
|
{
|
||||||
return this->address_;
|
return this->address_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sockaddr& address::get_addr() const
|
const sockaddr& address::get_addr() const
|
||||||
{
|
{
|
||||||
return this->address_;
|
return this->address_;
|
||||||
}
|
}
|
||||||
|
|
||||||
sockaddr_in& address::get_in_addr()
|
sockaddr_in& address::get_in_addr()
|
||||||
{
|
{
|
||||||
return this->address4_;
|
return this->address4_;
|
||||||
}
|
}
|
||||||
|
|
||||||
sockaddr_in6& address::get_in6_addr()
|
sockaddr_in6& address::get_in6_addr()
|
||||||
{
|
{
|
||||||
return this->address6_;
|
return this->address6_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sockaddr_in& address::get_in_addr() const
|
const sockaddr_in& address::get_in_addr() const
|
||||||
{
|
{
|
||||||
return this->address4_;
|
return this->address4_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sockaddr_in6& address::get_in6_addr() const
|
const sockaddr_in6& address::get_in6_addr() const
|
||||||
{
|
{
|
||||||
return this->address6_;
|
return this->address6_;
|
||||||
}
|
}
|
||||||
|
|
||||||
socklen_t address::get_size() const
|
socklen_t address::get_size() const
|
||||||
{
|
{
|
||||||
switch (this->address_.sa_family)
|
switch (this->address_.sa_family)
|
||||||
{
|
{
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
return static_cast<socklen_t>(sizeof(this->address4_));
|
return static_cast<socklen_t>(sizeof(this->address4_));
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
return static_cast<socklen_t>(sizeof(this->address6_));
|
return static_cast<socklen_t>(sizeof(this->address6_));
|
||||||
default:
|
default:
|
||||||
return static_cast<socklen_t>(sizeof(this->address_));
|
return static_cast<socklen_t>(sizeof(this->address_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
socklen_t address::get_max_size() const
|
socklen_t address::get_max_size() const
|
||||||
{
|
{
|
||||||
constexpr auto s = sizeof(this->address_);
|
constexpr auto s = sizeof(this->address_);
|
||||||
constexpr auto s4 = sizeof(this->address4_);
|
constexpr auto s4 = sizeof(this->address4_);
|
||||||
constexpr auto s6 = sizeof(this->address6_);
|
constexpr auto s6 = sizeof(this->address6_);
|
||||||
constexpr auto sstore = sizeof(this->storage_);
|
constexpr auto sstore = sizeof(this->storage_);
|
||||||
constexpr auto max_size = std::max(sstore, std::max(s, std::max(s4, s6)));
|
constexpr auto max_size = std::max(sstore, std::max(s, std::max(s4, s6)));
|
||||||
static_assert(max_size == sstore);
|
static_assert(max_size == sstore);
|
||||||
|
|
||||||
return static_cast<socklen_t>(max_size);
|
return static_cast<socklen_t>(max_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool address::is_ipv4() const
|
bool address::is_ipv4() const
|
||||||
{
|
{
|
||||||
return this->address_.sa_family == AF_INET;
|
return this->address_.sa_family == AF_INET;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool address::is_ipv6() const
|
bool address::is_ipv6() const
|
||||||
{
|
{
|
||||||
return this->address_.sa_family == AF_INET6;
|
return this->address_.sa_family == AF_INET6;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool address::is_supported() const
|
bool address::is_supported() const
|
||||||
{
|
{
|
||||||
return is_ipv4() || is_ipv6();
|
return is_ipv4() || is_ipv6();
|
||||||
}
|
}
|
||||||
|
|
||||||
void address::parse(std::string addr, const std::optional<int>& family)
|
void address::parse(std::string addr, const std::optional<int>& family)
|
||||||
{
|
{
|
||||||
std::optional<uint16_t> port_value{};
|
std::optional<uint16_t> port_value{};
|
||||||
|
|
||||||
const auto pos = addr.find_last_of(':');
|
const auto pos = addr.find_last_of(':');
|
||||||
if (pos != std::string::npos)
|
if (pos != std::string::npos)
|
||||||
{
|
{
|
||||||
auto port = addr.substr(pos + 1);
|
auto port = addr.substr(pos + 1);
|
||||||
port_value = uint16_t(atoi(port.data()));
|
port_value = uint16_t(atoi(port.data()));
|
||||||
addr = addr.substr(0, pos);
|
addr = addr.substr(0, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->resolve(addr, family);
|
this->resolve(addr, family);
|
||||||
|
|
||||||
if (port_value)
|
if (port_value)
|
||||||
{
|
{
|
||||||
this->set_port(*port_value);
|
this->set_port(*port_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void address::resolve(const std::string& hostname, const std::optional<int>& family)
|
void address::resolve(const std::string& hostname, const std::optional<int>& family)
|
||||||
{
|
{
|
||||||
const auto port = this->get_port();
|
const auto port = this->get_port();
|
||||||
auto port_reset_action = utils::finally([this, port]()
|
auto port_reset_action = utils::finally([this, port]() { this->set_port(port); });
|
||||||
{
|
|
||||||
this->set_port(port);
|
|
||||||
});
|
|
||||||
|
|
||||||
const auto result = resolve_multiple(hostname);
|
const auto result = resolve_multiple(hostname);
|
||||||
for (const auto& addr : result)
|
for (const auto& addr : result)
|
||||||
{
|
{
|
||||||
if (addr.is_supported() && (!family || addr.get_addr().sa_family == *family))
|
if (addr.is_supported() && (!family || addr.get_addr().sa_family == *family))
|
||||||
{
|
{
|
||||||
this->set_address(&addr.get_addr(), addr.get_size());
|
this->set_address(&addr.get_addr(), addr.get_size());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
port_reset_action.cancel();
|
port_reset_action.cancel();
|
||||||
throw std::runtime_error{"Unable to resolve hostname: " + hostname};
|
throw std::runtime_error{"Unable to resolve hostname: " + hostname};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<address> address::resolve_multiple(const std::string& hostname)
|
std::vector<address> address::resolve_multiple(const std::string& hostname)
|
||||||
{
|
{
|
||||||
std::vector<address> results{};
|
std::vector<address> results{};
|
||||||
|
|
||||||
addrinfo* result = nullptr;
|
addrinfo* result = nullptr;
|
||||||
if (!getaddrinfo(hostname.data(), nullptr, nullptr, &result))
|
if (!getaddrinfo(hostname.data(), nullptr, nullptr, &result))
|
||||||
{
|
{
|
||||||
const auto _2 = utils::finally([&result]
|
const auto _2 = utils::finally([&result] { freeaddrinfo(result); });
|
||||||
{
|
|
||||||
freeaddrinfo(result);
|
|
||||||
});
|
|
||||||
|
|
||||||
for (auto* i = result; i; i = i->ai_next)
|
for (auto* i = result; i; i = i->ai_next)
|
||||||
{
|
{
|
||||||
if (i->ai_family == AF_INET || i->ai_family == AF_INET6)
|
if (i->ai_family == AF_INET || i->ai_family == AF_INET6)
|
||||||
{
|
{
|
||||||
address a{};
|
address a{};
|
||||||
a.set_address(i->ai_addr, static_cast<socklen_t>(i->ai_addrlen));
|
a.set_address(i->ai_addr, static_cast<socklen_t>(i->ai_addrlen));
|
||||||
results.emplace_back(std::move(a));
|
results.emplace_back(std::move(a));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t std::hash<network::address>::operator()(const network::address& a) const noexcept
|
std::size_t std::hash<network::address>::operator()(const network::address& a) const noexcept
|
||||||
{
|
{
|
||||||
const uint32_t family = a.get_addr().sa_family;
|
const uint32_t family = a.get_addr().sa_family;
|
||||||
const uint32_t port = a.get_port();
|
const uint32_t port = a.get_port();
|
||||||
|
|
||||||
std::size_t hash = std::hash<uint32_t>{}(family);
|
std::size_t hash = std::hash<uint32_t>{}(family);
|
||||||
hash ^= std::hash<uint32_t>{}(port);
|
hash ^= std::hash<uint32_t>{}(port);
|
||||||
switch (a.get_addr().sa_family)
|
switch (a.get_addr().sa_family)
|
||||||
{
|
{
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
hash ^= std::hash<decltype(a.get_in_addr().sin_addr.s_addr)>{}(a.get_in_addr().sin_addr.s_addr);
|
hash ^= std::hash<decltype(a.get_in_addr().sin_addr.s_addr)>{}(a.get_in_addr().sin_addr.s_addr);
|
||||||
break;
|
break;
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
hash ^= std::hash<std::string_view>{}(std::string_view{
|
hash ^= std::hash<std::string_view>{}(
|
||||||
reinterpret_cast<const char*>(a.get_in6_addr().sin6_addr.s6_addr),
|
std::string_view{reinterpret_cast<const char*>(a.get_in6_addr().sin6_addr.s6_addr),
|
||||||
sizeof(a.get_in6_addr().sin6_addr.s6_addr)
|
sizeof(a.get_in6_addr().sin6_addr.s6_addr)});
|
||||||
});
|
break;
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,71 +36,71 @@ using socklen_t = int;
|
|||||||
|
|
||||||
namespace network
|
namespace network
|
||||||
{
|
{
|
||||||
void initialize_wsa();
|
void initialize_wsa();
|
||||||
|
|
||||||
class address
|
class address
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
address();
|
address();
|
||||||
address(const std::string& addr, const std::optional<int>& family = {});
|
address(const std::string& addr, const std::optional<int>& family = {});
|
||||||
address(const sockaddr_in& addr);
|
address(const sockaddr_in& addr);
|
||||||
address(const sockaddr_in6& addr);
|
address(const sockaddr_in6& addr);
|
||||||
address(const sockaddr* addr, socklen_t length);
|
address(const sockaddr* addr, socklen_t length);
|
||||||
|
|
||||||
void set_ipv4(uint32_t ip);
|
void set_ipv4(uint32_t ip);
|
||||||
void set_ipv4(const in_addr& addr);
|
void set_ipv4(const in_addr& addr);
|
||||||
void set_ipv6(const in6_addr& addr);
|
void set_ipv6(const in6_addr& addr);
|
||||||
void set_address(const sockaddr* addr, socklen_t length);
|
void set_address(const sockaddr* addr, socklen_t length);
|
||||||
|
|
||||||
void set_port(unsigned short port);
|
void set_port(unsigned short port);
|
||||||
[[nodiscard]] unsigned short get_port() const;
|
[[nodiscard]] unsigned short get_port() const;
|
||||||
|
|
||||||
sockaddr& get_addr();
|
sockaddr& get_addr();
|
||||||
sockaddr_in& get_in_addr();
|
sockaddr_in& get_in_addr();
|
||||||
sockaddr_in6& get_in6_addr();
|
sockaddr_in6& get_in6_addr();
|
||||||
|
|
||||||
const sockaddr& get_addr() const;
|
const sockaddr& get_addr() const;
|
||||||
const sockaddr_in& get_in_addr() const;
|
const sockaddr_in& get_in_addr() const;
|
||||||
const sockaddr_in6& get_in6_addr() const;
|
const sockaddr_in6& get_in6_addr() const;
|
||||||
|
|
||||||
socklen_t get_size() const;
|
socklen_t get_size() const;
|
||||||
socklen_t get_max_size() const;
|
socklen_t get_max_size() const;
|
||||||
|
|
||||||
bool is_ipv4() const;
|
bool is_ipv4() const;
|
||||||
bool is_ipv6() const;
|
bool is_ipv6() const;
|
||||||
bool is_supported() const;
|
bool is_supported() const;
|
||||||
|
|
||||||
[[nodiscard]] bool is_local() const;
|
[[nodiscard]] bool is_local() const;
|
||||||
[[nodiscard]] std::string to_string() const;
|
[[nodiscard]] std::string to_string() const;
|
||||||
|
|
||||||
bool operator==(const address& obj) const;
|
bool operator==(const address& obj) const;
|
||||||
|
|
||||||
bool operator!=(const address& obj) const
|
bool operator!=(const address& obj) const
|
||||||
{
|
{
|
||||||
return !(*this == obj);
|
return !(*this == obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<address> resolve_multiple(const std::string& hostname);
|
static std::vector<address> resolve_multiple(const std::string& hostname);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
sockaddr address_;
|
sockaddr address_;
|
||||||
sockaddr_in address4_;
|
sockaddr_in address4_;
|
||||||
sockaddr_in6 address6_;
|
sockaddr_in6 address6_;
|
||||||
sockaddr_storage storage_;
|
sockaddr_storage storage_;
|
||||||
};
|
};
|
||||||
|
|
||||||
void parse(std::string addr, const std::optional<int>& family = {});
|
void parse(std::string addr, const std::optional<int>& family = {});
|
||||||
void resolve(const std::string& hostname, const std::optional<int>& family = {});
|
void resolve(const std::string& hostname, const std::optional<int>& family = {});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace std
|
namespace std
|
||||||
{
|
{
|
||||||
template <>
|
template <>
|
||||||
struct hash<network::address>
|
struct hash<network::address>
|
||||||
{
|
{
|
||||||
std::size_t operator()(const network::address& a) const noexcept;
|
std::size_t operator()(const network::address& a) const noexcept;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,226 +6,225 @@ using namespace std::literals;
|
|||||||
|
|
||||||
namespace network
|
namespace network
|
||||||
{
|
{
|
||||||
socket::socket(const int af)
|
socket::socket(const int af)
|
||||||
: address_family_(af)
|
: address_family_(af)
|
||||||
{
|
{
|
||||||
initialize_wsa();
|
initialize_wsa();
|
||||||
this->socket_ = ::socket(af, SOCK_DGRAM, IPPROTO_UDP);
|
this->socket_ = ::socket(af, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
|
|
||||||
if (af == AF_INET6)
|
if (af == AF_INET6)
|
||||||
{
|
{
|
||||||
int i = 1;
|
int i = 1;
|
||||||
setsockopt(this->socket_, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&i),
|
setsockopt(this->socket_, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&i),
|
||||||
static_cast<int>(sizeof(i)));
|
static_cast<int>(sizeof(i)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
socket::~socket()
|
socket::~socket()
|
||||||
{
|
{
|
||||||
this->release();
|
this->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
socket::socket(socket&& obj) noexcept
|
socket::socket(socket&& obj) noexcept
|
||||||
{
|
{
|
||||||
this->operator=(std::move(obj));
|
this->operator=(std::move(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
socket& socket::operator=(socket&& obj) noexcept
|
socket& socket::operator=(socket&& obj) noexcept
|
||||||
{
|
{
|
||||||
if (this != &obj)
|
if (this != &obj)
|
||||||
{
|
{
|
||||||
this->release();
|
this->release();
|
||||||
this->socket_ = obj.socket_;
|
this->socket_ = obj.socket_;
|
||||||
this->port_ = obj.port_;
|
this->port_ = obj.port_;
|
||||||
this->address_family_ = obj.address_family_;
|
this->address_family_ = obj.address_family_;
|
||||||
|
|
||||||
obj.socket_ = INVALID_SOCKET;
|
obj.socket_ = INVALID_SOCKET;
|
||||||
obj.address_family_ = AF_UNSPEC;
|
obj.address_family_ = AF_UNSPEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void socket::release()
|
void socket::release()
|
||||||
{
|
{
|
||||||
if (this->socket_ != INVALID_SOCKET)
|
if (this->socket_ != INVALID_SOCKET)
|
||||||
{
|
{
|
||||||
closesocket(this->socket_);
|
closesocket(this->socket_);
|
||||||
this->socket_ = INVALID_SOCKET;
|
this->socket_ = INVALID_SOCKET;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool socket::bind_port(const address& target)
|
bool socket::bind_port(const address& target)
|
||||||
{
|
{
|
||||||
const auto result = bind(this->socket_, &target.get_addr(), target.get_size()) == 0;
|
const auto result = bind(this->socket_, &target.get_addr(), target.get_size()) == 0;
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
this->port_ = target.get_port();
|
this->port_ = target.get_port();
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool socket::send(const address& target, const void* data, const size_t size) const
|
bool socket::send(const address& target, const void* data, const size_t size) const
|
||||||
{
|
{
|
||||||
const auto res = sendto(this->socket_, static_cast<const char*>(data), static_cast<send_size>(size), 0,
|
const auto res = sendto(this->socket_, static_cast<const char*>(data), static_cast<send_size>(size), 0,
|
||||||
&target.get_addr(),
|
&target.get_addr(), target.get_size());
|
||||||
target.get_size());
|
return static_cast<size_t>(res) == size;
|
||||||
return static_cast<size_t>(res) == size;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool socket::send(const address& target, const std::string& data) const
|
bool socket::send(const address& target, const std::string& data) const
|
||||||
{
|
{
|
||||||
return this->send(target, data.data(), data.size());
|
return this->send(target, data.data(), data.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool socket::receive(address& source, std::string& data) const
|
bool socket::receive(address& source, std::string& data) const
|
||||||
{
|
{
|
||||||
char buffer[0x2000];
|
char buffer[0x2000];
|
||||||
auto len = source.get_max_size();
|
auto len = source.get_max_size();
|
||||||
|
|
||||||
const auto result = recvfrom(this->socket_, buffer, static_cast<int>(sizeof(buffer)), 0, &source.get_addr(),
|
const auto result =
|
||||||
&len);
|
recvfrom(this->socket_, buffer, static_cast<int>(sizeof(buffer)), 0, &source.get_addr(), &len);
|
||||||
if (result == SOCKET_ERROR)
|
if (result == SOCKET_ERROR)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.assign(buffer, buffer + result);
|
data.assign(buffer, buffer + result);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool socket::set_blocking(const bool blocking)
|
bool socket::set_blocking(const bool blocking)
|
||||||
{
|
{
|
||||||
return socket::set_blocking(this->socket_, blocking);
|
return socket::set_blocking(this->socket_, blocking);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool socket::set_blocking(SOCKET s, const bool blocking)
|
bool socket::set_blocking(SOCKET s, const bool blocking)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
unsigned long mode = blocking ? 0 : 1;
|
unsigned long mode = blocking ? 0 : 1;
|
||||||
return ioctlsocket(s, FIONBIO, &mode) == 0;
|
return ioctlsocket(s, FIONBIO, &mode) == 0;
|
||||||
#else
|
#else
|
||||||
int flags = fcntl(s, F_GETFL, 0);
|
int flags = fcntl(s, F_GETFL, 0);
|
||||||
if (flags == -1) return false;
|
if (flags == -1)
|
||||||
flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
|
return false;
|
||||||
return fcntl(s, F_SETFL, flags) == 0;
|
flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
|
||||||
|
return fcntl(s, F_SETFL, flags) == 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool socket::sleep(const std::chrono::milliseconds timeout) const
|
bool socket::sleep(const std::chrono::milliseconds timeout) const
|
||||||
{
|
{
|
||||||
/*fd_set fdr;
|
/*fd_set fdr;
|
||||||
FD_ZERO(&fdr);
|
FD_ZERO(&fdr);
|
||||||
FD_SET(this->socket_, &fdr);
|
FD_SET(this->socket_, &fdr);
|
||||||
|
|
||||||
const auto msec = timeout.count();
|
const auto msec = timeout.count();
|
||||||
|
|
||||||
timeval tv{};
|
timeval tv{};
|
||||||
tv.tv_sec = static_cast<long>(msec / 1000ll);
|
tv.tv_sec = static_cast<long>(msec / 1000ll);
|
||||||
tv.tv_usec = static_cast<long>((msec % 1000) * 1000);
|
tv.tv_usec = static_cast<long>((msec % 1000) * 1000);
|
||||||
|
|
||||||
const auto retval = select(static_cast<int>(this->socket_) + 1, &fdr, nullptr, nullptr, &tv);
|
const auto retval = select(static_cast<int>(this->socket_) + 1, &fdr, nullptr, nullptr, &tv);
|
||||||
if (retval == SOCKET_ERROR)
|
if (retval == SOCKET_ERROR)
|
||||||
{
|
{
|
||||||
std::this_thread::sleep_for(1ms);
|
std::this_thread::sleep_for(1ms);
|
||||||
return socket_is_ready;
|
return socket_is_ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (retval > 0)
|
if (retval > 0)
|
||||||
{
|
{
|
||||||
return socket_is_ready;
|
return socket_is_ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !socket_is_ready;*/
|
return !socket_is_ready;*/
|
||||||
|
|
||||||
std::vector<const socket*> sockets{};
|
std::vector<const socket*> sockets{};
|
||||||
sockets.push_back(this);
|
sockets.push_back(this);
|
||||||
|
|
||||||
return sleep_sockets(sockets, timeout);
|
return sleep_sockets(sockets, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool socket::sleep_until(const std::chrono::high_resolution_clock::time_point time_point) const
|
bool socket::sleep_until(const std::chrono::high_resolution_clock::time_point time_point) const
|
||||||
{
|
{
|
||||||
const auto duration = time_point - std::chrono::high_resolution_clock::now();
|
const auto duration = time_point - std::chrono::high_resolution_clock::now();
|
||||||
return this->sleep(std::chrono::duration_cast<std::chrono::milliseconds>(duration));
|
return this->sleep(std::chrono::duration_cast<std::chrono::milliseconds>(duration));
|
||||||
}
|
}
|
||||||
|
|
||||||
SOCKET socket::get_socket() const
|
SOCKET socket::get_socket() const
|
||||||
{
|
{
|
||||||
return this->socket_;
|
return this->socket_;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t socket::get_port() const
|
uint16_t socket::get_port() const
|
||||||
{
|
{
|
||||||
return this->port_;
|
return this->port_;
|
||||||
}
|
}
|
||||||
|
|
||||||
int socket::get_address_family() const
|
int socket::get_address_family() const
|
||||||
{
|
{
|
||||||
return this->address_family_;
|
return this->address_family_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool socket::sleep_sockets(const std::span<const socket*>& sockets, const std::chrono::milliseconds timeout)
|
bool socket::sleep_sockets(const std::span<const socket*>& sockets, const std::chrono::milliseconds timeout)
|
||||||
{
|
{
|
||||||
std::vector<pollfd> pfds{};
|
std::vector<pollfd> pfds{};
|
||||||
pfds.resize(sockets.size());
|
pfds.resize(sockets.size());
|
||||||
|
|
||||||
for (size_t i = 0; i < sockets.size(); ++i)
|
for (size_t i = 0; i < sockets.size(); ++i)
|
||||||
{
|
{
|
||||||
auto& pfd = pfds.at(i);
|
auto& pfd = pfds.at(i);
|
||||||
const auto& socket = sockets[i];
|
const auto& socket = sockets[i];
|
||||||
|
|
||||||
pfd.fd = socket->get_socket();
|
pfd.fd = socket->get_socket();
|
||||||
pfd.events = POLLIN;
|
pfd.events = POLLIN;
|
||||||
pfd.revents = 0;
|
pfd.revents = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto retval = poll(pfds.data(), static_cast<uint32_t>(pfds.size()),
|
const auto retval = poll(pfds.data(), static_cast<uint32_t>(pfds.size()), static_cast<int>(timeout.count()));
|
||||||
static_cast<int>(timeout.count()));
|
|
||||||
|
|
||||||
if (retval == SOCKET_ERROR)
|
if (retval == SOCKET_ERROR)
|
||||||
{
|
{
|
||||||
std::this_thread::sleep_for(1ms);
|
std::this_thread::sleep_for(1ms);
|
||||||
return socket_is_ready;
|
return socket_is_ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (retval > 0)
|
if (retval > 0)
|
||||||
{
|
{
|
||||||
return socket_is_ready;
|
return socket_is_ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !socket_is_ready;
|
return !socket_is_ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool socket::is_socket_ready(const SOCKET s, const bool in_poll)
|
bool socket::is_socket_ready(const SOCKET s, const bool in_poll)
|
||||||
{
|
{
|
||||||
pollfd pfd{};
|
pollfd pfd{};
|
||||||
|
|
||||||
pfd.fd = s;
|
pfd.fd = s;
|
||||||
pfd.events = in_poll ? POLLIN : POLLOUT;
|
pfd.events = in_poll ? POLLIN : POLLOUT;
|
||||||
pfd.revents = 0;
|
pfd.revents = 0;
|
||||||
|
|
||||||
const auto retval = poll(&pfd, 1, 0);
|
const auto retval = poll(&pfd, 1, 0);
|
||||||
|
|
||||||
if (retval == SOCKET_ERROR)
|
if (retval == SOCKET_ERROR)
|
||||||
{
|
{
|
||||||
std::this_thread::sleep_for(1ms);
|
std::this_thread::sleep_for(1ms);
|
||||||
return socket_is_ready;
|
return socket_is_ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (retval > 0)
|
if (retval > 0)
|
||||||
{
|
{
|
||||||
return socket_is_ready;
|
return socket_is_ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !socket_is_ready;
|
return !socket_is_ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool socket::sleep_sockets_until(const std::span<const socket*>& sockets,
|
bool socket::sleep_sockets_until(const std::span<const socket*>& sockets,
|
||||||
const std::chrono::high_resolution_clock::time_point time_point)
|
const std::chrono::high_resolution_clock::time_point time_point)
|
||||||
{
|
{
|
||||||
const auto duration = time_point - std::chrono::high_resolution_clock::now();
|
const auto duration = time_point - std::chrono::high_resolution_clock::now();
|
||||||
return sleep_sockets(sockets, std::chrono::duration_cast<std::chrono::milliseconds>(duration));
|
return sleep_sockets(sockets, std::chrono::duration_cast<std::chrono::milliseconds>(duration));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,63 +8,63 @@
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
using send_size = int;
|
using send_size = int;
|
||||||
#define GET_SOCKET_ERROR() (WSAGetLastError())
|
#define GET_SOCKET_ERROR() (WSAGetLastError())
|
||||||
#define poll WSAPoll
|
#define poll WSAPoll
|
||||||
#define SOCK_WOULDBLOCK WSAEWOULDBLOCK
|
#define SOCK_WOULDBLOCK WSAEWOULDBLOCK
|
||||||
#else
|
#else
|
||||||
using SOCKET = int;
|
using SOCKET = int;
|
||||||
using send_size = size_t;
|
using send_size = size_t;
|
||||||
#define INVALID_SOCKET (SOCKET)(~0)
|
#define INVALID_SOCKET (SOCKET)(~0)
|
||||||
#define SOCKET_ERROR (-1)
|
#define SOCKET_ERROR (-1)
|
||||||
#define GET_SOCKET_ERROR() (errno)
|
#define GET_SOCKET_ERROR() (errno)
|
||||||
#define closesocket close
|
#define closesocket close
|
||||||
#define SOCK_WOULDBLOCK EWOULDBLOCK
|
#define SOCK_WOULDBLOCK EWOULDBLOCK
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace network
|
namespace network
|
||||||
{
|
{
|
||||||
class socket
|
class socket
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
socket() = default;
|
socket() = default;
|
||||||
|
|
||||||
socket(int af);
|
socket(int af);
|
||||||
~socket();
|
~socket();
|
||||||
|
|
||||||
socket(const socket& obj) = delete;
|
socket(const socket& obj) = delete;
|
||||||
socket& operator=(const socket& obj) = delete;
|
socket& operator=(const socket& obj) = delete;
|
||||||
|
|
||||||
socket(socket&& obj) noexcept;
|
socket(socket&& obj) noexcept;
|
||||||
socket& operator=(socket&& obj) noexcept;
|
socket& operator=(socket&& obj) noexcept;
|
||||||
|
|
||||||
bool bind_port(const address& target);
|
bool bind_port(const address& target);
|
||||||
|
|
||||||
[[maybe_unused]] bool send(const address& target, const void* data, size_t size) const;
|
[[maybe_unused]] bool send(const address& target, const void* data, size_t size) const;
|
||||||
[[maybe_unused]] bool send(const address& target, const std::string& data) const;
|
[[maybe_unused]] bool send(const address& target, const std::string& data) const;
|
||||||
bool receive(address& source, std::string& data) const;
|
bool receive(address& source, std::string& data) const;
|
||||||
|
|
||||||
bool set_blocking(bool blocking);
|
bool set_blocking(bool blocking);
|
||||||
static bool set_blocking(SOCKET s, bool blocking);
|
static bool set_blocking(SOCKET s, bool blocking);
|
||||||
|
|
||||||
static constexpr bool socket_is_ready = true;
|
static constexpr bool socket_is_ready = true;
|
||||||
bool sleep(std::chrono::milliseconds timeout) const;
|
bool sleep(std::chrono::milliseconds timeout) const;
|
||||||
bool sleep_until(std::chrono::high_resolution_clock::time_point time_point) const;
|
bool sleep_until(std::chrono::high_resolution_clock::time_point time_point) const;
|
||||||
|
|
||||||
SOCKET get_socket() const;
|
SOCKET get_socket() const;
|
||||||
uint16_t get_port() const;
|
uint16_t get_port() const;
|
||||||
|
|
||||||
int get_address_family() const;
|
int get_address_family() const;
|
||||||
|
|
||||||
static bool sleep_sockets(const std::span<const socket*>& sockets, std::chrono::milliseconds timeout);
|
static bool sleep_sockets(const std::span<const socket*>& sockets, std::chrono::milliseconds timeout);
|
||||||
static bool sleep_sockets_until(const std::span<const socket*>& sockets,
|
static bool sleep_sockets_until(const std::span<const socket*>& sockets,
|
||||||
std::chrono::high_resolution_clock::time_point time_point);
|
std::chrono::high_resolution_clock::time_point time_point);
|
||||||
|
|
||||||
static bool is_socket_ready(SOCKET s, bool in_poll);
|
static bool is_socket_ready(SOCKET s, bool in_poll);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int address_family_{AF_UNSPEC};
|
int address_family_{AF_UNSPEC};
|
||||||
uint16_t port_ = 0;
|
uint16_t port_ = 0;
|
||||||
SOCKET socket_ = INVALID_SOCKET;
|
SOCKET socket_ = INVALID_SOCKET;
|
||||||
|
|
||||||
void release();
|
void release();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
#define OS_WINDOWS
|
#define OS_WINDOWS
|
||||||
#elif defined(__APPLE__) || defined(__MACH__)
|
#elif defined(__APPLE__) || defined(__MACH__)
|
||||||
#define OS_MAC
|
#define OS_MAC
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
#define OS_LINUX
|
#define OS_LINUX
|
||||||
#else
|
#else
|
||||||
@@ -11,9 +11,9 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef OS_WINDOWS
|
#ifdef OS_WINDOWS
|
||||||
#define EXPORT_SYMBOL __declspec(dllexport)
|
#define EXPORT_SYMBOL __declspec(dllexport)
|
||||||
#define IMPORT_SYMBOL __declspec(dllimport)
|
#define IMPORT_SYMBOL __declspec(dllimport)
|
||||||
#define NO_INLINE __declspec(noinline)
|
#define NO_INLINE __declspec(noinline)
|
||||||
|
|
||||||
#define DECLSPEC_ALIGN(n) __declspec(align(n))
|
#define DECLSPEC_ALIGN(n) __declspec(align(n))
|
||||||
|
|
||||||
@@ -24,21 +24,21 @@
|
|||||||
|
|
||||||
#define EXPORT_SYMBOL __attribute__((visibility("default")))
|
#define EXPORT_SYMBOL __attribute__((visibility("default")))
|
||||||
#define IMPORT_SYMBOL
|
#define IMPORT_SYMBOL
|
||||||
#define NO_INLINE __attribute__((noinline))
|
#define NO_INLINE __attribute__((noinline))
|
||||||
|
|
||||||
#define DECLSPEC_ALIGN(n) alignas(n)
|
#define DECLSPEC_ALIGN(n) alignas(n)
|
||||||
#define fopen_s fopen
|
#define fopen_s fopen
|
||||||
|
|
||||||
#define RESTRICTED_POINTER __restrict
|
#define RESTRICTED_POINTER __restrict
|
||||||
|
|
||||||
#ifdef OS_MAC
|
#ifdef OS_MAC
|
||||||
#define _fseeki64 fseeko
|
#define _fseeki64 fseeko
|
||||||
#define _ftelli64 ftello
|
#define _ftelli64 ftello
|
||||||
#define _stat64 stat
|
#define _stat64 stat
|
||||||
#else
|
#else
|
||||||
#define _fseeki64 fseeko64
|
#define _fseeki64 fseeko64
|
||||||
#define _ftelli64 ftello64
|
#define _ftelli64 ftello64
|
||||||
#define _stat64 stat64
|
#define _stat64 stat64
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,246 +1,250 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define ACCESS_MASK DWORD
|
#define ACCESS_MASK DWORD
|
||||||
#define DEVICE_TYPE DWORD
|
#define DEVICE_TYPE DWORD
|
||||||
|
|
||||||
#define FILE_DEVICE_DISK 0x00000007
|
#define FILE_DEVICE_DISK 0x00000007
|
||||||
#define FILE_DEVICE_CONSOLE 0x00000050
|
#define FILE_DEVICE_CONSOLE 0x00000050
|
||||||
|
|
||||||
#define FILE_SUPERSEDE 0x00000000
|
#define FILE_SUPERSEDE 0x00000000
|
||||||
#define FILE_OPEN 0x00000001
|
#define FILE_OPEN 0x00000001
|
||||||
#define FILE_CREATE 0x00000002
|
#define FILE_CREATE 0x00000002
|
||||||
#define FILE_OPEN_IF 0x00000003
|
#define FILE_OPEN_IF 0x00000003
|
||||||
#define FILE_OVERWRITE 0x00000004
|
#define FILE_OVERWRITE 0x00000004
|
||||||
#define FILE_OVERWRITE_IF 0x00000005
|
#define FILE_OVERWRITE_IF 0x00000005
|
||||||
#define FILE_MAXIMUM_DISPOSITION 0x00000005
|
#define FILE_MAXIMUM_DISPOSITION 0x00000005
|
||||||
|
|
||||||
#ifndef OS_WINDOWS
|
#ifndef OS_WINDOWS
|
||||||
#define GENERIC_READ 0x80000000
|
#define GENERIC_READ 0x80000000
|
||||||
#define GENERIC_WRITE 0x40000000
|
#define GENERIC_WRITE 0x40000000
|
||||||
#define GENERIC_EXECUTE 0x20000000
|
#define GENERIC_EXECUTE 0x20000000
|
||||||
#define GENERIC_ALL 0x10000000
|
#define GENERIC_ALL 0x10000000
|
||||||
|
|
||||||
#undef DELETE
|
#undef DELETE
|
||||||
#define DELETE 0x00010000
|
#define DELETE 0x00010000
|
||||||
#define READ_CONTROL 0x00020000
|
#define READ_CONTROL 0x00020000
|
||||||
#define WRITE_DAC 0x00040000
|
#define WRITE_DAC 0x00040000
|
||||||
#define WRITE_OWNER 0x00080000
|
#define WRITE_OWNER 0x00080000
|
||||||
#define SYNCHRONIZE 0x00100000
|
#define SYNCHRONIZE 0x00100000
|
||||||
#define STANDARD_RIGHTS_REQUIRED 0x000f0000
|
#define STANDARD_RIGHTS_REQUIRED 0x000f0000
|
||||||
|
|
||||||
#define FILE_READ_DATA 0x0001 /* file & pipe */
|
#define FILE_READ_DATA 0x0001 /* file & pipe */
|
||||||
#define FILE_LIST_DIRECTORY 0x0001 /* directory */
|
#define FILE_LIST_DIRECTORY 0x0001 /* directory */
|
||||||
#define FILE_WRITE_DATA 0x0002 /* file & pipe */
|
#define FILE_WRITE_DATA 0x0002 /* file & pipe */
|
||||||
#define FILE_ADD_FILE 0x0002 /* directory */
|
#define FILE_ADD_FILE 0x0002 /* directory */
|
||||||
#define FILE_APPEND_DATA 0x0004 /* file */
|
#define FILE_APPEND_DATA 0x0004 /* file */
|
||||||
#define FILE_ADD_SUBDIRECTORY 0x0004 /* directory */
|
#define FILE_ADD_SUBDIRECTORY 0x0004 /* directory */
|
||||||
#define FILE_CREATE_PIPE_INSTANCE 0x0004 /* named pipe */
|
#define FILE_CREATE_PIPE_INSTANCE 0x0004 /* named pipe */
|
||||||
#define FILE_READ_EA 0x0008 /* file & directory */
|
#define FILE_READ_EA 0x0008 /* file & directory */
|
||||||
#define FILE_READ_PROPERTIES FILE_READ_EA
|
#define FILE_READ_PROPERTIES FILE_READ_EA
|
||||||
#define FILE_WRITE_EA 0x0010 /* file & directory */
|
#define FILE_WRITE_EA 0x0010 /* file & directory */
|
||||||
#define FILE_WRITE_PROPERTIES FILE_WRITE_EA
|
#define FILE_WRITE_PROPERTIES FILE_WRITE_EA
|
||||||
#define FILE_EXECUTE 0x0020 /* file */
|
#define FILE_EXECUTE 0x0020 /* file */
|
||||||
#define FILE_TRAVERSE 0x0020 /* directory */
|
#define FILE_TRAVERSE 0x0020 /* directory */
|
||||||
#define FILE_DELETE_CHILD 0x0040 /* directory */
|
#define FILE_DELETE_CHILD 0x0040 /* directory */
|
||||||
#define FILE_READ_ATTRIBUTES 0x0080 /* all */
|
#define FILE_READ_ATTRIBUTES 0x0080 /* all */
|
||||||
#define FILE_WRITE_ATTRIBUTES 0x0100 /* all */
|
#define FILE_WRITE_ATTRIBUTES 0x0100 /* all */
|
||||||
#define FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x1ff)
|
#define FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1ff)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define FILE_DIRECTORY_FILE 0x00000001
|
#define FILE_DIRECTORY_FILE 0x00000001
|
||||||
#define FILE_WRITE_THROUGH 0x00000002
|
#define FILE_WRITE_THROUGH 0x00000002
|
||||||
#define FILE_SEQUENTIAL_ONLY 0x00000004
|
#define FILE_SEQUENTIAL_ONLY 0x00000004
|
||||||
#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008
|
#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008
|
||||||
|
|
||||||
#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010
|
#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010
|
||||||
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
|
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
|
||||||
#define FILE_NON_DIRECTORY_FILE 0x00000040
|
#define FILE_NON_DIRECTORY_FILE 0x00000040
|
||||||
#define FILE_CREATE_TREE_CONNECTION 0x00000080
|
#define FILE_CREATE_TREE_CONNECTION 0x00000080
|
||||||
|
|
||||||
#define FILE_ATTRIBUTE_NORMAL 0x00000080
|
#define FILE_ATTRIBUTE_NORMAL 0x00000080
|
||||||
|
|
||||||
#define PS_ATTRIBUTE_NUMBER_MASK 0x0000ffff
|
#define PS_ATTRIBUTE_NUMBER_MASK 0x0000ffff
|
||||||
#define PS_ATTRIBUTE_THREAD 0x00010000 // may be used with thread creation
|
#define PS_ATTRIBUTE_THREAD 0x00010000 // may be used with thread creation
|
||||||
#define PS_ATTRIBUTE_INPUT 0x00020000 // input only
|
#define PS_ATTRIBUTE_INPUT 0x00020000 // input only
|
||||||
#define PS_ATTRIBUTE_ADDITIVE 0x00040000 // "accumulated" e.g. bitmasks, counters, etc.
|
#define PS_ATTRIBUTE_ADDITIVE 0x00040000 // "accumulated" e.g. bitmasks, counters, etc.
|
||||||
|
|
||||||
#define SL_RESTART_SCAN 0x01
|
#define SL_RESTART_SCAN 0x01
|
||||||
#define SL_RETURN_SINGLE_ENTRY 0x02
|
#define SL_RETURN_SINGLE_ENTRY 0x02
|
||||||
#define SL_NO_CURSOR_UPDATE 0x10
|
#define SL_NO_CURSOR_UPDATE 0x10
|
||||||
|
|
||||||
#define SEC_IMAGE 0x01000000
|
#define SEC_IMAGE 0x01000000
|
||||||
|
|
||||||
typedef enum _FSINFOCLASS
|
typedef enum _FSINFOCLASS
|
||||||
{
|
{
|
||||||
FileFsVolumeInformation = 1, // q: FILE_FS_VOLUME_INFORMATION
|
FileFsVolumeInformation = 1, // q: FILE_FS_VOLUME_INFORMATION
|
||||||
FileFsLabelInformation, // s: FILE_FS_LABEL_INFORMATION (requires FILE_WRITE_DATA to volume)
|
FileFsLabelInformation, // s: FILE_FS_LABEL_INFORMATION (requires FILE_WRITE_DATA to volume)
|
||||||
FileFsSizeInformation, // q: FILE_FS_SIZE_INFORMATION
|
FileFsSizeInformation, // q: FILE_FS_SIZE_INFORMATION
|
||||||
FileFsDeviceInformation, // q: FILE_FS_DEVICE_INFORMATION
|
FileFsDeviceInformation, // q: FILE_FS_DEVICE_INFORMATION
|
||||||
FileFsAttributeInformation, // q: FILE_FS_ATTRIBUTE_INFORMATION
|
FileFsAttributeInformation, // q: FILE_FS_ATTRIBUTE_INFORMATION
|
||||||
FileFsControlInformation,
|
FileFsControlInformation,
|
||||||
// q, s: FILE_FS_CONTROL_INFORMATION (q: requires FILE_READ_DATA; s: requires FILE_WRITE_DATA to volume)
|
// q, s: FILE_FS_CONTROL_INFORMATION (q: requires FILE_READ_DATA; s: requires FILE_WRITE_DATA to volume)
|
||||||
FileFsFullSizeInformation, // q: FILE_FS_FULL_SIZE_INFORMATION
|
FileFsFullSizeInformation, // q: FILE_FS_FULL_SIZE_INFORMATION
|
||||||
FileFsObjectIdInformation, // q; s: FILE_FS_OBJECTID_INFORMATION (s: requires FILE_WRITE_DATA to volume)
|
FileFsObjectIdInformation, // q; s: FILE_FS_OBJECTID_INFORMATION (s: requires FILE_WRITE_DATA to volume)
|
||||||
FileFsDriverPathInformation, // q: FILE_FS_DRIVER_PATH_INFORMATION
|
FileFsDriverPathInformation, // q: FILE_FS_DRIVER_PATH_INFORMATION
|
||||||
FileFsVolumeFlagsInformation,
|
FileFsVolumeFlagsInformation,
|
||||||
// q; s: FILE_FS_VOLUME_FLAGS_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES to volume) // 10
|
// q; s: FILE_FS_VOLUME_FLAGS_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES to
|
||||||
FileFsSectorSizeInformation, // q: FILE_FS_SECTOR_SIZE_INFORMATION // since WIN8
|
// volume) // 10
|
||||||
FileFsDataCopyInformation, // q: FILE_FS_DATA_COPY_INFORMATION
|
FileFsSectorSizeInformation, // q: FILE_FS_SECTOR_SIZE_INFORMATION // since WIN8
|
||||||
FileFsMetadataSizeInformation, // q: FILE_FS_METADATA_SIZE_INFORMATION // since THRESHOLD
|
FileFsDataCopyInformation, // q: FILE_FS_DATA_COPY_INFORMATION
|
||||||
FileFsFullSizeInformationEx, // q: FILE_FS_FULL_SIZE_INFORMATION_EX // since REDSTONE5
|
FileFsMetadataSizeInformation, // q: FILE_FS_METADATA_SIZE_INFORMATION // since THRESHOLD
|
||||||
FileFsGuidInformation, // q: FILE_FS_GUID_INFORMATION // since 23H2
|
FileFsFullSizeInformationEx, // q: FILE_FS_FULL_SIZE_INFORMATION_EX // since REDSTONE5
|
||||||
FileFsMaximumInformation
|
FileFsGuidInformation, // q: FILE_FS_GUID_INFORMATION // since 23H2
|
||||||
|
FileFsMaximumInformation
|
||||||
} FSINFOCLASS, *PFSINFOCLASS;
|
} FSINFOCLASS, *PFSINFOCLASS;
|
||||||
|
|
||||||
typedef enum _FSINFOCLASS FS_INFORMATION_CLASS;
|
typedef enum _FSINFOCLASS FS_INFORMATION_CLASS;
|
||||||
|
|
||||||
typedef enum _FILE_INFORMATION_CLASS
|
typedef enum _FILE_INFORMATION_CLASS
|
||||||
{
|
{
|
||||||
FileDirectoryInformation = 1,
|
FileDirectoryInformation = 1,
|
||||||
// q: FILE_DIRECTORY_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
// q: FILE_DIRECTORY_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||||
FileFullDirectoryInformation,
|
FileFullDirectoryInformation,
|
||||||
// q: FILE_FULL_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
// q: FILE_FULL_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||||
FileBothDirectoryInformation,
|
FileBothDirectoryInformation,
|
||||||
// q: FILE_BOTH_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
// q: FILE_BOTH_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||||
FileBasicInformation,
|
FileBasicInformation,
|
||||||
// q; s: FILE_BASIC_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
|
// q; s: FILE_BASIC_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
|
||||||
FileStandardInformation, // q: FILE_STANDARD_INFORMATION, FILE_STANDARD_INFORMATION_EX
|
FileStandardInformation, // q: FILE_STANDARD_INFORMATION, FILE_STANDARD_INFORMATION_EX
|
||||||
FileInternalInformation, // q: FILE_INTERNAL_INFORMATION
|
FileInternalInformation, // q: FILE_INTERNAL_INFORMATION
|
||||||
FileEaInformation, // q: FILE_EA_INFORMATION
|
FileEaInformation, // q: FILE_EA_INFORMATION
|
||||||
FileAccessInformation, // q: FILE_ACCESS_INFORMATION
|
FileAccessInformation, // q: FILE_ACCESS_INFORMATION
|
||||||
FileNameInformation, // q: FILE_NAME_INFORMATION
|
FileNameInformation, // q: FILE_NAME_INFORMATION
|
||||||
FileRenameInformation, // s: FILE_RENAME_INFORMATION (requires DELETE) // 10
|
FileRenameInformation, // s: FILE_RENAME_INFORMATION (requires DELETE) // 10
|
||||||
FileLinkInformation, // s: FILE_LINK_INFORMATION
|
FileLinkInformation, // s: FILE_LINK_INFORMATION
|
||||||
FileNamesInformation, // q: FILE_NAMES_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
FileNamesInformation, // q: FILE_NAMES_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||||
FileDispositionInformation, // s: FILE_DISPOSITION_INFORMATION (requires DELETE)
|
FileDispositionInformation, // s: FILE_DISPOSITION_INFORMATION (requires DELETE)
|
||||||
FilePositionInformation, // q; s: FILE_POSITION_INFORMATION
|
FilePositionInformation, // q; s: FILE_POSITION_INFORMATION
|
||||||
FileFullEaInformation, // FILE_FULL_EA_INFORMATION
|
FileFullEaInformation, // FILE_FULL_EA_INFORMATION
|
||||||
FileModeInformation, // q; s: FILE_MODE_INFORMATION
|
FileModeInformation, // q; s: FILE_MODE_INFORMATION
|
||||||
FileAlignmentInformation, // q: FILE_ALIGNMENT_INFORMATION
|
FileAlignmentInformation, // q: FILE_ALIGNMENT_INFORMATION
|
||||||
FileAllInformation, // q: FILE_ALL_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
FileAllInformation, // q: FILE_ALL_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||||
FileAllocationInformation, // s: FILE_ALLOCATION_INFORMATION (requires FILE_WRITE_DATA)
|
FileAllocationInformation, // s: FILE_ALLOCATION_INFORMATION (requires FILE_WRITE_DATA)
|
||||||
FileEndOfFileInformation, // s: FILE_END_OF_FILE_INFORMATION (requires FILE_WRITE_DATA) // 20
|
FileEndOfFileInformation, // s: FILE_END_OF_FILE_INFORMATION (requires FILE_WRITE_DATA) // 20
|
||||||
FileAlternateNameInformation, // q: FILE_NAME_INFORMATION
|
FileAlternateNameInformation, // q: FILE_NAME_INFORMATION
|
||||||
FileStreamInformation, // q: FILE_STREAM_INFORMATION
|
FileStreamInformation, // q: FILE_STREAM_INFORMATION
|
||||||
FilePipeInformation,
|
FilePipeInformation,
|
||||||
// q; s: FILE_PIPE_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
|
// q; s: FILE_PIPE_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
|
||||||
FilePipeLocalInformation, // q: FILE_PIPE_LOCAL_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
FilePipeLocalInformation, // q: FILE_PIPE_LOCAL_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||||
FilePipeRemoteInformation,
|
FilePipeRemoteInformation,
|
||||||
// q; s: FILE_PIPE_REMOTE_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
|
// q; s: FILE_PIPE_REMOTE_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
|
||||||
FileMailslotQueryInformation, // q: FILE_MAILSLOT_QUERY_INFORMATION
|
FileMailslotQueryInformation, // q: FILE_MAILSLOT_QUERY_INFORMATION
|
||||||
FileMailslotSetInformation, // s: FILE_MAILSLOT_SET_INFORMATION
|
FileMailslotSetInformation, // s: FILE_MAILSLOT_SET_INFORMATION
|
||||||
FileCompressionInformation, // q: FILE_COMPRESSION_INFORMATION
|
FileCompressionInformation, // q: FILE_COMPRESSION_INFORMATION
|
||||||
FileObjectIdInformation, // q: FILE_OBJECTID_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
FileObjectIdInformation, // q: FILE_OBJECTID_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||||
FileCompletionInformation, // s: FILE_COMPLETION_INFORMATION // 30
|
FileCompletionInformation, // s: FILE_COMPLETION_INFORMATION // 30
|
||||||
FileMoveClusterInformation, // s: FILE_MOVE_CLUSTER_INFORMATION (requires FILE_WRITE_DATA)
|
FileMoveClusterInformation, // s: FILE_MOVE_CLUSTER_INFORMATION (requires FILE_WRITE_DATA)
|
||||||
FileQuotaInformation, // q: FILE_QUOTA_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
FileQuotaInformation, // q: FILE_QUOTA_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||||
FileReparsePointInformation,
|
FileReparsePointInformation,
|
||||||
// q: FILE_REPARSE_POINT_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
// q: FILE_REPARSE_POINT_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||||
FileNetworkOpenInformation, // q: FILE_NETWORK_OPEN_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
FileNetworkOpenInformation, // q: FILE_NETWORK_OPEN_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||||
FileAttributeTagInformation, // q: FILE_ATTRIBUTE_TAG_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
FileAttributeTagInformation, // q: FILE_ATTRIBUTE_TAG_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||||
FileTrackingInformation, // s: FILE_TRACKING_INFORMATION (requires FILE_WRITE_DATA)
|
FileTrackingInformation, // s: FILE_TRACKING_INFORMATION (requires FILE_WRITE_DATA)
|
||||||
FileIdBothDirectoryInformation,
|
FileIdBothDirectoryInformation,
|
||||||
// q: FILE_ID_BOTH_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
// q: FILE_ID_BOTH_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||||
FileIdFullDirectoryInformation,
|
FileIdFullDirectoryInformation,
|
||||||
// q: FILE_ID_FULL_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
// q: FILE_ID_FULL_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||||
FileValidDataLengthInformation,
|
FileValidDataLengthInformation,
|
||||||
// s: FILE_VALID_DATA_LENGTH_INFORMATION (requires FILE_WRITE_DATA and/or SeManageVolumePrivilege)
|
// s: FILE_VALID_DATA_LENGTH_INFORMATION (requires FILE_WRITE_DATA and/or SeManageVolumePrivilege)
|
||||||
FileShortNameInformation, // s: FILE_NAME_INFORMATION (requires DELETE) // 40
|
FileShortNameInformation, // s: FILE_NAME_INFORMATION (requires DELETE) // 40
|
||||||
FileIoCompletionNotificationInformation,
|
FileIoCompletionNotificationInformation,
|
||||||
// q; s: FILE_IO_COMPLETION_NOTIFICATION_INFORMATION (q: requires FILE_READ_ATTRIBUTES) // since VISTA
|
// q; s: FILE_IO_COMPLETION_NOTIFICATION_INFORMATION (q: requires FILE_READ_ATTRIBUTES) // since VISTA
|
||||||
FileIoStatusBlockRangeInformation, // s: FILE_IOSTATUSBLOCK_RANGE_INFORMATION (requires SeLockMemoryPrivilege)
|
FileIoStatusBlockRangeInformation, // s: FILE_IOSTATUSBLOCK_RANGE_INFORMATION (requires SeLockMemoryPrivilege)
|
||||||
FileIoPriorityHintInformation,
|
FileIoPriorityHintInformation,
|
||||||
// q; s: FILE_IO_PRIORITY_HINT_INFORMATION, FILE_IO_PRIORITY_HINT_INFORMATION_EX (q: requires FILE_READ_DATA)
|
// q; s: FILE_IO_PRIORITY_HINT_INFORMATION, FILE_IO_PRIORITY_HINT_INFORMATION_EX (q: requires FILE_READ_DATA)
|
||||||
FileSfioReserveInformation, // q; s: FILE_SFIO_RESERVE_INFORMATION (q: requires FILE_READ_DATA)
|
FileSfioReserveInformation, // q; s: FILE_SFIO_RESERVE_INFORMATION (q: requires FILE_READ_DATA)
|
||||||
FileSfioVolumeInformation, // q: FILE_SFIO_VOLUME_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
FileSfioVolumeInformation, // q: FILE_SFIO_VOLUME_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||||
FileHardLinkInformation, // q: FILE_LINKS_INFORMATION
|
FileHardLinkInformation, // q: FILE_LINKS_INFORMATION
|
||||||
FileProcessIdsUsingFileInformation, // q: FILE_PROCESS_IDS_USING_FILE_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
FileProcessIdsUsingFileInformation, // q: FILE_PROCESS_IDS_USING_FILE_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||||
FileNormalizedNameInformation, // q: FILE_NAME_INFORMATION
|
FileNormalizedNameInformation, // q: FILE_NAME_INFORMATION
|
||||||
FileNetworkPhysicalNameInformation, // q: FILE_NETWORK_PHYSICAL_NAME_INFORMATION
|
FileNetworkPhysicalNameInformation, // q: FILE_NETWORK_PHYSICAL_NAME_INFORMATION
|
||||||
FileIdGlobalTxDirectoryInformation,
|
FileIdGlobalTxDirectoryInformation,
|
||||||
// q: FILE_ID_GLOBAL_TX_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // since WIN7 // 50
|
// q: FILE_ID_GLOBAL_TX_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // since WIN7 //
|
||||||
FileIsRemoteDeviceInformation, // q: FILE_IS_REMOTE_DEVICE_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
// 50
|
||||||
FileUnusedInformation,
|
FileIsRemoteDeviceInformation, // q: FILE_IS_REMOTE_DEVICE_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||||
FileNumaNodeInformation, // q: FILE_NUMA_NODE_INFORMATION
|
FileUnusedInformation,
|
||||||
FileStandardLinkInformation, // q: FILE_STANDARD_LINK_INFORMATION
|
FileNumaNodeInformation, // q: FILE_NUMA_NODE_INFORMATION
|
||||||
FileRemoteProtocolInformation, // q: FILE_REMOTE_PROTOCOL_INFORMATION
|
FileStandardLinkInformation, // q: FILE_STANDARD_LINK_INFORMATION
|
||||||
FileRenameInformationBypassAccessCheck, // (kernel-mode only); s: FILE_RENAME_INFORMATION // since WIN8
|
FileRemoteProtocolInformation, // q: FILE_REMOTE_PROTOCOL_INFORMATION
|
||||||
FileLinkInformationBypassAccessCheck, // (kernel-mode only); s: FILE_LINK_INFORMATION
|
FileRenameInformationBypassAccessCheck, // (kernel-mode only); s: FILE_RENAME_INFORMATION // since WIN8
|
||||||
FileVolumeNameInformation, // q: FILE_VOLUME_NAME_INFORMATION
|
FileLinkInformationBypassAccessCheck, // (kernel-mode only); s: FILE_LINK_INFORMATION
|
||||||
FileIdInformation, // q: FILE_ID_INFORMATION
|
FileVolumeNameInformation, // q: FILE_VOLUME_NAME_INFORMATION
|
||||||
FileIdExtdDirectoryInformation,
|
FileIdInformation, // q: FILE_ID_INFORMATION
|
||||||
// q: FILE_ID_EXTD_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // 60
|
FileIdExtdDirectoryInformation,
|
||||||
FileReplaceCompletionInformation, // s: FILE_COMPLETION_INFORMATION // since WINBLUE
|
// q: FILE_ID_EXTD_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // 60
|
||||||
FileHardLinkFullIdInformation, // q: FILE_LINK_ENTRY_FULL_ID_INFORMATION // FILE_LINKS_FULL_ID_INFORMATION
|
FileReplaceCompletionInformation, // s: FILE_COMPLETION_INFORMATION // since WINBLUE
|
||||||
FileIdExtdBothDirectoryInformation,
|
FileHardLinkFullIdInformation, // q: FILE_LINK_ENTRY_FULL_ID_INFORMATION // FILE_LINKS_FULL_ID_INFORMATION
|
||||||
// q: FILE_ID_EXTD_BOTH_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // since THRESHOLD
|
FileIdExtdBothDirectoryInformation,
|
||||||
FileDispositionInformationEx, // s: FILE_DISPOSITION_INFO_EX (requires DELETE) // since REDSTONE
|
// q: FILE_ID_EXTD_BOTH_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // since THRESHOLD
|
||||||
FileRenameInformationEx, // s: FILE_RENAME_INFORMATION_EX
|
FileDispositionInformationEx, // s: FILE_DISPOSITION_INFO_EX (requires DELETE) // since REDSTONE
|
||||||
FileRenameInformationExBypassAccessCheck, // (kernel-mode only); s: FILE_RENAME_INFORMATION_EX
|
FileRenameInformationEx, // s: FILE_RENAME_INFORMATION_EX
|
||||||
FileDesiredStorageClassInformation,
|
FileRenameInformationExBypassAccessCheck, // (kernel-mode only); s: FILE_RENAME_INFORMATION_EX
|
||||||
// q; s: FILE_DESIRED_STORAGE_CLASS_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES) // since REDSTONE2
|
FileDesiredStorageClassInformation,
|
||||||
FileStatInformation, // q: FILE_STAT_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
// q; s: FILE_DESIRED_STORAGE_CLASS_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires
|
||||||
FileMemoryPartitionInformation, // s: FILE_MEMORY_PARTITION_INFORMATION // since REDSTONE3
|
// FILE_WRITE_ATTRIBUTES) // since REDSTONE2
|
||||||
FileStatLxInformation,
|
FileStatInformation, // q: FILE_STAT_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||||
// q: FILE_STAT_LX_INFORMATION (requires FILE_READ_ATTRIBUTES and FILE_READ_EA) // since REDSTONE4 // 70
|
FileMemoryPartitionInformation, // s: FILE_MEMORY_PARTITION_INFORMATION // since REDSTONE3
|
||||||
FileCaseSensitiveInformation,
|
FileStatLxInformation,
|
||||||
// q; s: FILE_CASE_SENSITIVE_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
|
// q: FILE_STAT_LX_INFORMATION (requires FILE_READ_ATTRIBUTES and FILE_READ_EA) // since REDSTONE4 // 70
|
||||||
FileLinkInformationEx, // s: FILE_LINK_INFORMATION_EX // since REDSTONE5
|
FileCaseSensitiveInformation,
|
||||||
FileLinkInformationExBypassAccessCheck, // (kernel-mode only); s: FILE_LINK_INFORMATION_EX
|
// q; s: FILE_CASE_SENSITIVE_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
|
||||||
FileStorageReserveIdInformation,
|
FileLinkInformationEx, // s: FILE_LINK_INFORMATION_EX // since REDSTONE5
|
||||||
// q; s: FILE_STORAGE_RESERVE_ID_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
|
FileLinkInformationExBypassAccessCheck, // (kernel-mode only); s: FILE_LINK_INFORMATION_EX
|
||||||
FileCaseSensitiveInformationForceAccessCheck, // q; s: FILE_CASE_SENSITIVE_INFORMATION
|
FileStorageReserveIdInformation,
|
||||||
FileKnownFolderInformation,
|
// q; s: FILE_STORAGE_RESERVE_ID_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
|
||||||
// q; s: FILE_KNOWN_FOLDER_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES) // since WIN11
|
FileCaseSensitiveInformationForceAccessCheck, // q; s: FILE_CASE_SENSITIVE_INFORMATION
|
||||||
FileStatBasicInformation, // since 23H2
|
FileKnownFolderInformation,
|
||||||
FileId64ExtdDirectoryInformation, // FILE_ID_64_EXTD_DIR_INFORMATION
|
// q; s: FILE_KNOWN_FOLDER_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES) //
|
||||||
FileId64ExtdBothDirectoryInformation, // FILE_ID_64_EXTD_BOTH_DIR_INFORMATION
|
// since WIN11
|
||||||
FileIdAllExtdDirectoryInformation, // FILE_ID_ALL_EXTD_DIR_INFORMATION
|
FileStatBasicInformation, // since 23H2
|
||||||
FileIdAllExtdBothDirectoryInformation, // FILE_ID_ALL_EXTD_BOTH_DIR_INFORMATION
|
FileId64ExtdDirectoryInformation, // FILE_ID_64_EXTD_DIR_INFORMATION
|
||||||
FileStreamReservationInformation, // FILE_STREAM_RESERVATION_INFORMATION // since 24H2
|
FileId64ExtdBothDirectoryInformation, // FILE_ID_64_EXTD_BOTH_DIR_INFORMATION
|
||||||
FileMupProviderInfo, // MUP_PROVIDER_INFORMATION
|
FileIdAllExtdDirectoryInformation, // FILE_ID_ALL_EXTD_DIR_INFORMATION
|
||||||
FileMaximumInformation
|
FileIdAllExtdBothDirectoryInformation, // FILE_ID_ALL_EXTD_BOTH_DIR_INFORMATION
|
||||||
|
FileStreamReservationInformation, // FILE_STREAM_RESERVATION_INFORMATION // since 24H2
|
||||||
|
FileMupProviderInfo, // MUP_PROVIDER_INFORMATION
|
||||||
|
FileMaximumInformation
|
||||||
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
|
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
|
||||||
|
|
||||||
typedef enum _OBJECT_INFORMATION_CLASS
|
typedef enum _OBJECT_INFORMATION_CLASS
|
||||||
{
|
{
|
||||||
ObjectBasicInformation, // q: OBJECT_BASIC_INFORMATION
|
ObjectBasicInformation, // q: OBJECT_BASIC_INFORMATION
|
||||||
ObjectNameInformation, // q: OBJECT_NAME_INFORMATION
|
ObjectNameInformation, // q: OBJECT_NAME_INFORMATION
|
||||||
ObjectTypeInformation, // q: OBJECT_TYPE_INFORMATION
|
ObjectTypeInformation, // q: OBJECT_TYPE_INFORMATION
|
||||||
ObjectTypesInformation, // q: OBJECT_TYPES_INFORMATION
|
ObjectTypesInformation, // q: OBJECT_TYPES_INFORMATION
|
||||||
ObjectHandleFlagInformation, // qs: OBJECT_HANDLE_FLAG_INFORMATION
|
ObjectHandleFlagInformation, // qs: OBJECT_HANDLE_FLAG_INFORMATION
|
||||||
ObjectSessionInformation, // s: void // change object session // (requires SeTcbPrivilege)
|
ObjectSessionInformation, // s: void // change object session // (requires SeTcbPrivilege)
|
||||||
ObjectSessionObjectInformation, // s: void // change object session // (requires SeTcbPrivilege)
|
ObjectSessionObjectInformation, // s: void // change object session // (requires SeTcbPrivilege)
|
||||||
MaxObjectInfoClass
|
MaxObjectInfoClass
|
||||||
} OBJECT_INFORMATION_CLASS;
|
} OBJECT_INFORMATION_CLASS;
|
||||||
|
|
||||||
typedef enum _HARDERROR_RESPONSE_OPTION
|
typedef enum _HARDERROR_RESPONSE_OPTION
|
||||||
{
|
{
|
||||||
OptionAbortRetryIgnore,
|
OptionAbortRetryIgnore,
|
||||||
OptionOk,
|
OptionOk,
|
||||||
OptionOkCancel,
|
OptionOkCancel,
|
||||||
OptionRetryCancel,
|
OptionRetryCancel,
|
||||||
OptionYesNo,
|
OptionYesNo,
|
||||||
OptionYesNoCancel,
|
OptionYesNoCancel,
|
||||||
OptionShutdownSystem,
|
OptionShutdownSystem,
|
||||||
OptionOkNoWait,
|
OptionOkNoWait,
|
||||||
OptionCancelTryContinue
|
OptionCancelTryContinue
|
||||||
} HARDERROR_RESPONSE_OPTION;
|
} HARDERROR_RESPONSE_OPTION;
|
||||||
|
|
||||||
typedef enum _HARDERROR_RESPONSE
|
typedef enum _HARDERROR_RESPONSE
|
||||||
{
|
{
|
||||||
ResponseReturnToCaller,
|
ResponseReturnToCaller,
|
||||||
ResponseNotHandled,
|
ResponseNotHandled,
|
||||||
ResponseAbort,
|
ResponseAbort,
|
||||||
ResponseCancel,
|
ResponseCancel,
|
||||||
ResponseIgnore,
|
ResponseIgnore,
|
||||||
ResponseNo,
|
ResponseNo,
|
||||||
ResponseOk,
|
ResponseOk,
|
||||||
ResponseRetry,
|
ResponseRetry,
|
||||||
ResponseYes,
|
ResponseYes,
|
||||||
ResponseTryAgain,
|
ResponseTryAgain,
|
||||||
ResponseContinue
|
ResponseContinue
|
||||||
} HARDERROR_RESPONSE;
|
} HARDERROR_RESPONSE;
|
||||||
|
|
||||||
typedef USHORT RTL_ATOM;
|
typedef USHORT RTL_ATOM;
|
||||||
@@ -248,113 +252,112 @@ typedef USHORT RTL_ATOM;
|
|||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
struct IO_STATUS_BLOCK
|
struct IO_STATUS_BLOCK
|
||||||
{
|
{
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
NTSTATUS Status;
|
NTSTATUS Status;
|
||||||
typename Traits::PVOID Pointer;
|
typename Traits::PVOID Pointer;
|
||||||
};
|
};
|
||||||
|
|
||||||
typename Traits::ULONG_PTR Information;
|
typename Traits::ULONG_PTR Information;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
struct OBJECT_ATTRIBUTES
|
struct OBJECT_ATTRIBUTES
|
||||||
{
|
{
|
||||||
ULONG Length;
|
ULONG Length;
|
||||||
typename Traits::HANDLE RootDirectory;
|
typename Traits::HANDLE RootDirectory;
|
||||||
EMULATOR_CAST(typename Traits::PVOID, UNICODE_STRING*) ObjectName;
|
EMULATOR_CAST(typename Traits::PVOID, UNICODE_STRING*) ObjectName;
|
||||||
ULONG Attributes;
|
ULONG Attributes;
|
||||||
typename Traits::PVOID SecurityDescriptor; // PSECURITY_DESCRIPTOR;
|
typename Traits::PVOID SecurityDescriptor; // PSECURITY_DESCRIPTOR;
|
||||||
typename Traits::PVOID SecurityQualityOfService; // PSECURITY_QUALITY_OF_SERVICE
|
typename Traits::PVOID SecurityQualityOfService; // PSECURITY_QUALITY_OF_SERVICE
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct _FILE_FS_DEVICE_INFORMATION
|
typedef struct _FILE_FS_DEVICE_INFORMATION
|
||||||
{
|
{
|
||||||
DEVICE_TYPE DeviceType;
|
DEVICE_TYPE DeviceType;
|
||||||
ULONG Characteristics;
|
ULONG Characteristics;
|
||||||
} FILE_FS_DEVICE_INFORMATION, *PFILE_FS_DEVICE_INFORMATION;
|
} FILE_FS_DEVICE_INFORMATION, *PFILE_FS_DEVICE_INFORMATION;
|
||||||
|
|
||||||
typedef struct _FILE_POSITION_INFORMATION
|
typedef struct _FILE_POSITION_INFORMATION
|
||||||
{
|
{
|
||||||
LARGE_INTEGER CurrentByteOffset;
|
LARGE_INTEGER CurrentByteOffset;
|
||||||
} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION;
|
} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION;
|
||||||
|
|
||||||
typedef struct _FILE_STANDARD_INFORMATION
|
typedef struct _FILE_STANDARD_INFORMATION
|
||||||
{
|
{
|
||||||
LARGE_INTEGER AllocationSize;
|
LARGE_INTEGER AllocationSize;
|
||||||
LARGE_INTEGER EndOfFile;
|
LARGE_INTEGER EndOfFile;
|
||||||
ULONG NumberOfLinks;
|
ULONG NumberOfLinks;
|
||||||
BOOLEAN DeletePending;
|
BOOLEAN DeletePending;
|
||||||
BOOLEAN Directory;
|
BOOLEAN Directory;
|
||||||
} FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION;
|
} FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION;
|
||||||
|
|
||||||
typedef struct _FILE_NAME_INFORMATION
|
typedef struct _FILE_NAME_INFORMATION
|
||||||
{
|
{
|
||||||
ULONG FileNameLength;
|
ULONG FileNameLength;
|
||||||
char16_t FileName[1];
|
char16_t FileName[1];
|
||||||
} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION;
|
} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION;
|
||||||
|
|
||||||
typedef struct _FILE_BASIC_INFORMATION
|
typedef struct _FILE_BASIC_INFORMATION
|
||||||
{
|
{
|
||||||
LARGE_INTEGER CreationTime; // Specifies the time that the file was created.
|
LARGE_INTEGER CreationTime; // Specifies the time that the file was created.
|
||||||
LARGE_INTEGER LastAccessTime; // Specifies the time that the file was last accessed.
|
LARGE_INTEGER LastAccessTime; // Specifies the time that the file was last accessed.
|
||||||
LARGE_INTEGER LastWriteTime; // Specifies the time that the file was last written to.
|
LARGE_INTEGER LastWriteTime; // Specifies the time that the file was last written to.
|
||||||
LARGE_INTEGER ChangeTime; // Specifies the last time the file was changed.
|
LARGE_INTEGER ChangeTime; // Specifies the last time the file was changed.
|
||||||
ULONG FileAttributes; // Specifies one or more FILE_ATTRIBUTE_XXX flags.
|
ULONG FileAttributes; // Specifies one or more FILE_ATTRIBUTE_XXX flags.
|
||||||
} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
|
} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
|
||||||
|
|
||||||
typedef struct _FILE_DIRECTORY_INFORMATION
|
typedef struct _FILE_DIRECTORY_INFORMATION
|
||||||
{
|
{
|
||||||
ULONG NextEntryOffset;
|
ULONG NextEntryOffset;
|
||||||
ULONG FileIndex;
|
ULONG FileIndex;
|
||||||
LARGE_INTEGER CreationTime;
|
LARGE_INTEGER CreationTime;
|
||||||
LARGE_INTEGER LastAccessTime;
|
LARGE_INTEGER LastAccessTime;
|
||||||
LARGE_INTEGER LastWriteTime;
|
LARGE_INTEGER LastWriteTime;
|
||||||
LARGE_INTEGER ChangeTime;
|
LARGE_INTEGER ChangeTime;
|
||||||
LARGE_INTEGER EndOfFile;
|
LARGE_INTEGER EndOfFile;
|
||||||
LARGE_INTEGER AllocationSize;
|
LARGE_INTEGER AllocationSize;
|
||||||
ULONG FileAttributes;
|
ULONG FileAttributes;
|
||||||
ULONG FileNameLength;
|
ULONG FileNameLength;
|
||||||
char16_t FileName[1];
|
char16_t FileName[1];
|
||||||
} FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION;
|
} FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION;
|
||||||
|
|
||||||
typedef struct _FILE_FULL_DIR_INFORMATION
|
typedef struct _FILE_FULL_DIR_INFORMATION
|
||||||
{
|
{
|
||||||
ULONG NextEntryOffset;
|
ULONG NextEntryOffset;
|
||||||
ULONG FileIndex;
|
ULONG FileIndex;
|
||||||
LARGE_INTEGER CreationTime;
|
LARGE_INTEGER CreationTime;
|
||||||
LARGE_INTEGER LastAccessTime;
|
LARGE_INTEGER LastAccessTime;
|
||||||
LARGE_INTEGER LastWriteTime;
|
LARGE_INTEGER LastWriteTime;
|
||||||
LARGE_INTEGER ChangeTime;
|
LARGE_INTEGER ChangeTime;
|
||||||
LARGE_INTEGER EndOfFile;
|
LARGE_INTEGER EndOfFile;
|
||||||
LARGE_INTEGER AllocationSize;
|
LARGE_INTEGER AllocationSize;
|
||||||
ULONG FileAttributes;
|
ULONG FileAttributes;
|
||||||
ULONG FileNameLength;
|
ULONG FileNameLength;
|
||||||
ULONG EaSize;
|
ULONG EaSize;
|
||||||
char16_t FileName[1];
|
char16_t FileName[1];
|
||||||
} FILE_FULL_DIR_INFORMATION, *PFILE_FULL_DIR_INFORMATION;
|
} FILE_FULL_DIR_INFORMATION, *PFILE_FULL_DIR_INFORMATION;
|
||||||
|
|
||||||
typedef struct _FILE_BOTH_DIR_INFORMATION
|
typedef struct _FILE_BOTH_DIR_INFORMATION
|
||||||
{
|
{
|
||||||
ULONG NextEntryOffset;
|
ULONG NextEntryOffset;
|
||||||
ULONG FileIndex;
|
ULONG FileIndex;
|
||||||
LARGE_INTEGER CreationTime;
|
LARGE_INTEGER CreationTime;
|
||||||
LARGE_INTEGER LastAccessTime;
|
LARGE_INTEGER LastAccessTime;
|
||||||
LARGE_INTEGER LastWriteTime;
|
LARGE_INTEGER LastWriteTime;
|
||||||
LARGE_INTEGER ChangeTime;
|
LARGE_INTEGER ChangeTime;
|
||||||
LARGE_INTEGER EndOfFile;
|
LARGE_INTEGER EndOfFile;
|
||||||
LARGE_INTEGER AllocationSize;
|
LARGE_INTEGER AllocationSize;
|
||||||
ULONG FileAttributes;
|
ULONG FileAttributes;
|
||||||
ULONG FileNameLength;
|
ULONG FileNameLength;
|
||||||
ULONG EaSize;
|
ULONG EaSize;
|
||||||
char ShortNameLength;
|
char ShortNameLength;
|
||||||
char16_t ShortName[12];
|
char16_t ShortName[12];
|
||||||
char16_t FileName[1];
|
char16_t FileName[1];
|
||||||
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;
|
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;
|
||||||
|
|
||||||
#ifndef OS_WINDOWS
|
#ifndef OS_WINDOWS
|
||||||
typedef BOOLEAN SECURITY_CONTEXT_TRACKING_MODE,
|
typedef BOOLEAN SECURITY_CONTEXT_TRACKING_MODE, *PSECURITY_CONTEXT_TRACKING_MODE;
|
||||||
* PSECURITY_CONTEXT_TRACKING_MODE;
|
|
||||||
typedef struct _SECURITY_QUALITY_OF_SERVICE
|
typedef struct _SECURITY_QUALITY_OF_SERVICE
|
||||||
{
|
{
|
||||||
DWORD Length;
|
DWORD Length;
|
||||||
@@ -367,17 +370,17 @@ typedef struct _SECURITY_QUALITY_OF_SERVICE
|
|||||||
|
|
||||||
typedef struct _PORT_VIEW64
|
typedef struct _PORT_VIEW64
|
||||||
{
|
{
|
||||||
ULONG Length;
|
ULONG Length;
|
||||||
EMULATOR_CAST(std::uint64_t, HANDLE) SectionHandle;
|
EMULATOR_CAST(std::uint64_t, HANDLE) SectionHandle;
|
||||||
ULONG SectionOffset;
|
ULONG SectionOffset;
|
||||||
EMULATOR_CAST(std::int64_t, SIZE_T) ViewSize;
|
EMULATOR_CAST(std::int64_t, SIZE_T) ViewSize;
|
||||||
EmulatorTraits<Emu64>::PVOID ViewBase;
|
EmulatorTraits<Emu64>::PVOID ViewBase;
|
||||||
EmulatorTraits<Emu64>::PVOID ViewRemoteBase;
|
EmulatorTraits<Emu64>::PVOID ViewRemoteBase;
|
||||||
} PORT_VIEW64, *PPORT_VIEW64;
|
} PORT_VIEW64, *PPORT_VIEW64;
|
||||||
|
|
||||||
typedef struct _REMOTE_PORT_VIEW64
|
typedef struct _REMOTE_PORT_VIEW64
|
||||||
{
|
{
|
||||||
ULONG Length;
|
ULONG Length;
|
||||||
EMULATOR_CAST(std::int64_t, SIZE_T) ViewSize;
|
EMULATOR_CAST(std::int64_t, SIZE_T) ViewSize;
|
||||||
EmulatorTraits<Emu64>::PVOID ViewBase;
|
EmulatorTraits<Emu64>::PVOID ViewBase;
|
||||||
} REMOTE_PORT_VIEW64, *PREMOTE_PORT_VIEW64;
|
} REMOTE_PORT_VIEW64, *PREMOTE_PORT_VIEW64;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,21 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define PAGE_EXECUTE 0x10
|
#define PAGE_EXECUTE 0x10
|
||||||
#define PAGE_EXECUTE_READ 0x20
|
#define PAGE_EXECUTE_READ 0x20
|
||||||
#define PAGE_EXECUTE_READWRITE 0x40
|
#define PAGE_EXECUTE_READWRITE 0x40
|
||||||
#define PAGE_EXECUTE_WRITECOPY 0x80
|
#define PAGE_EXECUTE_WRITECOPY 0x80
|
||||||
|
|
||||||
#define PAGE_NOACCESS 0x01
|
#define PAGE_NOACCESS 0x01
|
||||||
#define PAGE_READONLY 0x02
|
#define PAGE_READONLY 0x02
|
||||||
#define PAGE_READWRITE 0x04
|
#define PAGE_READWRITE 0x04
|
||||||
#define PAGE_WRITECOPY 0x08
|
#define PAGE_WRITECOPY 0x08
|
||||||
|
|
||||||
#define PAGE_TARGETS_INVALID 0x40000000
|
#define PAGE_TARGETS_INVALID 0x40000000
|
||||||
#define PAGE_TARGETS_NO_UPDATE 0x40000000
|
#define PAGE_TARGETS_NO_UPDATE 0x40000000
|
||||||
|
|
||||||
#define PAGE_GUARD 0x100
|
#define PAGE_GUARD 0x100
|
||||||
#define PAGE_NOCACHE 0x200
|
#define PAGE_NOCACHE 0x200
|
||||||
#define PAGE_WRITECOMBINE 0x400
|
#define PAGE_WRITECOMBINE 0x400
|
||||||
|
|
||||||
#define MEM_COMMIT 0x00001000
|
#define MEM_COMMIT 0x00001000
|
||||||
#define MEM_RESERVE 0x00002000
|
#define MEM_RESERVE 0x00002000
|
||||||
@@ -38,94 +38,92 @@
|
|||||||
|
|
||||||
typedef enum _MEMORY_INFORMATION_CLASS
|
typedef enum _MEMORY_INFORMATION_CLASS
|
||||||
{
|
{
|
||||||
MemoryBasicInformation, // q: MEMORY_BASIC_INFORMATION
|
MemoryBasicInformation, // q: MEMORY_BASIC_INFORMATION
|
||||||
MemoryWorkingSetInformation, // q: MEMORY_WORKING_SET_INFORMATION
|
MemoryWorkingSetInformation, // q: MEMORY_WORKING_SET_INFORMATION
|
||||||
MemoryMappedFilenameInformation, // q: UNICODE_STRING
|
MemoryMappedFilenameInformation, // q: UNICODE_STRING
|
||||||
MemoryRegionInformation, // q: MEMORY_REGION_INFORMATION
|
MemoryRegionInformation, // q: MEMORY_REGION_INFORMATION
|
||||||
MemoryWorkingSetExInformation, // q: MEMORY_WORKING_SET_EX_INFORMATION // since VISTA
|
MemoryWorkingSetExInformation, // q: MEMORY_WORKING_SET_EX_INFORMATION // since VISTA
|
||||||
MemorySharedCommitInformation, // q: MEMORY_SHARED_COMMIT_INFORMATION // since WIN8
|
MemorySharedCommitInformation, // q: MEMORY_SHARED_COMMIT_INFORMATION // since WIN8
|
||||||
MemoryImageInformation, // q: MEMORY_IMAGE_INFORMATION
|
MemoryImageInformation, // q: MEMORY_IMAGE_INFORMATION
|
||||||
MemoryRegionInformationEx, // MEMORY_REGION_INFORMATION
|
MemoryRegionInformationEx, // MEMORY_REGION_INFORMATION
|
||||||
MemoryPrivilegedBasicInformation, // MEMORY_BASIC_INFORMATION
|
MemoryPrivilegedBasicInformation, // MEMORY_BASIC_INFORMATION
|
||||||
MemoryEnclaveImageInformation, // MEMORY_ENCLAVE_IMAGE_INFORMATION // since REDSTONE3
|
MemoryEnclaveImageInformation, // MEMORY_ENCLAVE_IMAGE_INFORMATION // since REDSTONE3
|
||||||
MemoryBasicInformationCapped, // 10
|
MemoryBasicInformationCapped, // 10
|
||||||
MemoryPhysicalContiguityInformation, // MEMORY_PHYSICAL_CONTIGUITY_INFORMATION // since 20H1
|
MemoryPhysicalContiguityInformation, // MEMORY_PHYSICAL_CONTIGUITY_INFORMATION // since 20H1
|
||||||
MemoryBadInformation, // since WIN11
|
MemoryBadInformation, // since WIN11
|
||||||
MemoryBadInformationAllProcesses, // since 22H1
|
MemoryBadInformationAllProcesses, // since 22H1
|
||||||
MemoryImageExtensionInformation, // since 24H2
|
MemoryImageExtensionInformation, // since 24H2
|
||||||
MaxMemoryInfoClass
|
MaxMemoryInfoClass
|
||||||
} MEMORY_INFORMATION_CLASS;
|
} MEMORY_INFORMATION_CLASS;
|
||||||
|
|
||||||
typedef enum _SECTION_INHERIT
|
typedef enum _SECTION_INHERIT
|
||||||
{
|
{
|
||||||
ViewShare = 1,
|
ViewShare = 1,
|
||||||
ViewUnmap = 2
|
ViewUnmap = 2
|
||||||
} SECTION_INHERIT;
|
} SECTION_INHERIT;
|
||||||
|
|
||||||
|
|
||||||
typedef struct DECLSPEC_ALIGN(16) _EMU_MEMORY_BASIC_INFORMATION64
|
typedef struct DECLSPEC_ALIGN(16) _EMU_MEMORY_BASIC_INFORMATION64
|
||||||
{
|
{
|
||||||
void* BaseAddress;
|
void* BaseAddress;
|
||||||
void* AllocationBase;
|
void* AllocationBase;
|
||||||
DWORD AllocationProtect;
|
DWORD AllocationProtect;
|
||||||
WORD PartitionId;
|
WORD PartitionId;
|
||||||
std::int64_t RegionSize;
|
std::int64_t RegionSize;
|
||||||
DWORD State;
|
DWORD State;
|
||||||
DWORD Protect;
|
DWORD Protect;
|
||||||
DWORD Type;
|
DWORD Type;
|
||||||
} EMU_MEMORY_BASIC_INFORMATION64, *PEMU_MEMORY_BASIC_INFORMATION64;
|
} EMU_MEMORY_BASIC_INFORMATION64, *PEMU_MEMORY_BASIC_INFORMATION64;
|
||||||
|
|
||||||
|
|
||||||
typedef struct _MEMORY_IMAGE_INFORMATION64
|
typedef struct _MEMORY_IMAGE_INFORMATION64
|
||||||
{
|
{
|
||||||
void* ImageBase;
|
void* ImageBase;
|
||||||
std::int64_t SizeOfImage;
|
std::int64_t SizeOfImage;
|
||||||
|
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
ULONG ImageFlags;
|
ULONG ImageFlags;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
ULONG ImagePartialMap : 1;
|
ULONG ImagePartialMap : 1;
|
||||||
ULONG ImageNotExecutable : 1;
|
ULONG ImageNotExecutable : 1;
|
||||||
ULONG ImageSigningLevel : 4; // REDSTONE3
|
ULONG ImageSigningLevel : 4; // REDSTONE3
|
||||||
ULONG ImageExtensionPresent : 1; // since 24H2
|
ULONG ImageExtensionPresent : 1; // since 24H2
|
||||||
ULONG Reserved : 25;
|
ULONG Reserved : 25;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
} MEMORY_IMAGE_INFORMATION64, *PMEMORY_IMAGE_INFORMATION64;
|
} MEMORY_IMAGE_INFORMATION64, *PMEMORY_IMAGE_INFORMATION64;
|
||||||
|
|
||||||
typedef struct _MEMORY_REGION_INFORMATION
|
typedef struct _MEMORY_REGION_INFORMATION
|
||||||
{
|
{
|
||||||
void* AllocationBase;
|
void* AllocationBase;
|
||||||
ULONG AllocationProtect;
|
ULONG AllocationProtect;
|
||||||
|
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
ULONG RegionType;
|
ULONG RegionType;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
ULONG Private : 1;
|
ULONG Private : 1;
|
||||||
ULONG MappedDataFile : 1;
|
ULONG MappedDataFile : 1;
|
||||||
ULONG MappedImage : 1;
|
ULONG MappedImage : 1;
|
||||||
ULONG MappedPageFile : 1;
|
ULONG MappedPageFile : 1;
|
||||||
ULONG MappedPhysical : 1;
|
ULONG MappedPhysical : 1;
|
||||||
ULONG DirectMapped : 1;
|
ULONG DirectMapped : 1;
|
||||||
ULONG SoftwareEnclave : 1; // REDSTONE3
|
ULONG SoftwareEnclave : 1; // REDSTONE3
|
||||||
ULONG PageSize64K : 1;
|
ULONG PageSize64K : 1;
|
||||||
ULONG PlaceholderReservation : 1; // REDSTONE4
|
ULONG PlaceholderReservation : 1; // REDSTONE4
|
||||||
ULONG MappedAwe : 1; // 21H1
|
ULONG MappedAwe : 1; // 21H1
|
||||||
ULONG MappedWriteWatch : 1;
|
ULONG MappedWriteWatch : 1;
|
||||||
ULONG PageSizeLarge : 1;
|
ULONG PageSizeLarge : 1;
|
||||||
ULONG PageSizeHuge : 1;
|
ULONG PageSizeHuge : 1;
|
||||||
ULONG Reserved : 19;
|
ULONG Reserved : 19;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
std::int64_t RegionSize;
|
std::int64_t RegionSize;
|
||||||
std::int64_t CommitSize;
|
std::int64_t CommitSize;
|
||||||
DWORD64 PartitionId; // 19H1
|
DWORD64 PartitionId; // 19H1
|
||||||
DWORD64 NodePreference; // 20H1
|
DWORD64 NodePreference; // 20H1
|
||||||
} MEMORY_REGION_INFORMATION64, *PMEMORY_REGION_INFORMATION64;
|
} MEMORY_REGION_INFORMATION64, *PMEMORY_REGION_INFORMATION64;
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
struct EMU_WSABUF
|
struct EMU_WSABUF
|
||||||
{
|
{
|
||||||
ULONG len;
|
ULONG len;
|
||||||
EMULATOR_CAST(typename Traits::PVOID, CHAR*) buf;
|
EMULATOR_CAST(typename Traits::PVOID, CHAR*) buf;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
#pragma warning(disable: 4201) // nameless struct/union
|
#pragma warning(disable : 4201) // nameless struct/union
|
||||||
#pragma warning(disable: 4702) // unreachable code
|
#pragma warning(disable : 4702) // unreachable code
|
||||||
#else
|
#else
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||||
@@ -27,18 +27,12 @@
|
|||||||
#ifdef OS_WINDOWS
|
#ifdef OS_WINDOWS
|
||||||
#pragma comment(lib, "ntdll")
|
#pragma comment(lib, "ntdll")
|
||||||
|
|
||||||
extern "C" {
|
extern "C"
|
||||||
NTSYSCALLAPI
|
{
|
||||||
NTSTATUS
|
NTSYSCALLAPI NTSTATUS NTAPI NtQuerySystemInformationEx(
|
||||||
NTAPI
|
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _In_reads_bytes_(InputBufferLength) PVOID InputBuffer,
|
||||||
NtQuerySystemInformationEx(
|
_In_ ULONG InputBufferLength, _Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation,
|
||||||
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
|
_In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength);
|
||||||
_In_reads_bytes_(InputBufferLength) PVOID InputBuffer,
|
|
||||||
_In_ ULONG InputBufferLength,
|
|
||||||
_Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation,
|
|
||||||
_In_ ULONG SystemInformationLength,
|
|
||||||
_Out_opt_ PULONG ReturnLength
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -20,31 +20,30 @@ using LONGLONG = std::int64_t;
|
|||||||
|
|
||||||
typedef union _ULARGE_INTEGER
|
typedef union _ULARGE_INTEGER
|
||||||
{
|
{
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
DWORD LowPart;
|
DWORD LowPart;
|
||||||
DWORD HighPart;
|
DWORD HighPart;
|
||||||
};
|
};
|
||||||
|
|
||||||
ULONGLONG QuadPart;
|
ULONGLONG QuadPart;
|
||||||
} ULARGE_INTEGER;
|
} ULARGE_INTEGER;
|
||||||
|
|
||||||
typedef union _LARGE_INTEGER
|
typedef union _LARGE_INTEGER
|
||||||
{
|
{
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
DWORD LowPart;
|
DWORD LowPart;
|
||||||
LONG HighPart;
|
LONG HighPart;
|
||||||
};
|
};
|
||||||
|
|
||||||
LONGLONG QuadPart;
|
LONGLONG QuadPart;
|
||||||
} LARGE_INTEGER;
|
} LARGE_INTEGER;
|
||||||
|
|
||||||
using BYTE = std::uint8_t;
|
using BYTE = std::uint8_t;
|
||||||
#define CHAR BYTE
|
#define CHAR BYTE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
using WORD = std::uint16_t;
|
using WORD = std::uint16_t;
|
||||||
|
|
||||||
#define UCHAR unsigned char
|
#define UCHAR unsigned char
|
||||||
@@ -53,7 +52,6 @@ using WORD = std::uint16_t;
|
|||||||
using CSHORT = short;
|
using CSHORT = short;
|
||||||
using USHORT = WORD;
|
using USHORT = WORD;
|
||||||
|
|
||||||
|
|
||||||
#define DUMMYSTRUCTNAME
|
#define DUMMYSTRUCTNAME
|
||||||
|
|
||||||
#ifndef TRUE
|
#ifndef TRUE
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,63 +2,63 @@
|
|||||||
|
|
||||||
typedef enum _KEY_INFORMATION_CLASS
|
typedef enum _KEY_INFORMATION_CLASS
|
||||||
{
|
{
|
||||||
KeyBasicInformation, // KEY_BASIC_INFORMATION
|
KeyBasicInformation, // KEY_BASIC_INFORMATION
|
||||||
KeyNodeInformation, // KEY_NODE_INFORMATION
|
KeyNodeInformation, // KEY_NODE_INFORMATION
|
||||||
KeyFullInformation, // KEY_FULL_INFORMATION
|
KeyFullInformation, // KEY_FULL_INFORMATION
|
||||||
KeyNameInformation, // KEY_NAME_INFORMATION
|
KeyNameInformation, // KEY_NAME_INFORMATION
|
||||||
KeyCachedInformation, // KEY_CACHED_INFORMATION
|
KeyCachedInformation, // KEY_CACHED_INFORMATION
|
||||||
KeyFlagsInformation, // KEY_FLAGS_INFORMATION
|
KeyFlagsInformation, // KEY_FLAGS_INFORMATION
|
||||||
KeyVirtualizationInformation, // KEY_VIRTUALIZATION_INFORMATION
|
KeyVirtualizationInformation, // KEY_VIRTUALIZATION_INFORMATION
|
||||||
KeyHandleTagsInformation, // KEY_HANDLE_TAGS_INFORMATION
|
KeyHandleTagsInformation, // KEY_HANDLE_TAGS_INFORMATION
|
||||||
KeyTrustInformation, // KEY_TRUST_INFORMATION
|
KeyTrustInformation, // KEY_TRUST_INFORMATION
|
||||||
KeyLayerInformation, // KEY_LAYER_INFORMATION
|
KeyLayerInformation, // KEY_LAYER_INFORMATION
|
||||||
MaxKeyInfoClass
|
MaxKeyInfoClass
|
||||||
} KEY_INFORMATION_CLASS;
|
} KEY_INFORMATION_CLASS;
|
||||||
|
|
||||||
typedef enum _KEY_VALUE_INFORMATION_CLASS
|
typedef enum _KEY_VALUE_INFORMATION_CLASS
|
||||||
{
|
{
|
||||||
KeyValueBasicInformation, // KEY_VALUE_BASIC_INFORMATION
|
KeyValueBasicInformation, // KEY_VALUE_BASIC_INFORMATION
|
||||||
KeyValueFullInformation, // KEY_VALUE_FULL_INFORMATION
|
KeyValueFullInformation, // KEY_VALUE_FULL_INFORMATION
|
||||||
KeyValuePartialInformation, // KEY_VALUE_PARTIAL_INFORMATION
|
KeyValuePartialInformation, // KEY_VALUE_PARTIAL_INFORMATION
|
||||||
KeyValueFullInformationAlign64,
|
KeyValueFullInformationAlign64,
|
||||||
KeyValuePartialInformationAlign64, // KEY_VALUE_PARTIAL_INFORMATION_ALIGN64
|
KeyValuePartialInformationAlign64, // KEY_VALUE_PARTIAL_INFORMATION_ALIGN64
|
||||||
KeyValueLayerInformation, // KEY_VALUE_LAYER_INFORMATION
|
KeyValueLayerInformation, // KEY_VALUE_LAYER_INFORMATION
|
||||||
MaxKeyValueInfoClass
|
MaxKeyValueInfoClass
|
||||||
} KEY_VALUE_INFORMATION_CLASS;
|
} KEY_VALUE_INFORMATION_CLASS;
|
||||||
|
|
||||||
struct KEY_NAME_INFORMATION
|
struct KEY_NAME_INFORMATION
|
||||||
{
|
{
|
||||||
std::uint32_t NameLength;
|
std::uint32_t NameLength;
|
||||||
char16_t Name[1];
|
char16_t Name[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct KEY_HANDLE_TAGS_INFORMATION
|
struct KEY_HANDLE_TAGS_INFORMATION
|
||||||
{
|
{
|
||||||
ULONG HandleTags;
|
ULONG HandleTags;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct KEY_VALUE_BASIC_INFORMATION
|
struct KEY_VALUE_BASIC_INFORMATION
|
||||||
{
|
{
|
||||||
ULONG TitleIndex;
|
ULONG TitleIndex;
|
||||||
ULONG Type;
|
ULONG Type;
|
||||||
ULONG NameLength;
|
ULONG NameLength;
|
||||||
char16_t Name[1];
|
char16_t Name[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct KEY_VALUE_PARTIAL_INFORMATION
|
struct KEY_VALUE_PARTIAL_INFORMATION
|
||||||
{
|
{
|
||||||
ULONG TitleIndex;
|
ULONG TitleIndex;
|
||||||
ULONG Type;
|
ULONG Type;
|
||||||
ULONG DataLength;
|
ULONG DataLength;
|
||||||
UCHAR Data[1];
|
UCHAR Data[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct KEY_VALUE_FULL_INFORMATION
|
struct KEY_VALUE_FULL_INFORMATION
|
||||||
{
|
{
|
||||||
ULONG TitleIndex;
|
ULONG TitleIndex;
|
||||||
ULONG Type;
|
ULONG Type;
|
||||||
ULONG DataOffset;
|
ULONG DataOffset;
|
||||||
ULONG DataLength;
|
ULONG DataLength;
|
||||||
ULONG NameLength;
|
ULONG NameLength;
|
||||||
char16_t Name[1];
|
char16_t Name[1];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,42 +5,41 @@
|
|||||||
using NTSTATUS = std::uint32_t;
|
using NTSTATUS = std::uint32_t;
|
||||||
|
|
||||||
#ifndef OS_WINDOWS
|
#ifndef OS_WINDOWS
|
||||||
#define STATUS_WAIT_0 ((NTSTATUS)0x00000000L)
|
#define STATUS_WAIT_0 ((NTSTATUS)0x00000000L)
|
||||||
#define STATUS_TIMEOUT ((NTSTATUS)0x00000102L)
|
#define STATUS_TIMEOUT ((NTSTATUS)0x00000102L)
|
||||||
|
|
||||||
#define STATUS_ACCESS_VIOLATION ((NTSTATUS)0xC0000005L)
|
#define STATUS_ACCESS_VIOLATION ((NTSTATUS)0xC0000005L)
|
||||||
#define STATUS_INVALID_HANDLE ((NTSTATUS)0xC0000008L)
|
#define STATUS_INVALID_HANDLE ((NTSTATUS)0xC0000008L)
|
||||||
#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000DL)
|
#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000DL)
|
||||||
|
#define STATUS_ILLEGAL_INSTRUCTION ((NTSTATUS)0xC000001DL)
|
||||||
|
|
||||||
#define STATUS_PENDING ((DWORD)0x00000103L)
|
#define STATUS_PENDING ((NTSTATUS)0x00000103L)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
||||||
#define STATUS_WAIT_1 ((NTSTATUS)0x00000001L)
|
#define STATUS_WAIT_1 ((NTSTATUS)0x00000001L)
|
||||||
|
|
||||||
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0x00000001L)
|
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0x00000001L)
|
||||||
#define STATUS_ALERTED ((NTSTATUS)0x00000101L)
|
#define STATUS_ALERTED ((NTSTATUS)0x00000101L)
|
||||||
|
|
||||||
#define STATUS_OBJECT_NAME_EXISTS ((NTSTATUS)0x40000000L)
|
#define STATUS_OBJECT_NAME_EXISTS ((NTSTATUS)0x40000000L)
|
||||||
|
|
||||||
#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L)
|
#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L)
|
||||||
|
|
||||||
#define STATUS_ILLEGAL_INSTRUCTION ((DWORD )0xC000001DL)
|
#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
|
||||||
#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
|
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
|
||||||
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
|
#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
|
||||||
#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
|
#define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL)
|
||||||
#define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL)
|
#define STATUS_FILE_INVALID ((NTSTATUS)0xC0000098L)
|
||||||
#define STATUS_FILE_INVALID ((NTSTATUS)0xC0000098L)
|
#define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS)0xC00000A0L)
|
||||||
#define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS)0xC00000A0L)
|
#define STATUS_FILE_IS_A_DIRECTORY ((NTSTATUS)0xC00000BAL)
|
||||||
#define STATUS_FILE_IS_A_DIRECTORY ((NTSTATUS)0xC00000BAL)
|
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL)
|
||||||
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL)
|
#define STATUS_INVALID_ADDRESS ((NTSTATUS)0xC0000141L)
|
||||||
#define STATUS_INVALID_ADDRESS ((NTSTATUS)0xC0000141L)
|
#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L)
|
||||||
#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L)
|
#define STATUS_CONNECTION_REFUSED ((NTSTATUS)0xC0000236L)
|
||||||
#define STATUS_CONNECTION_REFUSED ((NTSTATUS)0xC0000236L)
|
#define STATUS_ADDRESS_ALREADY_ASSOCIATED ((NTSTATUS)0xC0000328L)
|
||||||
#define STATUS_ADDRESS_ALREADY_ASSOCIATED ((NTSTATUS)0xC0000328L)
|
|
||||||
|
|
||||||
#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)
|
#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)
|
||||||
|
|
||||||
|
#define FILE_DEVICE_NETWORK 0x00000012
|
||||||
#define FILE_DEVICE_NETWORK 0x00000012
|
#define FSCTL_AFD_BASE FILE_DEVICE_NETWORK
|
||||||
#define FSCTL_AFD_BASE FILE_DEVICE_NETWORK
|
|
||||||
|
|||||||
@@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
typedef enum _EVENT_TYPE
|
typedef enum _EVENT_TYPE
|
||||||
{
|
{
|
||||||
NotificationEvent,
|
NotificationEvent,
|
||||||
SynchronizationEvent
|
SynchronizationEvent
|
||||||
} EVENT_TYPE;
|
} EVENT_TYPE;
|
||||||
|
|
||||||
typedef enum _WAIT_TYPE
|
typedef enum _WAIT_TYPE
|
||||||
{
|
{
|
||||||
WaitAll,
|
WaitAll,
|
||||||
WaitAny,
|
WaitAny,
|
||||||
WaitNotification,
|
WaitNotification,
|
||||||
WaitDequeue,
|
WaitDequeue,
|
||||||
WaitDpc,
|
WaitDpc,
|
||||||
} WAIT_TYPE;
|
} WAIT_TYPE;
|
||||||
|
|||||||
@@ -2,70 +2,69 @@
|
|||||||
|
|
||||||
typedef enum _THREADINFOCLASS
|
typedef enum _THREADINFOCLASS
|
||||||
{
|
{
|
||||||
ThreadBasicInformation, // q: THREAD_BASIC_INFORMATION
|
ThreadBasicInformation, // q: THREAD_BASIC_INFORMATION
|
||||||
ThreadTimes, // q: KERNEL_USER_TIMES
|
ThreadTimes, // q: KERNEL_USER_TIMES
|
||||||
ThreadPriority, // s: KPRIORITY (requires SeIncreaseBasePriorityPrivilege)
|
ThreadPriority, // s: KPRIORITY (requires SeIncreaseBasePriorityPrivilege)
|
||||||
ThreadBasePriority, // s: KPRIORITY
|
ThreadBasePriority, // s: KPRIORITY
|
||||||
ThreadAffinityMask, // s: KAFFINITY
|
ThreadAffinityMask, // s: KAFFINITY
|
||||||
ThreadImpersonationToken, // s: HANDLE
|
ThreadImpersonationToken, // s: HANDLE
|
||||||
ThreadDescriptorTableEntry, // q: DESCRIPTOR_TABLE_ENTRY (or WOW64_DESCRIPTOR_TABLE_ENTRY)
|
ThreadDescriptorTableEntry, // q: DESCRIPTOR_TABLE_ENTRY (or WOW64_DESCRIPTOR_TABLE_ENTRY)
|
||||||
ThreadEnableAlignmentFaultFixup, // s: BOOLEAN
|
ThreadEnableAlignmentFaultFixup, // s: BOOLEAN
|
||||||
ThreadEventPair,
|
ThreadEventPair,
|
||||||
ThreadQuerySetWin32StartAddress, // q: ULONG_PTR
|
ThreadQuerySetWin32StartAddress, // q: ULONG_PTR
|
||||||
ThreadZeroTlsCell, // s: ULONG // TlsIndex // 10
|
ThreadZeroTlsCell, // s: ULONG // TlsIndex // 10
|
||||||
ThreadPerformanceCount, // q: LARGE_INTEGER
|
ThreadPerformanceCount, // q: LARGE_INTEGER
|
||||||
ThreadAmILastThread, // q: ULONG
|
ThreadAmILastThread, // q: ULONG
|
||||||
ThreadIdealProcessor, // s: ULONG
|
ThreadIdealProcessor, // s: ULONG
|
||||||
ThreadPriorityBoost, // qs: ULONG
|
ThreadPriorityBoost, // qs: ULONG
|
||||||
ThreadSetTlsArrayAddress, // s: ULONG_PTR // Obsolete
|
ThreadSetTlsArrayAddress, // s: ULONG_PTR // Obsolete
|
||||||
ThreadIsIoPending, // q: ULONG
|
ThreadIsIoPending, // q: ULONG
|
||||||
ThreadHideFromDebugger, // q: BOOLEAN; s: void
|
ThreadHideFromDebugger, // q: BOOLEAN; s: void
|
||||||
ThreadBreakOnTermination, // qs: ULONG
|
ThreadBreakOnTermination, // qs: ULONG
|
||||||
ThreadSwitchLegacyState, // s: void // NtCurrentThread // NPX/FPU
|
ThreadSwitchLegacyState, // s: void // NtCurrentThread // NPX/FPU
|
||||||
ThreadIsTerminated, // q: ULONG // 20
|
ThreadIsTerminated, // q: ULONG // 20
|
||||||
ThreadLastSystemCall, // q: THREAD_LAST_SYSCALL_INFORMATION
|
ThreadLastSystemCall, // q: THREAD_LAST_SYSCALL_INFORMATION
|
||||||
ThreadIoPriority, // qs: IO_PRIORITY_HINT (requires SeIncreaseBasePriorityPrivilege)
|
ThreadIoPriority, // qs: IO_PRIORITY_HINT (requires SeIncreaseBasePriorityPrivilege)
|
||||||
ThreadCycleTime, // q: THREAD_CYCLE_TIME_INFORMATION
|
ThreadCycleTime, // q: THREAD_CYCLE_TIME_INFORMATION
|
||||||
ThreadPagePriority, // qs: PAGE_PRIORITY_INFORMATION
|
ThreadPagePriority, // qs: PAGE_PRIORITY_INFORMATION
|
||||||
ThreadActualBasePriority, // s: LONG (requires SeIncreaseBasePriorityPrivilege)
|
ThreadActualBasePriority, // s: LONG (requires SeIncreaseBasePriorityPrivilege)
|
||||||
ThreadTebInformation, // q: THREAD_TEB_INFORMATION (requires THREAD_GET_CONTEXT + THREAD_SET_CONTEXT)
|
ThreadTebInformation, // q: THREAD_TEB_INFORMATION (requires THREAD_GET_CONTEXT + THREAD_SET_CONTEXT)
|
||||||
ThreadCSwitchMon, // Obsolete
|
ThreadCSwitchMon, // Obsolete
|
||||||
ThreadCSwitchPmu,
|
ThreadCSwitchPmu,
|
||||||
ThreadWow64Context, // qs: WOW64_CONTEXT, ARM_NT_CONTEXT since 20H1
|
ThreadWow64Context, // qs: WOW64_CONTEXT, ARM_NT_CONTEXT since 20H1
|
||||||
ThreadGroupInformation, // qs: GROUP_AFFINITY // 30
|
ThreadGroupInformation, // qs: GROUP_AFFINITY // 30
|
||||||
ThreadUmsInformation, // q: THREAD_UMS_INFORMATION // Obsolete
|
ThreadUmsInformation, // q: THREAD_UMS_INFORMATION // Obsolete
|
||||||
ThreadCounterProfiling, // q: BOOLEAN; s: THREAD_PROFILING_INFORMATION?
|
ThreadCounterProfiling, // q: BOOLEAN; s: THREAD_PROFILING_INFORMATION?
|
||||||
ThreadIdealProcessorEx, // qs: PROCESSOR_NUMBER; s: previous PROCESSOR_NUMBER on return
|
ThreadIdealProcessorEx, // qs: PROCESSOR_NUMBER; s: previous PROCESSOR_NUMBER on return
|
||||||
ThreadCpuAccountingInformation, // q: BOOLEAN; s: HANDLE (NtOpenSession) // NtCurrentThread // since WIN8
|
ThreadCpuAccountingInformation, // q: BOOLEAN; s: HANDLE (NtOpenSession) // NtCurrentThread // since WIN8
|
||||||
ThreadSuspendCount, // q: ULONG // since WINBLUE
|
ThreadSuspendCount, // q: ULONG // since WINBLUE
|
||||||
ThreadHeterogeneousCpuPolicy, // q: KHETERO_CPU_POLICY // since THRESHOLD
|
ThreadHeterogeneousCpuPolicy, // q: KHETERO_CPU_POLICY // since THRESHOLD
|
||||||
ThreadContainerId, // q: GUID
|
ThreadContainerId, // q: GUID
|
||||||
ThreadNameInformation, // qs: THREAD_NAME_INFORMATION
|
ThreadNameInformation, // qs: THREAD_NAME_INFORMATION
|
||||||
ThreadSelectedCpuSets,
|
ThreadSelectedCpuSets,
|
||||||
ThreadSystemThreadInformation, // q: SYSTEM_THREAD_INFORMATION // 40
|
ThreadSystemThreadInformation, // q: SYSTEM_THREAD_INFORMATION // 40
|
||||||
ThreadActualGroupAffinity, // q: GROUP_AFFINITY // since THRESHOLD2
|
ThreadActualGroupAffinity, // q: GROUP_AFFINITY // since THRESHOLD2
|
||||||
ThreadDynamicCodePolicyInfo, // q: ULONG; s: ULONG (NtCurrentThread)
|
ThreadDynamicCodePolicyInfo, // q: ULONG; s: ULONG (NtCurrentThread)
|
||||||
ThreadExplicitCaseSensitivity, // qs: ULONG; s: 0 disables, otherwise enables
|
ThreadExplicitCaseSensitivity, // qs: ULONG; s: 0 disables, otherwise enables
|
||||||
ThreadWorkOnBehalfTicket, // RTL_WORK_ON_BEHALF_TICKET_EX
|
ThreadWorkOnBehalfTicket, // RTL_WORK_ON_BEHALF_TICKET_EX
|
||||||
ThreadSubsystemInformation, // q: SUBSYSTEM_INFORMATION_TYPE // since REDSTONE2
|
ThreadSubsystemInformation, // q: SUBSYSTEM_INFORMATION_TYPE // since REDSTONE2
|
||||||
ThreadDbgkWerReportActive, // s: ULONG; s: 0 disables, otherwise enables
|
ThreadDbgkWerReportActive, // s: ULONG; s: 0 disables, otherwise enables
|
||||||
ThreadAttachContainer, // s: HANDLE (job object) // NtCurrentThread
|
ThreadAttachContainer, // s: HANDLE (job object) // NtCurrentThread
|
||||||
ThreadManageWritesToExecutableMemory, // MANAGE_WRITES_TO_EXECUTABLE_MEMORY // since REDSTONE3
|
ThreadManageWritesToExecutableMemory, // MANAGE_WRITES_TO_EXECUTABLE_MEMORY // since REDSTONE3
|
||||||
ThreadPowerThrottlingState, // POWER_THROTTLING_THREAD_STATE // since REDSTONE3 (set), WIN11 22H2 (query)
|
ThreadPowerThrottlingState, // POWER_THROTTLING_THREAD_STATE // since REDSTONE3 (set), WIN11 22H2 (query)
|
||||||
ThreadWorkloadClass, // THREAD_WORKLOAD_CLASS // since REDSTONE5 // 50
|
ThreadWorkloadClass, // THREAD_WORKLOAD_CLASS // since REDSTONE5 // 50
|
||||||
ThreadCreateStateChange, // since WIN11
|
ThreadCreateStateChange, // since WIN11
|
||||||
ThreadApplyStateChange,
|
ThreadApplyStateChange,
|
||||||
ThreadStrongerBadHandleChecks, // since 22H1
|
ThreadStrongerBadHandleChecks, // since 22H1
|
||||||
ThreadEffectiveIoPriority, // q: IO_PRIORITY_HINT
|
ThreadEffectiveIoPriority, // q: IO_PRIORITY_HINT
|
||||||
ThreadEffectivePagePriority, // q: ULONG
|
ThreadEffectivePagePriority, // q: ULONG
|
||||||
ThreadUpdateLockOwnership, // since 24H2
|
ThreadUpdateLockOwnership, // since 24H2
|
||||||
ThreadSchedulerSharedDataSlot, // SCHEDULER_SHARED_DATA_SLOT_INFORMATION
|
ThreadSchedulerSharedDataSlot, // SCHEDULER_SHARED_DATA_SLOT_INFORMATION
|
||||||
ThreadTebInformationAtomic, // THREAD_TEB_INFORMATION
|
ThreadTebInformationAtomic, // THREAD_TEB_INFORMATION
|
||||||
ThreadIndexInformation, // THREAD_INDEX_INFORMATION
|
ThreadIndexInformation, // THREAD_INDEX_INFORMATION
|
||||||
MaxThreadInfoClass
|
MaxThreadInfoClass
|
||||||
} THREADINFOCLASS;
|
} THREADINFOCLASS;
|
||||||
|
|
||||||
|
|
||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
struct THREAD_NAME_INFORMATION
|
struct THREAD_NAME_INFORMATION
|
||||||
{
|
{
|
||||||
@@ -80,4 +79,4 @@ typedef struct _THREAD_BASIC_INFORMATION64
|
|||||||
EMULATOR_CAST(std::uint64_t, KAFFINITY) AffinityMask;
|
EMULATOR_CAST(std::uint64_t, KAFFINITY) AffinityMask;
|
||||||
EMULATOR_CAST(std::uint32_t, KPRIORITY) Priority;
|
EMULATOR_CAST(std::uint32_t, KPRIORITY) Priority;
|
||||||
EMULATOR_CAST(std::uint32_t, KPRIORITY) BasePriority;
|
EMULATOR_CAST(std::uint32_t, KPRIORITY) BasePriority;
|
||||||
} THREAD_BASIC_INFORMATION64, *PTHREAD_BASIC_INFORMATION64;
|
} THREAD_BASIC_INFORMATION64, *PTHREAD_BASIC_INFORMATION64;
|
||||||
|
|||||||
@@ -19,19 +19,19 @@ struct EmulatorTraits;
|
|||||||
template <>
|
template <>
|
||||||
struct EmulatorTraits<Emu32>
|
struct EmulatorTraits<Emu32>
|
||||||
{
|
{
|
||||||
using PVOID = std::uint32_t;
|
using PVOID = std::uint32_t;
|
||||||
using ULONG_PTR = std::uint32_t;
|
using ULONG_PTR = std::uint32_t;
|
||||||
using SIZE_T = std::uint32_t;
|
using SIZE_T = std::uint32_t;
|
||||||
using UNICODE = char16_t;
|
using UNICODE = char16_t;
|
||||||
using HANDLE = std::uint32_t;
|
using HANDLE = std::uint32_t;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct EmulatorTraits<Emu64>
|
struct EmulatorTraits<Emu64>
|
||||||
{
|
{
|
||||||
using PVOID = std::uint64_t;
|
using PVOID = std::uint64_t;
|
||||||
using ULONG_PTR = std::uint64_t;
|
using ULONG_PTR = std::uint64_t;
|
||||||
using SIZE_T = std::uint64_t;
|
using SIZE_T = std::uint64_t;
|
||||||
using UNICODE = char16_t;
|
using UNICODE = char16_t;
|
||||||
using HANDLE = std::uint64_t;
|
using HANDLE = std::uint64_t;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,76 +5,76 @@
|
|||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
struct UNICODE_STRING
|
struct UNICODE_STRING
|
||||||
{
|
{
|
||||||
USHORT Length;
|
USHORT Length;
|
||||||
USHORT MaximumLength;
|
USHORT MaximumLength;
|
||||||
EMULATOR_CAST(typename Traits::PVOID, char16_t*) Buffer;
|
EMULATOR_CAST(typename Traits::PVOID, char16_t*) Buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::string u16_to_u8(const std::u16string_view u16_view)
|
inline std::string u16_to_u8(const std::u16string_view u16_view)
|
||||||
{
|
{
|
||||||
std::string utf8_str;
|
std::string utf8_str;
|
||||||
utf8_str.reserve(u16_view.size() * 2);
|
utf8_str.reserve(u16_view.size() * 2);
|
||||||
for (const char16_t ch : u16_view)
|
for (const char16_t ch : u16_view)
|
||||||
{
|
{
|
||||||
if (ch <= 0x7F)
|
if (ch <= 0x7F)
|
||||||
{
|
{
|
||||||
utf8_str.push_back(static_cast<char>(ch));
|
utf8_str.push_back(static_cast<char>(ch));
|
||||||
}
|
}
|
||||||
else if (ch <= 0x7FF)
|
else if (ch <= 0x7FF)
|
||||||
{
|
{
|
||||||
utf8_str.push_back(static_cast<char>(0xC0 | (ch >> 6)));
|
utf8_str.push_back(static_cast<char>(0xC0 | (ch >> 6)));
|
||||||
utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
|
utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
utf8_str.push_back(static_cast<char>(0xE0 | (ch >> 12)));
|
utf8_str.push_back(static_cast<char>(0xE0 | (ch >> 12)));
|
||||||
utf8_str.push_back(static_cast<char>(0x80 | ((ch >> 6) & 0x3F)));
|
utf8_str.push_back(static_cast<char>(0x80 | ((ch >> 6) & 0x3F)));
|
||||||
utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
|
utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return utf8_str;
|
return utf8_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string w_to_u8(const std::wstring_view w_view)
|
inline std::string w_to_u8(const std::wstring_view w_view)
|
||||||
{
|
{
|
||||||
std::string utf8_str;
|
std::string utf8_str;
|
||||||
utf8_str.reserve(w_view.size() * 2);
|
utf8_str.reserve(w_view.size() * 2);
|
||||||
for (const wchar_t w_ch : w_view)
|
for (const wchar_t w_ch : w_view)
|
||||||
{
|
{
|
||||||
const auto ch = static_cast<char16_t>(w_ch);
|
const auto ch = static_cast<char16_t>(w_ch);
|
||||||
if (ch <= 0x7F)
|
if (ch <= 0x7F)
|
||||||
{
|
{
|
||||||
utf8_str.push_back(static_cast<char>(ch));
|
utf8_str.push_back(static_cast<char>(ch));
|
||||||
}
|
}
|
||||||
else if (ch <= 0x7FF)
|
else if (ch <= 0x7FF)
|
||||||
{
|
{
|
||||||
utf8_str.push_back(static_cast<char>(0xC0 | (ch >> 6)));
|
utf8_str.push_back(static_cast<char>(0xC0 | (ch >> 6)));
|
||||||
utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
|
utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
utf8_str.push_back(static_cast<char>(0xE0 | (ch >> 12)));
|
utf8_str.push_back(static_cast<char>(0xE0 | (ch >> 12)));
|
||||||
utf8_str.push_back(static_cast<char>(0x80 | ((ch >> 6) & 0x3F)));
|
utf8_str.push_back(static_cast<char>(0x80 | ((ch >> 6) & 0x3F)));
|
||||||
utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
|
utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return utf8_str;
|
return utf8_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef OS_WINDOWS
|
#ifndef OS_WINDOWS
|
||||||
inline int open_unicode(FILE** handle, const std::u16string& fileName, const std::u16string& mode)
|
inline int open_unicode(FILE** handle, const std::u16string& fileName, const std::u16string& mode)
|
||||||
{
|
{
|
||||||
*handle = fopen(u16_to_u8(fileName).c_str(), u16_to_u8(mode).c_str());
|
*handle = fopen(u16_to_u8(fileName).c_str(), u16_to_u8(mode).c_str());
|
||||||
return errno;
|
return errno;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
inline std::wstring u16_to_w(const std::u16string& u16str)
|
inline std::wstring u16_to_w(const std::u16string& u16str)
|
||||||
{
|
{
|
||||||
return std::wstring(reinterpret_cast<const wchar_t*>(u16str.data()), u16str.size());
|
return std::wstring(reinterpret_cast<const wchar_t*>(u16str.data()), u16str.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto open_unicode(FILE** handle, const std::u16string& fileName, const std::u16string& mode)
|
inline auto open_unicode(FILE** handle, const std::u16string& fileName, const std::u16string& mode)
|
||||||
{
|
{
|
||||||
return _wfopen_s(handle, u16_to_w(fileName).c_str(), u16_to_w(mode).c_str());
|
return _wfopen_s(handle, u16_to_w(fileName).c_str(), u16_to_w(mode).c_str());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -2,96 +2,95 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
|
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
|
||||||
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
|
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
|
||||||
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
|
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
|
||||||
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
|
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
|
||||||
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
|
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
|
||||||
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
|
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
|
||||||
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
|
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
|
||||||
// IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage)
|
// IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage)
|
||||||
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data
|
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data
|
||||||
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP
|
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP
|
||||||
#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory
|
#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory
|
||||||
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory
|
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory
|
||||||
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers
|
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers
|
||||||
#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table
|
#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table
|
||||||
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors
|
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors
|
||||||
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
|
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
|
||||||
|
|
||||||
#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 // Section contains extended relocations.
|
#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 // Section contains extended relocations.
|
||||||
#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 // Section can be discarded.
|
#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 // Section can be discarded.
|
||||||
#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 // Section is not cachable.
|
#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 // Section is not cachable.
|
||||||
#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 // Section is not pageable.
|
#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 // Section is not pageable.
|
||||||
#define IMAGE_SCN_MEM_SHARED 0x10000000 // Section is shareable.
|
#define IMAGE_SCN_MEM_SHARED 0x10000000 // Section is shareable.
|
||||||
#define IMAGE_SCN_MEM_EXECUTE 0x20000000 // Section is executable.
|
#define IMAGE_SCN_MEM_EXECUTE 0x20000000 // Section is executable.
|
||||||
#define IMAGE_SCN_MEM_READ 0x40000000 // Section is readable.
|
#define IMAGE_SCN_MEM_READ 0x40000000 // Section is readable.
|
||||||
#define IMAGE_SCN_MEM_WRITE 0x80000000 // Section is writeable.
|
#define IMAGE_SCN_MEM_WRITE 0x80000000 // Section is writeable.
|
||||||
|
|
||||||
#define IMAGE_SCN_CNT_CODE 0x00000020 // Section contains code.
|
#define IMAGE_SCN_CNT_CODE 0x00000020 // Section contains code.
|
||||||
#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 // Section contains initialized data.
|
#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 // Section contains initialized data.
|
||||||
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 // Section contains uninitialized data.
|
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 // Section contains uninitialized data.
|
||||||
|
|
||||||
#define IMAGE_REL_BASED_ABSOLUTE 0
|
#define IMAGE_REL_BASED_ABSOLUTE 0
|
||||||
#define IMAGE_REL_BASED_HIGH 1
|
#define IMAGE_REL_BASED_HIGH 1
|
||||||
#define IMAGE_REL_BASED_LOW 2
|
#define IMAGE_REL_BASED_LOW 2
|
||||||
#define IMAGE_REL_BASED_HIGHLOW 3
|
#define IMAGE_REL_BASED_HIGHLOW 3
|
||||||
#define IMAGE_REL_BASED_HIGHADJ 4
|
#define IMAGE_REL_BASED_HIGHADJ 4
|
||||||
#define IMAGE_REL_BASED_MIPS_JMPADDR 5
|
#define IMAGE_REL_BASED_MIPS_JMPADDR 5
|
||||||
#define IMAGE_REL_BASED_ARM_MOV32A 5
|
#define IMAGE_REL_BASED_ARM_MOV32A 5
|
||||||
#define IMAGE_REL_BASED_ARM_MOV32 5
|
#define IMAGE_REL_BASED_ARM_MOV32 5
|
||||||
#define IMAGE_REL_BASED_SECTION 6
|
#define IMAGE_REL_BASED_SECTION 6
|
||||||
#define IMAGE_REL_BASED_REL 7
|
#define IMAGE_REL_BASED_REL 7
|
||||||
#define IMAGE_REL_BASED_ARM_MOV32T 7
|
#define IMAGE_REL_BASED_ARM_MOV32T 7
|
||||||
#define IMAGE_REL_BASED_THUMB_MOV32 7
|
#define IMAGE_REL_BASED_THUMB_MOV32 7
|
||||||
#define IMAGE_REL_BASED_MIPS_JMPADDR16 9
|
#define IMAGE_REL_BASED_MIPS_JMPADDR16 9
|
||||||
#define IMAGE_REL_BASED_IA64_IMM64 9
|
#define IMAGE_REL_BASED_IA64_IMM64 9
|
||||||
#define IMAGE_REL_BASED_DIR64 10
|
#define IMAGE_REL_BASED_DIR64 10
|
||||||
#define IMAGE_REL_BASED_HIGH3ADJ 11
|
#define IMAGE_REL_BASED_HIGH3ADJ 11
|
||||||
|
|
||||||
#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040
|
#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040
|
||||||
#define IMAGE_FILE_DLL 0x2000
|
#define IMAGE_FILE_DLL 0x2000
|
||||||
|
|
||||||
#define IMAGE_FILE_MACHINE_I386 0x014c
|
#define IMAGE_FILE_MACHINE_I386 0x014c
|
||||||
#define IMAGE_FILE_MACHINE_AMD64 0x8664
|
#define IMAGE_FILE_MACHINE_AMD64 0x8664
|
||||||
|
|
||||||
#define PROCESSOR_ARCHITECTURE_AMD64 9
|
#define PROCESSOR_ARCHITECTURE_AMD64 9
|
||||||
|
|
||||||
enum class PEMachineType : std::uint16_t
|
enum class PEMachineType : std::uint16_t
|
||||||
{
|
{
|
||||||
UNKNOWN = 0,
|
UNKNOWN = 0,
|
||||||
I386 = 0x014c, // Intel 386.
|
I386 = 0x014c, // Intel 386.
|
||||||
R3000 = 0x0162, // MIPS little-endian, 0x160 big-endian
|
R3000 = 0x0162, // MIPS little-endian, 0x160 big-endian
|
||||||
R4000 = 0x0166, // MIPS little-endian
|
R4000 = 0x0166, // MIPS little-endian
|
||||||
R10000 = 0x0168, // MIPS little-endian
|
R10000 = 0x0168, // MIPS little-endian
|
||||||
WCEMIPSV2 = 0x0169, // MIPS little-endian WCE v2
|
WCEMIPSV2 = 0x0169, // MIPS little-endian WCE v2
|
||||||
ALPHA = 0x0184, // Alpha_AXP
|
ALPHA = 0x0184, // Alpha_AXP
|
||||||
SH3 = 0x01a2, // SH3 little-endian
|
SH3 = 0x01a2, // SH3 little-endian
|
||||||
SH3DSP = 0x01a3,
|
SH3DSP = 0x01a3,
|
||||||
SH3E = 0x01a4, // SH3E little-endian
|
SH3E = 0x01a4, // SH3E little-endian
|
||||||
SH4 = 0x01a6, // SH4 little-endian
|
SH4 = 0x01a6, // SH4 little-endian
|
||||||
SH5 = 0x01a8, // SH5
|
SH5 = 0x01a8, // SH5
|
||||||
ARM = 0x01c0, // ARM Little-Endian
|
ARM = 0x01c0, // ARM Little-Endian
|
||||||
THUMB = 0x01c2, // ARM Thumb/Thumb-2 Little-Endian
|
THUMB = 0x01c2, // ARM Thumb/Thumb-2 Little-Endian
|
||||||
ARMNT = 0x01c4, // ARM Thumb-2 Little-Endian
|
ARMNT = 0x01c4, // ARM Thumb-2 Little-Endian
|
||||||
AM33 = 0x01d3,
|
AM33 = 0x01d3,
|
||||||
POWERPC = 0x01F0, // IBM PowerPC Little-Endian
|
POWERPC = 0x01F0, // IBM PowerPC Little-Endian
|
||||||
POWERPCFP = 0x01f1,
|
POWERPCFP = 0x01f1,
|
||||||
IA64 = 0x0200, // Intel 64
|
IA64 = 0x0200, // Intel 64
|
||||||
MIPS16 = 0x0266, // MIPS
|
MIPS16 = 0x0266, // MIPS
|
||||||
ALPHA64 = 0x0284, // ALPHA64
|
ALPHA64 = 0x0284, // ALPHA64
|
||||||
MIPSFPU = 0x0366, // MIPS
|
MIPSFPU = 0x0366, // MIPS
|
||||||
MIPSFPU16 = 0x0466, // MIPS
|
MIPSFPU16 = 0x0466, // MIPS
|
||||||
AXP64 = ALPHA64,
|
AXP64 = ALPHA64,
|
||||||
TRICORE = 0x0520, // Infineon
|
TRICORE = 0x0520, // Infineon
|
||||||
CEF = 0x0CEF,
|
CEF = 0x0CEF,
|
||||||
EBC = 0x0EBC, // EFI Byte Code
|
EBC = 0x0EBC, // EFI Byte Code
|
||||||
AMD64 = 0x8664, // AMD64 (K8)
|
AMD64 = 0x8664, // AMD64 (K8)
|
||||||
M32R = 0x9041, // M32R little-endian
|
M32R = 0x9041, // M32R little-endian
|
||||||
CEE = 0xC0EE,
|
CEE = 0xC0EE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#pragma pack(push, 4)
|
#pragma pack(push, 4)
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -102,65 +101,64 @@ struct PEOptionalHeaderBasePart2_t
|
|||||||
template <>
|
template <>
|
||||||
struct PEOptionalHeaderBasePart2_t<std::uint32_t>
|
struct PEOptionalHeaderBasePart2_t<std::uint32_t>
|
||||||
{
|
{
|
||||||
std::uint32_t BaseOfData;
|
std::uint32_t BaseOfData;
|
||||||
std::uint32_t ImageBase;
|
std::uint32_t ImageBase;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct PEOptionalHeaderBasePart2_t<std::uint64_t>
|
struct PEOptionalHeaderBasePart2_t<std::uint64_t>
|
||||||
{
|
{
|
||||||
std::uint64_t ImageBase;
|
std::uint64_t ImageBase;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct PEOptionalHeaderBasePart1_t
|
struct PEOptionalHeaderBasePart1_t
|
||||||
{
|
{
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
k_NumberOfDataDirectors = 16
|
k_NumberOfDataDirectors = 16
|
||||||
};
|
};
|
||||||
|
|
||||||
uint16_t Magic;
|
uint16_t Magic;
|
||||||
uint8_t MajorLinkerVersion;
|
uint8_t MajorLinkerVersion;
|
||||||
uint8_t MinorLinkerVersion;
|
uint8_t MinorLinkerVersion;
|
||||||
uint32_t SizeOfCode;
|
uint32_t SizeOfCode;
|
||||||
uint32_t SizeOfInitializedData;
|
uint32_t SizeOfInitializedData;
|
||||||
uint32_t SizeOfUninitializedData;
|
uint32_t SizeOfUninitializedData;
|
||||||
uint32_t AddressOfEntryPoint;
|
uint32_t AddressOfEntryPoint;
|
||||||
uint32_t BaseOfCode;
|
uint32_t BaseOfCode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct PEDirectory_t2
|
struct PEDirectory_t2
|
||||||
{
|
{
|
||||||
std::uint32_t VirtualAddress;
|
std::uint32_t VirtualAddress;
|
||||||
std::uint32_t Size;
|
std::uint32_t Size;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct PEOptionalHeaderBasePart3_t : PEOptionalHeaderBasePart1_t<T>, PEOptionalHeaderBasePart2_t<T>
|
struct PEOptionalHeaderBasePart3_t : PEOptionalHeaderBasePart1_t<T>, PEOptionalHeaderBasePart2_t<T>
|
||||||
{
|
{
|
||||||
uint32_t SectionAlignment;
|
uint32_t SectionAlignment;
|
||||||
uint32_t FileAlignment;
|
uint32_t FileAlignment;
|
||||||
uint16_t MajorOperatingSystemVersion;
|
uint16_t MajorOperatingSystemVersion;
|
||||||
uint16_t MinorOperatingSystemVersion;
|
uint16_t MinorOperatingSystemVersion;
|
||||||
uint16_t MajorImageVersion;
|
uint16_t MajorImageVersion;
|
||||||
uint16_t MinorImageVersion;
|
uint16_t MinorImageVersion;
|
||||||
uint16_t MajorSubsystemVersion;
|
uint16_t MajorSubsystemVersion;
|
||||||
uint16_t MinorSubsystemVersion;
|
uint16_t MinorSubsystemVersion;
|
||||||
uint32_t Win32VersionValue;
|
uint32_t Win32VersionValue;
|
||||||
uint32_t SizeOfImage;
|
uint32_t SizeOfImage;
|
||||||
uint32_t SizeOfHeaders;
|
uint32_t SizeOfHeaders;
|
||||||
uint32_t CheckSum;
|
uint32_t CheckSum;
|
||||||
uint16_t Subsystem;
|
uint16_t Subsystem;
|
||||||
uint16_t DllCharacteristics;
|
uint16_t DllCharacteristics;
|
||||||
T SizeOfStackReserve;
|
T SizeOfStackReserve;
|
||||||
T SizeOfStackCommit;
|
T SizeOfStackCommit;
|
||||||
T SizeOfHeapReserve;
|
T SizeOfHeapReserve;
|
||||||
T SizeOfHeapCommit;
|
T SizeOfHeapCommit;
|
||||||
uint32_t LoaderFlags;
|
uint32_t LoaderFlags;
|
||||||
uint32_t NumberOfRvaAndSizes;
|
uint32_t NumberOfRvaAndSizes;
|
||||||
PEDirectory_t2 DataDirectory[PEOptionalHeaderBasePart1_t<T>::k_NumberOfDataDirectors];
|
PEDirectory_t2 DataDirectory[PEOptionalHeaderBasePart1_t<T>::k_NumberOfDataDirectors];
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -171,113 +169,116 @@ struct PEOptionalHeader_t
|
|||||||
template <>
|
template <>
|
||||||
struct PEOptionalHeader_t<std::uint32_t> : PEOptionalHeaderBasePart3_t<std::uint32_t>
|
struct PEOptionalHeader_t<std::uint32_t> : PEOptionalHeaderBasePart3_t<std::uint32_t>
|
||||||
{
|
{
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
k_Magic = 0x10b, // IMAGE_NT_OPTIONAL_HDR32_MAGIC
|
k_Magic = 0x10b, // IMAGE_NT_OPTIONAL_HDR32_MAGIC
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct PEOptionalHeader_t<std::uint64_t> : PEOptionalHeaderBasePart3_t<std::uint64_t>
|
struct PEOptionalHeader_t<std::uint64_t> : PEOptionalHeaderBasePart3_t<std::uint64_t>
|
||||||
{
|
{
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
k_Magic = 0x20b, // IMAGE_NT_OPTIONAL_HDR64_MAGIC
|
k_Magic = 0x20b, // IMAGE_NT_OPTIONAL_HDR64_MAGIC
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PEFileHeader_t
|
struct PEFileHeader_t
|
||||||
{
|
{
|
||||||
PEMachineType Machine;
|
PEMachineType Machine;
|
||||||
std::uint16_t NumberOfSections;
|
std::uint16_t NumberOfSections;
|
||||||
std::uint32_t TimeDateStamp;
|
std::uint32_t TimeDateStamp;
|
||||||
std::uint32_t PointerToSymbolTable;
|
std::uint32_t PointerToSymbolTable;
|
||||||
std::uint32_t NumberOfSymbols;
|
std::uint32_t NumberOfSymbols;
|
||||||
std::uint16_t SizeOfOptionalHeader;
|
std::uint16_t SizeOfOptionalHeader;
|
||||||
std::uint16_t Characteristics;
|
std::uint16_t Characteristics;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct PENTHeaders_t
|
struct PENTHeaders_t
|
||||||
{
|
{
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
k_Signature = 0x00004550, // IMAGE_NT_SIGNATURE
|
k_Signature = 0x00004550, // IMAGE_NT_SIGNATURE
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t Signature;
|
uint32_t Signature;
|
||||||
PEFileHeader_t FileHeader;
|
PEFileHeader_t FileHeader;
|
||||||
PEOptionalHeader_t<T> OptionalHeader;
|
PEOptionalHeader_t<T> OptionalHeader;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PEDosHeader_t
|
struct PEDosHeader_t
|
||||||
{
|
{
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
k_Magic = 0x5A4D
|
k_Magic = 0x5A4D
|
||||||
};
|
};
|
||||||
|
|
||||||
std::uint16_t e_magic; // Magic number ( k_Magic )
|
std::uint16_t e_magic; // Magic number ( k_Magic )
|
||||||
std::uint16_t e_cblp; // Bytes on last page of file
|
std::uint16_t e_cblp; // Bytes on last page of file
|
||||||
std::uint16_t e_cp; // Pages in file
|
std::uint16_t e_cp; // Pages in file
|
||||||
std::uint16_t e_crlc; // Relocations
|
std::uint16_t e_crlc; // Relocations
|
||||||
std::uint16_t e_cparhdr; // Size of header in paragraphs
|
std::uint16_t e_cparhdr; // Size of header in paragraphs
|
||||||
std::uint16_t e_minalloc; // Minimum extra paragraphs needed
|
std::uint16_t e_minalloc; // Minimum extra paragraphs needed
|
||||||
std::uint16_t e_maxalloc; // Maximum extra paragraphs needed
|
std::uint16_t e_maxalloc; // Maximum extra paragraphs needed
|
||||||
std::uint16_t e_ss; // Initial (relative) SS value
|
std::uint16_t e_ss; // Initial (relative) SS value
|
||||||
std::uint16_t e_sp; // Initial SP value
|
std::uint16_t e_sp; // Initial SP value
|
||||||
std::uint16_t e_csum; // Checksum
|
std::uint16_t e_csum; // Checksum
|
||||||
std::uint16_t e_ip; // Initial IP value
|
std::uint16_t e_ip; // Initial IP value
|
||||||
std::uint16_t e_cs; // Initial (relative) CS value
|
std::uint16_t e_cs; // Initial (relative) CS value
|
||||||
std::uint16_t e_lfarlc; // File address of relocation table
|
std::uint16_t e_lfarlc; // File address of relocation table
|
||||||
std::uint16_t e_ovno; // Overlay number
|
std::uint16_t e_ovno; // Overlay number
|
||||||
std::uint16_t e_res[4]; // Reserved words
|
std::uint16_t e_res[4]; // Reserved words
|
||||||
std::uint16_t e_oemid; // OEM identifier (for e_oeminfo)
|
std::uint16_t e_oemid; // OEM identifier (for e_oeminfo)
|
||||||
std::uint16_t e_oeminfo; // OEM information; e_oemid specific
|
std::uint16_t e_oeminfo; // OEM information; e_oemid specific
|
||||||
std::uint16_t e_res2[10]; // Reserved words
|
std::uint16_t e_res2[10]; // Reserved words
|
||||||
std::uint32_t e_lfanew; // File address of new exe header
|
std::uint32_t e_lfanew; // File address of new exe header
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
#define IMAGE_SIZEOF_SHORT_NAME 8
|
#define IMAGE_SIZEOF_SHORT_NAME 8
|
||||||
|
|
||||||
#ifndef OS_WINDOWS
|
#ifndef OS_WINDOWS
|
||||||
typedef struct _IMAGE_SECTION_HEADER
|
typedef struct _IMAGE_SECTION_HEADER
|
||||||
{
|
{
|
||||||
std::uint8_t Name[IMAGE_SIZEOF_SHORT_NAME];
|
std::uint8_t Name[IMAGE_SIZEOF_SHORT_NAME];
|
||||||
union {
|
union
|
||||||
std:: uint32_t PhysicalAddress;
|
{
|
||||||
std::uint32_t VirtualSize;
|
std::uint32_t PhysicalAddress;
|
||||||
|
std::uint32_t VirtualSize;
|
||||||
} Misc;
|
} Misc;
|
||||||
std::uint32_t VirtualAddress;
|
std::uint32_t VirtualAddress;
|
||||||
std::uint32_t SizeOfRawData;
|
std::uint32_t SizeOfRawData;
|
||||||
std::uint32_t PointerToRawData;
|
std::uint32_t PointerToRawData;
|
||||||
std::uint32_t PointerToRelocations;
|
std::uint32_t PointerToRelocations;
|
||||||
std::uint32_t PointerToLinenumbers;
|
std::uint32_t PointerToLinenumbers;
|
||||||
std::uint16_t NumberOfRelocations;
|
std::uint16_t NumberOfRelocations;
|
||||||
std::uint16_t NumberOfLinenumbers;
|
std::uint16_t NumberOfLinenumbers;
|
||||||
std::uint32_t Characteristics;
|
std::uint32_t Characteristics;
|
||||||
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
|
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
|
||||||
|
|
||||||
typedef struct _IMAGE_EXPORT_DIRECTORY {
|
typedef struct _IMAGE_EXPORT_DIRECTORY
|
||||||
DWORD Characteristics;
|
{
|
||||||
DWORD TimeDateStamp;
|
DWORD Characteristics;
|
||||||
WORD MajorVersion;
|
DWORD TimeDateStamp;
|
||||||
WORD MinorVersion;
|
WORD MajorVersion;
|
||||||
DWORD Name;
|
WORD MinorVersion;
|
||||||
DWORD Base;
|
DWORD Name;
|
||||||
DWORD NumberOfFunctions;
|
DWORD Base;
|
||||||
DWORD NumberOfNames;
|
DWORD NumberOfFunctions;
|
||||||
DWORD AddressOfFunctions;
|
DWORD NumberOfNames;
|
||||||
DWORD AddressOfNames;
|
DWORD AddressOfFunctions;
|
||||||
DWORD AddressOfNameOrdinals;
|
DWORD AddressOfNames;
|
||||||
|
DWORD AddressOfNameOrdinals;
|
||||||
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
|
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
|
||||||
|
|
||||||
typedef struct _IMAGE_BASE_RELOCATION {
|
typedef struct _IMAGE_BASE_RELOCATION
|
||||||
DWORD VirtualAddress;
|
{
|
||||||
DWORD SizeOfBlock;
|
DWORD VirtualAddress;
|
||||||
WORD TypeOffset[1];
|
DWORD SizeOfBlock;
|
||||||
|
WORD TypeOffset[1];
|
||||||
} IMAGE_BASE_RELOCATION, *PIMAGE_BASE_RELOCATION;
|
} IMAGE_BASE_RELOCATION, *PIMAGE_BASE_RELOCATION;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -285,56 +286,56 @@ typedef struct _IMAGE_BASE_RELOCATION {
|
|||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
struct SECTION_IMAGE_INFORMATION
|
struct SECTION_IMAGE_INFORMATION
|
||||||
{
|
{
|
||||||
typename Traits::PVOID TransferAddress;
|
typename Traits::PVOID TransferAddress;
|
||||||
ULONG ZeroBits;
|
ULONG ZeroBits;
|
||||||
typename Traits::SIZE_T MaximumStackSize;
|
typename Traits::SIZE_T MaximumStackSize;
|
||||||
typename Traits::SIZE_T CommittedStackSize;
|
typename Traits::SIZE_T CommittedStackSize;
|
||||||
ULONG SubSystemType;
|
ULONG SubSystemType;
|
||||||
|
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
USHORT SubSystemMinorVersion;
|
USHORT SubSystemMinorVersion;
|
||||||
USHORT SubSystemMajorVersion;
|
USHORT SubSystemMajorVersion;
|
||||||
};
|
};
|
||||||
|
|
||||||
ULONG SubSystemVersion;
|
ULONG SubSystemVersion;
|
||||||
};
|
};
|
||||||
|
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
USHORT MajorOperatingSystemVersion;
|
USHORT MajorOperatingSystemVersion;
|
||||||
USHORT MinorOperatingSystemVersion;
|
USHORT MinorOperatingSystemVersion;
|
||||||
};
|
};
|
||||||
|
|
||||||
ULONG OperatingSystemVersion;
|
ULONG OperatingSystemVersion;
|
||||||
};
|
};
|
||||||
|
|
||||||
USHORT ImageCharacteristics;
|
USHORT ImageCharacteristics;
|
||||||
USHORT DllCharacteristics;
|
USHORT DllCharacteristics;
|
||||||
PEMachineType Machine;
|
PEMachineType Machine;
|
||||||
BOOLEAN ImageContainsCode;
|
BOOLEAN ImageContainsCode;
|
||||||
|
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
UCHAR ImageFlags;
|
UCHAR ImageFlags;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
UCHAR ComPlusNativeReady : 1;
|
UCHAR ComPlusNativeReady : 1;
|
||||||
UCHAR ComPlusILOnly : 1;
|
UCHAR ComPlusILOnly : 1;
|
||||||
UCHAR ImageDynamicallyRelocated : 1;
|
UCHAR ImageDynamicallyRelocated : 1;
|
||||||
UCHAR ImageMappedFlat : 1;
|
UCHAR ImageMappedFlat : 1;
|
||||||
UCHAR BaseBelow4gb : 1;
|
UCHAR BaseBelow4gb : 1;
|
||||||
UCHAR ComPlusPrefer32bit : 1;
|
UCHAR ComPlusPrefer32bit : 1;
|
||||||
UCHAR Reserved : 2;
|
UCHAR Reserved : 2;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
ULONG LoaderFlags;
|
ULONG LoaderFlags;
|
||||||
ULONG ImageFileSize;
|
ULONG ImageFileSize;
|
||||||
ULONG CheckSum;
|
ULONG CheckSum;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,114 +5,114 @@
|
|||||||
|
|
||||||
namespace utils
|
namespace utils
|
||||||
{
|
{
|
||||||
template <typename T, typename S = const uint8_t>
|
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>>)
|
requires(std::is_trivially_copyable_v<T> && std::is_same_v<uint8_t, std::remove_cv_t<S>>)
|
||||||
class safe_object_accessor
|
class safe_object_accessor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
safe_object_accessor(const std::span<S> buffer, const size_t offset)
|
safe_object_accessor(const std::span<S> buffer, const size_t offset)
|
||||||
: buffer_(buffer)
|
: buffer_(buffer),
|
||||||
, offset_(offset)
|
offset_(offset)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Object is copied to make sure platform-dependent alignment requirements
|
* Object is copied to make sure platform-dependent alignment requirements
|
||||||
* are respected
|
* are respected
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
T get(const size_t element_index = 0) const
|
T get(const size_t element_index = 0) const
|
||||||
{
|
{
|
||||||
T value{};
|
T value{};
|
||||||
memcpy(&value, get_valid_pointer(element_index), size);
|
memcpy(&value, get_valid_pointer(element_index), size);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set(const T value, const size_t element_index = 0) const
|
void set(const T value, const size_t element_index = 0) const
|
||||||
{
|
{
|
||||||
memcpy(get_valid_pointer(element_index), &value, size);
|
memcpy(get_valid_pointer(element_index), &value, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr auto size = sizeof(T);
|
static constexpr auto size = sizeof(T);
|
||||||
|
|
||||||
std::span<S> buffer_{};
|
std::span<S> buffer_{};
|
||||||
size_t offset_{};
|
size_t offset_{};
|
||||||
|
|
||||||
S* get_valid_pointer(const size_t element_index) const
|
S* get_valid_pointer(const size_t element_index) const
|
||||||
{
|
{
|
||||||
const auto start_offset = offset_ + (size * element_index);
|
const auto start_offset = offset_ + (size * element_index);
|
||||||
const auto end_offset = start_offset + size;
|
const auto end_offset = start_offset + size;
|
||||||
if (end_offset > buffer_.size())
|
if (end_offset > buffer_.size())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Buffer accessor overflow");
|
throw std::runtime_error("Buffer accessor overflow");
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer_.data() + start_offset;
|
return buffer_.data() + start_offset;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
requires(std::is_same_v<uint8_t, std::remove_cv_t<T>>)
|
requires(std::is_same_v<uint8_t, std::remove_cv_t<T>>)
|
||||||
class safe_buffer_accessor
|
class safe_buffer_accessor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
safe_buffer_accessor(const std::span<T> buffer)
|
safe_buffer_accessor(const std::span<T> buffer)
|
||||||
: buffer_(buffer)
|
: buffer_(buffer)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S>
|
template <typename S>
|
||||||
safe_buffer_accessor(const safe_buffer_accessor<S>& obj)
|
safe_buffer_accessor(const safe_buffer_accessor<S>& obj)
|
||||||
: buffer_(obj.get_buffer())
|
: buffer_(obj.get_buffer())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S>
|
template <typename S>
|
||||||
safe_object_accessor<S, T> as(const size_t offset) const
|
safe_object_accessor<S, T> as(const size_t offset) const
|
||||||
{
|
{
|
||||||
return {this->buffer_, offset};
|
return {this->buffer_, offset};
|
||||||
}
|
}
|
||||||
|
|
||||||
T* get_pointer_for_range(const size_t offset, const size_t size) const
|
T* get_pointer_for_range(const size_t offset, const size_t size) const
|
||||||
{
|
{
|
||||||
this->validate(offset, size);
|
this->validate(offset, size);
|
||||||
return this->buffer_.data() + offset;
|
return this->buffer_.data() + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
void validate(const size_t offset, const size_t size) const
|
void validate(const size_t offset, const size_t size) const
|
||||||
{
|
{
|
||||||
const auto end = offset + size;
|
const auto end = offset + size;
|
||||||
if (end > buffer_.size())
|
if (end > buffer_.size())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Buffer accessor overflow");
|
throw std::runtime_error("Buffer accessor overflow");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S = char>
|
template <typename S = char>
|
||||||
std::basic_string<S> as_string(const size_t offset) const
|
std::basic_string<S> as_string(const size_t offset) const
|
||||||
{
|
{
|
||||||
safe_object_accessor<S> string_accessor{this->buffer_, offset};
|
safe_object_accessor<S> string_accessor{this->buffer_, offset};
|
||||||
std::basic_string<S> result{};
|
std::basic_string<S> result{};
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
auto value = string_accessor.get(result.size());
|
auto value = string_accessor.get(result.size());
|
||||||
if (!value)
|
if (!value)
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.push_back(std::move(value));
|
result.push_back(std::move(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::span<T> get_buffer() const
|
std::span<T> get_buffer() const
|
||||||
{
|
{
|
||||||
return this->buffer_;
|
return this->buffer_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::span<T> buffer_{};
|
const std::span<T> buffer_{};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,54 +4,60 @@
|
|||||||
|
|
||||||
namespace utils::concurrency
|
namespace utils::concurrency
|
||||||
{
|
{
|
||||||
template <typename T, typename MutexType = std::mutex>
|
template <typename T, typename MutexType = std::mutex>
|
||||||
class container
|
class container
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
template <typename R = void, typename F>
|
template <typename R = void, typename F>
|
||||||
R access(F&& accessor) const
|
R access(F&& accessor) const
|
||||||
{
|
{
|
||||||
std::lock_guard<MutexType> _{mutex_};
|
std::lock_guard<MutexType> _{mutex_};
|
||||||
return accessor(object_);
|
return accessor(object_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename R = void, typename F>
|
template <typename R = void, typename F>
|
||||||
R access(F&& accessor)
|
R access(F&& accessor)
|
||||||
{
|
{
|
||||||
std::lock_guard<MutexType> _{mutex_};
|
std::lock_guard<MutexType> _{mutex_};
|
||||||
return accessor(object_);
|
return accessor(object_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename R = void, typename F>
|
template <typename R = void, typename F>
|
||||||
R access_with_lock(F&& accessor) const
|
R access_with_lock(F&& accessor) const
|
||||||
{
|
{
|
||||||
std::unique_lock<MutexType> lock{mutex_};
|
std::unique_lock<MutexType> lock{mutex_};
|
||||||
return accessor(object_, lock);
|
return accessor(object_, lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename R = void, typename F>
|
template <typename R = void, typename F>
|
||||||
R access_with_lock(F&& accessor)
|
R access_with_lock(F&& accessor)
|
||||||
{
|
{
|
||||||
std::unique_lock<MutexType> lock{mutex_};
|
std::unique_lock<MutexType> lock{mutex_};
|
||||||
return accessor(object_, lock);
|
return accessor(object_, lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
T& get_raw() { return object_; }
|
T& get_raw()
|
||||||
const T& get_raw() const { return object_; }
|
{
|
||||||
|
return object_;
|
||||||
|
}
|
||||||
|
const T& get_raw() const
|
||||||
|
{
|
||||||
|
return object_;
|
||||||
|
}
|
||||||
|
|
||||||
T copy() const
|
T copy() const
|
||||||
{
|
{
|
||||||
std::unique_lock<MutexType> lock{mutex_};
|
std::unique_lock<MutexType> lock{mutex_};
|
||||||
return object_;
|
return object_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_lock<MutexType> acquire_lock()
|
std::unique_lock<MutexType> acquire_lock()
|
||||||
{
|
{
|
||||||
return std::unique_lock<MutexType>{mutex_};
|
return std::unique_lock<MutexType>{mutex_};
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable MutexType mutex_{};
|
mutable MutexType mutex_{};
|
||||||
T object_{};
|
T object_{};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,19 +7,19 @@
|
|||||||
|
|
||||||
namespace utils
|
namespace utils
|
||||||
{
|
{
|
||||||
struct string_hash
|
struct string_hash
|
||||||
{
|
{
|
||||||
using is_transparent = void;
|
using is_transparent = void;
|
||||||
|
|
||||||
size_t operator()(const std::string_view str) const
|
size_t operator()(const std::string_view str) const
|
||||||
{
|
{
|
||||||
constexpr std::hash<std::string_view> hasher{};
|
constexpr std::hash<std::string_view> hasher{};
|
||||||
return hasher(str);
|
return hasher(str);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using unordered_string_map = std::unordered_map<std::string, T, string_hash, std::equal_to<>>;
|
using unordered_string_map = std::unordered_map<std::string, T, string_hash, std::equal_to<>>;
|
||||||
|
|
||||||
using unordered_string_set = std::unordered_set<std::string, string_hash, std::equal_to<>>;
|
using unordered_string_set = std::unordered_set<std::string, string_hash, std::equal_to<>>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,91 +5,91 @@
|
|||||||
|
|
||||||
namespace utils
|
namespace utils
|
||||||
{
|
{
|
||||||
class file_handle
|
class file_handle
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
file_handle() = default;
|
file_handle() = default;
|
||||||
|
|
||||||
file_handle(FILE* file)
|
file_handle(FILE* file)
|
||||||
: file_(file)
|
: file_(file)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
~file_handle()
|
~file_handle()
|
||||||
{
|
{
|
||||||
this->release();
|
this->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
file_handle(const file_handle&) = delete;
|
file_handle(const file_handle&) = delete;
|
||||||
file_handle& operator=(const file_handle&) = delete;
|
file_handle& operator=(const file_handle&) = delete;
|
||||||
|
|
||||||
file_handle(file_handle&& obj) noexcept
|
file_handle(file_handle&& obj) noexcept
|
||||||
: file_handle()
|
: file_handle()
|
||||||
{
|
{
|
||||||
this->operator=(std::move(obj));
|
this->operator=(std::move(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
file_handle& operator=(file_handle&& obj) noexcept
|
file_handle& operator=(file_handle&& obj) noexcept
|
||||||
{
|
{
|
||||||
if (this != &obj)
|
if (this != &obj)
|
||||||
{
|
{
|
||||||
this->release();
|
this->release();
|
||||||
this->file_ = obj.file_;
|
this->file_ = obj.file_;
|
||||||
obj.file_ = {};
|
obj.file_ = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
file_handle& operator=(FILE* file) noexcept
|
file_handle& operator=(FILE* file) noexcept
|
||||||
{
|
{
|
||||||
this->release();
|
this->release();
|
||||||
this->file_ = file;
|
this->file_ = file;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] operator bool() const
|
[[nodiscard]] operator bool() const
|
||||||
{
|
{
|
||||||
return this->file_;
|
return this->file_;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] operator FILE*() const
|
[[nodiscard]] operator FILE*() const
|
||||||
{
|
{
|
||||||
return this->file_;
|
return this->file_;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] int64_t size() const
|
[[nodiscard]] int64_t size() const
|
||||||
{
|
{
|
||||||
const auto current_position = this->tell();
|
const auto current_position = this->tell();
|
||||||
|
|
||||||
this->seek_to(0, SEEK_END);
|
this->seek_to(0, SEEK_END);
|
||||||
const auto size = this->tell();
|
const auto size = this->tell();
|
||||||
this->seek_to(current_position);
|
this->seek_to(current_position);
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool seek_to(const int64_t position, const int origin = SEEK_SET) const
|
bool seek_to(const int64_t position, const int origin = SEEK_SET) const
|
||||||
{
|
{
|
||||||
return _fseeki64(this->file_, position, origin) == 0;
|
return _fseeki64(this->file_, position, origin) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] int64_t tell() const
|
[[nodiscard]] int64_t tell() const
|
||||||
{
|
{
|
||||||
return _ftelli64(this->file_);
|
return _ftelli64(this->file_);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FILE* file_{};
|
FILE* file_{};
|
||||||
|
|
||||||
void release()
|
void release()
|
||||||
{
|
{
|
||||||
if (this->file_)
|
if (this->file_)
|
||||||
{
|
{
|
||||||
(void)fclose(this->file_);
|
(void)fclose(this->file_);
|
||||||
this->file_ = {};
|
this->file_ = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,52 +4,53 @@
|
|||||||
|
|
||||||
namespace utils
|
namespace utils
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Copied from here: https://github.com/microsoft/GSL/blob/e0880931ae5885eb988d1a8a57acf8bc2b8dacda/include/gsl/util#L57
|
* Copied from here:
|
||||||
*/
|
* https://github.com/microsoft/GSL/blob/e0880931ae5885eb988d1a8a57acf8bc2b8dacda/include/gsl/util#L57
|
||||||
|
*/
|
||||||
|
|
||||||
template <class F>
|
template <class F>
|
||||||
class final_action
|
class final_action
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static_assert(!std::is_reference<F>::value && !std::is_const<F>::value &&
|
static_assert(!std::is_reference<F>::value && !std::is_const<F>::value && !std::is_volatile<F>::value,
|
||||||
!std::is_volatile<F>::value,
|
"Final_action should store its callable by value");
|
||||||
"Final_action should store its callable by value");
|
|
||||||
|
|
||||||
explicit final_action(F f) noexcept : f_(std::move(f))
|
explicit final_action(F f) noexcept
|
||||||
{
|
: f_(std::move(f))
|
||||||
}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
final_action(final_action&& other) noexcept
|
final_action(final_action&& other) noexcept
|
||||||
: f_(std::move(other.f_)), invoke_(std::exchange(other.invoke_, false))
|
: f_(std::move(other.f_)),
|
||||||
{
|
invoke_(std::exchange(other.invoke_, false))
|
||||||
}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
final_action(const final_action&) = delete;
|
final_action(const final_action&) = delete;
|
||||||
final_action& operator=(const final_action&) = delete;
|
final_action& operator=(const final_action&) = delete;
|
||||||
final_action& operator=(final_action&&) = delete;
|
final_action& operator=(final_action&&) = delete;
|
||||||
|
|
||||||
~final_action() noexcept
|
~final_action() noexcept
|
||||||
{
|
{
|
||||||
if (invoke_) f_();
|
if (invoke_)
|
||||||
}
|
f_();
|
||||||
|
}
|
||||||
|
|
||||||
// Added by momo5502
|
// Added by momo5502
|
||||||
void cancel()
|
void cancel()
|
||||||
{
|
{
|
||||||
invoke_ = false;
|
invoke_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
F f_;
|
F f_;
|
||||||
bool invoke_{true};
|
bool invoke_{true};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class F>
|
template <class F>
|
||||||
final_action<typename std::remove_cv<typename std::remove_reference<F>::type>::type>
|
final_action<typename std::remove_cv<typename std::remove_reference<F>::type>::type> finally(F&& f) noexcept
|
||||||
finally(F&& f) noexcept
|
{
|
||||||
{
|
return final_action<typename std::remove_cv<typename std::remove_reference<F>::type>::type>(std::forward<F>(f));
|
||||||
return final_action<typename std::remove_cv<typename std::remove_reference<F>::type>::type>(
|
}
|
||||||
std::forward<F>(f));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,121 +4,123 @@
|
|||||||
|
|
||||||
namespace utils::io
|
namespace utils::io
|
||||||
{
|
{
|
||||||
bool remove_file(const std::filesystem::path& file)
|
bool remove_file(const std::filesystem::path& file)
|
||||||
{
|
{
|
||||||
std::error_code ec{};
|
std::error_code ec{};
|
||||||
return std::filesystem::remove(file, ec) && !ec;
|
return std::filesystem::remove(file, ec) && !ec;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool move_file(const std::filesystem::path& src, const std::filesystem::path& target)
|
bool move_file(const std::filesystem::path& src, const std::filesystem::path& target)
|
||||||
{
|
{
|
||||||
copy_folder(src, target);
|
copy_folder(src, target);
|
||||||
return remove_file(src);
|
return remove_file(src);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool file_exists(const std::filesystem::path& file)
|
bool file_exists(const std::filesystem::path& file)
|
||||||
{
|
{
|
||||||
return std::ifstream(file).good();
|
return std::ifstream(file).good();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool write_file(const std::filesystem::path& file, const std::vector<uint8_t>& data, const bool append)
|
bool write_file(const std::filesystem::path& file, const std::vector<uint8_t>& data, const bool append)
|
||||||
{
|
{
|
||||||
if (file.has_parent_path())
|
if (file.has_parent_path())
|
||||||
{
|
{
|
||||||
io::create_directory(file.parent_path());
|
io::create_directory(file.parent_path());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ofstream stream(
|
std::ofstream stream(file, std::ios::binary | std::ofstream::out |
|
||||||
file, std::ios::binary | std::ofstream::out | (append ? std::ofstream::app : std::ofstream::out));
|
(append ? std::ofstream::app : std::ofstream::out));
|
||||||
|
|
||||||
if (stream.is_open())
|
if (stream.is_open())
|
||||||
{
|
{
|
||||||
stream.write(reinterpret_cast<const char*>(data.data()), static_cast<std::streamsize>(data.size()));
|
stream.write(reinterpret_cast<const char*>(data.data()), static_cast<std::streamsize>(data.size()));
|
||||||
stream.close();
|
stream.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> read_file(const std::filesystem::path& file)
|
std::vector<uint8_t> read_file(const std::filesystem::path& file)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> data;
|
std::vector<uint8_t> data;
|
||||||
read_file(file, &data);
|
read_file(file, &data);
|
||||||
return 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<uint8_t>* data)
|
||||||
{
|
{
|
||||||
if (!data) return false;
|
if (!data)
|
||||||
data->clear();
|
return false;
|
||||||
|
data->clear();
|
||||||
|
|
||||||
std::ifstream stream(file, std::ios::binary);
|
std::ifstream stream(file, std::ios::binary);
|
||||||
if (!stream) return false;
|
if (!stream)
|
||||||
|
return false;
|
||||||
|
|
||||||
*data = std::vector<uint8_t>{(std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>()};
|
*data = std::vector<uint8_t>{(std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>()};
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t file_size(const std::filesystem::path& file)
|
std::size_t file_size(const std::filesystem::path& file)
|
||||||
{
|
{
|
||||||
std::ifstream stream(file, std::ios::binary);
|
std::ifstream stream(file, std::ios::binary);
|
||||||
|
|
||||||
if (stream)
|
if (stream)
|
||||||
{
|
{
|
||||||
stream.seekg(0, std::ios::end);
|
stream.seekg(0, std::ios::end);
|
||||||
return static_cast<std::size_t>(stream.tellg());
|
return static_cast<std::size_t>(stream.tellg());
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool create_directory(const std::filesystem::path& directory)
|
bool create_directory(const std::filesystem::path& directory)
|
||||||
{
|
{
|
||||||
std::error_code ec{};
|
std::error_code ec{};
|
||||||
return std::filesystem::create_directories(directory, ec) && !ec;
|
return std::filesystem::create_directories(directory, ec) && !ec;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool directory_exists(const std::filesystem::path& directory)
|
bool directory_exists(const std::filesystem::path& directory)
|
||||||
{
|
{
|
||||||
std::error_code ec{};
|
std::error_code ec{};
|
||||||
return std::filesystem::is_directory(directory, ec) && !ec;
|
return std::filesystem::is_directory(directory, ec) && !ec;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool directory_is_empty(const std::filesystem::path& directory)
|
bool directory_is_empty(const std::filesystem::path& directory)
|
||||||
{
|
{
|
||||||
std::error_code ec{};
|
std::error_code ec{};
|
||||||
return std::filesystem::is_empty(directory, ec) && !ec;
|
return std::filesystem::is_empty(directory, ec) && !ec;
|
||||||
}
|
}
|
||||||
|
|
||||||
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target)
|
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target)
|
||||||
{
|
{
|
||||||
std::error_code ec{};
|
std::error_code ec{};
|
||||||
std::filesystem::copy(src, target,
|
std::filesystem::copy(
|
||||||
std::filesystem::copy_options::overwrite_existing |
|
src, target, std::filesystem::copy_options::overwrite_existing | std::filesystem::copy_options::recursive,
|
||||||
std::filesystem::copy_options::recursive, ec);
|
ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::filesystem::path> list_files(const std::filesystem::path& directory, const bool recursive)
|
std::vector<std::filesystem::path> list_files(const std::filesystem::path& directory, const bool recursive)
|
||||||
{
|
{
|
||||||
std::error_code code{};
|
std::error_code code{};
|
||||||
std::vector<std::filesystem::path> files;
|
std::vector<std::filesystem::path> files;
|
||||||
|
|
||||||
if (recursive)
|
if (recursive)
|
||||||
{
|
{
|
||||||
for (auto& file : std::filesystem::recursive_directory_iterator(directory, code))
|
for (auto& file : std::filesystem::recursive_directory_iterator(directory, code))
|
||||||
{
|
{
|
||||||
files.push_back(file.path());
|
files.push_back(file.path());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (auto& file : std::filesystem::directory_iterator(directory, code))
|
for (auto& file : std::filesystem::directory_iterator(directory, code))
|
||||||
{
|
{
|
||||||
files.push_back(file.path());
|
files.push_back(file.path());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,17 +6,17 @@
|
|||||||
|
|
||||||
namespace utils::io
|
namespace utils::io
|
||||||
{
|
{
|
||||||
bool remove_file(const std::filesystem::path& file);
|
bool remove_file(const std::filesystem::path& file);
|
||||||
bool move_file(const std::filesystem::path& src, const std::filesystem::path& target);
|
bool move_file(const std::filesystem::path& src, const std::filesystem::path& target);
|
||||||
bool file_exists(const std::filesystem::path& file);
|
bool file_exists(const std::filesystem::path& file);
|
||||||
bool write_file(const std::filesystem::path& file, const std::vector<uint8_t>& data, bool append = false);
|
bool write_file(const std::filesystem::path& file, const std::vector<uint8_t>& data, bool append = false);
|
||||||
bool read_file(const std::filesystem::path& file, std::vector<uint8_t>* data);
|
bool read_file(const std::filesystem::path& file, std::vector<uint8_t>* data);
|
||||||
std::vector<uint8_t> read_file(const std::filesystem::path& file);
|
std::vector<uint8_t> read_file(const std::filesystem::path& file);
|
||||||
size_t file_size(const std::filesystem::path& file);
|
size_t file_size(const std::filesystem::path& file);
|
||||||
bool create_directory(const std::filesystem::path& directory);
|
bool create_directory(const std::filesystem::path& directory);
|
||||||
bool directory_exists(const std::filesystem::path& directory);
|
bool directory_exists(const std::filesystem::path& directory);
|
||||||
bool directory_is_empty(const std::filesystem::path& directory);
|
bool directory_is_empty(const std::filesystem::path& directory);
|
||||||
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target);
|
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target);
|
||||||
|
|
||||||
std::vector<std::filesystem::path> list_files(const std::filesystem::path& directory, bool recursive = false);
|
std::vector<std::filesystem::path> list_files(const std::filesystem::path& directory, bool recursive = false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,80 +10,80 @@
|
|||||||
|
|
||||||
namespace utils::nt
|
namespace utils::nt
|
||||||
{
|
{
|
||||||
using HandleFunction = HANDLE();
|
using HandleFunction = HANDLE();
|
||||||
|
|
||||||
inline HANDLE null_handle()
|
inline HANDLE null_handle()
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline HANDLE invalid_handle()
|
inline HANDLE invalid_handle()
|
||||||
{
|
{
|
||||||
return INVALID_HANDLE_VALUE;
|
return INVALID_HANDLE_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <HandleFunction InvalidHandleFunction = null_handle>
|
template <HandleFunction InvalidHandleFunction = null_handle>
|
||||||
class handle
|
class handle
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
handle() = default;
|
handle() = default;
|
||||||
|
|
||||||
handle(const HANDLE h)
|
handle(const HANDLE h)
|
||||||
: handle_(h)
|
: handle_(h)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
~handle()
|
~handle()
|
||||||
{
|
{
|
||||||
if (*this)
|
if (*this)
|
||||||
{
|
{
|
||||||
CloseHandle(this->handle_);
|
CloseHandle(this->handle_);
|
||||||
this->handle_ = InvalidHandleFunction();
|
this->handle_ = InvalidHandleFunction();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handle(const handle&) = delete;
|
handle(const handle&) = delete;
|
||||||
handle& operator=(const handle&) = delete;
|
handle& operator=(const handle&) = delete;
|
||||||
|
|
||||||
handle(handle&& obj) noexcept
|
handle(handle&& obj) noexcept
|
||||||
: handle()
|
: handle()
|
||||||
{
|
{
|
||||||
this->operator=(std::move(obj));
|
this->operator=(std::move(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
handle& operator=(handle&& obj) noexcept
|
handle& operator=(handle&& obj) noexcept
|
||||||
{
|
{
|
||||||
if (this != &obj)
|
if (this != &obj)
|
||||||
{
|
{
|
||||||
this->~handle();
|
this->~handle();
|
||||||
this->handle_ = obj.handle_;
|
this->handle_ = obj.handle_;
|
||||||
obj.handle_ = InvalidHandleFunction();
|
obj.handle_ = InvalidHandleFunction();
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle& operator=(HANDLE h) noexcept
|
handle& operator=(HANDLE h) noexcept
|
||||||
{
|
{
|
||||||
this->~handle();
|
this->~handle();
|
||||||
this->handle_ = h;
|
this->handle_ = h;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] operator bool() const
|
[[nodiscard]] operator bool() const
|
||||||
{
|
{
|
||||||
return this->handle_ != InvalidHandleFunction();
|
return this->handle_ != InvalidHandleFunction();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] operator HANDLE() const
|
[[nodiscard]] operator HANDLE() const
|
||||||
{
|
{
|
||||||
return this->handle_;
|
return this->handle_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HANDLE handle_{InvalidHandleFunction()};
|
HANDLE handle_{InvalidHandleFunction()};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -6,45 +6,42 @@
|
|||||||
|
|
||||||
namespace utils::string
|
namespace utils::string
|
||||||
{
|
{
|
||||||
inline char char_to_lower(const char val)
|
inline char char_to_lower(const char val)
|
||||||
{
|
{
|
||||||
return static_cast<char>(std::tolower(static_cast<unsigned char>(val)));
|
return static_cast<char>(std::tolower(static_cast<unsigned char>(val)));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline char16_t char_to_lower(const char16_t val)
|
inline char16_t char_to_lower(const char16_t val)
|
||||||
{
|
{
|
||||||
if (val >= u'A' && val <= u'Z')
|
if (val >= u'A' && val <= u'Z')
|
||||||
{
|
{
|
||||||
return val + 32;
|
return val + 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline wchar_t char_to_lower(const wchar_t val)
|
inline wchar_t char_to_lower(const wchar_t val)
|
||||||
{
|
{
|
||||||
return std::towlower(val);
|
return std::towlower(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Elem, class Traits, class Alloc>
|
template <class Elem, class Traits, class Alloc>
|
||||||
void to_lower_inplace(std::basic_string<Elem, Traits, Alloc>& str)
|
void to_lower_inplace(std::basic_string<Elem, Traits, Alloc>& str)
|
||||||
{
|
{
|
||||||
std::ranges::transform(str, str.begin(), [](const Elem e)
|
std::ranges::transform(str, str.begin(), [](const Elem e) { return char_to_lower(e); });
|
||||||
{
|
}
|
||||||
return char_to_lower(e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Elem, class Traits, class Alloc>
|
template <class Elem, class Traits, class Alloc>
|
||||||
std::basic_string<Elem, Traits, Alloc> to_lower(std::basic_string<Elem, Traits, Alloc> str)
|
std::basic_string<Elem, Traits, Alloc> to_lower(std::basic_string<Elem, Traits, Alloc> str)
|
||||||
{
|
{
|
||||||
to_lower_inplace(str);
|
to_lower_inplace(str);
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Elem, class Traits, class Alloc>
|
template <class Elem, class Traits, class Alloc>
|
||||||
std::basic_string<Elem, Traits, Alloc> to_lower_consume(std::basic_string<Elem, Traits, Alloc>& str)
|
std::basic_string<Elem, Traits, Alloc> to_lower_consume(std::basic_string<Elem, Traits, Alloc>& str)
|
||||||
{
|
{
|
||||||
return to_lower(std::move(str));
|
return to_lower(std::move(str));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,23 +4,23 @@
|
|||||||
|
|
||||||
namespace utils
|
namespace utils
|
||||||
{
|
{
|
||||||
template <typename Clock = std::chrono::high_resolution_clock>
|
template <typename Clock = std::chrono::high_resolution_clock>
|
||||||
class timer
|
class timer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void update()
|
void update()
|
||||||
{
|
{
|
||||||
this->point_ = Clock::now();
|
this->point_ = Clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_elapsed(typename Clock::duration duration) const
|
bool has_elapsed(typename Clock::duration duration) const
|
||||||
{
|
{
|
||||||
const auto now = Clock::now();
|
const auto now = Clock::now();
|
||||||
const auto diff = now - this->point_;
|
const auto diff = now - this->point_;
|
||||||
return diff > duration;
|
return diff > duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typename Clock::time_point point_{Clock::now()};
|
typename Clock::time_point point_{Clock::now()};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,52 +4,52 @@
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
T* offset_pointer(void* data, const size_t offset)
|
T* offset_pointer(void* data, const size_t offset)
|
||||||
{
|
{
|
||||||
return reinterpret_cast<T*>(static_cast<uint8_t*>(data) + offset);
|
return reinterpret_cast<T*>(static_cast<uint8_t*>(data) + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const T* offset_pointer(const void* data, const size_t offset)
|
const T* offset_pointer(const void* data, const size_t offset)
|
||||||
{
|
{
|
||||||
return reinterpret_cast<const T*>(static_cast<const uint8_t*>(data) + offset);
|
return reinterpret_cast<const T*>(static_cast<const uint8_t*>(data) + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool is_within_start_and_end(const uint64_t value, const uint64_t start, const uint64_t end)
|
constexpr bool is_within_start_and_end(const uint64_t value, const uint64_t start, const uint64_t end)
|
||||||
{
|
{
|
||||||
return value >= start && value < end;
|
return value >= start && value < end;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool is_within_start_and_length(const uint64_t value, const uint64_t start, const uint64_t length)
|
constexpr bool is_within_start_and_length(const uint64_t value, const uint64_t start, const uint64_t length)
|
||||||
{
|
{
|
||||||
return is_within_start_and_end(value, start, start + length);
|
return is_within_start_and_end(value, start, start + length);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool regions_intersect(const uint64_t start1, const uint64_t end1, const uint64_t start2, const uint64_t end2)
|
constexpr bool regions_intersect(const uint64_t start1, const uint64_t end1, const uint64_t start2, const uint64_t end2)
|
||||||
{
|
{
|
||||||
return start1 < end2 && start2 < end1;
|
return start1 < end2 && start2 < end1;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool regions_with_length_intersect(const uint64_t start1, const uint64_t length1, const uint64_t start2,
|
constexpr bool regions_with_length_intersect(const uint64_t start1, const uint64_t length1, const uint64_t start2,
|
||||||
const uint64_t length2)
|
const uint64_t length2)
|
||||||
{
|
{
|
||||||
return regions_intersect(start1, start1 + length1, start2, start2 + length2);
|
return regions_intersect(start1, start1 + length1, start2, start2 + length2);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr uint64_t align_down(const uint64_t value, const uint64_t alignment)
|
constexpr uint64_t align_down(const uint64_t value, const uint64_t alignment)
|
||||||
{
|
{
|
||||||
return value & ~(alignment - 1);
|
return value & ~(alignment - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr uint64_t align_up(const uint64_t value, const uint64_t alignment)
|
constexpr uint64_t align_up(const uint64_t value, const uint64_t alignment)
|
||||||
{
|
{
|
||||||
return align_down(value + (alignment - 1), alignment);
|
return align_down(value + (alignment - 1), alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr uint64_t page_align_down(const uint64_t value, const uint64_t page_size = 0x1000)
|
constexpr uint64_t page_align_down(const uint64_t value, const uint64_t page_size = 0x1000)
|
||||||
{
|
{
|
||||||
return align_down(value, page_size);
|
return align_down(value, page_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr uint64_t page_align_up(const uint64_t value, const uint64_t page_size = 0x1000)
|
constexpr uint64_t page_align_up(const uint64_t value, const uint64_t page_size = 0x1000)
|
||||||
{
|
{
|
||||||
return align_up(value, page_size);
|
return align_up(value, page_size);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,156 +11,152 @@ using memory_operation = memory_permission;
|
|||||||
|
|
||||||
enum class instruction_hook_continuation : bool
|
enum class instruction_hook_continuation : bool
|
||||||
{
|
{
|
||||||
run_instruction = false,
|
run_instruction = false,
|
||||||
skip_instruction = true,
|
skip_instruction = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class memory_violation_continuation : bool
|
enum class memory_violation_continuation : bool
|
||||||
{
|
{
|
||||||
stop = false,
|
stop = false,
|
||||||
resume = true,
|
resume = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class memory_violation_type : uint8_t
|
enum class memory_violation_type : uint8_t
|
||||||
{
|
{
|
||||||
unmapped,
|
unmapped,
|
||||||
protection,
|
protection,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct basic_block
|
struct basic_block
|
||||||
{
|
{
|
||||||
uint64_t address;
|
uint64_t address;
|
||||||
size_t instruction_count;
|
size_t instruction_count;
|
||||||
size_t size;
|
size_t size;
|
||||||
};
|
};
|
||||||
|
|
||||||
using edge_generation_hook_callback = std::function<void(const basic_block& current_block,
|
using edge_generation_hook_callback =
|
||||||
const basic_block& previous_block)>;
|
std::function<void(const basic_block& current_block, const basic_block& previous_block)>;
|
||||||
using basic_block_hook_callback = std::function<void(const basic_block& block)>;
|
using basic_block_hook_callback = std::function<void(const basic_block& block)>;
|
||||||
|
|
||||||
using instruction_hook_callback = std::function<instruction_hook_continuation()>;
|
using instruction_hook_callback = std::function<instruction_hook_continuation()>;
|
||||||
|
|
||||||
using interrupt_hook_callback = std::function<void(int interrupt)>;
|
using interrupt_hook_callback = std::function<void(int interrupt)>;
|
||||||
using simple_memory_hook_callback = std::function<void(uint64_t address, size_t size, uint64_t value)>;
|
using simple_memory_hook_callback = std::function<void(uint64_t address, size_t size, uint64_t value)>;
|
||||||
using complex_memory_hook_callback = std::function<void(uint64_t address, size_t size, uint64_t value,
|
using complex_memory_hook_callback =
|
||||||
memory_operation operation)>;
|
std::function<void(uint64_t address, size_t size, uint64_t value, memory_operation operation)>;
|
||||||
using memory_violation_hook_callback = std::function<memory_violation_continuation(
|
using memory_violation_hook_callback = std::function<memory_violation_continuation(
|
||||||
uint64_t address, size_t size, memory_operation operation,
|
uint64_t address, size_t size, memory_operation operation, memory_violation_type type)>;
|
||||||
memory_violation_type type)>;
|
|
||||||
|
|
||||||
class emulator : public memory_manager
|
class emulator : public memory_manager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
emulator() = default;
|
emulator() = default;
|
||||||
|
|
||||||
emulator(const emulator&) = delete;
|
emulator(const emulator&) = delete;
|
||||||
emulator& operator=(const emulator&) = delete;
|
emulator& operator=(const emulator&) = delete;
|
||||||
|
|
||||||
emulator(emulator&&) = delete;
|
emulator(emulator&&) = delete;
|
||||||
emulator& operator=(emulator&&) = delete;
|
emulator& operator=(emulator&&) = delete;
|
||||||
|
|
||||||
virtual void start(uint64_t start, uint64_t end = 0, std::chrono::nanoseconds timeout = {}, size_t count = 0) = 0;
|
virtual void start(uint64_t start, uint64_t end = 0, std::chrono::nanoseconds timeout = {}, size_t count = 0) = 0;
|
||||||
virtual void stop() = 0;
|
virtual void stop() = 0;
|
||||||
|
|
||||||
virtual void read_raw_register(int reg, void* value, size_t size) = 0;
|
virtual void read_raw_register(int reg, void* value, size_t size) = 0;
|
||||||
virtual void write_raw_register(int reg, const void* value, size_t size) = 0;
|
virtual void write_raw_register(int reg, const void* value, size_t size) = 0;
|
||||||
|
|
||||||
virtual std::vector<std::byte> save_registers() = 0;
|
virtual std::vector<std::byte> save_registers() = 0;
|
||||||
virtual void restore_registers(const std::vector<std::byte>& register_data) = 0;
|
virtual void restore_registers(const std::vector<std::byte>& register_data) = 0;
|
||||||
|
|
||||||
virtual emulator_hook* hook_memory_violation(uint64_t address, size_t size,
|
virtual emulator_hook* hook_memory_violation(uint64_t address, size_t size,
|
||||||
memory_violation_hook_callback callback) = 0;
|
memory_violation_hook_callback callback) = 0;
|
||||||
|
|
||||||
virtual emulator_hook* hook_memory_access(uint64_t address, size_t size, memory_operation filter,
|
virtual emulator_hook* hook_memory_access(uint64_t address, size_t size, memory_operation filter,
|
||||||
complex_memory_hook_callback callback) = 0;
|
complex_memory_hook_callback callback) = 0;
|
||||||
virtual emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) = 0;
|
virtual emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) = 0;
|
||||||
|
|
||||||
virtual emulator_hook* hook_interrupt(interrupt_hook_callback callback) = 0;
|
virtual emulator_hook* hook_interrupt(interrupt_hook_callback callback) = 0;
|
||||||
|
|
||||||
virtual emulator_hook* hook_edge_generation(edge_generation_hook_callback callback) = 0;
|
virtual emulator_hook* hook_edge_generation(edge_generation_hook_callback callback) = 0;
|
||||||
virtual emulator_hook* hook_basic_block(basic_block_hook_callback callback) = 0;
|
virtual emulator_hook* hook_basic_block(basic_block_hook_callback callback) = 0;
|
||||||
|
|
||||||
virtual void delete_hook(emulator_hook* hook) = 0;
|
virtual void delete_hook(emulator_hook* hook) = 0;
|
||||||
|
|
||||||
emulator_hook* hook_memory_violation(memory_violation_hook_callback callback)
|
emulator_hook* hook_memory_violation(memory_violation_hook_callback callback)
|
||||||
{
|
{
|
||||||
return this->hook_memory_violation(0, std::numeric_limits<size_t>::max(), std::move(callback));
|
return this->hook_memory_violation(0, std::numeric_limits<size_t>::max(), std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
emulator_hook* hook_memory_read(const uint64_t address, const size_t size, simple_memory_hook_callback callback)
|
emulator_hook* hook_memory_read(const uint64_t address, const size_t size, simple_memory_hook_callback callback)
|
||||||
{
|
{
|
||||||
return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::read);
|
return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::read);
|
||||||
}
|
}
|
||||||
|
|
||||||
emulator_hook* hook_memory_write(const uint64_t address, const size_t size, simple_memory_hook_callback callback)
|
emulator_hook* hook_memory_write(const uint64_t address, const size_t size, simple_memory_hook_callback callback)
|
||||||
{
|
{
|
||||||
return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::write);
|
return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::write);
|
||||||
}
|
}
|
||||||
|
|
||||||
emulator_hook* hook_memory_execution(const uint64_t address, const size_t size,
|
emulator_hook* hook_memory_execution(const uint64_t address, const size_t size,
|
||||||
simple_memory_hook_callback callback)
|
simple_memory_hook_callback callback)
|
||||||
{
|
{
|
||||||
return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::exec);
|
return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::exec);
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(utils::buffer_serializer& buffer) const
|
void serialize(utils::buffer_serializer& buffer) const
|
||||||
{
|
{
|
||||||
this->perform_serialization(buffer, false);
|
this->perform_serialization(buffer, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void deserialize(utils::buffer_deserializer& buffer)
|
void deserialize(utils::buffer_deserializer& buffer)
|
||||||
{
|
{
|
||||||
this->perform_deserialization(buffer, false);
|
this->perform_deserialization(buffer, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void save_snapshot()
|
void save_snapshot()
|
||||||
{
|
{
|
||||||
utils::buffer_serializer serializer{};
|
utils::buffer_serializer serializer{};
|
||||||
this->perform_serialization(serializer, true);
|
this->perform_serialization(serializer, true);
|
||||||
this->last_snapshot_data_ = serializer.move_buffer();
|
this->last_snapshot_data_ = serializer.move_buffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void restore_snapshot()
|
void restore_snapshot()
|
||||||
{
|
{
|
||||||
if (this->last_snapshot_data_.empty())
|
if (this->last_snapshot_data_.empty())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::buffer_deserializer deserializer{this->last_snapshot_data_};
|
utils::buffer_deserializer deserializer{this->last_snapshot_data_};
|
||||||
this->perform_deserialization(deserializer, true);
|
this->perform_deserialization(deserializer, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool has_violation() const = 0;
|
virtual bool has_violation() const = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::byte> last_snapshot_data_{};
|
std::vector<std::byte> last_snapshot_data_{};
|
||||||
|
|
||||||
emulator_hook* hook_simple_memory_access(const uint64_t address, const size_t size,
|
emulator_hook* hook_simple_memory_access(const uint64_t address, const size_t size,
|
||||||
simple_memory_hook_callback callback, const memory_operation operation)
|
simple_memory_hook_callback callback, const memory_operation operation)
|
||||||
{
|
{
|
||||||
assert((static_cast<uint8_t>(operation) & (static_cast<uint8_t>(operation) - 1)) == 0);
|
assert((static_cast<uint8_t>(operation) & (static_cast<uint8_t>(operation) - 1)) == 0);
|
||||||
return this->hook_memory_access(address, size, operation,
|
return this->hook_memory_access(address, size, operation,
|
||||||
[c = std::move(callback)](const uint64_t a, const size_t s,
|
[c = std::move(callback)](const uint64_t a, const size_t s,
|
||||||
const uint64_t value,
|
const uint64_t value,
|
||||||
memory_operation)
|
memory_operation) { c(a, s, value); });
|
||||||
{
|
}
|
||||||
c(a, s, value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void perform_serialization(utils::buffer_serializer& buffer, const bool is_snapshot) const
|
void perform_serialization(utils::buffer_serializer& buffer, const bool is_snapshot) const
|
||||||
{
|
{
|
||||||
this->serialize_state(buffer, is_snapshot);
|
this->serialize_state(buffer, is_snapshot);
|
||||||
this->serialize_memory_state(buffer, is_snapshot);
|
this->serialize_memory_state(buffer, is_snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void perform_deserialization(utils::buffer_deserializer& buffer, const bool is_snapshot)
|
void perform_deserialization(utils::buffer_deserializer& buffer, const bool is_snapshot)
|
||||||
{
|
{
|
||||||
this->deserialize_state(buffer, is_snapshot);
|
this->deserialize_state(buffer, is_snapshot);
|
||||||
this->deserialize_memory_state(buffer, is_snapshot);
|
this->deserialize_memory_state(buffer, is_snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void serialize_state(utils::buffer_serializer& buffer, bool is_snapshot) const = 0;
|
virtual void serialize_state(utils::buffer_serializer& buffer, bool is_snapshot) const = 0;
|
||||||
virtual void deserialize_state(utils::buffer_deserializer& buffer, bool is_snapshot) = 0;
|
virtual void deserialize_state(utils::buffer_deserializer& buffer, bool is_snapshot) = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,541 +10,547 @@
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
constexpr auto MIN_ALLOCATION_ADDRESS = 0x0000000000010000ULL;
|
constexpr auto MIN_ALLOCATION_ADDRESS = 0x0000000000010000ULL;
|
||||||
constexpr auto MAX_ALLOCATION_ADDRESS = 0x00007ffffffeffffULL;
|
constexpr auto MAX_ALLOCATION_ADDRESS = 0x00007ffffffeffffULL;
|
||||||
|
|
||||||
void split_regions(memory_manager::committed_region_map& regions, const std::vector<uint64_t>& split_points)
|
void split_regions(memory_manager::committed_region_map& regions, const std::vector<uint64_t>& split_points)
|
||||||
{
|
{
|
||||||
for (auto i = regions.begin(); i != regions.end(); ++i)
|
for (auto i = regions.begin(); i != regions.end(); ++i)
|
||||||
{
|
{
|
||||||
for (const auto split_point : split_points)
|
for (const auto split_point : split_points)
|
||||||
{
|
{
|
||||||
if (is_within_start_and_length(split_point, i->first, i->second.length) && i->first != split_point)
|
if (is_within_start_and_length(split_point, i->first, i->second.length) && i->first != split_point)
|
||||||
{
|
{
|
||||||
const auto first_length = split_point - i->first;
|
const auto first_length = split_point - i->first;
|
||||||
const auto second_length = i->second.length - first_length;
|
const auto second_length = i->second.length - first_length;
|
||||||
|
|
||||||
i->second.length = first_length;
|
i->second.length = first_length;
|
||||||
|
|
||||||
regions[split_point] = memory_manager::committed_region{second_length, i->second.pemissions};
|
regions[split_point] = memory_manager::committed_region{second_length, i->second.pemissions};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void merge_regions(memory_manager::committed_region_map& regions)
|
void merge_regions(memory_manager::committed_region_map& regions)
|
||||||
{
|
{
|
||||||
for (auto i = regions.begin(); i != regions.end();)
|
for (auto i = regions.begin(); i != regions.end();)
|
||||||
{
|
{
|
||||||
assert(i->second.length > 0);
|
assert(i->second.length > 0);
|
||||||
|
|
||||||
auto next = i;
|
auto next = i;
|
||||||
std::advance(next, 1);
|
std::advance(next, 1);
|
||||||
|
|
||||||
if (next == regions.end())
|
if (next == regions.end())
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(next->second.length > 0);
|
assert(next->second.length > 0);
|
||||||
|
|
||||||
const auto end = i->first + i->second.length;
|
const auto end = i->first + i->second.length;
|
||||||
assert(end <= next->first);
|
assert(end <= next->first);
|
||||||
|
|
||||||
if (end != next->first || i->second.pemissions != next->second.pemissions)
|
if (end != next->first || i->second.pemissions != next->second.pemissions)
|
||||||
{
|
{
|
||||||
++i;
|
++i;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
i->second.length += next->second.length;
|
i->second.length += next->second.length;
|
||||||
regions.erase(next);
|
regions.erase(next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace utils
|
namespace utils
|
||||||
{
|
{
|
||||||
static void serialize(buffer_serializer& buffer, const memory_manager::committed_region& region)
|
static void serialize(buffer_serializer& buffer, const memory_manager::committed_region& region)
|
||||||
{
|
{
|
||||||
buffer.write<uint64_t>(region.length);
|
buffer.write<uint64_t>(region.length);
|
||||||
buffer.write(region.pemissions);
|
buffer.write(region.pemissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void deserialize(buffer_deserializer& buffer, memory_manager::committed_region& region)
|
static void deserialize(buffer_deserializer& buffer, memory_manager::committed_region& region)
|
||||||
{
|
{
|
||||||
region.length = static_cast<size_t>(buffer.read<uint64_t>());
|
region.length = static_cast<size_t>(buffer.read<uint64_t>());
|
||||||
region.pemissions = buffer.read<memory_permission>();
|
region.pemissions = buffer.read<memory_permission>();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void serialize(buffer_serializer& buffer, const memory_manager::reserved_region& region)
|
static void serialize(buffer_serializer& buffer, const memory_manager::reserved_region& region)
|
||||||
{
|
{
|
||||||
buffer.write(region.is_mmio);
|
buffer.write(region.is_mmio);
|
||||||
buffer.write<uint64_t>(region.length);
|
buffer.write<uint64_t>(region.length);
|
||||||
buffer.write_map(region.committed_regions);
|
buffer.write_map(region.committed_regions);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void deserialize(buffer_deserializer& buffer, memory_manager::reserved_region& region)
|
static void deserialize(buffer_deserializer& buffer, memory_manager::reserved_region& region)
|
||||||
{
|
{
|
||||||
buffer.read(region.is_mmio);
|
buffer.read(region.is_mmio);
|
||||||
region.length = static_cast<size_t>(buffer.read<uint64_t>());
|
region.length = static_cast<size_t>(buffer.read<uint64_t>());
|
||||||
buffer.read_map(region.committed_regions);
|
buffer.read_map(region.committed_regions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void memory_manager::serialize_memory_state(utils::buffer_serializer& buffer, const bool is_snapshot) const
|
void memory_manager::serialize_memory_state(utils::buffer_serializer& buffer, const bool is_snapshot) const
|
||||||
{
|
{
|
||||||
buffer.write_map(this->reserved_regions_);
|
buffer.write_map(this->reserved_regions_);
|
||||||
|
|
||||||
if (is_snapshot)
|
if (is_snapshot)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> data{};
|
std::vector<uint8_t> data{};
|
||||||
|
|
||||||
for (const auto& reserved_region : this->reserved_regions_)
|
for (const auto& reserved_region : this->reserved_regions_)
|
||||||
{
|
{
|
||||||
if (reserved_region.second.is_mmio)
|
if (reserved_region.second.is_mmio)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& region : reserved_region.second.committed_regions)
|
for (const auto& region : reserved_region.second.committed_regions)
|
||||||
{
|
{
|
||||||
data.resize(region.second.length);
|
data.resize(region.second.length);
|
||||||
|
|
||||||
this->read_memory(region.first, data.data(), region.second.length);
|
this->read_memory(region.first, data.data(), region.second.length);
|
||||||
|
|
||||||
buffer.write(data.data(), region.second.length);
|
buffer.write(data.data(), region.second.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer, const bool is_snapshot)
|
void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer, const bool is_snapshot)
|
||||||
{
|
{
|
||||||
if (!is_snapshot)
|
if (!is_snapshot)
|
||||||
{
|
{
|
||||||
for (const auto& reserved_region : this->reserved_regions_)
|
for (const auto& reserved_region : this->reserved_regions_)
|
||||||
{
|
{
|
||||||
for (const auto& region : reserved_region.second.committed_regions)
|
for (const auto& region : reserved_region.second.committed_regions)
|
||||||
{
|
{
|
||||||
this->unmap_memory(region.first, region.second.length);
|
this->unmap_memory(region.first, region.second.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.read_map(this->reserved_regions_);
|
buffer.read_map(this->reserved_regions_);
|
||||||
|
|
||||||
if (is_snapshot)
|
if (is_snapshot)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> data{};
|
std::vector<uint8_t> data{};
|
||||||
|
|
||||||
for (auto i = this->reserved_regions_.begin(); i != this->reserved_regions_.end();)
|
for (auto i = this->reserved_regions_.begin(); i != this->reserved_regions_.end();)
|
||||||
{
|
{
|
||||||
auto& reserved_region = i->second;
|
auto& reserved_region = i->second;
|
||||||
if (reserved_region.is_mmio)
|
if (reserved_region.is_mmio)
|
||||||
{
|
{
|
||||||
i = this->reserved_regions_.erase(i);
|
i = this->reserved_regions_.erase(i);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
++i;
|
++i;
|
||||||
|
|
||||||
for (const auto& region : reserved_region.committed_regions)
|
for (const auto& region : reserved_region.committed_regions)
|
||||||
{
|
{
|
||||||
data.resize(region.second.length);
|
data.resize(region.second.length);
|
||||||
|
|
||||||
buffer.read(data.data(), region.second.length);
|
buffer.read(data.data(), region.second.length);
|
||||||
|
|
||||||
this->map_memory(region.first, region.second.length, region.second.pemissions);
|
this->map_memory(region.first, region.second.length, region.second.pemissions);
|
||||||
this->write_memory(region.first, data.data(), region.second.length);
|
this->write_memory(region.first, data.data(), region.second.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool memory_manager::protect_memory(const uint64_t address, const size_t size, const memory_permission permissions,
|
bool memory_manager::protect_memory(const uint64_t address, const size_t size, const memory_permission permissions,
|
||||||
memory_permission* old_permissions)
|
memory_permission* old_permissions)
|
||||||
{
|
{
|
||||||
const auto entry = this->find_reserved_region(address);
|
const auto entry = this->find_reserved_region(address);
|
||||||
if (entry == this->reserved_regions_.end())
|
if (entry == this->reserved_regions_.end())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto end = address + size;
|
const auto end = address + size;
|
||||||
const auto region_end = entry->first + entry->second.length;
|
const auto region_end = entry->first + entry->second.length;
|
||||||
|
|
||||||
if (region_end < end)
|
if (region_end < end)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Cross region protect not supported yet!");
|
throw std::runtime_error("Cross region protect not supported yet!");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<memory_permission> old_first_permissions{};
|
std::optional<memory_permission> old_first_permissions{};
|
||||||
|
|
||||||
auto& committed_regions = entry->second.committed_regions;
|
auto& committed_regions = entry->second.committed_regions;
|
||||||
split_regions(committed_regions, {address, end});
|
split_regions(committed_regions, {address, end});
|
||||||
|
|
||||||
for (auto& sub_region : committed_regions)
|
for (auto& sub_region : committed_regions)
|
||||||
{
|
{
|
||||||
if (sub_region.first >= end)
|
if (sub_region.first >= end)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto sub_region_end = sub_region.first + sub_region.second.length;
|
const auto sub_region_end = sub_region.first + sub_region.second.length;
|
||||||
if (sub_region.first >= address && sub_region_end <= end)
|
if (sub_region.first >= address && sub_region_end <= end)
|
||||||
{
|
{
|
||||||
if (!old_first_permissions.has_value())
|
if (!old_first_permissions.has_value())
|
||||||
{
|
{
|
||||||
old_first_permissions = sub_region.second.pemissions;
|
old_first_permissions = sub_region.second.pemissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->apply_memory_protection(sub_region.first, sub_region.second.length, permissions);
|
this->apply_memory_protection(sub_region.first, sub_region.second.length, permissions);
|
||||||
sub_region.second.pemissions = permissions;
|
sub_region.second.pemissions = permissions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (old_permissions)
|
if (old_permissions)
|
||||||
{
|
{
|
||||||
*old_permissions = old_first_permissions.value_or(memory_permission::none);
|
*old_permissions = old_first_permissions.value_or(memory_permission::none);
|
||||||
}
|
}
|
||||||
|
|
||||||
merge_regions(committed_regions);
|
merge_regions(committed_regions);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool memory_manager::allocate_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb,
|
bool memory_manager::allocate_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb,
|
||||||
mmio_write_callback write_cb)
|
mmio_write_callback write_cb)
|
||||||
{
|
{
|
||||||
if (this->overlaps_reserved_region(address, size))
|
if (this->overlaps_reserved_region(address, size))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->map_mmio(address, size, std::move(read_cb), std::move(write_cb));
|
this->map_mmio(address, size, std::move(read_cb), std::move(write_cb));
|
||||||
|
|
||||||
const auto entry = this->reserved_regions_.try_emplace(address,
|
const auto entry = this->reserved_regions_
|
||||||
reserved_region{
|
.try_emplace(address,
|
||||||
.length = size,
|
reserved_region{
|
||||||
.is_mmio = true,
|
.length = size,
|
||||||
}).first;
|
.is_mmio = true,
|
||||||
|
})
|
||||||
|
.first;
|
||||||
|
|
||||||
entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write};
|
entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write};
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool memory_manager::allocate_memory(const uint64_t address, const size_t size, const memory_permission permissions,
|
bool memory_manager::allocate_memory(const uint64_t address, const size_t size, const memory_permission permissions,
|
||||||
const bool reserve_only)
|
const bool reserve_only)
|
||||||
{
|
{
|
||||||
if (this->overlaps_reserved_region(address, size))
|
if (this->overlaps_reserved_region(address, size))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto entry = this->reserved_regions_.try_emplace(address, reserved_region{.length = size,}).first;
|
const auto entry = this->reserved_regions_
|
||||||
|
.try_emplace(address,
|
||||||
|
reserved_region{
|
||||||
|
.length = size,
|
||||||
|
})
|
||||||
|
.first;
|
||||||
|
|
||||||
if (!reserve_only)
|
if (!reserve_only)
|
||||||
{
|
{
|
||||||
this->map_memory(address, size, permissions);
|
this->map_memory(address, size, permissions);
|
||||||
entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write};
|
entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write};
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool memory_manager::commit_memory(const uint64_t address, const size_t size, const memory_permission permissions)
|
bool memory_manager::commit_memory(const uint64_t address, const size_t size, const memory_permission permissions)
|
||||||
{
|
{
|
||||||
const auto entry = this->find_reserved_region(address);
|
const auto entry = this->find_reserved_region(address);
|
||||||
if (entry == this->reserved_regions_.end())
|
if (entry == this->reserved_regions_.end())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto end = address + size;
|
const auto end = address + size;
|
||||||
const auto region_end = entry->first + entry->second.length;
|
const auto region_end = entry->first + entry->second.length;
|
||||||
|
|
||||||
if (region_end < end)
|
if (region_end < end)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Cross region commit not supported yet!");
|
throw std::runtime_error("Cross region commit not supported yet!");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& committed_regions = entry->second.committed_regions;
|
auto& committed_regions = entry->second.committed_regions;
|
||||||
split_regions(committed_regions, {address, end});
|
split_regions(committed_regions, {address, end});
|
||||||
|
|
||||||
uint64_t last_region_start{};
|
uint64_t last_region_start{};
|
||||||
const committed_region* last_region{nullptr};
|
const committed_region* last_region{nullptr};
|
||||||
|
|
||||||
for (auto& sub_region : committed_regions)
|
for (auto& sub_region : committed_regions)
|
||||||
{
|
{
|
||||||
if (sub_region.first >= end)
|
if (sub_region.first >= end)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto sub_region_end = sub_region.first + sub_region.second.length;
|
const auto sub_region_end = sub_region.first + sub_region.second.length;
|
||||||
if (sub_region.first >= address && sub_region_end <= end)
|
if (sub_region.first >= address && sub_region_end <= end)
|
||||||
{
|
{
|
||||||
const auto map_start = last_region ? (last_region_start + last_region->length) : address;
|
const auto map_start = last_region ? (last_region_start + last_region->length) : address;
|
||||||
const auto map_length = sub_region.first - map_start;
|
const auto map_length = sub_region.first - map_start;
|
||||||
|
|
||||||
if (map_length > 0)
|
if (map_length > 0)
|
||||||
{
|
{
|
||||||
this->map_memory(map_start, map_length, permissions);
|
this->map_memory(map_start, map_length, permissions);
|
||||||
committed_regions[map_start] = committed_region{map_length, permissions};
|
committed_regions[map_start] = committed_region{map_length, permissions};
|
||||||
}
|
}
|
||||||
|
|
||||||
last_region_start = sub_region.first;
|
last_region_start = sub_region.first;
|
||||||
last_region = &sub_region.second;
|
last_region = &sub_region.second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!last_region || (last_region_start + last_region->length) < end)
|
if (!last_region || (last_region_start + last_region->length) < end)
|
||||||
{
|
{
|
||||||
const auto map_start = last_region ? (last_region_start + last_region->length) : address;
|
const auto map_start = last_region ? (last_region_start + last_region->length) : address;
|
||||||
const auto map_length = end - map_start;
|
const auto map_length = end - map_start;
|
||||||
|
|
||||||
this->map_memory(map_start, map_length, permissions);
|
this->map_memory(map_start, map_length, permissions);
|
||||||
committed_regions[map_start] = committed_region{map_length, permissions};
|
committed_regions[map_start] = committed_region{map_length, permissions};
|
||||||
}
|
}
|
||||||
|
|
||||||
merge_regions(committed_regions);
|
merge_regions(committed_regions);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool memory_manager::decommit_memory(const uint64_t address, const size_t size)
|
bool memory_manager::decommit_memory(const uint64_t address, const size_t size)
|
||||||
{
|
{
|
||||||
const auto entry = this->find_reserved_region(address);
|
const auto entry = this->find_reserved_region(address);
|
||||||
if (entry == this->reserved_regions_.end())
|
if (entry == this->reserved_regions_.end())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry->second.is_mmio)
|
if (entry->second.is_mmio)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Not allowed to decommit MMIO!");
|
throw std::runtime_error("Not allowed to decommit MMIO!");
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto end = address + size;
|
const auto end = address + size;
|
||||||
const auto region_end = entry->first + entry->second.length;
|
const auto region_end = entry->first + entry->second.length;
|
||||||
|
|
||||||
if (region_end < end)
|
if (region_end < end)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Cross region decommit not supported yet!");
|
throw std::runtime_error("Cross region decommit not supported yet!");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& committed_regions = entry->second.committed_regions;
|
auto& committed_regions = entry->second.committed_regions;
|
||||||
|
|
||||||
split_regions(committed_regions, {address, end});
|
split_regions(committed_regions, {address, end});
|
||||||
|
|
||||||
for (auto i = committed_regions.begin(); i != committed_regions.end();)
|
for (auto i = committed_regions.begin(); i != committed_regions.end();)
|
||||||
{
|
{
|
||||||
if (i->first >= end)
|
if (i->first >= end)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto sub_region_end = i->first + i->second.length;
|
const auto sub_region_end = i->first + i->second.length;
|
||||||
if (i->first >= address && sub_region_end <= end)
|
if (i->first >= address && sub_region_end <= end)
|
||||||
{
|
{
|
||||||
this->unmap_memory(i->first, i->second.length);
|
this->unmap_memory(i->first, i->second.length);
|
||||||
i = committed_regions.erase(i);
|
i = committed_regions.erase(i);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool memory_manager::release_memory(const uint64_t address, size_t size)
|
bool memory_manager::release_memory(const uint64_t address, size_t size)
|
||||||
{
|
{
|
||||||
const auto entry = this->reserved_regions_.find(address);
|
const auto entry = this->reserved_regions_.find(address);
|
||||||
if (entry == this->reserved_regions_.end())
|
if (entry == this->reserved_regions_.end())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!size)
|
if (!size)
|
||||||
{
|
{
|
||||||
size = entry->second.length;
|
size = entry->second.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size > entry->second.length)
|
if (size > entry->second.length)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Cross region release not supported yet!");
|
throw std::runtime_error("Cross region release not supported yet!");
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto end = address + size;
|
const auto end = address + size;
|
||||||
auto& committed_regions = entry->second.committed_regions;
|
auto& committed_regions = entry->second.committed_regions;
|
||||||
|
|
||||||
split_regions(committed_regions, {end});
|
split_regions(committed_regions, {end});
|
||||||
|
|
||||||
for (auto i = committed_regions.begin(); i != committed_regions.end();)
|
for (auto i = committed_regions.begin(); i != committed_regions.end();)
|
||||||
{
|
{
|
||||||
if (i->first >= end)
|
if (i->first >= end)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto sub_region_end = i->first + i->second.length;
|
const auto sub_region_end = i->first + i->second.length;
|
||||||
if (i->first >= address && sub_region_end <= end)
|
if (i->first >= address && sub_region_end <= end)
|
||||||
{
|
{
|
||||||
this->unmap_memory(i->first, i->second.length);
|
this->unmap_memory(i->first, i->second.length);
|
||||||
i = committed_regions.erase(i);
|
i = committed_regions.erase(i);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entry->second.length -= size;
|
entry->second.length -= size;
|
||||||
if (entry->second.length > 0)
|
if (entry->second.length > 0)
|
||||||
{
|
{
|
||||||
this->reserved_regions_[address + size] = std::move(entry->second);
|
this->reserved_regions_[address + size] = std::move(entry->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->reserved_regions_.erase(entry);
|
this->reserved_regions_.erase(entry);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t memory_manager::find_free_allocation_base(const size_t size, const uint64_t start) const
|
uint64_t memory_manager::find_free_allocation_base(const size_t size, const uint64_t start) const
|
||||||
{
|
{
|
||||||
uint64_t start_address =
|
uint64_t start_address = std::max(MIN_ALLOCATION_ADDRESS, start ? start : 0x100000000ULL);
|
||||||
std::max(MIN_ALLOCATION_ADDRESS, start ? start : 0x100000000ULL);
|
|
||||||
|
|
||||||
for (const auto& region : this->reserved_regions_)
|
for (const auto& region : this->reserved_regions_)
|
||||||
{
|
{
|
||||||
const auto region_end = region.first + region.second.length;
|
const auto region_end = region.first + region.second.length;
|
||||||
if (region_end < start_address)
|
if (region_end < start_address)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!regions_with_length_intersect(start_address, size, region.first, region.second.length))
|
if (!regions_with_length_intersect(start_address, size, region.first, region.second.length))
|
||||||
{
|
{
|
||||||
return start_address;
|
return start_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
start_address = page_align_up(region_end);
|
start_address = page_align_up(region_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (start_address + size <= MAX_ALLOCATION_ADDRESS)
|
if (start_address + size <= MAX_ALLOCATION_ADDRESS)
|
||||||
{
|
{
|
||||||
return start_address;
|
return start_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
region_info memory_manager::get_region_info(const uint64_t address)
|
region_info memory_manager::get_region_info(const uint64_t address)
|
||||||
{
|
{
|
||||||
region_info result{};
|
region_info result{};
|
||||||
result.start = MIN_ALLOCATION_ADDRESS;
|
result.start = MIN_ALLOCATION_ADDRESS;
|
||||||
result.length = MAX_ALLOCATION_ADDRESS - result.start;
|
result.length = MAX_ALLOCATION_ADDRESS - result.start;
|
||||||
result.permissions = memory_permission::none;
|
result.permissions = memory_permission::none;
|
||||||
result.allocation_base = {};
|
result.allocation_base = {};
|
||||||
result.allocation_length = result.length;
|
result.allocation_length = result.length;
|
||||||
result.is_committed = false;
|
result.is_committed = false;
|
||||||
result.is_reserved = false;
|
result.is_reserved = false;
|
||||||
|
|
||||||
if (this->reserved_regions_.empty())
|
if (this->reserved_regions_.empty())
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto upper_bound = this->reserved_regions_.upper_bound(address);
|
auto upper_bound = this->reserved_regions_.upper_bound(address);
|
||||||
if (upper_bound == this->reserved_regions_.begin())
|
if (upper_bound == this->reserved_regions_.begin())
|
||||||
{
|
{
|
||||||
result.length = upper_bound->first - result.start;
|
result.length = upper_bound->first - result.start;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto entry = --upper_bound;
|
const auto entry = --upper_bound;
|
||||||
const auto lower_end = entry->first + entry->second.length;
|
const auto lower_end = entry->first + entry->second.length;
|
||||||
if (lower_end <= address)
|
if (lower_end <= address)
|
||||||
{
|
{
|
||||||
result.start = lower_end;
|
result.start = lower_end;
|
||||||
result.length = MAX_ALLOCATION_ADDRESS - result.start;
|
result.length = MAX_ALLOCATION_ADDRESS - result.start;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have a reserved region
|
// We have a reserved region
|
||||||
const auto& reserved_region = entry->second;
|
const auto& reserved_region = entry->second;
|
||||||
const auto& committed_regions = reserved_region.committed_regions;
|
const auto& committed_regions = reserved_region.committed_regions;
|
||||||
|
|
||||||
result.is_reserved = true;
|
result.is_reserved = true;
|
||||||
result.allocation_base = entry->first;
|
result.allocation_base = entry->first;
|
||||||
result.allocation_length = reserved_region.length;
|
result.allocation_length = reserved_region.length;
|
||||||
result.start = result.allocation_base;
|
result.start = result.allocation_base;
|
||||||
result.length = result.allocation_length;
|
result.length = result.allocation_length;
|
||||||
|
|
||||||
if (committed_regions.empty())
|
if (committed_regions.empty())
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto committed_bound = committed_regions.upper_bound(address);
|
auto committed_bound = committed_regions.upper_bound(address);
|
||||||
if (committed_bound == committed_regions.begin())
|
if (committed_bound == committed_regions.begin())
|
||||||
{
|
{
|
||||||
result.length = committed_bound->first - result.start;
|
result.length = committed_bound->first - result.start;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto committed_entry = --committed_bound;
|
const auto committed_entry = --committed_bound;
|
||||||
const auto committed_lower_end = committed_entry->first + committed_entry->second.length;
|
const auto committed_lower_end = committed_entry->first + committed_entry->second.length;
|
||||||
if (committed_lower_end <= address)
|
if (committed_lower_end <= address)
|
||||||
{
|
{
|
||||||
result.start = committed_lower_end;
|
result.start = committed_lower_end;
|
||||||
result.length = lower_end - result.start;
|
result.length = lower_end - result.start;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.is_committed = true;
|
result.is_committed = true;
|
||||||
result.start = committed_entry->first;
|
result.start = committed_entry->first;
|
||||||
result.length = committed_entry->second.length;
|
result.length = committed_entry->second.length;
|
||||||
result.permissions = committed_entry->second.pemissions;
|
result.permissions = committed_entry->second.pemissions;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
memory_manager::reserved_region_map::iterator memory_manager::find_reserved_region(const uint64_t address)
|
memory_manager::reserved_region_map::iterator memory_manager::find_reserved_region(const uint64_t address)
|
||||||
{
|
{
|
||||||
if (this->reserved_regions_.empty())
|
if (this->reserved_regions_.empty())
|
||||||
{
|
{
|
||||||
return this->reserved_regions_.end();
|
return this->reserved_regions_.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto upper_bound = this->reserved_regions_.upper_bound(address);
|
auto upper_bound = this->reserved_regions_.upper_bound(address);
|
||||||
if (upper_bound == this->reserved_regions_.begin())
|
if (upper_bound == this->reserved_regions_.begin())
|
||||||
{
|
{
|
||||||
return this->reserved_regions_.end();
|
return this->reserved_regions_.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto entry = --upper_bound;
|
const auto entry = --upper_bound;
|
||||||
if (entry->first + entry->second.length <= address)
|
if (entry->first + entry->second.length <= address)
|
||||||
{
|
{
|
||||||
return this->reserved_regions_.end();
|
return this->reserved_regions_.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool memory_manager::overlaps_reserved_region(const uint64_t address, const size_t size) const
|
bool memory_manager::overlaps_reserved_region(const uint64_t address, const size_t size) const
|
||||||
{
|
{
|
||||||
for (const auto& region : this->reserved_regions_)
|
for (const auto& region : this->reserved_regions_)
|
||||||
{
|
{
|
||||||
if (regions_with_length_intersect(address, size, region.first, region.second.length))
|
if (regions_with_length_intersect(address, size, region.first, region.second.length))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
|
|
||||||
struct region_info : basic_memory_region
|
struct region_info : basic_memory_region
|
||||||
{
|
{
|
||||||
uint64_t allocation_base{};
|
uint64_t allocation_base{};
|
||||||
size_t allocation_length{};
|
size_t allocation_length{};
|
||||||
bool is_reserved{};
|
bool is_reserved{};
|
||||||
bool is_committed{};
|
bool is_committed{};
|
||||||
};
|
};
|
||||||
|
|
||||||
using mmio_read_callback = std::function<uint64_t(uint64_t addr, size_t size)>;
|
using mmio_read_callback = std::function<uint64_t(uint64_t addr, size_t size)>;
|
||||||
@@ -18,115 +18,114 @@ using mmio_write_callback = std::function<void(uint64_t addr, size_t size, uint6
|
|||||||
|
|
||||||
class memory_manager
|
class memory_manager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct committed_region
|
struct committed_region
|
||||||
{
|
{
|
||||||
size_t length{};
|
size_t length{};
|
||||||
memory_permission pemissions{};
|
memory_permission pemissions{};
|
||||||
};
|
};
|
||||||
|
|
||||||
using committed_region_map = std::map<uint64_t, committed_region>;
|
using committed_region_map = std::map<uint64_t, committed_region>;
|
||||||
|
|
||||||
struct reserved_region
|
struct reserved_region
|
||||||
{
|
{
|
||||||
size_t length{};
|
size_t length{};
|
||||||
committed_region_map committed_regions{};
|
committed_region_map committed_regions{};
|
||||||
bool is_mmio{false};
|
bool is_mmio{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual ~memory_manager() = default;
|
virtual ~memory_manager() = default;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T read_memory(const uint64_t address) const
|
T read_memory(const uint64_t address) const
|
||||||
{
|
{
|
||||||
T value{};
|
T value{};
|
||||||
this->read_memory(address, &value, sizeof(value));
|
this->read_memory(address, &value, sizeof(value));
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T read_memory(const void* address) const
|
T read_memory(const void* address) const
|
||||||
{
|
{
|
||||||
return this->read_memory<T>(reinterpret_cast<uint64_t>(address));
|
return this->read_memory<T>(reinterpret_cast<uint64_t>(address));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::byte> read_memory(const uint64_t address, const size_t size) const
|
std::vector<std::byte> read_memory(const uint64_t address, const size_t size) const
|
||||||
{
|
{
|
||||||
std::vector<std::byte> data{};
|
std::vector<std::byte> data{};
|
||||||
data.resize(size);
|
data.resize(size);
|
||||||
|
|
||||||
this->read_memory(address, data.data(), data.size());
|
this->read_memory(address, data.data(), data.size());
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::byte> read_memory(const void* address, const size_t size) const
|
std::vector<std::byte> read_memory(const void* address, const size_t size) const
|
||||||
{
|
{
|
||||||
return this->read_memory(reinterpret_cast<uint64_t>(address), size);
|
return this->read_memory(reinterpret_cast<uint64_t>(address), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void write_memory(const uint64_t address, const T& value)
|
void write_memory(const uint64_t address, const T& value)
|
||||||
{
|
{
|
||||||
this->write_memory(address, &value, sizeof(value));
|
this->write_memory(address, &value, sizeof(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void write_memory(void* address, const T& value)
|
void write_memory(void* address, const T& value)
|
||||||
{
|
{
|
||||||
this->write_memory(reinterpret_cast<uint64_t>(address), &value, sizeof(value));
|
this->write_memory(reinterpret_cast<uint64_t>(address), &value, sizeof(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_memory(void* address, const void* data, const size_t size)
|
void write_memory(void* address, const void* data, const size_t size)
|
||||||
{
|
{
|
||||||
this->write_memory(reinterpret_cast<uint64_t>(address), data, size);
|
this->write_memory(reinterpret_cast<uint64_t>(address), data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void read_memory(uint64_t address, void* data, size_t size) const = 0;
|
virtual void read_memory(uint64_t address, void* data, size_t size) const = 0;
|
||||||
virtual bool try_read_memory(uint64_t address, void* data, size_t size) const = 0;
|
virtual bool try_read_memory(uint64_t address, void* data, size_t size) const = 0;
|
||||||
virtual void write_memory(uint64_t address, const void* data, size_t size) = 0;
|
virtual void write_memory(uint64_t address, const void* data, size_t size) = 0;
|
||||||
|
|
||||||
bool protect_memory(uint64_t address, size_t size, memory_permission permissions,
|
bool protect_memory(uint64_t address, size_t size, memory_permission permissions,
|
||||||
memory_permission* old_permissions = nullptr);
|
memory_permission* old_permissions = nullptr);
|
||||||
|
|
||||||
bool allocate_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb);
|
bool allocate_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb);
|
||||||
bool allocate_memory(uint64_t address, size_t size, memory_permission permissions,
|
bool allocate_memory(uint64_t address, size_t size, memory_permission permissions, bool reserve_only = false);
|
||||||
bool reserve_only = false);
|
|
||||||
|
|
||||||
bool commit_memory(uint64_t address, size_t size, memory_permission permissions);
|
bool commit_memory(uint64_t address, size_t size, memory_permission permissions);
|
||||||
bool decommit_memory(uint64_t address, size_t size);
|
bool decommit_memory(uint64_t address, size_t size);
|
||||||
|
|
||||||
bool release_memory(uint64_t address, size_t size);
|
bool release_memory(uint64_t address, size_t size);
|
||||||
|
|
||||||
uint64_t find_free_allocation_base(size_t size, uint64_t start = 0) const;
|
uint64_t find_free_allocation_base(size_t size, uint64_t start = 0) const;
|
||||||
|
|
||||||
region_info get_region_info(uint64_t address);
|
region_info get_region_info(uint64_t address);
|
||||||
|
|
||||||
uint64_t allocate_memory(const size_t size, const memory_permission permissions, const bool reserve_only = false)
|
uint64_t allocate_memory(const size_t size, const memory_permission permissions, const bool reserve_only = false)
|
||||||
{
|
{
|
||||||
const auto allocation_base = this->find_free_allocation_base(size);
|
const auto allocation_base = this->find_free_allocation_base(size);
|
||||||
if (!allocate_memory(allocation_base, size, permissions, reserve_only))
|
if (!allocate_memory(allocation_base, size, permissions, reserve_only))
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return allocation_base;
|
return allocation_base;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using reserved_region_map = std::map<uint64_t, reserved_region>;
|
using reserved_region_map = std::map<uint64_t, reserved_region>;
|
||||||
reserved_region_map reserved_regions_{};
|
reserved_region_map reserved_regions_{};
|
||||||
|
|
||||||
reserved_region_map::iterator find_reserved_region(uint64_t address);
|
reserved_region_map::iterator find_reserved_region(uint64_t address);
|
||||||
bool overlaps_reserved_region(uint64_t address, size_t size) const;
|
bool overlaps_reserved_region(uint64_t address, size_t size) const;
|
||||||
|
|
||||||
virtual void map_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb) = 0;
|
virtual void map_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb) = 0;
|
||||||
virtual void map_memory(uint64_t address, size_t size, memory_permission permissions) = 0;
|
virtual void map_memory(uint64_t address, size_t size, memory_permission permissions) = 0;
|
||||||
virtual void unmap_memory(uint64_t address, size_t size) = 0;
|
virtual void unmap_memory(uint64_t address, size_t size) = 0;
|
||||||
|
|
||||||
virtual void apply_memory_protection(uint64_t address, size_t size, memory_permission permissions) = 0;
|
virtual void apply_memory_protection(uint64_t address, size_t size, memory_permission permissions) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void serialize_memory_state(utils::buffer_serializer& buffer, bool is_snapshot) const;
|
void serialize_memory_state(utils::buffer_serializer& buffer, bool is_snapshot) const;
|
||||||
void deserialize_memory_state(utils::buffer_deserializer& buffer, bool is_snapshot);
|
void deserialize_memory_state(utils::buffer_deserializer& buffer, bool is_snapshot);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,62 +3,52 @@
|
|||||||
|
|
||||||
enum class memory_permission : uint8_t
|
enum class memory_permission : uint8_t
|
||||||
{
|
{
|
||||||
none = 0,
|
none = 0,
|
||||||
read = 1 << 0,
|
read = 1 << 0,
|
||||||
write = 1 << 1,
|
write = 1 << 1,
|
||||||
exec = 1 << 2,
|
exec = 1 << 2,
|
||||||
read_write = read | write,
|
read_write = read | write,
|
||||||
all = read | write | exec
|
all = read | write | exec
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
inline constexpr memory_permission
|
inline constexpr memory_permission operator&(const memory_permission x, const memory_permission y)
|
||||||
operator&(const memory_permission x, const memory_permission y)
|
|
||||||
{
|
{
|
||||||
return static_cast<memory_permission>
|
return static_cast<memory_permission>(static_cast<uint8_t>(x) & static_cast<uint8_t>(y));
|
||||||
(static_cast<uint8_t>(x) & static_cast<uint8_t>(y));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline constexpr memory_permission
|
inline constexpr memory_permission operator|(const memory_permission x, const memory_permission y)
|
||||||
operator|(const memory_permission x, const memory_permission y)
|
|
||||||
{
|
{
|
||||||
return static_cast<memory_permission>
|
return static_cast<memory_permission>(static_cast<uint8_t>(x) | static_cast<uint8_t>(y));
|
||||||
(static_cast<uint8_t>(x) | static_cast<uint8_t>(y));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline constexpr memory_permission
|
inline constexpr memory_permission operator^(const memory_permission x, const memory_permission y)
|
||||||
operator^(const memory_permission x, const memory_permission y)
|
|
||||||
{
|
{
|
||||||
return static_cast<memory_permission>
|
return static_cast<memory_permission>(static_cast<uint8_t>(x) ^ static_cast<uint8_t>(y));
|
||||||
(static_cast<uint8_t>(x) ^ static_cast<uint8_t>(y));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline constexpr memory_permission
|
inline constexpr memory_permission operator~(memory_permission x)
|
||||||
operator~(memory_permission x)
|
|
||||||
{
|
{
|
||||||
return static_cast<memory_permission>(~static_cast<uint8_t>(x));
|
return static_cast<memory_permission>(~static_cast<uint8_t>(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline memory_permission&
|
inline memory_permission& operator&=(memory_permission& x, const memory_permission y)
|
||||||
operator&=(memory_permission& x, const memory_permission y)
|
|
||||||
{
|
{
|
||||||
x = x & y;
|
x = x & y;
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline memory_permission&
|
inline memory_permission& operator|=(memory_permission& x, const memory_permission y)
|
||||||
operator|=(memory_permission& x, const memory_permission y)
|
|
||||||
{
|
{
|
||||||
x = x | y;
|
x = x | y;
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline memory_permission&
|
inline memory_permission& operator^=(memory_permission& x, const memory_permission y)
|
||||||
operator^=(memory_permission& x, const memory_permission y)
|
|
||||||
{
|
{
|
||||||
x = x ^ y;
|
x = x ^ y;
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
|
|
||||||
struct basic_memory_region
|
struct basic_memory_region
|
||||||
{
|
{
|
||||||
uint64_t start{};
|
uint64_t start{};
|
||||||
size_t length{};
|
size_t length{};
|
||||||
memory_permission permissions{};
|
memory_permission permissions{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct memory_region : basic_memory_region
|
struct memory_region : basic_memory_region
|
||||||
{
|
{
|
||||||
bool committed{};
|
bool committed{};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,52 +3,52 @@
|
|||||||
|
|
||||||
class scoped_hook
|
class scoped_hook
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
scoped_hook() = default;
|
scoped_hook() = default;
|
||||||
|
|
||||||
scoped_hook(emulator& emu, emulator_hook* hook)
|
scoped_hook(emulator& emu, emulator_hook* hook)
|
||||||
: emu_(&emu)
|
: emu_(&emu),
|
||||||
, hook_(hook)
|
hook_(hook)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
~scoped_hook()
|
~scoped_hook()
|
||||||
{
|
{
|
||||||
this->remove();
|
this->remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
scoped_hook(const scoped_hook&) = delete;
|
scoped_hook(const scoped_hook&) = delete;
|
||||||
scoped_hook& operator=(const scoped_hook&) = delete;
|
scoped_hook& operator=(const scoped_hook&) = delete;
|
||||||
|
|
||||||
scoped_hook(scoped_hook&& obj) noexcept
|
scoped_hook(scoped_hook&& obj) noexcept
|
||||||
{
|
{
|
||||||
this->operator=(std::move(obj));
|
this->operator=(std::move(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
scoped_hook& operator=(scoped_hook&& obj) noexcept
|
scoped_hook& operator=(scoped_hook&& obj) noexcept
|
||||||
{
|
{
|
||||||
if (this != &obj)
|
if (this != &obj)
|
||||||
{
|
{
|
||||||
this->remove();
|
this->remove();
|
||||||
this->emu_ = obj.emu_;
|
this->emu_ = obj.emu_;
|
||||||
this->hook_ = obj.hook_;
|
this->hook_ = obj.hook_;
|
||||||
|
|
||||||
obj.hook_ = {};
|
obj.hook_ = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove()
|
void remove()
|
||||||
{
|
{
|
||||||
if (this->hook_)
|
if (this->hook_)
|
||||||
{
|
{
|
||||||
this->emu_->delete_hook(this->hook_);
|
this->emu_->delete_hook(this->hook_);
|
||||||
this->hook_ = {};
|
this->hook_ = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
emulator* emu_{};
|
emulator* emu_{};
|
||||||
emulator_hook* hook_{};
|
emulator_hook* hook_{};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,488 +11,483 @@
|
|||||||
|
|
||||||
namespace utils
|
namespace utils
|
||||||
{
|
{
|
||||||
class buffer_serializer;
|
class buffer_serializer;
|
||||||
class buffer_deserializer;
|
class buffer_deserializer;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
concept Serializable = requires(T a, const T ac, buffer_serializer& serializer, buffer_deserializer& deserializer)
|
concept Serializable = requires(T a, const T ac, buffer_serializer& serializer, buffer_deserializer& deserializer) {
|
||||||
{
|
{ ac.serialize(serializer) } -> std::same_as<void>;
|
||||||
{ ac.serialize(serializer) } -> std::same_as<void>;
|
{ a.deserialize(deserializer) } -> std::same_as<void>;
|
||||||
{ a.deserialize(deserializer) } -> std::same_as<void>;
|
};
|
||||||
};
|
|
||||||
|
|
||||||
/* Use concept instead, to prevent overhead of virtual function calls
|
/* Use concept instead, to prevent overhead of virtual function calls
|
||||||
struct serializable
|
struct serializable
|
||||||
{
|
{
|
||||||
virtual ~serializable() = default;
|
virtual ~serializable() = default;
|
||||||
virtual void serialize(buffer_serializer& buffer) const = 0;
|
virtual void serialize(buffer_serializer& buffer) const = 0;
|
||||||
virtual void deserialize(buffer_deserializer& buffer) = 0;
|
virtual void deserialize(buffer_deserializer& buffer) = 0;
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
template <typename, typename = void>
|
template <typename, typename = void>
|
||||||
struct has_serialize_function : std::false_type
|
struct has_serialize_function : std::false_type
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct has_serialize_function<T, std::void_t<decltype(serialize(std::declval<buffer_serializer&>(),
|
struct has_serialize_function<T,
|
||||||
std::declval<const std::remove_cvref_t<T>&>())
|
std::void_t<decltype(serialize(std::declval<buffer_serializer&>(),
|
||||||
)>>
|
std::declval<const std::remove_cvref_t<T>&>()))>>
|
||||||
: std::true_type
|
: std::true_type
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename, typename = void>
|
template <typename, typename = void>
|
||||||
struct has_deserialize_function : std::false_type
|
struct has_deserialize_function : std::false_type
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct has_deserialize_function<T, std::void_t<decltype(deserialize(
|
struct has_deserialize_function<T, std::void_t<decltype(deserialize(std::declval<buffer_deserializer&>(),
|
||||||
std::declval<buffer_deserializer&>(),
|
std::declval<std::remove_cvref_t<T>&>()))>>
|
||||||
std::declval<std::remove_cvref_t<T>&>()))>>
|
: std::true_type
|
||||||
: std::true_type
|
{
|
||||||
{
|
};
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct has_deserializer_constructor
|
struct has_deserializer_constructor : std::bool_constant<std::is_constructible_v<T, buffer_deserializer&>>
|
||||||
: std::bool_constant<std::is_constructible_v<T, buffer_deserializer&>>
|
{
|
||||||
{
|
};
|
||||||
};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class buffer_deserializer
|
class buffer_deserializer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
buffer_deserializer(const std::span<T> buffer, bool no_debugging = false)
|
buffer_deserializer(const std::span<T> buffer, bool no_debugging = false)
|
||||||
: no_debugging_(no_debugging)
|
: no_debugging_(no_debugging),
|
||||||
, buffer_(reinterpret_cast<const std::byte*>(buffer.data()), buffer.size() * sizeof(T))
|
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");
|
static_assert(std::is_trivially_copyable_v<T>, "Type must be trivially copyable");
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
buffer_deserializer(const std::vector<T>& buffer, bool no_debugging = false)
|
buffer_deserializer(const std::vector<T>& buffer, bool no_debugging = false)
|
||||||
: buffer_deserializer(std::span(buffer), no_debugging)
|
: buffer_deserializer(std::span(buffer), no_debugging)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::span<const std::byte> read_data(const size_t length)
|
std::span<const std::byte> read_data(const size_t length)
|
||||||
{
|
{
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
const uint64_t real_old_size = this->offset_;
|
const uint64_t real_old_size = this->offset_;
|
||||||
(void)real_old_size;
|
(void)real_old_size;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (this->offset_ + length > this->buffer_.size())
|
if (this->offset_ + length > this->buffer_.size())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::span result(this->buffer_.data() + this->offset_, length);
|
const std::span result(this->buffer_.data() + this->offset_, length);
|
||||||
this->offset_ += length;
|
this->offset_ += length;
|
||||||
|
|
||||||
(void)this->no_debugging_;
|
(void)this->no_debugging_;
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if (!this->no_debugging_)
|
if (!this->no_debugging_)
|
||||||
{
|
{
|
||||||
uint64_t old_size{};
|
uint64_t old_size{};
|
||||||
if (this->offset_ + sizeof(old_size) > this->buffer_.size())
|
if (this->offset_ + sizeof(old_size) > this->buffer_.size())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(&old_size, this->buffer_.data() + this->offset_, sizeof(old_size));
|
memcpy(&old_size, this->buffer_.data() + this->offset_, sizeof(old_size));
|
||||||
if (old_size != real_old_size)
|
if (old_size != real_old_size)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Reading from serialized buffer mismatches written data!");
|
throw std::runtime_error("Reading from serialized buffer mismatches written data!");
|
||||||
}
|
}
|
||||||
|
|
||||||
this->offset_ += sizeof(old_size);
|
this->offset_ += sizeof(old_size);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void read(void* data, const size_t length)
|
void read(void* data, const size_t length)
|
||||||
{
|
{
|
||||||
const auto span = this->read_data(length);
|
const auto span = this->read_data(length);
|
||||||
memcpy(data, span.data(), length);
|
memcpy(data, span.data(), length);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void read(T& object)
|
void read(T& object)
|
||||||
{
|
{
|
||||||
constexpr auto is_trivially_copyable = std::is_trivially_copyable_v<T>;
|
constexpr auto is_trivially_copyable = std::is_trivially_copyable_v<T>;
|
||||||
|
|
||||||
if constexpr (Serializable<T>)
|
if constexpr (Serializable<T>)
|
||||||
{
|
{
|
||||||
object.deserialize(*this);
|
object.deserialize(*this);
|
||||||
}
|
}
|
||||||
else if constexpr (detail::has_deserialize_function<T>::value)
|
else if constexpr (detail::has_deserialize_function<T>::value)
|
||||||
{
|
{
|
||||||
deserialize(*this, object);
|
deserialize(*this, object);
|
||||||
}
|
}
|
||||||
else if constexpr (is_trivially_copyable)
|
else if constexpr (is_trivially_copyable)
|
||||||
{
|
{
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
T* type_{};
|
T* type_{};
|
||||||
void* void_;
|
void* void_;
|
||||||
} pointers;
|
} pointers;
|
||||||
|
|
||||||
pointers.type_ = &object;
|
pointers.type_ = &object;
|
||||||
|
|
||||||
this->read(pointers.void_, sizeof(object));
|
this->read(pointers.void_, sizeof(object));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
static_assert(!is_trivially_copyable, "Key must be trivially copyable or implement serializable!");
|
static_assert(!is_trivially_copyable, "Key must be trivially copyable or implement serializable!");
|
||||||
std::abort();
|
std::abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T read()
|
T read()
|
||||||
{
|
{
|
||||||
auto object = this->construct_object<T>();
|
auto object = this->construct_object<T>();
|
||||||
this->read(object);
|
this->read(object);
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void read_optional(std::optional<T>& val)
|
void read_optional(std::optional<T>& val)
|
||||||
{
|
{
|
||||||
if (this->read<bool>())
|
if (this->read<bool>())
|
||||||
{
|
{
|
||||||
val.emplace(this->read<T>());
|
val.emplace(this->read<T>());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
val = std::nullopt;
|
val = std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename F>
|
template <typename T, typename F>
|
||||||
requires(std::is_invocable_r_v<T, F>)
|
requires(std::is_invocable_r_v<T, F>)
|
||||||
void read_optional(std::optional<T>& val, const F& factory)
|
void read_optional(std::optional<T>& val, const F& factory)
|
||||||
{
|
{
|
||||||
if (this->read<bool>())
|
if (this->read<bool>())
|
||||||
{
|
{
|
||||||
val.emplace(factory());
|
val.emplace(factory());
|
||||||
this->read<T>(*val);
|
this->read<T>(*val);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
val = {};
|
val = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void read_vector(std::vector<T>& result)
|
void read_vector(std::vector<T>& result)
|
||||||
{
|
{
|
||||||
const auto size = this->read<uint64_t>();
|
const auto size = this->read<uint64_t>();
|
||||||
result.clear();
|
result.clear();
|
||||||
result.reserve(size);
|
result.reserve(size);
|
||||||
|
|
||||||
for (uint64_t i = 0; i < size; ++i)
|
for (uint64_t i = 0; i < size; ++i)
|
||||||
{
|
{
|
||||||
result.emplace_back(this->read<T>());
|
result.emplace_back(this->read<T>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::vector<T> read_vector()
|
std::vector<T> read_vector()
|
||||||
{
|
{
|
||||||
std::vector<T> result{};
|
std::vector<T> result{};
|
||||||
this->read_vector(result);
|
this->read_vector(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Map>
|
template <typename Map>
|
||||||
void read_map(Map& map)
|
void read_map(Map& map)
|
||||||
{
|
{
|
||||||
using key_type = typename Map::key_type;
|
using key_type = typename Map::key_type;
|
||||||
using value_type = typename Map::mapped_type;
|
using value_type = typename Map::mapped_type;
|
||||||
|
|
||||||
map.clear();
|
map.clear();
|
||||||
|
|
||||||
const auto size = this->read<uint64_t>();
|
const auto size = this->read<uint64_t>();
|
||||||
|
|
||||||
for (uint64_t i = 0; i < size; ++i)
|
for (uint64_t i = 0; i < size; ++i)
|
||||||
{
|
{
|
||||||
auto key = this->read<key_type>();
|
auto key = this->read<key_type>();
|
||||||
auto value = this->read<value_type>();
|
auto value = this->read<value_type>();
|
||||||
|
|
||||||
map.emplace(std::move(key), std::move(value));
|
map.emplace(std::move(key), std::move(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Map>
|
template <typename Map>
|
||||||
Map read_map()
|
Map read_map()
|
||||||
{
|
{
|
||||||
Map map{};
|
Map map{};
|
||||||
this->read_map(map);
|
this->read_map(map);
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T = char>
|
template <typename T = char>
|
||||||
void read_string(std::basic_string<T>& result)
|
void read_string(std::basic_string<T>& result)
|
||||||
{
|
{
|
||||||
const auto size = this->read<uint64_t>();
|
const auto size = this->read<uint64_t>();
|
||||||
|
|
||||||
result.clear();
|
result.clear();
|
||||||
result.reserve(size);
|
result.reserve(size);
|
||||||
|
|
||||||
for (uint64_t i = 0; i < size; ++i)
|
for (uint64_t i = 0; i < size; ++i)
|
||||||
{
|
{
|
||||||
result.push_back(this->read<T>());
|
result.push_back(this->read<T>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T= char>
|
template <typename T = char>
|
||||||
std::basic_string<T> read_string()
|
std::basic_string<T> read_string()
|
||||||
{
|
{
|
||||||
std::basic_string<T> result{};
|
std::basic_string<T> result{};
|
||||||
this->read_string(result);
|
this->read_string(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t get_remaining_size() const
|
size_t get_remaining_size() const
|
||||||
{
|
{
|
||||||
return this->buffer_.size() - offset_;
|
return this->buffer_.size() - offset_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::span<const std::byte> get_remaining_data()
|
std::span<const std::byte> get_remaining_data()
|
||||||
{
|
{
|
||||||
return this->read_data(this->get_remaining_size());
|
return this->read_data(this->get_remaining_size());
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t get_offset() const
|
size_t get_offset() const
|
||||||
{
|
{
|
||||||
return this->offset_;
|
return this->offset_;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename F>
|
template <typename T, typename F>
|
||||||
requires(std::is_invocable_r_v<T, F>)
|
requires(std::is_invocable_r_v<T, F>)
|
||||||
void register_factory(F factory)
|
void register_factory(F factory)
|
||||||
{
|
{
|
||||||
this->factories_[std::type_index(typeid(T))] = [f = std::move(factory)]() -> T* {
|
this->factories_[std::type_index(typeid(T))] = [f = std::move(factory)]() -> T* { return new T(f()); };
|
||||||
return new T(f());
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool no_debugging_{false};
|
bool no_debugging_{false};
|
||||||
size_t offset_{0};
|
size_t offset_{0};
|
||||||
std::span<const std::byte> buffer_{};
|
std::span<const std::byte> buffer_{};
|
||||||
std::unordered_map<std::type_index, std::function<void*()>> factories_{};
|
std::unordered_map<std::type_index, std::function<void*()>> factories_{};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T construct_object()
|
T construct_object()
|
||||||
{
|
{
|
||||||
if constexpr (detail::has_deserializer_constructor<T>::value)
|
if constexpr (detail::has_deserializer_constructor<T>::value)
|
||||||
{
|
{
|
||||||
return T(*this);
|
return T(*this);
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_default_constructible_v<T>)
|
else if constexpr (std::is_default_constructible_v<T>)
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const auto factory = this->factories_.find(std::type_index(typeid(T)));
|
const auto factory = this->factories_.find(std::type_index(typeid(T)));
|
||||||
if (factory == this->factories_.end())
|
if (factory == this->factories_.end())
|
||||||
{
|
{
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Object construction failed. Missing factory for type: " +
|
||||||
"Object construction failed. Missing factory for type: " + std::string(typeid(T).name()));
|
std::string(typeid(T).name()));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* object = static_cast<T*>(factory->second());
|
auto* object = static_cast<T*>(factory->second());
|
||||||
auto obj = std::move(*object);
|
auto obj = std::move(*object);
|
||||||
delete object;
|
delete object;
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class buffer_serializer
|
class buffer_serializer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
buffer_serializer() = default;
|
buffer_serializer() = default;
|
||||||
|
|
||||||
void write(const void* buffer, const size_t length)
|
void write(const void* buffer, const size_t length)
|
||||||
{
|
{
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
const uint64_t old_size = this->buffer_.size();
|
const uint64_t old_size = this->buffer_.size();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const auto* byte_buffer = static_cast<const std::byte*>(buffer);
|
const auto* byte_buffer = static_cast<const std::byte*>(buffer);
|
||||||
this->buffer_.insert(this->buffer_.end(), byte_buffer, byte_buffer + length);
|
this->buffer_.insert(this->buffer_.end(), byte_buffer, byte_buffer + length);
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
const auto* security_buffer = reinterpret_cast<const std::byte*>(&old_size);
|
const auto* security_buffer = reinterpret_cast<const std::byte*>(&old_size);
|
||||||
this->buffer_.insert(this->buffer_.end(), security_buffer, security_buffer + sizeof(old_size));
|
this->buffer_.insert(this->buffer_.end(), security_buffer, security_buffer + sizeof(old_size));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(const buffer_serializer& object)
|
void write(const buffer_serializer& object)
|
||||||
{
|
{
|
||||||
const auto& buffer = object.get_buffer();
|
const auto& buffer = object.get_buffer();
|
||||||
this->write(buffer.data(), buffer.size());
|
this->write(buffer.data(), buffer.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void write(const T& object)
|
void write(const T& object)
|
||||||
{
|
{
|
||||||
constexpr auto is_trivially_copyable = std::is_trivially_copyable_v<T>;
|
constexpr auto is_trivially_copyable = std::is_trivially_copyable_v<T>;
|
||||||
|
|
||||||
if constexpr (Serializable<T>)
|
if constexpr (Serializable<T>)
|
||||||
{
|
{
|
||||||
object.serialize(*this);
|
object.serialize(*this);
|
||||||
}
|
}
|
||||||
else if constexpr (detail::has_serialize_function<T>::value)
|
else if constexpr (detail::has_serialize_function<T>::value)
|
||||||
{
|
{
|
||||||
serialize(*this, object);
|
serialize(*this, object);
|
||||||
}
|
}
|
||||||
else if constexpr (is_trivially_copyable)
|
else if constexpr (is_trivially_copyable)
|
||||||
{
|
{
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
const T* type_{};
|
const T* type_{};
|
||||||
const void* void_;
|
const void* void_;
|
||||||
} pointers;
|
} pointers;
|
||||||
|
|
||||||
pointers.type_ = &object;
|
pointers.type_ = &object;
|
||||||
|
|
||||||
this->write(pointers.void_, sizeof(object));
|
this->write(pointers.void_, sizeof(object));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
static_assert(!is_trivially_copyable, "Key must be trivially copyable or implement serializable!");
|
static_assert(!is_trivially_copyable, "Key must be trivially copyable or implement serializable!");
|
||||||
std::abort();
|
std::abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void write_optional(const std::optional<T>& val)
|
void write_optional(const std::optional<T>& val)
|
||||||
{
|
{
|
||||||
this->write(val.has_value());
|
this->write(val.has_value());
|
||||||
|
|
||||||
if (val.has_value())
|
if (val.has_value())
|
||||||
{
|
{
|
||||||
this->write(*val);
|
this->write(*val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void write_span(const std::span<T> vec)
|
void write_span(const std::span<T> vec)
|
||||||
{
|
{
|
||||||
this->write(static_cast<uint64_t>(vec.size()));
|
this->write(static_cast<uint64_t>(vec.size()));
|
||||||
|
|
||||||
for (const auto& v : vec)
|
for (const auto& v : vec)
|
||||||
{
|
{
|
||||||
this->write(v);
|
this->write(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void write_vector(const std::vector<T> vec)
|
void write_vector(const std::vector<T> vec)
|
||||||
{
|
{
|
||||||
this->write_span(std::span(vec));
|
this->write_span(std::span(vec));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void write_string(const std::basic_string_view<T> str)
|
void write_string(const std::basic_string_view<T> str)
|
||||||
{
|
{
|
||||||
this->write_span<const T>(str);
|
this->write_span<const T>(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void write_string(const std::basic_string<T>& str)
|
void write_string(const std::basic_string<T>& str)
|
||||||
{
|
{
|
||||||
this->write_string(std::basic_string_view<T>(str));
|
this->write_string(std::basic_string_view<T>(str));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Map>
|
template <typename Map>
|
||||||
void write_map(const Map& map)
|
void write_map(const Map& map)
|
||||||
{
|
{
|
||||||
this->write<uint64_t>(map.size());
|
this->write<uint64_t>(map.size());
|
||||||
|
|
||||||
for (const auto& entry : map)
|
for (const auto& entry : map)
|
||||||
{
|
{
|
||||||
this->write(entry.first);
|
this->write(entry.first);
|
||||||
this->write(entry.second);
|
this->write(entry.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<std::byte>& get_buffer() const
|
const std::vector<std::byte>& get_buffer() const
|
||||||
{
|
{
|
||||||
return this->buffer_;
|
return this->buffer_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::byte> move_buffer()
|
std::vector<std::byte> move_buffer()
|
||||||
{
|
{
|
||||||
return std::move(this->buffer_);
|
return std::move(this->buffer_);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::byte> buffer_{};
|
std::vector<std::byte> buffer_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline void buffer_deserializer::read<bool>(bool& object)
|
inline void buffer_deserializer::read<bool>(bool& object)
|
||||||
{
|
{
|
||||||
object = this->read<uint8_t>() != 0;
|
object = this->read<uint8_t>() != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline void buffer_deserializer::read<std::string>(std::string& object)
|
inline void buffer_deserializer::read<std::string>(std::string& object)
|
||||||
{
|
{
|
||||||
object = this->read_string<char>();
|
object = this->read_string<char>();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline void buffer_deserializer::read<std::wstring>(std::wstring& object)
|
inline void buffer_deserializer::read<std::wstring>(std::wstring& object)
|
||||||
{
|
{
|
||||||
object = this->read_string<wchar_t>();
|
object = this->read_string<wchar_t>();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline void buffer_deserializer::read<std::u16string>(std::u16string& object)
|
inline void buffer_deserializer::read<std::u16string>(std::u16string& object)
|
||||||
{
|
{
|
||||||
object = this->read_string<char16_t>();
|
object = this->read_string<char16_t>();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline void buffer_serializer::write<bool>(const bool& object)
|
inline void buffer_serializer::write<bool>(const bool& object)
|
||||||
{
|
{
|
||||||
this->write<uint8_t>(object ? 1 : 0);
|
this->write<uint8_t>(object ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline void buffer_serializer::write<std::string>(const std::string& object)
|
inline void buffer_serializer::write<std::string>(const std::string& object)
|
||||||
{
|
{
|
||||||
this->write_string(object);
|
this->write_string(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline void buffer_serializer::write<std::wstring>(const std::wstring& object)
|
inline void buffer_serializer::write<std::wstring>(const std::wstring& object)
|
||||||
{
|
{
|
||||||
this->write_string(object);
|
this->write_string(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline void buffer_serializer::write<std::u16string>(const std::u16string& object)
|
inline void buffer_serializer::write<std::u16string>(const std::u16string& object)
|
||||||
{
|
{
|
||||||
this->write_string(object);
|
this->write_string(object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,41 +7,41 @@
|
|||||||
|
|
||||||
namespace utils
|
namespace utils
|
||||||
{
|
{
|
||||||
inline void serialize(buffer_serializer& buffer, const std::chrono::steady_clock::time_point& tp)
|
inline void serialize(buffer_serializer& buffer, const std::chrono::steady_clock::time_point& tp)
|
||||||
{
|
{
|
||||||
buffer.write(tp.time_since_epoch().count());
|
buffer.write(tp.time_since_epoch().count());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void deserialize(buffer_deserializer& buffer, std::chrono::steady_clock::time_point& tp)
|
inline void deserialize(buffer_deserializer& buffer, std::chrono::steady_clock::time_point& tp)
|
||||||
{
|
{
|
||||||
using time_point = std::chrono::steady_clock::time_point;
|
using time_point = std::chrono::steady_clock::time_point;
|
||||||
using duration = time_point::duration;
|
using duration = time_point::duration;
|
||||||
|
|
||||||
const auto count = buffer.read<duration::rep>();
|
const auto count = buffer.read<duration::rep>();
|
||||||
tp = time_point{duration{count}};
|
tp = time_point{duration{count}};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void serialize(buffer_serializer& buffer, const std::chrono::system_clock::time_point& tp)
|
inline void serialize(buffer_serializer& buffer, const std::chrono::system_clock::time_point& tp)
|
||||||
{
|
{
|
||||||
buffer.write(tp.time_since_epoch().count());
|
buffer.write(tp.time_since_epoch().count());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void deserialize(buffer_deserializer& buffer, std::chrono::system_clock::time_point& tp)
|
inline void deserialize(buffer_deserializer& buffer, std::chrono::system_clock::time_point& tp)
|
||||||
{
|
{
|
||||||
using time_point = std::chrono::system_clock::time_point;
|
using time_point = std::chrono::system_clock::time_point;
|
||||||
using duration = time_point::duration;
|
using duration = time_point::duration;
|
||||||
|
|
||||||
const auto count = buffer.read<duration::rep>();
|
const auto count = buffer.read<duration::rep>();
|
||||||
tp = time_point{duration{count}};
|
tp = time_point{duration{count}};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void serialize(buffer_serializer& buffer, const std::filesystem::path& path)
|
inline void serialize(buffer_serializer& buffer, const std::filesystem::path& path)
|
||||||
{
|
{
|
||||||
buffer.write_string<char16_t>(path.u16string());
|
buffer.write_string<char16_t>(path.u16string());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void deserialize(buffer_deserializer& buffer, std::filesystem::path& path)
|
inline void deserialize(buffer_deserializer& buffer, std::filesystem::path& path)
|
||||||
{
|
{
|
||||||
path = buffer.read_string<char16_t>();
|
path = buffer.read_string<char16_t>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,94 +2,94 @@
|
|||||||
|
|
||||||
#include "emulator.hpp"
|
#include "emulator.hpp"
|
||||||
|
|
||||||
template <typename PointerType, typename Register, Register InstructionPointer, Register
|
template <typename PointerType, typename Register, Register InstructionPointer, Register StackPointer,
|
||||||
StackPointer, typename HookableInstructions>
|
typename HookableInstructions>
|
||||||
class typed_emulator : public emulator
|
class typed_emulator : public emulator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using registers = Register;
|
using registers = Register;
|
||||||
using pointer_type = PointerType;
|
using pointer_type = PointerType;
|
||||||
using hookable_instructions = HookableInstructions;
|
using hookable_instructions = HookableInstructions;
|
||||||
|
|
||||||
static constexpr size_t pointer_size = sizeof(pointer_type);
|
static constexpr size_t pointer_size = sizeof(pointer_type);
|
||||||
static constexpr registers stack_pointer = StackPointer;
|
static constexpr registers stack_pointer = StackPointer;
|
||||||
static constexpr registers instruction_pointer = InstructionPointer;
|
static constexpr registers instruction_pointer = InstructionPointer;
|
||||||
|
|
||||||
void start_from_ip(const std::chrono::nanoseconds timeout = {}, const size_t count = 0)
|
void start_from_ip(const std::chrono::nanoseconds timeout = {}, const size_t count = 0)
|
||||||
{
|
{
|
||||||
this->start(this->read_instruction_pointer(), 0, timeout, count);
|
this->start(this->read_instruction_pointer(), 0, timeout, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_register(registers reg, const void* value, const size_t size)
|
void write_register(registers reg, const void* value, const size_t size)
|
||||||
{
|
{
|
||||||
this->write_raw_register(static_cast<int>(reg), value, size);
|
this->write_raw_register(static_cast<int>(reg), value, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_register(registers reg, void* value, const size_t size)
|
void read_register(registers reg, void* value, const size_t size)
|
||||||
{
|
{
|
||||||
this->read_raw_register(static_cast<int>(reg), value, size);
|
this->read_raw_register(static_cast<int>(reg), value, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T = pointer_type>
|
template <typename T = pointer_type>
|
||||||
T reg(const registers regid)
|
T reg(const registers regid)
|
||||||
{
|
{
|
||||||
T value{};
|
T value{};
|
||||||
this->read_register(regid, &value, sizeof(value));
|
this->read_register(regid, &value, sizeof(value));
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T = pointer_type, typename S>
|
template <typename T = pointer_type, typename S>
|
||||||
void reg(const registers regid, const S& maybe_value)
|
void reg(const registers regid, const S& maybe_value)
|
||||||
{
|
{
|
||||||
T value = static_cast<T>(maybe_value);
|
T value = static_cast<T>(maybe_value);
|
||||||
this->write_register(regid, &value, sizeof(value));
|
this->write_register(regid, &value, sizeof(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
pointer_type read_instruction_pointer()
|
pointer_type read_instruction_pointer()
|
||||||
{
|
{
|
||||||
return this->reg(instruction_pointer);
|
return this->reg(instruction_pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
pointer_type read_stack_pointer()
|
pointer_type read_stack_pointer()
|
||||||
{
|
{
|
||||||
return this->reg(stack_pointer);
|
return this->reg(stack_pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
pointer_type read_stack(const size_t index)
|
pointer_type read_stack(const size_t index)
|
||||||
{
|
{
|
||||||
pointer_type result{};
|
pointer_type result{};
|
||||||
const auto sp = this->read_stack_pointer();
|
const auto sp = this->read_stack_pointer();
|
||||||
|
|
||||||
this->read_memory(sp + (index * pointer_size), &result, sizeof(result));
|
this->read_memory(sp + (index * pointer_size), &result, sizeof(result));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void push_stack(const pointer_type& value)
|
void push_stack(const pointer_type& value)
|
||||||
{
|
{
|
||||||
const auto sp = this->read_stack_pointer() - pointer_size;
|
const auto sp = this->read_stack_pointer() - pointer_size;
|
||||||
this->reg(stack_pointer, sp);
|
this->reg(stack_pointer, sp);
|
||||||
this->write_memory(sp, &value, sizeof(value));
|
this->write_memory(sp, &value, sizeof(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
pointer_type pop_stack()
|
pointer_type pop_stack()
|
||||||
{
|
{
|
||||||
pointer_type result{};
|
pointer_type result{};
|
||||||
const auto sp = this->read_stack_pointer();
|
const auto sp = this->read_stack_pointer();
|
||||||
this->read_memory(sp, &result, sizeof(result));
|
this->read_memory(sp, &result, sizeof(result));
|
||||||
this->reg(stack_pointer, sp + pointer_size);
|
this->reg(stack_pointer, sp + pointer_size);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
emulator_hook* hook_instruction(hookable_instructions instruction_type, instruction_hook_callback callback)
|
emulator_hook* hook_instruction(hookable_instructions instruction_type, instruction_hook_callback callback)
|
||||||
{
|
{
|
||||||
return this->hook_instruction(static_cast<int>(instruction_type), std::move(callback));
|
return this->hook_instruction(static_cast<int>(instruction_type), std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) override = 0;
|
emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) override = 0;
|
||||||
|
|
||||||
void read_raw_register(int reg, void* value, size_t size) override = 0;
|
void read_raw_register(int reg, void* value, size_t size) override = 0;
|
||||||
void write_raw_register(int reg, const void* value, size_t size) override = 0;
|
void write_raw_register(int reg, const void* value, size_t size) override = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
|
|
||||||
enum class x64_hookable_instructions
|
enum class x64_hookable_instructions
|
||||||
{
|
{
|
||||||
invalid,
|
invalid,
|
||||||
syscall,
|
syscall,
|
||||||
cpuid,
|
cpuid,
|
||||||
rdtsc,
|
rdtsc,
|
||||||
rdtscp,
|
rdtscp,
|
||||||
};
|
};
|
||||||
|
|
||||||
using x64_emulator = typed_emulator<uint64_t, x64_register, x64_register::rip,
|
using x64_emulator =
|
||||||
x64_register::rsp, x64_hookable_instructions>;
|
typed_emulator<uint64_t, x64_register, x64_register::rip, x64_register::rsp, x64_hookable_instructions>;
|
||||||
|
|||||||
@@ -2,244 +2,244 @@
|
|||||||
|
|
||||||
enum class x64_register
|
enum class x64_register
|
||||||
{
|
{
|
||||||
invalid = 0,
|
invalid = 0,
|
||||||
ah,
|
ah,
|
||||||
al,
|
al,
|
||||||
ax,
|
ax,
|
||||||
bh,
|
bh,
|
||||||
bl,
|
bl,
|
||||||
bp,
|
bp,
|
||||||
bpl,
|
bpl,
|
||||||
bx,
|
bx,
|
||||||
ch,
|
ch,
|
||||||
cl,
|
cl,
|
||||||
cs,
|
cs,
|
||||||
cx,
|
cx,
|
||||||
dh,
|
dh,
|
||||||
di,
|
di,
|
||||||
dil,
|
dil,
|
||||||
dl,
|
dl,
|
||||||
ds,
|
ds,
|
||||||
dx,
|
dx,
|
||||||
eax,
|
eax,
|
||||||
ebp,
|
ebp,
|
||||||
ebx,
|
ebx,
|
||||||
ecx,
|
ecx,
|
||||||
edi,
|
edi,
|
||||||
edx,
|
edx,
|
||||||
eflags,
|
eflags,
|
||||||
eip,
|
eip,
|
||||||
es = eip + 2,
|
es = eip + 2,
|
||||||
esi,
|
esi,
|
||||||
esp,
|
esp,
|
||||||
fpsw,
|
fpsw,
|
||||||
fs,
|
fs,
|
||||||
gs,
|
gs,
|
||||||
ip,
|
ip,
|
||||||
rax,
|
rax,
|
||||||
rbp,
|
rbp,
|
||||||
rbx,
|
rbx,
|
||||||
rcx,
|
rcx,
|
||||||
rdi,
|
rdi,
|
||||||
rdx,
|
rdx,
|
||||||
rip,
|
rip,
|
||||||
rsi = rip + 2,
|
rsi = rip + 2,
|
||||||
rsp,
|
rsp,
|
||||||
si,
|
si,
|
||||||
sil,
|
sil,
|
||||||
sp,
|
sp,
|
||||||
spl,
|
spl,
|
||||||
ss,
|
ss,
|
||||||
cr0,
|
cr0,
|
||||||
cr1,
|
cr1,
|
||||||
cr2,
|
cr2,
|
||||||
cr3,
|
cr3,
|
||||||
cr4,
|
cr4,
|
||||||
cr8 = cr4 + 4,
|
cr8 = cr4 + 4,
|
||||||
dr0 = cr8 + 8,
|
dr0 = cr8 + 8,
|
||||||
dr1,
|
dr1,
|
||||||
dr2,
|
dr2,
|
||||||
dr3,
|
dr3,
|
||||||
dr4,
|
dr4,
|
||||||
dr5,
|
dr5,
|
||||||
dr6,
|
dr6,
|
||||||
dr7,
|
dr7,
|
||||||
fp0 = dr7 + 9,
|
fp0 = dr7 + 9,
|
||||||
fp1,
|
fp1,
|
||||||
fp2,
|
fp2,
|
||||||
fp3,
|
fp3,
|
||||||
fp4,
|
fp4,
|
||||||
fp5,
|
fp5,
|
||||||
fp6,
|
fp6,
|
||||||
fp7,
|
fp7,
|
||||||
k0,
|
k0,
|
||||||
k1,
|
k1,
|
||||||
k2,
|
k2,
|
||||||
k3,
|
k3,
|
||||||
k4,
|
k4,
|
||||||
k5,
|
k5,
|
||||||
k6,
|
k6,
|
||||||
k7,
|
k7,
|
||||||
mm0,
|
mm0,
|
||||||
mm1,
|
mm1,
|
||||||
mm2,
|
mm2,
|
||||||
mm3,
|
mm3,
|
||||||
mm4,
|
mm4,
|
||||||
mm5,
|
mm5,
|
||||||
mm6,
|
mm6,
|
||||||
mm7,
|
mm7,
|
||||||
r8,
|
r8,
|
||||||
r9,
|
r9,
|
||||||
r10,
|
r10,
|
||||||
r11,
|
r11,
|
||||||
r12,
|
r12,
|
||||||
r13,
|
r13,
|
||||||
r14,
|
r14,
|
||||||
r15,
|
r15,
|
||||||
st0,
|
st0,
|
||||||
st1,
|
st1,
|
||||||
st2,
|
st2,
|
||||||
st3,
|
st3,
|
||||||
st4,
|
st4,
|
||||||
st5,
|
st5,
|
||||||
st6,
|
st6,
|
||||||
st7,
|
st7,
|
||||||
xmm0,
|
xmm0,
|
||||||
xmm1,
|
xmm1,
|
||||||
xmm2,
|
xmm2,
|
||||||
xmm3,
|
xmm3,
|
||||||
xmm4,
|
xmm4,
|
||||||
xmm5,
|
xmm5,
|
||||||
xmm6,
|
xmm6,
|
||||||
xmm7,
|
xmm7,
|
||||||
xmm8,
|
xmm8,
|
||||||
xmm9,
|
xmm9,
|
||||||
xmm10,
|
xmm10,
|
||||||
xmm11,
|
xmm11,
|
||||||
xmm12,
|
xmm12,
|
||||||
xmm13,
|
xmm13,
|
||||||
xmm14,
|
xmm14,
|
||||||
xmm15,
|
xmm15,
|
||||||
xmm16,
|
xmm16,
|
||||||
xmm17,
|
xmm17,
|
||||||
xmm18,
|
xmm18,
|
||||||
xmm19,
|
xmm19,
|
||||||
xmm20,
|
xmm20,
|
||||||
xmm21,
|
xmm21,
|
||||||
xmm22,
|
xmm22,
|
||||||
xmm23,
|
xmm23,
|
||||||
xmm24,
|
xmm24,
|
||||||
xmm25,
|
xmm25,
|
||||||
xmm26,
|
xmm26,
|
||||||
xmm27,
|
xmm27,
|
||||||
xmm28,
|
xmm28,
|
||||||
xmm29,
|
xmm29,
|
||||||
xmm30,
|
xmm30,
|
||||||
xmm31,
|
xmm31,
|
||||||
ymm0,
|
ymm0,
|
||||||
ymm1,
|
ymm1,
|
||||||
ymm2,
|
ymm2,
|
||||||
ymm3,
|
ymm3,
|
||||||
ymm4,
|
ymm4,
|
||||||
ymm5,
|
ymm5,
|
||||||
ymm6,
|
ymm6,
|
||||||
ymm7,
|
ymm7,
|
||||||
ymm8,
|
ymm8,
|
||||||
ymm9,
|
ymm9,
|
||||||
ymm10,
|
ymm10,
|
||||||
ymm11,
|
ymm11,
|
||||||
ymm12,
|
ymm12,
|
||||||
ymm13,
|
ymm13,
|
||||||
ymm14,
|
ymm14,
|
||||||
ymm15,
|
ymm15,
|
||||||
ymm16,
|
ymm16,
|
||||||
ymm17,
|
ymm17,
|
||||||
ymm18,
|
ymm18,
|
||||||
ymm19,
|
ymm19,
|
||||||
ymm20,
|
ymm20,
|
||||||
ymm21,
|
ymm21,
|
||||||
ymm22,
|
ymm22,
|
||||||
ymm23,
|
ymm23,
|
||||||
ymm24,
|
ymm24,
|
||||||
ymm25,
|
ymm25,
|
||||||
ymm26,
|
ymm26,
|
||||||
ymm27,
|
ymm27,
|
||||||
ymm28,
|
ymm28,
|
||||||
ymm29,
|
ymm29,
|
||||||
ymm30,
|
ymm30,
|
||||||
ymm31,
|
ymm31,
|
||||||
zmm0,
|
zmm0,
|
||||||
zmm1,
|
zmm1,
|
||||||
zmm2,
|
zmm2,
|
||||||
zmm3,
|
zmm3,
|
||||||
zmm4,
|
zmm4,
|
||||||
zmm5,
|
zmm5,
|
||||||
zmm6,
|
zmm6,
|
||||||
zmm7,
|
zmm7,
|
||||||
zmm8,
|
zmm8,
|
||||||
zmm9,
|
zmm9,
|
||||||
zmm10,
|
zmm10,
|
||||||
zmm11,
|
zmm11,
|
||||||
zmm12,
|
zmm12,
|
||||||
zmm13,
|
zmm13,
|
||||||
zmm14,
|
zmm14,
|
||||||
zmm15,
|
zmm15,
|
||||||
zmm16,
|
zmm16,
|
||||||
zmm17,
|
zmm17,
|
||||||
zmm18,
|
zmm18,
|
||||||
zmm19,
|
zmm19,
|
||||||
zmm20,
|
zmm20,
|
||||||
zmm21,
|
zmm21,
|
||||||
zmm22,
|
zmm22,
|
||||||
zmm23,
|
zmm23,
|
||||||
zmm24,
|
zmm24,
|
||||||
zmm25,
|
zmm25,
|
||||||
zmm26,
|
zmm26,
|
||||||
zmm27,
|
zmm27,
|
||||||
zmm28,
|
zmm28,
|
||||||
zmm29,
|
zmm29,
|
||||||
zmm30,
|
zmm30,
|
||||||
zmm31,
|
zmm31,
|
||||||
r8b,
|
r8b,
|
||||||
r9b,
|
r9b,
|
||||||
r10b,
|
r10b,
|
||||||
r11b,
|
r11b,
|
||||||
r12b,
|
r12b,
|
||||||
r13b,
|
r13b,
|
||||||
r14b,
|
r14b,
|
||||||
r15b,
|
r15b,
|
||||||
r8d,
|
r8d,
|
||||||
r9d,
|
r9d,
|
||||||
r10d,
|
r10d,
|
||||||
r11d,
|
r11d,
|
||||||
r12d,
|
r12d,
|
||||||
r13d,
|
r13d,
|
||||||
r14d,
|
r14d,
|
||||||
r15d,
|
r15d,
|
||||||
r8w,
|
r8w,
|
||||||
r9w,
|
r9w,
|
||||||
r10w,
|
r10w,
|
||||||
r11w,
|
r11w,
|
||||||
r12w,
|
r12w,
|
||||||
r13w,
|
r13w,
|
||||||
r14w,
|
r14w,
|
||||||
r15w,
|
r15w,
|
||||||
idtr,
|
idtr,
|
||||||
gdtr,
|
gdtr,
|
||||||
ldtr,
|
ldtr,
|
||||||
tr,
|
tr,
|
||||||
fpcw,
|
fpcw,
|
||||||
fptag,
|
fptag,
|
||||||
msr,
|
msr,
|
||||||
mxcsr,
|
mxcsr,
|
||||||
fs_base,
|
fs_base,
|
||||||
gs_base,
|
gs_base,
|
||||||
flags,
|
flags,
|
||||||
rflags,
|
rflags,
|
||||||
fip,
|
fip,
|
||||||
fcs,
|
fcs,
|
||||||
fdp,
|
fdp,
|
||||||
fds,
|
fds,
|
||||||
fop,
|
fop,
|
||||||
end, // Must be last
|
end, // Must be last
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,192 +9,183 @@ bool use_gdb = false;
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
void run_emulation(windows_emulator& win_emu)
|
void run_emulation(windows_emulator& win_emu)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
win_emu.log.disable_output(true);
|
win_emu.log.disable_output(true);
|
||||||
win_emu.start();
|
win_emu.start();
|
||||||
|
|
||||||
if (win_emu.process().exception_rip.has_value())
|
if (win_emu.process().exception_rip.has_value())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Exception!");
|
throw std::runtime_error("Exception!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
win_emu.log.disable_output(false);
|
win_emu.log.disable_output(false);
|
||||||
win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 "\n",
|
win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 "\n",
|
||||||
win_emu.emu().read_instruction_pointer());
|
win_emu.emu().read_instruction_pointer());
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
win_emu.log.disable_output(false);
|
win_emu.log.disable_output(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void forward_emulator(windows_emulator& win_emu)
|
void forward_emulator(windows_emulator& win_emu)
|
||||||
{
|
{
|
||||||
const auto target = win_emu.process().executable->find_export("vulnerable");
|
const auto target = win_emu.process().executable->find_export("vulnerable");
|
||||||
win_emu.emu().hook_memory_execution(target, 1, [&](uint64_t, size_t, uint64_t)
|
win_emu.emu().hook_memory_execution(target, 1, [&](uint64_t, size_t, uint64_t) { win_emu.emu().stop(); });
|
||||||
{
|
|
||||||
win_emu.emu().stop();
|
|
||||||
});
|
|
||||||
|
|
||||||
run_emulation(win_emu);
|
run_emulation(win_emu);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct fuzzer_executer : fuzzer::executer
|
struct fuzzer_executer : fuzzer::executer
|
||||||
{
|
{
|
||||||
windows_emulator emu{};
|
windows_emulator emu{};
|
||||||
std::span<const std::byte> emulator_data{};
|
std::span<const std::byte> emulator_data{};
|
||||||
std::unordered_set<uint64_t> visited_blocks{};
|
std::unordered_set<uint64_t> visited_blocks{};
|
||||||
const std::function<fuzzer::coverage_functor>* handler{nullptr};
|
const std::function<fuzzer::coverage_functor>* handler{nullptr};
|
||||||
|
|
||||||
|
fuzzer_executer(std::span<const std::byte> data)
|
||||||
|
: emulator_data(data)
|
||||||
|
{
|
||||||
|
emu.fuzzing = true;
|
||||||
|
emu.emu().hook_basic_block([&](const basic_block& block) {
|
||||||
|
if (this->handler && visited_blocks.emplace(block.address).second)
|
||||||
|
{
|
||||||
|
(*this->handler)(block.address);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
fuzzer_executer(std::span<const std::byte> data)
|
utils::buffer_deserializer deserializer{emulator_data};
|
||||||
: emulator_data(data)
|
emu.deserialize(deserializer);
|
||||||
{
|
emu.save_snapshot();
|
||||||
emu.fuzzing = true;
|
|
||||||
emu.emu().hook_basic_block([&](const basic_block& block)
|
|
||||||
{
|
|
||||||
if (this->handler && visited_blocks.emplace(block.address).second)
|
|
||||||
{
|
|
||||||
(*this->handler)(block.address);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
utils::buffer_deserializer deserializer{emulator_data};
|
const auto ret = emu.emu().read_stack(0);
|
||||||
emu.deserialize(deserializer);
|
|
||||||
emu.save_snapshot();
|
|
||||||
|
|
||||||
const auto ret = emu.emu().read_stack(0);
|
emu.emu().hook_memory_execution(ret, 1, [&](uint64_t, size_t, uint64_t) { emu.emu().stop(); });
|
||||||
|
}
|
||||||
|
|
||||||
emu.emu().hook_memory_execution(ret, 1, [&](uint64_t, size_t, uint64_t)
|
void restore_emulator()
|
||||||
{
|
{
|
||||||
emu.emu().stop();
|
/*utils::buffer_deserializer deserializer{ emulator_data };
|
||||||
});
|
emu.deserialize(deserializer);*/
|
||||||
}
|
emu.restore_snapshot();
|
||||||
|
}
|
||||||
|
|
||||||
void restore_emulator()
|
fuzzer::execution_result execute(std::span<const uint8_t> data,
|
||||||
{
|
const std::function<fuzzer::coverage_functor>& coverage_handler) override
|
||||||
/*utils::buffer_deserializer deserializer{ emulator_data };
|
{
|
||||||
emu.deserialize(deserializer);*/
|
// printf("Input size: %zd\n", data.size());
|
||||||
emu.restore_snapshot();
|
this->handler = &coverage_handler;
|
||||||
}
|
this->visited_blocks.clear();
|
||||||
|
|
||||||
fuzzer::execution_result execute(std::span<const uint8_t> data,
|
restore_emulator();
|
||||||
const std::function<fuzzer::coverage_functor>& coverage_handler) override
|
|
||||||
{
|
|
||||||
//printf("Input size: %zd\n", data.size());
|
|
||||||
this->handler = &coverage_handler;
|
|
||||||
this->visited_blocks.clear();
|
|
||||||
|
|
||||||
restore_emulator();
|
const auto memory = emu.emu().allocate_memory(page_align_up(std::max(data.size(), size_t(1))),
|
||||||
|
memory_permission::read_write);
|
||||||
|
emu.emu().write_memory(memory, data.data(), data.size());
|
||||||
|
|
||||||
const auto memory = emu.emu().allocate_memory(page_align_up(std::max(data.size(), size_t(1))),
|
emu.emu().reg(x64_register::rcx, memory);
|
||||||
memory_permission::read_write);
|
emu.emu().reg<uint64_t>(x64_register::rdx, data.size());
|
||||||
emu.emu().write_memory(memory, data.data(), data.size());
|
|
||||||
|
|
||||||
emu.emu().reg(x64_register::rcx, memory);
|
try
|
||||||
emu.emu().reg<uint64_t>(x64_register::rdx, data.size());
|
{
|
||||||
|
run_emulation(emu);
|
||||||
|
return fuzzer::execution_result::success;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
return fuzzer::execution_result::error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
try
|
struct my_fuzzing_handler : fuzzer::fuzzing_handler
|
||||||
{
|
{
|
||||||
run_emulation(emu);
|
std::vector<std::byte> emulator_state{};
|
||||||
return fuzzer::execution_result::success;
|
std::atomic_bool stop_fuzzing{false};
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
return fuzzer::execution_result::error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct my_fuzzing_handler : fuzzer::fuzzing_handler
|
my_fuzzing_handler(std::vector<std::byte> emulator_state)
|
||||||
{
|
: emulator_state(std::move(emulator_state))
|
||||||
std::vector<std::byte> emulator_state{};
|
{
|
||||||
std::atomic_bool stop_fuzzing{false};
|
}
|
||||||
|
|
||||||
my_fuzzing_handler(std::vector<std::byte> emulator_state)
|
std::unique_ptr<fuzzer::executer> make_executer() override
|
||||||
: emulator_state(std::move(emulator_state))
|
{
|
||||||
{
|
return std::make_unique<fuzzer_executer>(emulator_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<fuzzer::executer> make_executer() override
|
bool stop() override
|
||||||
{
|
{
|
||||||
return std::make_unique<fuzzer_executer>(emulator_state);
|
return stop_fuzzing;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
bool stop() override
|
void run_fuzzer(const windows_emulator& base_emulator)
|
||||||
{
|
{
|
||||||
return stop_fuzzing;
|
const auto concurrency = std::thread::hardware_concurrency() + 2;
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void run_fuzzer(const windows_emulator& base_emulator)
|
utils::buffer_serializer serializer{};
|
||||||
{
|
base_emulator.serialize(serializer);
|
||||||
const auto concurrency = std::thread::hardware_concurrency() + 2;
|
|
||||||
|
|
||||||
utils::buffer_serializer serializer{};
|
my_fuzzing_handler handler{serializer.move_buffer()};
|
||||||
base_emulator.serialize(serializer);
|
|
||||||
|
|
||||||
my_fuzzing_handler handler{serializer.move_buffer()};
|
fuzzer::run(handler, concurrency);
|
||||||
|
}
|
||||||
|
|
||||||
fuzzer::run(handler, concurrency);
|
void run(const std::string_view application)
|
||||||
}
|
{
|
||||||
|
emulator_settings settings{
|
||||||
|
.application = application,
|
||||||
|
};
|
||||||
|
|
||||||
void run(const std::string_view application)
|
windows_emulator win_emu{std::move(settings)};
|
||||||
{
|
|
||||||
emulator_settings settings{
|
|
||||||
.application = application,
|
|
||||||
};
|
|
||||||
|
|
||||||
windows_emulator win_emu{std::move(settings)};
|
forward_emulator(win_emu);
|
||||||
|
run_fuzzer(win_emu);
|
||||||
forward_emulator(win_emu);
|
}
|
||||||
run_fuzzer(win_emu);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(const int argc, char** argv)
|
int main(const int argc, char** argv)
|
||||||
{
|
{
|
||||||
if (argc <= 1)
|
if (argc <= 1)
|
||||||
{
|
{
|
||||||
puts("Application not specified!");
|
puts("Application not specified!");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//setvbuf(stdout, nullptr, _IOFBF, 0x10000);
|
// setvbuf(stdout, nullptr, _IOFBF, 0x10000);
|
||||||
if (argc > 2 && argv[1] == "-d"s)
|
if (argc > 2 && argv[1] == "-d"s)
|
||||||
{
|
{
|
||||||
use_gdb = true;
|
use_gdb = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
run(argv[use_gdb ? 2 : 1]);
|
run(argv[use_gdb ? 2 : 1]);
|
||||||
}
|
} while (use_gdb);
|
||||||
while (use_gdb);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
{
|
{
|
||||||
puts(e.what());
|
puts(e.what());
|
||||||
|
|
||||||
#if defined(_WIN32) && 0
|
#if defined(_WIN32) && 0
|
||||||
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
|
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int)
|
int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int)
|
||||||
{
|
{
|
||||||
return main(__argc, __argv);
|
return main(__argc, __argv);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,128 +5,121 @@
|
|||||||
|
|
||||||
namespace fuzzer
|
namespace fuzzer
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
class fuzzing_context
|
class fuzzing_context
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
fuzzing_context(input_generator& generator, fuzzing_handler& handler)
|
fuzzing_context(input_generator& generator, fuzzing_handler& handler)
|
||||||
: generator(generator)
|
: generator(generator),
|
||||||
, handler(handler)
|
handler(handler)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop()
|
void stop()
|
||||||
{
|
{
|
||||||
this->stop_ = true;
|
this->stop_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool should_stop()
|
bool should_stop()
|
||||||
{
|
{
|
||||||
if (this->stop_)
|
if (this->stop_)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!handler.stop())
|
if (!handler.stop())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->stop_ = true;
|
this->stop_ = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
input_generator& generator;
|
input_generator& generator;
|
||||||
fuzzing_handler& handler;
|
fuzzing_handler& handler;
|
||||||
std::atomic_uint64_t executions{0};
|
std::atomic_uint64_t executions{0};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::atomic_bool stop_{false};
|
std::atomic_bool stop_{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
void perform_fuzzing_iteration(fuzzing_context& context, executer& executer)
|
void perform_fuzzing_iteration(fuzzing_context& context, executer& executer)
|
||||||
{
|
{
|
||||||
++context.executions;
|
++context.executions;
|
||||||
context.generator.access_input([&](const std::span<const uint8_t> input)
|
context.generator.access_input([&](const std::span<const uint8_t> input) {
|
||||||
{
|
uint64_t score{0};
|
||||||
uint64_t score{0};
|
const auto result = executer.execute(input, [&](uint64_t) { ++score; });
|
||||||
const auto result = executer.execute(input, [&](uint64_t)
|
|
||||||
{
|
|
||||||
++score;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result == execution_result::error)
|
if (result == execution_result::error)
|
||||||
{
|
{
|
||||||
printf("Found error!\n");
|
printf("Found error!\n");
|
||||||
context.stop();
|
context.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
return score;
|
return score;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void worker(fuzzing_context& context)
|
void worker(fuzzing_context& context)
|
||||||
{
|
{
|
||||||
const auto executer = context.handler.make_executer();
|
const auto executer = context.handler.make_executer();
|
||||||
|
|
||||||
while (!context.should_stop())
|
while (!context.should_stop())
|
||||||
{
|
{
|
||||||
perform_fuzzing_iteration(context, *executer);
|
perform_fuzzing_iteration(context, *executer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct worker_pool
|
struct worker_pool
|
||||||
{
|
{
|
||||||
fuzzing_context* context_{nullptr};
|
fuzzing_context* context_{nullptr};
|
||||||
std::vector<std::thread> workers_{};
|
std::vector<std::thread> workers_{};
|
||||||
|
|
||||||
worker_pool(fuzzing_context& context, const size_t concurrency)
|
worker_pool(fuzzing_context& context, const size_t concurrency)
|
||||||
: context_(&context)
|
: context_(&context)
|
||||||
{
|
{
|
||||||
this->workers_.reserve(concurrency);
|
this->workers_.reserve(concurrency);
|
||||||
|
|
||||||
for (size_t i = 0; i < concurrency; ++i)
|
for (size_t i = 0; i < concurrency; ++i)
|
||||||
{
|
{
|
||||||
this->workers_.emplace_back([&context]
|
this->workers_.emplace_back([&context] { worker(context); });
|
||||||
{
|
}
|
||||||
worker(context);
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~worker_pool()
|
~worker_pool()
|
||||||
{
|
{
|
||||||
if (this->workers_.empty())
|
if (this->workers_.empty())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->context_->stop();
|
this->context_->stop();
|
||||||
|
|
||||||
for (auto& w : this->workers_)
|
for (auto& w : this->workers_)
|
||||||
{
|
{
|
||||||
w.join();
|
w.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void run(fuzzing_handler& handler, const size_t concurrency)
|
void run(fuzzing_handler& handler, const size_t concurrency)
|
||||||
{
|
{
|
||||||
input_generator generator{};
|
input_generator generator{};
|
||||||
fuzzing_context context{generator, handler};
|
fuzzing_context context{generator, handler};
|
||||||
worker_pool pool{context, concurrency};
|
worker_pool pool{context, concurrency};
|
||||||
|
|
||||||
while (!context.should_stop())
|
while (!context.should_stop())
|
||||||
{
|
{
|
||||||
std::this_thread::sleep_for(std::chrono::seconds{1});
|
std::this_thread::sleep_for(std::chrono::seconds{1});
|
||||||
|
|
||||||
const auto executions = context.executions.exchange(0);
|
const auto executions = context.executions.exchange(0);
|
||||||
const auto highest_scorer = context.generator.get_highest_scorer();
|
const auto highest_scorer = context.generator.get_highest_scorer();
|
||||||
const auto avg_score = context.generator.get_average_score();
|
const auto avg_score = context.generator.get_average_score();
|
||||||
printf("Executions/s: %" PRIu64 " - Score: %" PRIx64 " - Avg: %.3f\n", executions, highest_scorer.score,
|
printf("Executions/s: %" PRIu64 " - Score: %" PRIx64 " - Avg: %.3f\n", executions, highest_scorer.score,
|
||||||
avg_score);
|
avg_score);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,33 +7,33 @@
|
|||||||
|
|
||||||
namespace fuzzer
|
namespace fuzzer
|
||||||
{
|
{
|
||||||
using coverage_functor = void(uint64_t address);
|
using coverage_functor = void(uint64_t address);
|
||||||
|
|
||||||
enum class execution_result
|
enum class execution_result
|
||||||
{
|
{
|
||||||
success,
|
success,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct executer
|
struct executer
|
||||||
{
|
{
|
||||||
virtual ~executer() = default;
|
virtual ~executer() = default;
|
||||||
|
|
||||||
virtual execution_result execute(std::span<const uint8_t> data,
|
virtual execution_result execute(std::span<const uint8_t> data,
|
||||||
const std::function<coverage_functor>& coverage_handler) = 0;
|
const std::function<coverage_functor>& coverage_handler) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fuzzing_handler
|
struct fuzzing_handler
|
||||||
{
|
{
|
||||||
virtual ~fuzzing_handler() = default;
|
virtual ~fuzzing_handler() = default;
|
||||||
|
|
||||||
virtual std::unique_ptr<executer> make_executer() = 0;
|
virtual std::unique_ptr<executer> make_executer() = 0;
|
||||||
|
|
||||||
virtual bool stop()
|
virtual bool stop()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void run(fuzzing_handler& handler, size_t concurrency = std::thread::hardware_concurrency());
|
void run(fuzzing_handler& handler, size_t concurrency = std::thread::hardware_concurrency());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,120 +4,118 @@
|
|||||||
|
|
||||||
namespace fuzzer
|
namespace fuzzer
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
constexpr size_t MAX_TOP_SCORER = 20;
|
constexpr size_t MAX_TOP_SCORER = 20;
|
||||||
|
|
||||||
void mutate_input(random_generator& rng, std::vector<uint8_t>& input)
|
void mutate_input(random_generator& rng, std::vector<uint8_t>& input)
|
||||||
{
|
{
|
||||||
if (input.empty() || rng.get(3) == 0)
|
if (input.empty() || rng.get(3) == 0)
|
||||||
{
|
{
|
||||||
const auto new_bytes = rng.get_geometric<size_t>() + 1;
|
const auto new_bytes = rng.get_geometric<size_t>() + 1;
|
||||||
input.resize(input.size() + new_bytes);
|
input.resize(input.size() + new_bytes);
|
||||||
}
|
}
|
||||||
else if (rng.get(10) == 0)
|
else if (rng.get(10) == 0)
|
||||||
{
|
{
|
||||||
const auto remove_bytes = rng.get_geometric<size_t>() % input.size();
|
const auto remove_bytes = rng.get_geometric<size_t>() % input.size();
|
||||||
input.resize(input.size() - remove_bytes);
|
input.resize(input.size() - remove_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto mutations = (rng.get_geometric<size_t>() + 1) % input.size();
|
const auto mutations = (rng.get_geometric<size_t>() + 1) % input.size();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < mutations; ++i)
|
||||||
|
{
|
||||||
|
const auto index = rng.get<size_t>(input.size());
|
||||||
|
input[index] = rng.get<uint8_t>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < mutations; ++i)
|
input_generator::input_generator() = default;
|
||||||
{
|
|
||||||
const auto index = rng.get<size_t>(input.size());
|
|
||||||
input[index] = rng.get<uint8_t>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input_generator::input_generator() = default;
|
std::vector<uint8_t> input_generator::generate_next_input()
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> input{};
|
||||||
|
std::unique_lock lock{this->mutex_};
|
||||||
|
|
||||||
std::vector<uint8_t> input_generator::generate_next_input()
|
if (!this->top_scorer_.empty())
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> input{};
|
const auto index = this->rng.get<size_t>() % this->top_scorer_.size();
|
||||||
std::unique_lock lock{this->mutex_};
|
input = this->top_scorer_[index].data;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this->top_scorer_.empty())
|
mutate_input(this->rng, input);
|
||||||
{
|
|
||||||
const auto index = this->rng.get<size_t>() % this->top_scorer_.size();
|
|
||||||
input = this->top_scorer_[index].data;
|
|
||||||
}
|
|
||||||
|
|
||||||
mutate_input(this->rng, input);
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
return input;
|
void input_generator::access_input(const std::function<input_handler>& handler)
|
||||||
}
|
{
|
||||||
|
auto next_input = this->generate_next_input();
|
||||||
|
const auto score = handler(next_input);
|
||||||
|
|
||||||
void input_generator::access_input(const std::function<input_handler>& handler)
|
input_entry e{};
|
||||||
{
|
e.data = std::move(next_input);
|
||||||
auto next_input = this->generate_next_input();
|
e.score = score;
|
||||||
const auto score = handler(next_input);
|
|
||||||
|
|
||||||
input_entry e{};
|
this->store_input_entry(std::move(e));
|
||||||
e.data = std::move(next_input);
|
}
|
||||||
e.score = score;
|
|
||||||
|
|
||||||
this->store_input_entry(std::move(e));
|
input_entry input_generator::get_highest_scorer()
|
||||||
}
|
{
|
||||||
|
std::unique_lock lock{this->mutex_};
|
||||||
|
return this->highest_scorer_;
|
||||||
|
}
|
||||||
|
|
||||||
input_entry input_generator::get_highest_scorer()
|
double input_generator::get_average_score()
|
||||||
{
|
{
|
||||||
std::unique_lock lock{this->mutex_};
|
std::unique_lock lock{this->mutex_};
|
||||||
return this->highest_scorer_;
|
|
||||||
}
|
|
||||||
|
|
||||||
double input_generator::get_average_score()
|
double score{0.0};
|
||||||
{
|
for (const auto& e : this->top_scorer_)
|
||||||
std::unique_lock lock{this->mutex_};
|
{
|
||||||
|
score += static_cast<double>(e.score);
|
||||||
|
}
|
||||||
|
|
||||||
double score{0.0};
|
return score / static_cast<double>(this->top_scorer_.size());
|
||||||
for (const auto& e : this->top_scorer_)
|
}
|
||||||
{
|
|
||||||
score += static_cast<double>(e.score);
|
|
||||||
}
|
|
||||||
|
|
||||||
return score / static_cast<double>(this->top_scorer_.size());
|
void input_generator::store_input_entry(input_entry entry)
|
||||||
}
|
{
|
||||||
|
std::unique_lock lock{this->mutex_};
|
||||||
|
|
||||||
void input_generator::store_input_entry(input_entry entry)
|
if (entry.score < this->lowest_score && this->rng.get(40) != 0)
|
||||||
{
|
{
|
||||||
std::unique_lock lock{this->mutex_};
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (entry.score < this->lowest_score && this->rng.get(40) != 0)
|
if (entry.score > this->highest_scorer_.score)
|
||||||
{
|
{
|
||||||
return;
|
this->highest_scorer_ = entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.score > this->highest_scorer_.score)
|
if (this->top_scorer_.size() < MAX_TOP_SCORER)
|
||||||
{
|
{
|
||||||
this->highest_scorer_ = entry;
|
this->top_scorer_.emplace_back(std::move(entry));
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this->top_scorer_.size() < MAX_TOP_SCORER)
|
const auto insert_at_random = this->rng.get(10) == 0;
|
||||||
{
|
const auto index =
|
||||||
this->top_scorer_.emplace_back(std::move(entry));
|
insert_at_random ? (this->rng.get<size_t>() % this->top_scorer_.size()) : this->lowest_scorer;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto insert_at_random = this->rng.get(10) == 0;
|
this->top_scorer_[index] = std::move(entry);
|
||||||
const auto index = insert_at_random
|
|
||||||
? (this->rng.get<size_t>() % this->top_scorer_.size())
|
|
||||||
: this->lowest_scorer;
|
|
||||||
|
|
||||||
this->top_scorer_[index] = std::move(entry);
|
this->lowest_score = this->top_scorer_[0].score;
|
||||||
|
this->lowest_scorer = 0;
|
||||||
|
|
||||||
this->lowest_score = this->top_scorer_[0].score;
|
for (size_t i = 1; i < this->top_scorer_.size(); ++i)
|
||||||
this->lowest_scorer = 0;
|
{
|
||||||
|
if (this->top_scorer_[i].score < this->lowest_score)
|
||||||
for (size_t i = 1; i < this->top_scorer_.size(); ++i)
|
{
|
||||||
{
|
this->lowest_score = this->top_scorer_[i].score;
|
||||||
if (this->top_scorer_[i].score < this->lowest_score)
|
this->lowest_scorer = i;
|
||||||
{
|
}
|
||||||
this->lowest_score = this->top_scorer_[i].score;
|
}
|
||||||
this->lowest_scorer = i;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,37 +8,37 @@
|
|||||||
|
|
||||||
namespace fuzzer
|
namespace fuzzer
|
||||||
{
|
{
|
||||||
using input_score = uint64_t;
|
using input_score = uint64_t;
|
||||||
using input_handler = input_score(std::span<const uint8_t>);
|
using input_handler = input_score(std::span<const uint8_t>);
|
||||||
|
|
||||||
struct input_entry
|
struct input_entry
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> data{};
|
std::vector<uint8_t> data{};
|
||||||
input_score score{};
|
input_score score{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class input_generator
|
class input_generator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
input_generator();
|
input_generator();
|
||||||
|
|
||||||
void access_input(const std::function<input_handler>& handler);
|
void access_input(const std::function<input_handler>& handler);
|
||||||
|
|
||||||
input_entry get_highest_scorer();
|
input_entry get_highest_scorer();
|
||||||
double get_average_score();
|
double get_average_score();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::mutex mutex_{};
|
std::mutex mutex_{};
|
||||||
random_generator rng{};
|
random_generator rng{};
|
||||||
|
|
||||||
std::vector<input_entry> top_scorer_{};
|
std::vector<input_entry> top_scorer_{};
|
||||||
input_score lowest_score{0};
|
input_score lowest_score{0};
|
||||||
size_t lowest_scorer{0};
|
size_t lowest_scorer{0};
|
||||||
|
|
||||||
input_entry highest_scorer_{};
|
input_entry highest_scorer_{};
|
||||||
|
|
||||||
std::vector<uint8_t> generate_next_input();
|
std::vector<uint8_t> generate_next_input();
|
||||||
|
|
||||||
void store_input_entry(input_entry entry);
|
void store_input_entry(input_entry entry);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,33 +3,33 @@
|
|||||||
|
|
||||||
namespace fuzzer
|
namespace fuzzer
|
||||||
{
|
{
|
||||||
random_generator::random_generator()
|
random_generator::random_generator()
|
||||||
: rng_(std::random_device()())
|
: rng_(std::random_device()())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::mt19937::result_type random_generator::generate_number()
|
std::mt19937::result_type random_generator::generate_number()
|
||||||
{
|
{
|
||||||
return this->distribution_(this->rng_);
|
return this->distribution_(this->rng_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void random_generator::fill(void* data, const size_t size)
|
void random_generator::fill(void* data, const size_t size)
|
||||||
{
|
{
|
||||||
this->fill(std::span(static_cast<uint8_t*>(data), size));
|
this->fill(std::span(static_cast<uint8_t*>(data), size));
|
||||||
}
|
}
|
||||||
|
|
||||||
void random_generator::fill(std::span<uint8_t> data)
|
void random_generator::fill(std::span<uint8_t> data)
|
||||||
{
|
{
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
while (i < data.size())
|
while (i < data.size())
|
||||||
{
|
{
|
||||||
const auto number = this->generate_number();
|
const auto number = this->generate_number();
|
||||||
|
|
||||||
const auto remaining_data = data.size() - i;
|
const auto remaining_data = data.size() - i;
|
||||||
const auto data_to_fill = std::min(remaining_data, sizeof(number));
|
const auto data_to_fill = std::min(remaining_data, sizeof(number));
|
||||||
|
|
||||||
memcpy(data.data() + i, &number, data_to_fill);
|
memcpy(data.data() + i, &number, data_to_fill);
|
||||||
i += data_to_fill;
|
i += data_to_fill;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,65 +6,65 @@
|
|||||||
|
|
||||||
namespace fuzzer
|
namespace fuzzer
|
||||||
{
|
{
|
||||||
class random_generator
|
class random_generator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
random_generator();
|
random_generator();
|
||||||
|
|
||||||
void fill(std::span<uint8_t> data);
|
void fill(std::span<uint8_t> data);
|
||||||
void fill(void* data, size_t size);
|
void fill(void* data, size_t size);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
requires(std::is_trivially_copyable_v<T>)
|
requires(std::is_trivially_copyable_v<T>)
|
||||||
T get()
|
T get()
|
||||||
{
|
{
|
||||||
T value{};
|
T value{};
|
||||||
this->fill(&value, sizeof(value));
|
this->fill(&value, sizeof(value));
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T get(const T& max)
|
T get(const T& max)
|
||||||
{
|
{
|
||||||
return this->get<T>() % max;
|
return this->get<T>() % max;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T get(T min, T max)
|
T get(T min, T max)
|
||||||
{
|
{
|
||||||
if (max < min)
|
if (max < min)
|
||||||
{
|
{
|
||||||
std::swap(max, min);
|
std::swap(max, min);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto diff = max - min;
|
const auto diff = max - min;
|
||||||
|
|
||||||
return (this->get<T>() % diff) + min;
|
return (this->get<T>() % diff) + min;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T get_geometric()
|
T get_geometric()
|
||||||
{
|
{
|
||||||
T value{0};
|
T value{0};
|
||||||
|
|
||||||
while (this->get<bool>())
|
while (this->get<bool>())
|
||||||
{
|
{
|
||||||
++value;
|
++value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::mt19937 rng_;
|
std::mt19937 rng_;
|
||||||
std::uniform_int_distribution<std::mt19937::result_type> distribution_{};
|
std::uniform_int_distribution<std::mt19937::result_type> distribution_{};
|
||||||
|
|
||||||
std::mt19937::result_type generate_number();
|
std::mt19937::result_type generate_number();
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline bool random_generator::get<bool>()
|
inline bool random_generator::get<bool>()
|
||||||
{
|
{
|
||||||
return (this->generate_number() & 1) != 0;
|
return (this->generate_number() & 1) != 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,12 +19,12 @@ __declspec(dllexport) bool do_the_task = true;
|
|||||||
|
|
||||||
struct tls_struct
|
struct tls_struct
|
||||||
{
|
{
|
||||||
DWORD num = 1337;
|
DWORD num = 1337;
|
||||||
|
|
||||||
tls_struct()
|
tls_struct()
|
||||||
{
|
{
|
||||||
num = GetCurrentThreadId();
|
num = GetCurrentThreadId();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
thread_local tls_struct tls_var{};
|
thread_local tls_struct tls_var{};
|
||||||
@@ -32,297 +32,293 @@ thread_local tls_struct tls_var{};
|
|||||||
// getenv is broken right now :(
|
// getenv is broken right now :(
|
||||||
std::string read_env(const char* env)
|
std::string read_env(const char* env)
|
||||||
{
|
{
|
||||||
char buffer[0x1000] = {};
|
char buffer[0x1000] = {};
|
||||||
if (!GetEnvironmentVariableA(env, buffer, sizeof(buffer)))
|
if (!GetEnvironmentVariableA(env, buffer, sizeof(buffer)))
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool test_threads()
|
bool test_threads()
|
||||||
{
|
{
|
||||||
constexpr auto thread_count = 5ULL;
|
constexpr auto thread_count = 5ULL;
|
||||||
|
|
||||||
std::atomic<uint64_t> counter{0};
|
std::atomic<uint64_t> counter{0};
|
||||||
|
|
||||||
std::vector<std::thread> threads{};
|
std::vector<std::thread> threads{};
|
||||||
threads.reserve(thread_count);
|
threads.reserve(thread_count);
|
||||||
|
|
||||||
for (auto i = 0ULL; i < thread_count; ++i)
|
for (auto i = 0ULL; i < thread_count; ++i)
|
||||||
{
|
{
|
||||||
threads.emplace_back([&counter]
|
threads.emplace_back([&counter] {
|
||||||
{
|
++counter;
|
||||||
++counter;
|
std::this_thread::yield();
|
||||||
std::this_thread::yield();
|
++counter;
|
||||||
++counter;
|
// Host scheduling/cpu performance can have impact on emulator scheduling
|
||||||
// Host scheduling/cpu performance can have impact on emulator scheduling
|
// std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
//std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
++counter;
|
||||||
++counter;
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& t : threads)
|
for (auto& t : threads)
|
||||||
{
|
{
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
return counter == (thread_count * 3ULL);
|
return counter == (thread_count * 3ULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool test_tls()
|
bool test_tls()
|
||||||
{
|
{
|
||||||
std::atomic_bool kill{false};
|
std::atomic_bool kill{false};
|
||||||
std::atomic_uint32_t successes{0};
|
std::atomic_uint32_t successes{0};
|
||||||
constexpr uint32_t thread_count = 2;
|
constexpr uint32_t thread_count = 2;
|
||||||
|
|
||||||
std::vector<std::thread> ts{};
|
std::vector<std::thread> ts{};
|
||||||
kill = false;
|
kill = false;
|
||||||
|
|
||||||
for (size_t i = 0; i < thread_count; ++i)
|
for (size_t i = 0; i < thread_count; ++i)
|
||||||
{
|
{
|
||||||
ts.emplace_back([&]
|
ts.emplace_back([&] {
|
||||||
{
|
while (!kill)
|
||||||
while (!kill)
|
{
|
||||||
{
|
std::this_thread::yield();
|
||||||
std::this_thread::yield();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (tls_var.num == GetCurrentThreadId())
|
if (tls_var.num == GetCurrentThreadId())
|
||||||
{
|
{
|
||||||
++successes;
|
++successes;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadLibraryA("d3dcompiler_47.dll");
|
LoadLibraryA("d3dcompiler_47.dll");
|
||||||
LoadLibraryA("dsound.dll");
|
LoadLibraryA("dsound.dll");
|
||||||
/*LoadLibraryA("d3d9.dll");
|
/*LoadLibraryA("d3d9.dll");
|
||||||
LoadLibraryA("dxgi.dll");
|
LoadLibraryA("dxgi.dll");
|
||||||
LoadLibraryA("wlanapi.dll");*/
|
LoadLibraryA("wlanapi.dll");*/
|
||||||
|
|
||||||
kill = true;
|
kill = true;
|
||||||
|
|
||||||
for (auto& t : ts)
|
for (auto& t : ts)
|
||||||
{
|
{
|
||||||
if (t.joinable())
|
if (t.joinable())
|
||||||
{
|
{
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return successes == thread_count;
|
return successes == thread_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool test_env()
|
bool test_env()
|
||||||
{
|
{
|
||||||
const auto computername = read_env("COMPUTERNAME");
|
const auto computername = read_env("COMPUTERNAME");
|
||||||
|
|
||||||
SetEnvironmentVariableA("BLUB", "LUL");
|
SetEnvironmentVariableA("BLUB", "LUL");
|
||||||
|
|
||||||
const auto blub = read_env("BLUB");
|
const auto blub = read_env("BLUB");
|
||||||
|
|
||||||
return !computername.empty() && blub == "LUL";
|
return !computername.empty() && blub == "LUL";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool test_io()
|
bool test_io()
|
||||||
{
|
{
|
||||||
const auto* filename = "a.txt";
|
const auto* filename = "a.txt";
|
||||||
|
|
||||||
FILE* fp{};
|
FILE* fp{};
|
||||||
(void)fopen_s(&fp, filename, "wb");
|
(void)fopen_s(&fp, filename, "wb");
|
||||||
|
|
||||||
if (!fp)
|
if (!fp)
|
||||||
{
|
{
|
||||||
puts("Bad file");
|
puts("Bad file");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string text = "Blub";
|
const std::string text = "Blub";
|
||||||
|
|
||||||
(void)fwrite(text.data(), 1, text.size(), fp);
|
(void)fwrite(text.data(), 1, text.size(), fp);
|
||||||
(void)fclose(fp);
|
(void)fclose(fp);
|
||||||
|
|
||||||
std::ifstream t(filename);
|
std::ifstream t(filename);
|
||||||
t.seekg(0, std::ios::end);
|
t.seekg(0, std::ios::end);
|
||||||
const size_t size = t.tellg();
|
const size_t size = t.tellg();
|
||||||
std::string buffer(size, ' ');
|
std::string buffer(size, ' ');
|
||||||
t.seekg(0);
|
t.seekg(0);
|
||||||
t.read(buffer.data(), static_cast<std::streamsize>(size));
|
t.read(buffer.data(), static_cast<std::streamsize>(size));
|
||||||
|
|
||||||
return text == buffer;
|
return text == buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool test_dir_io()
|
bool test_dir_io()
|
||||||
{
|
{
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
|
|
||||||
for (auto i : std::filesystem::directory_iterator(R"(C:\Windows\System32\)"))
|
for (auto i : std::filesystem::directory_iterator(R"(C:\Windows\System32\)"))
|
||||||
{
|
{
|
||||||
++count;
|
++count;
|
||||||
if (count > 30)
|
if (count > 30)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return count > 30;
|
return count > 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> read_registry_string(const HKEY root, const char* path, const char* value)
|
std::optional<std::string> read_registry_string(const HKEY root, const char* path, const char* value)
|
||||||
{
|
{
|
||||||
HKEY key{};
|
HKEY key{};
|
||||||
if (RegOpenKeyExA(root, path, 0, KEY_READ, &key) !=
|
if (RegOpenKeyExA(root, path, 0, KEY_READ, &key) != ERROR_SUCCESS)
|
||||||
ERROR_SUCCESS)
|
{
|
||||||
{
|
return std::nullopt;
|
||||||
return std::nullopt;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
char data[MAX_PATH]{};
|
char data[MAX_PATH]{};
|
||||||
DWORD length = sizeof(data);
|
DWORD length = sizeof(data);
|
||||||
const auto res = RegQueryValueExA(key, value, nullptr, nullptr, reinterpret_cast<uint8_t*>(data), &length);
|
const auto res = RegQueryValueExA(key, value, nullptr, nullptr, reinterpret_cast<uint8_t*>(data), &length);
|
||||||
|
|
||||||
if (RegCloseKey(key) != ERROR_SUCCESS)
|
if (RegCloseKey(key) != ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res != ERROR_SUCCESS)
|
if (res != ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length == 0)
|
if (length == 0)
|
||||||
{
|
{
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return {std::string(data, min(length - 1, sizeof(data)))};
|
return {std::string(data, min(length - 1, sizeof(data)))};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool test_registry()
|
bool test_registry()
|
||||||
{
|
{
|
||||||
const auto val = read_registry_string(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows\CurrentVersion)",
|
const auto val =
|
||||||
"ProgramFilesDir");
|
read_registry_string(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows\CurrentVersion)", "ProgramFilesDir");
|
||||||
if (!val)
|
if (!val)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return *val == "C:\\Program Files";
|
return *val == "C:\\Program Files";
|
||||||
}
|
}
|
||||||
|
|
||||||
void throw_exception()
|
void throw_exception()
|
||||||
{
|
{
|
||||||
if (do_the_task)
|
if (do_the_task)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("OK");
|
throw std::runtime_error("OK");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool test_exceptions()
|
bool test_exceptions()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
throw_exception();
|
throw_exception();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
return e.what() == std::string("OK");
|
return e.what() == std::string("OK");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void throw_access_violation()
|
void throw_access_violation()
|
||||||
{
|
{
|
||||||
if (do_the_task)
|
if (do_the_task)
|
||||||
{
|
{
|
||||||
*reinterpret_cast<int*>(1) = 1;
|
*reinterpret_cast<int*>(1) = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool test_access_violation_exception()
|
bool test_access_violation_exception()
|
||||||
{
|
{
|
||||||
__try
|
__try
|
||||||
{
|
{
|
||||||
throw_access_violation();
|
throw_access_violation();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||||
{
|
{
|
||||||
return GetExceptionCode() == STATUS_ACCESS_VIOLATION;
|
return GetExceptionCode() == STATUS_ACCESS_VIOLATION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool test_ud2_exception(void* address)
|
bool test_ud2_exception(void* address)
|
||||||
{
|
{
|
||||||
__try
|
__try
|
||||||
{
|
{
|
||||||
static_cast<void(*)()>(address)();
|
static_cast<void (*)()>(address)();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||||
{
|
{
|
||||||
return GetExceptionCode() == STATUS_ILLEGAL_INSTRUCTION;
|
return GetExceptionCode() == STATUS_ILLEGAL_INSTRUCTION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool test_illegal_instruction_exception()
|
bool test_illegal_instruction_exception()
|
||||||
{
|
{
|
||||||
const auto address = VirtualAlloc(nullptr, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
const auto address = VirtualAlloc(nullptr, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||||
if (!address)
|
if (!address)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(address, "\x0F\x0B", 2); // ud2
|
memcpy(address, "\x0F\x0B", 2); // ud2
|
||||||
|
|
||||||
const auto res = test_ud2_exception(address);
|
const auto res = test_ud2_exception(address);
|
||||||
|
|
||||||
VirtualFree(address, 0x1000, MEM_RELEASE);
|
VirtualFree(address, 0x1000, MEM_RELEASE);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool test_native_exceptions()
|
bool test_native_exceptions()
|
||||||
{
|
{
|
||||||
return test_access_violation_exception()
|
return test_access_violation_exception() && test_illegal_instruction_exception();
|
||||||
&& test_illegal_instruction_exception();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_time()
|
void print_time()
|
||||||
{
|
{
|
||||||
const auto epoch_time = std::chrono::system_clock::now().time_since_epoch();
|
const auto epoch_time = std::chrono::system_clock::now().time_since_epoch();
|
||||||
printf("Time: %lld\n", epoch_time.count());
|
printf("Time: %lld\n", epoch_time.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
#define RUN_TEST(func, name) \
|
#define RUN_TEST(func, name) \
|
||||||
{ \
|
{ \
|
||||||
printf("Running test '" name "': "); \
|
printf("Running test '" name "': "); \
|
||||||
const auto res = func(); \
|
const auto res = func(); \
|
||||||
valid &= res; \
|
valid &= res; \
|
||||||
puts(res ? "Success" : "Fail"); \
|
puts(res ? "Success" : "Fail"); \
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, const char* argv[])
|
int main(int argc, const char* argv[])
|
||||||
{
|
{
|
||||||
if (argc == 2 && argv[1] == "-time"sv)
|
if (argc == 2 && argv[1] == "-time"sv)
|
||||||
{
|
{
|
||||||
print_time();
|
print_time();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool valid = true;
|
bool valid = true;
|
||||||
|
|
||||||
RUN_TEST(test_io, "I/O")
|
RUN_TEST(test_io, "I/O")
|
||||||
RUN_TEST(test_dir_io, "Dir I/O")
|
RUN_TEST(test_dir_io, "Dir I/O")
|
||||||
RUN_TEST(test_registry, "Registry")
|
RUN_TEST(test_registry, "Registry")
|
||||||
RUN_TEST(test_threads, "Threads")
|
RUN_TEST(test_threads, "Threads")
|
||||||
RUN_TEST(test_env, "Environment")
|
RUN_TEST(test_env, "Environment")
|
||||||
RUN_TEST(test_exceptions, "Exceptions")
|
RUN_TEST(test_exceptions, "Exceptions")
|
||||||
RUN_TEST(test_native_exceptions, "Native Exceptions")
|
RUN_TEST(test_native_exceptions, "Native Exceptions")
|
||||||
RUN_TEST(test_tls, "TLS")
|
RUN_TEST(test_tls, "TLS")
|
||||||
|
|
||||||
return valid ? 0 : 1;
|
return valid ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,36 +8,35 @@
|
|||||||
template <typename ReturnType, typename... Args>
|
template <typename ReturnType, typename... Args>
|
||||||
class function_wrapper : public object
|
class function_wrapper : public object
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using user_data_pointer = void*;
|
using user_data_pointer = void*;
|
||||||
using c_function_type = ReturnType(Args..., user_data_pointer);
|
using c_function_type = ReturnType(Args..., user_data_pointer);
|
||||||
using functor_type = std::function<ReturnType(Args...)>;
|
using functor_type = std::function<ReturnType(Args...)>;
|
||||||
|
|
||||||
function_wrapper() = default;
|
function_wrapper() = default;
|
||||||
|
|
||||||
function_wrapper(functor_type functor)
|
function_wrapper(functor_type functor)
|
||||||
: functor_(std::make_unique<functor_type>(std::move(functor)))
|
: functor_(std::make_unique<functor_type>(std::move(functor)))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
c_function_type* get_c_function() const
|
c_function_type* get_c_function() const
|
||||||
{
|
{
|
||||||
return +[](Args... args, user_data_pointer user_data) -> ReturnType
|
return +[](Args... args, user_data_pointer user_data) -> ReturnType {
|
||||||
{
|
return (*static_cast<functor_type*>(user_data))(std::forward<Args>(args)...);
|
||||||
return (*static_cast<functor_type*>(user_data))(std::forward<Args>(args)...);
|
};
|
||||||
};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void* get_function() const
|
void* get_function() const
|
||||||
{
|
{
|
||||||
return reinterpret_cast<void*>(this->get_c_function());
|
return reinterpret_cast<void*>(this->get_c_function());
|
||||||
}
|
}
|
||||||
|
|
||||||
user_data_pointer get_user_data() const
|
user_data_pointer get_user_data() const
|
||||||
{
|
{
|
||||||
return this->functor_.get();
|
return this->functor_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<functor_type> functor_{};
|
std::unique_ptr<functor_type> functor_{};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
struct object
|
struct object
|
||||||
{
|
{
|
||||||
object() = default;
|
object() = default;
|
||||||
virtual ~object() = default;
|
virtual ~object() = default;
|
||||||
|
|
||||||
object(object&&) = default;
|
object(object&&) = default;
|
||||||
object(const object&) = default;
|
object(const object&) = default;
|
||||||
object& operator=(object&&) = default;
|
object& operator=(object&&) = default;
|
||||||
object& operator=(const object&) = default;
|
object& operator=(const object&) = default;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
#pragma warning(disable: 4505)
|
#pragma warning(disable : 4505)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __clang__
|
#ifdef __clang__
|
||||||
@@ -25,27 +25,27 @@
|
|||||||
|
|
||||||
namespace unicorn
|
namespace unicorn
|
||||||
{
|
{
|
||||||
struct unicorn_error : std::runtime_error
|
struct unicorn_error : std::runtime_error
|
||||||
{
|
{
|
||||||
unicorn_error(const uc_err error_code)
|
unicorn_error(const uc_err error_code)
|
||||||
: std::runtime_error(uc_strerror(error_code))
|
: std::runtime_error(uc_strerror(error_code)),
|
||||||
, code(error_code)
|
code(error_code)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
uc_err code{};
|
uc_err code{};
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void throw_if_unicorn_error(const uc_err error_code)
|
inline void throw_if_unicorn_error(const uc_err error_code)
|
||||||
{
|
{
|
||||||
if (error_code != UC_ERR_OK)
|
if (error_code != UC_ERR_OK)
|
||||||
{
|
{
|
||||||
throw unicorn_error(error_code);
|
throw unicorn_error(error_code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void uce(const uc_err error_code)
|
inline void uce(const uc_err error_code)
|
||||||
{
|
{
|
||||||
throw_if_unicorn_error(error_code);
|
throw_if_unicorn_error(error_code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,74 +4,73 @@
|
|||||||
|
|
||||||
namespace unicorn
|
namespace unicorn
|
||||||
{
|
{
|
||||||
class unicorn_hook
|
class unicorn_hook
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
unicorn_hook() = default;
|
unicorn_hook() = default;
|
||||||
|
|
||||||
unicorn_hook(uc_engine* uc)
|
unicorn_hook(uc_engine* uc)
|
||||||
: unicorn_hook(uc, {})
|
: unicorn_hook(uc, {})
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
unicorn_hook(uc_engine* uc, const uc_hook hook)
|
unicorn_hook(uc_engine* uc, const uc_hook hook)
|
||||||
: uc_(uc)
|
: uc_(uc),
|
||||||
, hook_(hook)
|
hook_(hook)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
~unicorn_hook()
|
~unicorn_hook()
|
||||||
{
|
{
|
||||||
release();
|
release();
|
||||||
}
|
}
|
||||||
|
|
||||||
unicorn_hook(const unicorn_hook&) = delete;
|
unicorn_hook(const unicorn_hook&) = delete;
|
||||||
unicorn_hook& operator=(const unicorn_hook&) = delete;
|
unicorn_hook& operator=(const unicorn_hook&) = delete;
|
||||||
|
|
||||||
|
unicorn_hook(unicorn_hook&& obj) noexcept
|
||||||
|
{
|
||||||
|
this->operator=(std::move(obj));
|
||||||
|
}
|
||||||
|
|
||||||
unicorn_hook(unicorn_hook&& obj) noexcept
|
uc_hook* make_reference()
|
||||||
{
|
{
|
||||||
this->operator=(std::move(obj));
|
if (!this->uc_)
|
||||||
}
|
{
|
||||||
|
throw std::runtime_error("Cannot make reference on default constructed hook");
|
||||||
|
}
|
||||||
|
|
||||||
uc_hook* make_reference()
|
this->release();
|
||||||
{
|
return &this->hook_;
|
||||||
if (!this->uc_)
|
}
|
||||||
{
|
|
||||||
throw std::runtime_error("Cannot make reference on default constructed hook");
|
|
||||||
}
|
|
||||||
|
|
||||||
this->release();
|
unicorn_hook& operator=(unicorn_hook&& obj) noexcept
|
||||||
return &this->hook_;
|
{
|
||||||
}
|
if (this != &obj)
|
||||||
|
{
|
||||||
|
this->release();
|
||||||
|
|
||||||
unicorn_hook& operator=(unicorn_hook&& obj) noexcept
|
this->uc_ = obj.uc_;
|
||||||
{
|
this->hook_ = obj.hook_;
|
||||||
if (this != &obj)
|
|
||||||
{
|
|
||||||
this->release();
|
|
||||||
|
|
||||||
this->uc_ = obj.uc_;
|
obj.hook_ = {};
|
||||||
this->hook_ = obj.hook_;
|
obj.uc_ = {};
|
||||||
|
}
|
||||||
|
|
||||||
obj.hook_ = {};
|
return *this;
|
||||||
obj.uc_ = {};
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
void release()
|
||||||
}
|
{
|
||||||
|
if (this->hook_ && this->uc_)
|
||||||
|
{
|
||||||
|
uc_hook_del(this->uc_, this->hook_);
|
||||||
|
this->hook_ = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void release()
|
private:
|
||||||
{
|
uc_engine* uc_{};
|
||||||
if (this->hook_ && this->uc_)
|
uc_hook hook_{};
|
||||||
{
|
};
|
||||||
uc_hook_del(this->uc_, this->hook_);
|
|
||||||
this->hook_ = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
uc_engine* uc_{};
|
|
||||||
uc_hook hook_{};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,62 +6,61 @@
|
|||||||
|
|
||||||
namespace unicorn
|
namespace unicorn
|
||||||
{
|
{
|
||||||
class unicorn_memory_regions
|
class unicorn_memory_regions
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
unicorn_memory_regions(uc_engine* uc)
|
unicorn_memory_regions(uc_engine* uc)
|
||||||
{
|
{
|
||||||
uce(uc_mem_regions(uc, &this->regions_, &this->count_));
|
uce(uc_mem_regions(uc, &this->regions_, &this->count_));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~unicorn_memory_regions()
|
||||||
|
{
|
||||||
|
this->release();
|
||||||
|
}
|
||||||
|
|
||||||
~unicorn_memory_regions()
|
unicorn_memory_regions(const unicorn_memory_regions&) = delete;
|
||||||
{
|
unicorn_memory_regions& operator=(const unicorn_memory_regions&) = delete;
|
||||||
this->release();
|
|
||||||
}
|
|
||||||
|
|
||||||
unicorn_memory_regions(const unicorn_memory_regions&) = delete;
|
unicorn_memory_regions(unicorn_memory_regions&& obj) noexcept
|
||||||
unicorn_memory_regions& operator=(const unicorn_memory_regions&) = delete;
|
{
|
||||||
|
this->operator=(std::move(obj));
|
||||||
|
}
|
||||||
|
|
||||||
unicorn_memory_regions(unicorn_memory_regions&& obj) noexcept
|
unicorn_memory_regions& operator=(unicorn_memory_regions&& obj) noexcept
|
||||||
{
|
{
|
||||||
this->operator=(std::move(obj));
|
if (this != &obj)
|
||||||
}
|
{
|
||||||
|
this->release();
|
||||||
|
|
||||||
unicorn_memory_regions& operator=(unicorn_memory_regions&& obj) noexcept
|
this->count_ = obj.count_;
|
||||||
{
|
this->regions_ = obj.regions_;
|
||||||
if (this != &obj)
|
|
||||||
{
|
|
||||||
this->release();
|
|
||||||
|
|
||||||
this->count_ = obj.count_;
|
obj.count_ = {};
|
||||||
this->regions_ = obj.regions_;
|
obj.regions_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
obj.count_ = {};
|
return *this;
|
||||||
obj.regions_ = nullptr;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
std::span<uc_mem_region> get_span() const
|
||||||
}
|
{
|
||||||
|
return {this->regions_, this->count_};
|
||||||
|
}
|
||||||
|
|
||||||
std::span<uc_mem_region> get_span() const
|
private:
|
||||||
{
|
uint32_t count_{};
|
||||||
return {this->regions_, this->count_};
|
uc_mem_region* regions_{};
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
void release()
|
||||||
uint32_t count_{};
|
{
|
||||||
uc_mem_region* regions_{};
|
if (this->regions_)
|
||||||
|
{
|
||||||
|
uc_free(regions_);
|
||||||
|
}
|
||||||
|
|
||||||
void release()
|
this->count_ = {};
|
||||||
{
|
this->regions_ = nullptr;
|
||||||
if (this->regions_)
|
}
|
||||||
{
|
};
|
||||||
uc_free(regions_);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->count_ = {};
|
|
||||||
this->regions_ = nullptr;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,6 @@
|
|||||||
|
|
||||||
namespace unicorn
|
namespace unicorn
|
||||||
{
|
{
|
||||||
UNICORN_EMULATOR_DLL_STORAGE
|
UNICORN_EMULATOR_DLL_STORAGE
|
||||||
std::unique_ptr<x64_emulator> create_x64_emulator();
|
std::unique_ptr<x64_emulator> create_x64_emulator();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,46 +2,46 @@
|
|||||||
|
|
||||||
namespace test
|
namespace test
|
||||||
{
|
{
|
||||||
TEST(EmulationTest, BasicEmulationWorks)
|
TEST(EmulationTest, BasicEmulationWorks)
|
||||||
{
|
{
|
||||||
auto emu = create_sample_emulator();
|
auto emu = create_sample_emulator();
|
||||||
emu.start();
|
emu.start();
|
||||||
|
|
||||||
ASSERT_TERMINATED_SUCCESSFULLY(emu);
|
ASSERT_TERMINATED_SUCCESSFULLY(emu);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(EmulationTest, CountedEmulationWorks)
|
TEST(EmulationTest, CountedEmulationWorks)
|
||||||
{
|
{
|
||||||
constexpr auto count = 200000;
|
constexpr auto count = 200000;
|
||||||
|
|
||||||
auto emu = create_sample_emulator();
|
auto emu = create_sample_emulator();
|
||||||
emu.start({}, count);
|
emu.start({}, count);
|
||||||
|
|
||||||
ASSERT_EQ(emu.process().executed_instructions, count);
|
ASSERT_EQ(emu.process().executed_instructions, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(EmulationTest, CountedEmulationIsAccurate)
|
TEST(EmulationTest, CountedEmulationIsAccurate)
|
||||||
{
|
{
|
||||||
auto emu = create_sample_emulator();
|
auto emu = create_sample_emulator();
|
||||||
emu.start();
|
emu.start();
|
||||||
|
|
||||||
ASSERT_TERMINATED_SUCCESSFULLY(emu);
|
ASSERT_TERMINATED_SUCCESSFULLY(emu);
|
||||||
|
|
||||||
const auto executedInstructions = emu.process().executed_instructions;
|
const auto executedInstructions = emu.process().executed_instructions;
|
||||||
|
|
||||||
auto new_emu = create_sample_emulator();
|
auto new_emu = create_sample_emulator();
|
||||||
|
|
||||||
constexpr auto offset = 1;
|
constexpr auto offset = 1;
|
||||||
const auto instructionsToExecute = executedInstructions - offset;
|
const auto instructionsToExecute = executedInstructions - offset;
|
||||||
|
|
||||||
new_emu.start({}, instructionsToExecute);
|
new_emu.start({}, instructionsToExecute);
|
||||||
|
|
||||||
ASSERT_EQ(new_emu.process().executed_instructions, instructionsToExecute);
|
ASSERT_EQ(new_emu.process().executed_instructions, instructionsToExecute);
|
||||||
ASSERT_NOT_TERMINATED(new_emu);
|
ASSERT_NOT_TERMINATED(new_emu);
|
||||||
|
|
||||||
new_emu.start({}, offset);
|
new_emu.start({}, offset);
|
||||||
|
|
||||||
ASSERT_TERMINATED_SUCCESSFULLY(new_emu);
|
ASSERT_TERMINATED_SUCCESSFULLY(new_emu);
|
||||||
ASSERT_EQ(new_emu.process().executed_instructions, executedInstructions);
|
ASSERT_EQ(new_emu.process().executed_instructions, executedInstructions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,37 +3,36 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <windows_emulator.hpp>
|
#include <windows_emulator.hpp>
|
||||||
|
|
||||||
#define ASSERT_NOT_TERMINATED(win_emu) \
|
#define ASSERT_NOT_TERMINATED(win_emu) \
|
||||||
do { \
|
do \
|
||||||
ASSERT_FALSE((win_emu).process().exit_status.has_value()); \
|
{ \
|
||||||
} while(false)
|
ASSERT_FALSE((win_emu).process().exit_status.has_value()); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#define ASSERT_TERMINATED_WITH_STATUS(win_emu, status) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
ASSERT_TRUE((win_emu).process().exit_status.has_value()); \
|
||||||
|
ASSERT_EQ(*(win_emu).process().exit_status, status); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
#define ASSERT_TERMINATED_WITH_STATUS(win_emu, status) \
|
#define ASSERT_TERMINATED_SUCCESSFULLY(win_emu) ASSERT_TERMINATED_WITH_STATUS(win_emu, STATUS_SUCCESS)
|
||||||
do { \
|
|
||||||
ASSERT_TRUE((win_emu).process().exit_status.has_value()); \
|
|
||||||
ASSERT_EQ(*(win_emu).process().exit_status, status); \
|
|
||||||
} while(false)
|
|
||||||
|
|
||||||
#define ASSERT_TERMINATED_SUCCESSFULLY(win_emu) \
|
|
||||||
ASSERT_TERMINATED_WITH_STATUS(win_emu, STATUS_SUCCESS)
|
|
||||||
|
|
||||||
namespace test
|
namespace test
|
||||||
{
|
{
|
||||||
inline windows_emulator create_sample_emulator(emulator_settings settings)
|
inline windows_emulator create_sample_emulator(emulator_settings settings)
|
||||||
{
|
{
|
||||||
settings.application = "./test-sample.exe";
|
settings.application = "./test-sample.exe";
|
||||||
return windows_emulator{std::move(settings)};
|
return windows_emulator{std::move(settings)};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline windows_emulator create_sample_emulator()
|
inline windows_emulator create_sample_emulator()
|
||||||
{
|
{
|
||||||
emulator_settings settings
|
emulator_settings settings{
|
||||||
{
|
.disable_logging = true,
|
||||||
.disable_logging = true,
|
.use_relative_time = true,
|
||||||
.use_relative_time = true,
|
};
|
||||||
};
|
|
||||||
|
|
||||||
return create_sample_emulator(std::move(settings));
|
return create_sample_emulator(std::move(settings));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
return RUN_ALL_TESTS();
|
return RUN_ALL_TESTS();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,77 +2,77 @@
|
|||||||
|
|
||||||
namespace test
|
namespace test
|
||||||
{
|
{
|
||||||
TEST(SerializationTest, SerializedDataIsReproducible)
|
TEST(SerializationTest, SerializedDataIsReproducible)
|
||||||
{
|
{
|
||||||
auto emu1 = create_sample_emulator();
|
auto emu1 = create_sample_emulator();
|
||||||
emu1.start();
|
emu1.start();
|
||||||
|
|
||||||
ASSERT_TERMINATED_SUCCESSFULLY(emu1);
|
ASSERT_TERMINATED_SUCCESSFULLY(emu1);
|
||||||
|
|
||||||
utils::buffer_serializer serializer1{};
|
utils::buffer_serializer serializer1{};
|
||||||
emu1.serialize(serializer1);
|
emu1.serialize(serializer1);
|
||||||
|
|
||||||
utils::buffer_deserializer deserializer{serializer1.get_buffer()};
|
utils::buffer_deserializer deserializer{serializer1.get_buffer()};
|
||||||
|
|
||||||
windows_emulator new_emu{};
|
windows_emulator new_emu{};
|
||||||
new_emu.deserialize(deserializer);
|
new_emu.deserialize(deserializer);
|
||||||
|
|
||||||
utils::buffer_serializer serializer2{};
|
utils::buffer_serializer serializer2{};
|
||||||
new_emu.serialize(serializer2);
|
new_emu.serialize(serializer2);
|
||||||
|
|
||||||
auto buffer1 = serializer1.move_buffer();
|
auto buffer1 = serializer1.move_buffer();
|
||||||
auto buffer2 = serializer2.move_buffer();
|
auto buffer2 = serializer2.move_buffer();
|
||||||
|
|
||||||
ASSERT_EQ(serializer1.get_buffer(), serializer2.get_buffer());
|
ASSERT_EQ(serializer1.get_buffer(), serializer2.get_buffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(SerializationTest, EmulationIsReproducible)
|
TEST(SerializationTest, EmulationIsReproducible)
|
||||||
{
|
{
|
||||||
auto emu1 = create_sample_emulator();
|
auto emu1 = create_sample_emulator();
|
||||||
emu1.start();
|
emu1.start();
|
||||||
|
|
||||||
ASSERT_TERMINATED_SUCCESSFULLY(emu1);
|
ASSERT_TERMINATED_SUCCESSFULLY(emu1);
|
||||||
|
|
||||||
utils::buffer_serializer serializer1{};
|
utils::buffer_serializer serializer1{};
|
||||||
emu1.serialize(serializer1);
|
emu1.serialize(serializer1);
|
||||||
|
|
||||||
auto emu2 = create_sample_emulator();
|
auto emu2 = create_sample_emulator();
|
||||||
emu2.start();
|
emu2.start();
|
||||||
|
|
||||||
ASSERT_TERMINATED_SUCCESSFULLY(emu2);
|
ASSERT_TERMINATED_SUCCESSFULLY(emu2);
|
||||||
|
|
||||||
utils::buffer_serializer serializer2{};
|
utils::buffer_serializer serializer2{};
|
||||||
emu2.serialize(serializer2);
|
emu2.serialize(serializer2);
|
||||||
|
|
||||||
ASSERT_EQ(serializer1.get_buffer(), serializer2.get_buffer());
|
ASSERT_EQ(serializer1.get_buffer(), serializer2.get_buffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(SerializationTest, DeserializedEmulatorBehavesLikeSource)
|
TEST(SerializationTest, DeserializedEmulatorBehavesLikeSource)
|
||||||
{
|
{
|
||||||
auto emu = create_sample_emulator();
|
auto emu = create_sample_emulator();
|
||||||
emu.start({}, 100);
|
emu.start({}, 100);
|
||||||
|
|
||||||
utils::buffer_serializer serializer{};
|
utils::buffer_serializer serializer{};
|
||||||
emu.serialize(serializer);
|
emu.serialize(serializer);
|
||||||
|
|
||||||
utils::buffer_deserializer deserializer{serializer.get_buffer()};
|
utils::buffer_deserializer deserializer{serializer.get_buffer()};
|
||||||
|
|
||||||
windows_emulator new_emu{};
|
windows_emulator new_emu{};
|
||||||
new_emu.log.disable_output(true);
|
new_emu.log.disable_output(true);
|
||||||
new_emu.deserialize(deserializer);
|
new_emu.deserialize(deserializer);
|
||||||
|
|
||||||
new_emu.start();
|
new_emu.start();
|
||||||
ASSERT_TERMINATED_SUCCESSFULLY(new_emu);
|
ASSERT_TERMINATED_SUCCESSFULLY(new_emu);
|
||||||
|
|
||||||
emu.start();
|
emu.start();
|
||||||
ASSERT_TERMINATED_SUCCESSFULLY(emu);
|
ASSERT_TERMINATED_SUCCESSFULLY(emu);
|
||||||
|
|
||||||
utils::buffer_serializer serializer1{};
|
utils::buffer_serializer serializer1{};
|
||||||
utils::buffer_serializer serializer2{};
|
utils::buffer_serializer serializer2{};
|
||||||
|
|
||||||
emu.serialize(serializer1);
|
emu.serialize(serializer1);
|
||||||
new_emu.serialize(serializer2);
|
new_emu.serialize(serializer2);
|
||||||
|
|
||||||
ASSERT_EQ(serializer1.get_buffer(), serializer2.get_buffer());
|
ASSERT_EQ(serializer1.get_buffer(), serializer2.get_buffer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,44 +2,41 @@
|
|||||||
|
|
||||||
namespace test
|
namespace test
|
||||||
{
|
{
|
||||||
TEST(TimeTest, SystemTimeIsAccurate)
|
TEST(TimeTest, SystemTimeIsAccurate)
|
||||||
{
|
{
|
||||||
std::string output_buffer{};
|
std::string output_buffer{};
|
||||||
|
|
||||||
const emulator_settings settings{
|
const emulator_settings settings{
|
||||||
.arguments = {u"-time"},
|
.arguments = {u"-time"},
|
||||||
.stdout_callback = [&output_buffer](const std::string_view data)
|
.stdout_callback = [&output_buffer](const std::string_view data) { output_buffer.append(data); },
|
||||||
{
|
.disable_logging = true,
|
||||||
output_buffer.append(data);
|
.use_relative_time = false,
|
||||||
},
|
};
|
||||||
.disable_logging = true,
|
|
||||||
.use_relative_time = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
auto emu = create_sample_emulator(settings);
|
auto emu = create_sample_emulator(settings);
|
||||||
emu.start();
|
emu.start();
|
||||||
|
|
||||||
constexpr auto prefix = "Time: "sv;
|
constexpr auto prefix = "Time: "sv;
|
||||||
|
|
||||||
ASSERT_TERMINATED_SUCCESSFULLY(emu);
|
ASSERT_TERMINATED_SUCCESSFULLY(emu);
|
||||||
ASSERT_TRUE(output_buffer.starts_with(prefix));
|
ASSERT_TRUE(output_buffer.starts_with(prefix));
|
||||||
|
|
||||||
output_buffer = output_buffer.substr(prefix.size());
|
output_buffer = output_buffer.substr(prefix.size());
|
||||||
while (!output_buffer.empty() && (output_buffer.back() == '\n' || output_buffer.back() == '\r'))
|
while (!output_buffer.empty() && (output_buffer.back() == '\n' || output_buffer.back() == '\r'))
|
||||||
{
|
{
|
||||||
output_buffer.pop_back();
|
output_buffer.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto time = strtoll(output_buffer.c_str(), nullptr, 10);
|
const auto time = strtoll(output_buffer.c_str(), nullptr, 10);
|
||||||
|
|
||||||
using time_point = std::chrono::system_clock::time_point;
|
using time_point = std::chrono::system_clock::time_point;
|
||||||
|
|
||||||
const time_point::duration time_duration(time);
|
const time_point::duration time_duration(time);
|
||||||
const time_point tp(time_duration);
|
const time_point tp(time_duration);
|
||||||
|
|
||||||
const auto now = std::chrono::system_clock::now();
|
const auto now = std::chrono::system_clock::now();
|
||||||
const auto diff = now - tp;
|
const auto diff = now - tp;
|
||||||
|
|
||||||
ASSERT_LE(diff, std::chrono::hours(1));
|
ASSERT_LE(diff, std::chrono::hours(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,149 +3,149 @@
|
|||||||
|
|
||||||
namespace context_frame
|
namespace context_frame
|
||||||
{
|
{
|
||||||
void restore(x64_emulator& emu, const CONTEXT64& context)
|
void restore(x64_emulator& emu, const CONTEXT64& context)
|
||||||
{
|
{
|
||||||
if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS_64)
|
if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS_64)
|
||||||
{
|
{
|
||||||
emu.reg(x64_register::dr0, context.Dr0);
|
emu.reg(x64_register::dr0, context.Dr0);
|
||||||
emu.reg(x64_register::dr1, context.Dr1);
|
emu.reg(x64_register::dr1, context.Dr1);
|
||||||
emu.reg(x64_register::dr2, context.Dr2);
|
emu.reg(x64_register::dr2, context.Dr2);
|
||||||
emu.reg(x64_register::dr3, context.Dr3);
|
emu.reg(x64_register::dr3, context.Dr3);
|
||||||
emu.reg(x64_register::dr6, context.Dr6);
|
emu.reg(x64_register::dr6, context.Dr6);
|
||||||
emu.reg(x64_register::dr7, context.Dr7);
|
emu.reg(x64_register::dr7, context.Dr7);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.ContextFlags & CONTEXT_CONTROL_64)
|
if (context.ContextFlags & CONTEXT_CONTROL_64)
|
||||||
{
|
{
|
||||||
emu.reg<uint16_t>(x64_register::ss, context.SegSs);
|
emu.reg<uint16_t>(x64_register::ss, context.SegSs);
|
||||||
emu.reg<uint16_t>(x64_register::cs, context.SegCs);
|
emu.reg<uint16_t>(x64_register::cs, context.SegCs);
|
||||||
|
|
||||||
emu.reg(x64_register::rip, context.Rip);
|
emu.reg(x64_register::rip, context.Rip);
|
||||||
emu.reg(x64_register::rsp, context.Rsp);
|
emu.reg(x64_register::rsp, context.Rsp);
|
||||||
|
|
||||||
emu.reg<uint32_t>(x64_register::eflags, context.EFlags);
|
emu.reg<uint32_t>(x64_register::eflags, context.EFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.ContextFlags & CONTEXT_INTEGER_64)
|
if (context.ContextFlags & CONTEXT_INTEGER_64)
|
||||||
{
|
{
|
||||||
emu.reg(x64_register::rax, context.Rax);
|
emu.reg(x64_register::rax, context.Rax);
|
||||||
emu.reg(x64_register::rbx, context.Rbx);
|
emu.reg(x64_register::rbx, context.Rbx);
|
||||||
emu.reg(x64_register::rcx, context.Rcx);
|
emu.reg(x64_register::rcx, context.Rcx);
|
||||||
emu.reg(x64_register::rdx, context.Rdx);
|
emu.reg(x64_register::rdx, context.Rdx);
|
||||||
emu.reg(x64_register::rbp, context.Rbp);
|
emu.reg(x64_register::rbp, context.Rbp);
|
||||||
emu.reg(x64_register::rsi, context.Rsi);
|
emu.reg(x64_register::rsi, context.Rsi);
|
||||||
emu.reg(x64_register::rdi, context.Rdi);
|
emu.reg(x64_register::rdi, context.Rdi);
|
||||||
emu.reg(x64_register::r8, context.R8);
|
emu.reg(x64_register::r8, context.R8);
|
||||||
emu.reg(x64_register::r9, context.R9);
|
emu.reg(x64_register::r9, context.R9);
|
||||||
emu.reg(x64_register::r10, context.R10);
|
emu.reg(x64_register::r10, context.R10);
|
||||||
emu.reg(x64_register::r11, context.R11);
|
emu.reg(x64_register::r11, context.R11);
|
||||||
emu.reg(x64_register::r12, context.R12);
|
emu.reg(x64_register::r12, context.R12);
|
||||||
emu.reg(x64_register::r13, context.R13);
|
emu.reg(x64_register::r13, context.R13);
|
||||||
emu.reg(x64_register::r14, context.R14);
|
emu.reg(x64_register::r14, context.R14);
|
||||||
emu.reg(x64_register::r15, context.R15);
|
emu.reg(x64_register::r15, context.R15);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if (context.ContextFlags & CONTEXT_SEGMENTS)
|
/*if (context.ContextFlags & CONTEXT_SEGMENTS)
|
||||||
{
|
{
|
||||||
emu.reg<uint16_t>(x64_register::ds, context.SegDs);
|
emu.reg<uint16_t>(x64_register::ds, context.SegDs);
|
||||||
emu.reg<uint16_t>(x64_register::es, context.SegEs);
|
emu.reg<uint16_t>(x64_register::es, context.SegEs);
|
||||||
emu.reg<uint16_t>(x64_register::fs, context.SegFs);
|
emu.reg<uint16_t>(x64_register::fs, context.SegFs);
|
||||||
emu.reg<uint16_t>(x64_register::gs, context.SegGs);
|
emu.reg<uint16_t>(x64_register::gs, context.SegGs);
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
if (context.ContextFlags & CONTEXT_FLOATING_POINT_64)
|
if (context.ContextFlags & CONTEXT_FLOATING_POINT_64)
|
||||||
{
|
{
|
||||||
emu.reg<uint16_t>(x64_register::fpcw, context.FltSave.ControlWord);
|
emu.reg<uint16_t>(x64_register::fpcw, context.FltSave.ControlWord);
|
||||||
emu.reg<uint16_t>(x64_register::fpsw, context.FltSave.StatusWord);
|
emu.reg<uint16_t>(x64_register::fpsw, context.FltSave.StatusWord);
|
||||||
emu.reg<uint16_t>(x64_register::fptag, context.FltSave.TagWord);
|
emu.reg<uint16_t>(x64_register::fptag, context.FltSave.TagWord);
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++)
|
for (int i = 0; i < 8; i++)
|
||||||
{
|
{
|
||||||
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::st0) + i);
|
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::st0) + i);
|
||||||
emu.reg<M128A>(reg, context.FltSave.FloatRegisters[i]);
|
emu.reg<M128A>(reg, context.FltSave.FloatRegisters[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.ContextFlags & CONTEXT_XSTATE_64)
|
if (context.ContextFlags & CONTEXT_XSTATE_64)
|
||||||
{
|
{
|
||||||
emu.reg<uint32_t>(x64_register::mxcsr, context.MxCsr);
|
emu.reg<uint32_t>(x64_register::mxcsr, context.MxCsr);
|
||||||
|
|
||||||
for (int i = 0; i < 16; i++)
|
for (int i = 0; i < 16; i++)
|
||||||
{
|
{
|
||||||
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::xmm0) + i);
|
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::xmm0) + i);
|
||||||
emu.reg<M128A>(reg, (&context.Xmm0)[i]);
|
emu.reg<M128A>(reg, (&context.Xmm0)[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void save(x64_emulator& emu, CONTEXT64& context)
|
void save(x64_emulator& emu, CONTEXT64& context)
|
||||||
{
|
{
|
||||||
if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS_64)
|
if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS_64)
|
||||||
{
|
{
|
||||||
context.Dr0 = emu.reg(x64_register::dr0);
|
context.Dr0 = emu.reg(x64_register::dr0);
|
||||||
context.Dr1 = emu.reg(x64_register::dr1);
|
context.Dr1 = emu.reg(x64_register::dr1);
|
||||||
context.Dr2 = emu.reg(x64_register::dr2);
|
context.Dr2 = emu.reg(x64_register::dr2);
|
||||||
context.Dr3 = emu.reg(x64_register::dr3);
|
context.Dr3 = emu.reg(x64_register::dr3);
|
||||||
context.Dr6 = emu.reg(x64_register::dr6);
|
context.Dr6 = emu.reg(x64_register::dr6);
|
||||||
context.Dr7 = emu.reg(x64_register::dr7);
|
context.Dr7 = emu.reg(x64_register::dr7);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.ContextFlags & CONTEXT_CONTROL_64)
|
if (context.ContextFlags & CONTEXT_CONTROL_64)
|
||||||
{
|
{
|
||||||
context.SegSs = emu.reg<uint16_t>(x64_register::ss);
|
context.SegSs = emu.reg<uint16_t>(x64_register::ss);
|
||||||
context.SegCs = emu.reg<uint16_t>(x64_register::cs);
|
context.SegCs = emu.reg<uint16_t>(x64_register::cs);
|
||||||
context.Rip = emu.reg(x64_register::rip);
|
context.Rip = emu.reg(x64_register::rip);
|
||||||
context.Rsp = emu.reg(x64_register::rsp);
|
context.Rsp = emu.reg(x64_register::rsp);
|
||||||
context.EFlags = emu.reg<uint32_t>(x64_register::eflags);
|
context.EFlags = emu.reg<uint32_t>(x64_register::eflags);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.ContextFlags & CONTEXT_INTEGER_64)
|
if (context.ContextFlags & CONTEXT_INTEGER_64)
|
||||||
{
|
{
|
||||||
context.Rax = emu.reg(x64_register::rax);
|
context.Rax = emu.reg(x64_register::rax);
|
||||||
context.Rbx = emu.reg(x64_register::rbx);
|
context.Rbx = emu.reg(x64_register::rbx);
|
||||||
context.Rcx = emu.reg(x64_register::rcx);
|
context.Rcx = emu.reg(x64_register::rcx);
|
||||||
context.Rdx = emu.reg(x64_register::rdx);
|
context.Rdx = emu.reg(x64_register::rdx);
|
||||||
context.Rbp = emu.reg(x64_register::rbp);
|
context.Rbp = emu.reg(x64_register::rbp);
|
||||||
context.Rsi = emu.reg(x64_register::rsi);
|
context.Rsi = emu.reg(x64_register::rsi);
|
||||||
context.Rdi = emu.reg(x64_register::rdi);
|
context.Rdi = emu.reg(x64_register::rdi);
|
||||||
context.R8 = emu.reg(x64_register::r8);
|
context.R8 = emu.reg(x64_register::r8);
|
||||||
context.R9 = emu.reg(x64_register::r9);
|
context.R9 = emu.reg(x64_register::r9);
|
||||||
context.R10 = emu.reg(x64_register::r10);
|
context.R10 = emu.reg(x64_register::r10);
|
||||||
context.R11 = emu.reg(x64_register::r11);
|
context.R11 = emu.reg(x64_register::r11);
|
||||||
context.R12 = emu.reg(x64_register::r12);
|
context.R12 = emu.reg(x64_register::r12);
|
||||||
context.R13 = emu.reg(x64_register::r13);
|
context.R13 = emu.reg(x64_register::r13);
|
||||||
context.R14 = emu.reg(x64_register::r14);
|
context.R14 = emu.reg(x64_register::r14);
|
||||||
context.R15 = emu.reg(x64_register::r15);
|
context.R15 = emu.reg(x64_register::r15);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.ContextFlags & CONTEXT_SEGMENTS_64)
|
if (context.ContextFlags & CONTEXT_SEGMENTS_64)
|
||||||
{
|
{
|
||||||
context.SegDs = emu.reg<uint16_t>(x64_register::ds);
|
context.SegDs = emu.reg<uint16_t>(x64_register::ds);
|
||||||
context.SegEs = emu.reg<uint16_t>(x64_register::es);
|
context.SegEs = emu.reg<uint16_t>(x64_register::es);
|
||||||
context.SegFs = emu.reg<uint16_t>(x64_register::fs);
|
context.SegFs = emu.reg<uint16_t>(x64_register::fs);
|
||||||
context.SegGs = emu.reg<uint16_t>(x64_register::gs);
|
context.SegGs = emu.reg<uint16_t>(x64_register::gs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.ContextFlags & CONTEXT_FLOATING_POINT_64)
|
if (context.ContextFlags & CONTEXT_FLOATING_POINT_64)
|
||||||
{
|
{
|
||||||
context.FltSave.ControlWord = emu.reg<uint16_t>(x64_register::fpcw);
|
context.FltSave.ControlWord = emu.reg<uint16_t>(x64_register::fpcw);
|
||||||
context.FltSave.StatusWord = emu.reg<uint16_t>(x64_register::fpsw);
|
context.FltSave.StatusWord = emu.reg<uint16_t>(x64_register::fpsw);
|
||||||
context.FltSave.TagWord = static_cast<BYTE>(emu.reg<uint16_t>(x64_register::fptag));
|
context.FltSave.TagWord = static_cast<BYTE>(emu.reg<uint16_t>(x64_register::fptag));
|
||||||
for (int i = 0; i < 8; i++)
|
for (int i = 0; i < 8; i++)
|
||||||
{
|
{
|
||||||
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::st0) + i);
|
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::st0) + i);
|
||||||
context.FltSave.FloatRegisters[i] = emu.reg<M128A>(reg);
|
context.FltSave.FloatRegisters[i] = emu.reg<M128A>(reg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.ContextFlags & CONTEXT_XSTATE_64)
|
if (context.ContextFlags & CONTEXT_XSTATE_64)
|
||||||
{
|
{
|
||||||
context.MxCsr = emu.reg<uint32_t>(x64_register::mxcsr);
|
context.MxCsr = emu.reg<uint32_t>(x64_register::mxcsr);
|
||||||
for (int i = 0; i < 16; i++)
|
for (int i = 0; i < 16; i++)
|
||||||
{
|
{
|
||||||
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::xmm0) + i);
|
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::xmm0) + i);
|
||||||
(&context.Xmm0)[i] = emu.reg<M128A>(reg);
|
(&context.Xmm0)[i] = emu.reg<M128A>(reg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
|
|
||||||
namespace context_frame
|
namespace context_frame
|
||||||
{
|
{
|
||||||
void save(x64_emulator& emu, CONTEXT64& context);
|
void save(x64_emulator& emu, CONTEXT64& context);
|
||||||
void restore(x64_emulator& emu, const CONTEXT64& context);
|
void restore(x64_emulator& emu, const CONTEXT64& context);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,137 +3,134 @@
|
|||||||
|
|
||||||
#include <utils/finally.hpp>
|
#include <utils/finally.hpp>
|
||||||
|
|
||||||
extern "C" {
|
extern "C"
|
||||||
|
{
|
||||||
#include <gdbstub.h>
|
#include <gdbstub.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
gdb_action_t map_gdb_action(const gdb_action action)
|
gdb_action_t map_gdb_action(const gdb_action action)
|
||||||
{
|
{
|
||||||
switch (action)
|
switch (action)
|
||||||
{
|
{
|
||||||
case gdb_action::none:
|
case gdb_action::none:
|
||||||
return ACT_NONE;
|
return ACT_NONE;
|
||||||
case gdb_action::resume:
|
case gdb_action::resume:
|
||||||
return ACT_RESUME;
|
return ACT_RESUME;
|
||||||
case gdb_action::shutdown:
|
case gdb_action::shutdown:
|
||||||
return ACT_SHUTDOWN;
|
return ACT_SHUTDOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error("Bad action");
|
throw std::runtime_error("Bad action");
|
||||||
}
|
}
|
||||||
|
|
||||||
breakpoint_type map_breakpoint_type(const bp_type_t type)
|
breakpoint_type map_breakpoint_type(const bp_type_t type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case BP_SOFTWARE:
|
case BP_SOFTWARE:
|
||||||
return breakpoint_type::software;
|
return breakpoint_type::software;
|
||||||
case BP_HARDWARE_EXEC:
|
case BP_HARDWARE_EXEC:
|
||||||
return breakpoint_type::hardware_exec;
|
return breakpoint_type::hardware_exec;
|
||||||
case BP_HARDWARE_WRITE:
|
case BP_HARDWARE_WRITE:
|
||||||
return breakpoint_type::hardware_write;
|
return breakpoint_type::hardware_write;
|
||||||
case BP_HARDWARE_READ:
|
case BP_HARDWARE_READ:
|
||||||
return breakpoint_type::hardware_read;
|
return breakpoint_type::hardware_read;
|
||||||
case BP_HARDWARE_READ_WRITE:
|
case BP_HARDWARE_READ_WRITE:
|
||||||
return breakpoint_type::hardware_read_write;
|
return breakpoint_type::hardware_read_write;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error("Bad breakpoint type");
|
throw std::runtime_error("Bad breakpoint type");
|
||||||
}
|
}
|
||||||
|
|
||||||
gdb_stub_handler& get_handler(void* args)
|
gdb_stub_handler& get_handler(void* args)
|
||||||
{
|
{
|
||||||
return *static_cast<gdb_stub_handler*>(args);
|
return *static_cast<gdb_stub_handler*>(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
gdb_action_t cont(void* args)
|
gdb_action_t cont(void* args)
|
||||||
{
|
{
|
||||||
return map_gdb_action(get_handler(args).cont());
|
return map_gdb_action(get_handler(args).cont());
|
||||||
}
|
}
|
||||||
|
|
||||||
gdb_action_t stepi(void* args)
|
gdb_action_t stepi(void* args)
|
||||||
{
|
{
|
||||||
return map_gdb_action(get_handler(args).stepi());
|
return map_gdb_action(get_handler(args).stepi());
|
||||||
}
|
}
|
||||||
|
|
||||||
int read_reg(void* args, const int regno, size_t* value)
|
int read_reg(void* args, const int regno, size_t* value)
|
||||||
{
|
{
|
||||||
return get_handler(args).read_reg(regno, value) ? 0 : 1;
|
return get_handler(args).read_reg(regno, value) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int write_reg(void* args, const int regno, const size_t value)
|
int write_reg(void* args, const int regno, const size_t value)
|
||||||
{
|
{
|
||||||
return get_handler(args).write_reg(regno, value) ? 0 : 1;
|
return get_handler(args).write_reg(regno, value) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int read_mem(void* args, const size_t addr, const size_t len, void* val)
|
int read_mem(void* args, const size_t addr, const size_t len, void* val)
|
||||||
{
|
{
|
||||||
return get_handler(args).read_mem(addr, len, val) ? 0 : 1;
|
return get_handler(args).read_mem(addr, len, val) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int write_mem(void* args, const size_t addr, const size_t len, void* val)
|
int write_mem(void* args, const size_t addr, const size_t len, void* val)
|
||||||
{
|
{
|
||||||
return get_handler(args).write_mem(addr, len, val) ? 0 : 1;
|
return get_handler(args).write_mem(addr, len, val) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool set_bp(void* args, const size_t addr, const bp_type_t type, const size_t size)
|
bool set_bp(void* args, const size_t addr, const bp_type_t type, const size_t size)
|
||||||
{
|
{
|
||||||
return get_handler(args).set_bp(map_breakpoint_type(type), addr, size);
|
return get_handler(args).set_bp(map_breakpoint_type(type), addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool del_bp(void* args, const size_t addr, const bp_type_t type, const size_t size)
|
bool del_bp(void* args, const size_t addr, const bp_type_t type, const size_t size)
|
||||||
{
|
{
|
||||||
return get_handler(args).del_bp(map_breakpoint_type(type), addr, size);
|
return get_handler(args).del_bp(map_breakpoint_type(type), addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_interrupt(void* args)
|
void on_interrupt(void* args)
|
||||||
{
|
{
|
||||||
get_handler(args).on_interrupt();
|
get_handler(args).on_interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
target_ops get_target_ops()
|
target_ops get_target_ops()
|
||||||
{
|
{
|
||||||
target_ops ops{};
|
target_ops ops{};
|
||||||
|
|
||||||
ops.cont = cont;
|
ops.cont = cont;
|
||||||
ops.stepi = stepi;
|
ops.stepi = stepi;
|
||||||
ops.read_reg = read_reg;
|
ops.read_reg = read_reg;
|
||||||
ops.write_reg = write_reg;
|
ops.write_reg = write_reg;
|
||||||
ops.read_mem = read_mem;
|
ops.read_mem = read_mem;
|
||||||
ops.write_mem = write_mem;
|
ops.write_mem = write_mem;
|
||||||
ops.set_bp = set_bp;
|
ops.set_bp = set_bp;
|
||||||
ops.del_bp = del_bp;
|
ops.del_bp = del_bp;
|
||||||
ops.on_interrupt = on_interrupt;
|
ops.on_interrupt = on_interrupt;
|
||||||
|
|
||||||
return ops;
|
return ops;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool run_gdb_stub(gdb_stub_handler& handler, std::string target_description, const size_t register_count,
|
bool run_gdb_stub(gdb_stub_handler& handler, std::string target_description, const size_t register_count,
|
||||||
std::string bind_address)
|
std::string bind_address)
|
||||||
{
|
{
|
||||||
const arch_info_t info
|
const arch_info_t info{
|
||||||
{
|
target_description.data(),
|
||||||
target_description.data(),
|
static_cast<int>(register_count),
|
||||||
static_cast<int>(register_count),
|
sizeof(uint64_t),
|
||||||
sizeof(uint64_t),
|
};
|
||||||
};
|
|
||||||
|
|
||||||
auto ops = get_target_ops();
|
auto ops = get_target_ops();
|
||||||
|
|
||||||
gdbstub_t stub{};
|
gdbstub_t stub{};
|
||||||
|
|
||||||
if (!gdbstub_init(&stub, &ops, info, bind_address.data()))
|
if (!gdbstub_init(&stub, &ops, info, bind_address.data()))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto _ = utils::finally([&]
|
const auto _ = utils::finally([&] { gdbstub_close(&stub); });
|
||||||
{
|
|
||||||
gdbstub_close(&stub);
|
|
||||||
});
|
|
||||||
|
|
||||||
return gdbstub_run(&stub, &handler);
|
return gdbstub_run(&stub, &handler);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,37 +2,37 @@
|
|||||||
|
|
||||||
enum class gdb_action : uint8_t
|
enum class gdb_action : uint8_t
|
||||||
{
|
{
|
||||||
none,
|
none,
|
||||||
resume,
|
resume,
|
||||||
shutdown,
|
shutdown,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class breakpoint_type : uint8_t
|
enum class breakpoint_type : uint8_t
|
||||||
{
|
{
|
||||||
software,
|
software,
|
||||||
hardware_exec,
|
hardware_exec,
|
||||||
hardware_write,
|
hardware_write,
|
||||||
hardware_read,
|
hardware_read,
|
||||||
hardware_read_write,
|
hardware_read_write,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct gdb_stub_handler
|
struct gdb_stub_handler
|
||||||
{
|
{
|
||||||
virtual ~gdb_stub_handler() = default;
|
virtual ~gdb_stub_handler() = default;
|
||||||
|
|
||||||
virtual gdb_action cont() = 0;
|
virtual gdb_action cont() = 0;
|
||||||
virtual gdb_action stepi() = 0;
|
virtual gdb_action stepi() = 0;
|
||||||
|
|
||||||
virtual bool read_reg(int regno, size_t* value) = 0;
|
virtual bool read_reg(int regno, size_t* value) = 0;
|
||||||
virtual bool write_reg(int regno, size_t value) = 0;
|
virtual bool write_reg(int regno, size_t value) = 0;
|
||||||
|
|
||||||
virtual bool read_mem(size_t addr, size_t len, void* val) = 0;
|
virtual bool read_mem(size_t addr, size_t len, void* val) = 0;
|
||||||
virtual bool write_mem(size_t addr, size_t len, void* val) = 0;
|
virtual bool write_mem(size_t addr, size_t len, void* val) = 0;
|
||||||
|
|
||||||
virtual bool set_bp(breakpoint_type type, size_t addr, size_t size) = 0;
|
virtual bool set_bp(breakpoint_type type, size_t addr, size_t size) = 0;
|
||||||
virtual bool del_bp(breakpoint_type type, size_t addr, size_t size) = 0;
|
virtual bool del_bp(breakpoint_type type, size_t addr, size_t size) = 0;
|
||||||
|
|
||||||
virtual void on_interrupt() = 0;
|
virtual void on_interrupt() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool run_gdb_stub(gdb_stub_handler& handler, std::string target_description, size_t register_count,
|
bool run_gdb_stub(gdb_stub_handler& handler, std::string target_description, size_t register_count,
|
||||||
|
|||||||
@@ -5,41 +5,41 @@
|
|||||||
|
|
||||||
class win_x64_gdb_stub_handler : public x64_gdb_stub_handler
|
class win_x64_gdb_stub_handler : public x64_gdb_stub_handler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
win_x64_gdb_stub_handler(windows_emulator& win_emu)
|
win_x64_gdb_stub_handler(windows_emulator& win_emu)
|
||||||
: x64_gdb_stub_handler(win_emu.emu())
|
: x64_gdb_stub_handler(win_emu.emu()),
|
||||||
, win_emu_(&win_emu)
|
win_emu_(&win_emu)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
gdb_action cont() override
|
gdb_action cont() override
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this->win_emu_->start();
|
this->win_emu_->start();
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
puts(e.what());
|
puts(e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
return gdb_action::resume;
|
return gdb_action::resume;
|
||||||
}
|
}
|
||||||
|
|
||||||
gdb_action stepi() override
|
gdb_action stepi() override
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this->win_emu_->start({}, 1);
|
this->win_emu_->start({}, 1);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
puts(e.what());
|
puts(e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
return gdb_action::resume;
|
return gdb_action::resume;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
windows_emulator* win_emu_{};
|
windows_emulator* win_emu_{};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,212 +4,194 @@
|
|||||||
#include "scoped_hook.hpp"
|
#include "scoped_hook.hpp"
|
||||||
|
|
||||||
inline std::vector gdb_registers{
|
inline std::vector gdb_registers{
|
||||||
x64_register::rax,
|
x64_register::rax, x64_register::rbx, x64_register::rcx, x64_register::rdx, x64_register::rsi, x64_register::rdi,
|
||||||
x64_register::rbx,
|
x64_register::rbp, x64_register::rsp, x64_register::r8, x64_register::r9, x64_register::r10, x64_register::r11,
|
||||||
x64_register::rcx,
|
x64_register::r12, x64_register::r13, x64_register::r14, x64_register::r15, x64_register::rip, x64_register::rflags,
|
||||||
x64_register::rdx,
|
/*x64_register::cs,
|
||||||
x64_register::rsi,
|
x64_register::ss,
|
||||||
x64_register::rdi,
|
x64_register::ds,
|
||||||
x64_register::rbp,
|
x64_register::es,
|
||||||
x64_register::rsp,
|
x64_register::fs,
|
||||||
x64_register::r8,
|
x64_register::gs,*/
|
||||||
x64_register::r9,
|
|
||||||
x64_register::r10,
|
|
||||||
x64_register::r11,
|
|
||||||
x64_register::r12,
|
|
||||||
x64_register::r13,
|
|
||||||
x64_register::r14,
|
|
||||||
x64_register::r15,
|
|
||||||
x64_register::rip,
|
|
||||||
x64_register::rflags,
|
|
||||||
/*x64_register::cs,
|
|
||||||
x64_register::ss,
|
|
||||||
x64_register::ds,
|
|
||||||
x64_register::es,
|
|
||||||
x64_register::fs,
|
|
||||||
x64_register::gs,*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline memory_operation map_breakpoint_type(const breakpoint_type type)
|
inline memory_operation map_breakpoint_type(const breakpoint_type type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case breakpoint_type::software:
|
case breakpoint_type::software:
|
||||||
case breakpoint_type::hardware_exec:
|
case breakpoint_type::hardware_exec:
|
||||||
return memory_operation::exec;
|
return memory_operation::exec;
|
||||||
case breakpoint_type::hardware_read:
|
case breakpoint_type::hardware_read:
|
||||||
return memory_permission::read;
|
return memory_permission::read;
|
||||||
case breakpoint_type::hardware_write:
|
case breakpoint_type::hardware_write:
|
||||||
return memory_permission::write;
|
return memory_permission::write;
|
||||||
case breakpoint_type::hardware_read_write:
|
case breakpoint_type::hardware_read_write:
|
||||||
return memory_permission::read_write;
|
return memory_permission::read_write;
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Bad bp type");
|
throw std::runtime_error("Bad bp type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct breakpoint_key
|
struct breakpoint_key
|
||||||
{
|
{
|
||||||
size_t addr{};
|
size_t addr{};
|
||||||
size_t size{};
|
size_t size{};
|
||||||
breakpoint_type type{};
|
breakpoint_type type{};
|
||||||
|
|
||||||
bool operator==(const breakpoint_key& other) const
|
bool operator==(const breakpoint_key& other) const
|
||||||
{
|
{
|
||||||
return this->addr == other.addr && this->size == other.size && this->type == other.type;
|
return this->addr == other.addr && this->size == other.size && this->type == other.type;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct std::hash<breakpoint_key>
|
struct std::hash<breakpoint_key>
|
||||||
{
|
{
|
||||||
std::size_t operator()(const breakpoint_key& k) const noexcept
|
std::size_t operator()(const breakpoint_key& k) const noexcept
|
||||||
{
|
{
|
||||||
return ((std::hash<size_t>()(k.addr)
|
return ((std::hash<size_t>()(k.addr) ^ (std::hash<size_t>()(k.size) << 1)) >> 1) ^
|
||||||
^ (std::hash<size_t>()(k.size) << 1)) >> 1)
|
(std::hash<size_t>()(static_cast<size_t>(k.type)) << 1);
|
||||||
^ (std::hash<size_t>()(static_cast<size_t>(k.type)) << 1);
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class x64_gdb_stub_handler : public gdb_stub_handler
|
class x64_gdb_stub_handler : public gdb_stub_handler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
x64_gdb_stub_handler(x64_emulator& emu)
|
x64_gdb_stub_handler(x64_emulator& emu)
|
||||||
: emu_(&emu)
|
: emu_(&emu)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
~x64_gdb_stub_handler() override = default;
|
~x64_gdb_stub_handler() override = default;
|
||||||
|
|
||||||
gdb_action cont() override
|
gdb_action cont() override
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this->emu_->start_from_ip();
|
this->emu_->start_from_ip();
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
puts(e.what());
|
puts(e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
return gdb_action::resume;
|
return gdb_action::resume;
|
||||||
}
|
}
|
||||||
|
|
||||||
gdb_action stepi() override
|
gdb_action stepi() override
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this->emu_->start_from_ip({}, 1);
|
this->emu_->start_from_ip({}, 1);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
puts(e.what());
|
puts(e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
return gdb_action::resume;
|
return gdb_action::resume;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool read_reg(const int regno, size_t* value) override
|
bool read_reg(const int regno, size_t* value) override
|
||||||
{
|
{
|
||||||
*value = 0;
|
*value = 0;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (static_cast<size_t>(regno) >= gdb_registers.size())
|
if (static_cast<size_t>(regno) >= gdb_registers.size())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->emu_->read_register(gdb_registers[regno], value, sizeof(*value));
|
this->emu_->read_register(gdb_registers[regno], value, sizeof(*value));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool write_reg(const int regno, const size_t value) override
|
bool write_reg(const int regno, const size_t value) override
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (static_cast<size_t>(regno) >= gdb_registers.size())
|
if (static_cast<size_t>(regno) >= gdb_registers.size())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->emu_->write_register(gdb_registers[regno], &value, sizeof(value));
|
this->emu_->write_register(gdb_registers[regno], &value, sizeof(value));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool read_mem(const size_t addr, const size_t len, void* val) override
|
bool read_mem(const size_t addr, const size_t len, void* val) override
|
||||||
{
|
{
|
||||||
return this->emu_->try_read_memory(addr, val, len);
|
return this->emu_->try_read_memory(addr, val, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool write_mem(const size_t addr, const size_t len, void* val) override
|
bool write_mem(const size_t addr, const size_t len, void* val) override
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this->emu_->write_memory(addr, val, len);
|
this->emu_->write_memory(addr, val, len);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool set_bp(const breakpoint_type type, const size_t addr, const size_t size) override
|
bool set_bp(const breakpoint_type type, const size_t addr, const size_t size) override
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this->hooks_[{addr, size, type}] = scoped_hook(*this->emu_, this->emu_->hook_memory_access(
|
this->hooks_[{addr, size, type}] = scoped_hook(
|
||||||
addr, size, map_breakpoint_type(type),
|
*this->emu_, this->emu_->hook_memory_access(
|
||||||
[this](uint64_t, size_t, uint64_t, memory_operation)
|
addr, size, map_breakpoint_type(type),
|
||||||
{
|
[this](uint64_t, size_t, uint64_t, memory_operation) { this->on_interrupt(); }));
|
||||||
this->on_interrupt();
|
|
||||||
}));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool del_bp(const breakpoint_type type, const size_t addr, const size_t size) override
|
bool del_bp(const breakpoint_type type, const size_t addr, const size_t size) override
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const auto entry = this->hooks_.find({addr, size, type});
|
const auto entry = this->hooks_.find({addr, size, type});
|
||||||
if (entry == this->hooks_.end())
|
if (entry == this->hooks_.end())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->hooks_.erase(entry);
|
this->hooks_.erase(entry);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_interrupt() override
|
void on_interrupt() override
|
||||||
{
|
{
|
||||||
this->emu_->stop();
|
this->emu_->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
x64_emulator* emu_{};
|
x64_emulator* emu_{};
|
||||||
std::unordered_map<breakpoint_key, scoped_hook> hooks_{};
|
std::unordered_map<breakpoint_key, scoped_hook> hooks_{};
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -7,88 +7,88 @@ typedef LONG TDI_STATUS;
|
|||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
struct TDI_CONNECTION_INFORMATION
|
struct TDI_CONNECTION_INFORMATION
|
||||||
{
|
{
|
||||||
LONG UserDataLength;
|
LONG UserDataLength;
|
||||||
typename Traits::PVOID UserData;
|
typename Traits::PVOID UserData;
|
||||||
LONG OptionsLength;
|
LONG OptionsLength;
|
||||||
typename Traits::PVOID Options;
|
typename Traits::PVOID Options;
|
||||||
LONG RemoteAddressLength;
|
LONG RemoteAddressLength;
|
||||||
typename Traits::PVOID RemoteAddress;
|
typename Traits::PVOID RemoteAddress;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
struct TDI_REQUEST
|
struct TDI_REQUEST
|
||||||
{
|
{
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
typename Traits::HANDLE AddressHandle;
|
typename Traits::HANDLE AddressHandle;
|
||||||
EMULATOR_CAST(typename Traits::PVOID, CONNECTION_CONTEXT) ConnectionContext;
|
EMULATOR_CAST(typename Traits::PVOID, CONNECTION_CONTEXT) ConnectionContext;
|
||||||
typename Traits::HANDLE ControlChannel;
|
typename Traits::HANDLE ControlChannel;
|
||||||
} Handle;
|
} Handle;
|
||||||
|
|
||||||
typename Traits::PVOID RequestNotifyObject;
|
typename Traits::PVOID RequestNotifyObject;
|
||||||
typename Traits::PVOID RequestContext;
|
typename Traits::PVOID RequestContext;
|
||||||
TDI_STATUS TdiStatus;
|
TDI_STATUS TdiStatus;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
struct TDI_REQUEST_SEND_DATAGRAM
|
struct TDI_REQUEST_SEND_DATAGRAM
|
||||||
{
|
{
|
||||||
TDI_REQUEST<Traits> Request;
|
TDI_REQUEST<Traits> Request;
|
||||||
EMULATOR_CAST(typename Traits::PVOID, PTDI_CONNECTION_INFORMATION) SendDatagramInformation;
|
EMULATOR_CAST(typename Traits::PVOID, PTDI_CONNECTION_INFORMATION) SendDatagramInformation;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
struct AFD_SEND_INFO
|
struct AFD_SEND_INFO
|
||||||
{
|
{
|
||||||
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
|
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
|
||||||
ULONG BufferCount;
|
ULONG BufferCount;
|
||||||
ULONG AfdFlags;
|
ULONG AfdFlags;
|
||||||
ULONG TdiFlags;
|
ULONG TdiFlags;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
struct AFD_SEND_DATAGRAM_INFO
|
struct AFD_SEND_DATAGRAM_INFO
|
||||||
{
|
{
|
||||||
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
|
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
|
||||||
ULONG BufferCount;
|
ULONG BufferCount;
|
||||||
ULONG AfdFlags;
|
ULONG AfdFlags;
|
||||||
TDI_REQUEST_SEND_DATAGRAM<Traits> TdiRequest;
|
TDI_REQUEST_SEND_DATAGRAM<Traits> TdiRequest;
|
||||||
TDI_CONNECTION_INFORMATION<Traits> TdiConnInfo;
|
TDI_CONNECTION_INFORMATION<Traits> TdiConnInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
struct AFD_RECV_INFO
|
struct AFD_RECV_INFO
|
||||||
{
|
{
|
||||||
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
|
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
|
||||||
ULONG BufferCount;
|
ULONG BufferCount;
|
||||||
ULONG AfdFlags;
|
ULONG AfdFlags;
|
||||||
ULONG TdiFlags;
|
ULONG TdiFlags;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
struct AFD_RECV_DATAGRAM_INFO
|
struct AFD_RECV_DATAGRAM_INFO
|
||||||
{
|
{
|
||||||
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
|
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
|
||||||
ULONG BufferCount;
|
ULONG BufferCount;
|
||||||
ULONG AfdFlags;
|
ULONG AfdFlags;
|
||||||
ULONG TdiFlags;
|
ULONG TdiFlags;
|
||||||
typename Traits::PVOID Address;
|
typename Traits::PVOID Address;
|
||||||
EMULATOR_CAST(typename Traits::PVOID, PULONG) AddressLength;
|
EMULATOR_CAST(typename Traits::PVOID, PULONG) AddressLength;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AFD_POLL_HANDLE_INFO64
|
struct AFD_POLL_HANDLE_INFO64
|
||||||
{
|
{
|
||||||
EmulatorTraits<Emu64>::HANDLE Handle;
|
EmulatorTraits<Emu64>::HANDLE Handle;
|
||||||
ULONG PollEvents;
|
ULONG PollEvents;
|
||||||
NTSTATUS Status;
|
NTSTATUS Status;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AFD_POLL_INFO64
|
struct AFD_POLL_INFO64
|
||||||
{
|
{
|
||||||
LARGE_INTEGER Timeout;
|
LARGE_INTEGER Timeout;
|
||||||
ULONG NumberOfHandles;
|
ULONG NumberOfHandles;
|
||||||
BOOLEAN Unique;
|
BOOLEAN Unique;
|
||||||
AFD_POLL_HANDLE_INFO64 Handles[1];
|
AFD_POLL_HANDLE_INFO64 Handles[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
#define AFD_POLL_RECEIVE_BIT 0
|
#define AFD_POLL_RECEIVE_BIT 0
|
||||||
@@ -117,59 +117,57 @@ struct AFD_POLL_INFO64
|
|||||||
#define AFD_NUM_POLL_EVENTS 11
|
#define AFD_NUM_POLL_EVENTS 11
|
||||||
#define AFD_POLL_ALL ((1 << AFD_NUM_POLL_EVENTS) - 1)
|
#define AFD_POLL_ALL ((1 << AFD_NUM_POLL_EVENTS) - 1)
|
||||||
|
|
||||||
#define _AFD_REQUEST(ioctl) \
|
#define _AFD_REQUEST(ioctl) ((((ULONG)(ioctl)) >> 2) & 0x03FF)
|
||||||
((((ULONG)(ioctl)) >> 2) & 0x03FF)
|
#define _AFD_BASE(ioctl) ((((ULONG)(ioctl)) >> 12) & 0xFFFFF)
|
||||||
#define _AFD_BASE(ioctl) \
|
|
||||||
((((ULONG)(ioctl)) >> 12) & 0xFFFFF)
|
|
||||||
|
|
||||||
#define FSCTL_AFD_BASE FILE_DEVICE_NETWORK
|
#define FSCTL_AFD_BASE FILE_DEVICE_NETWORK
|
||||||
|
|
||||||
#define AFD_BIND 0
|
#define AFD_BIND 0
|
||||||
#define AFD_CONNECT 1
|
#define AFD_CONNECT 1
|
||||||
#define AFD_START_LISTEN 2
|
#define AFD_START_LISTEN 2
|
||||||
#define AFD_WAIT_FOR_LISTEN 3
|
#define AFD_WAIT_FOR_LISTEN 3
|
||||||
#define AFD_ACCEPT 4
|
#define AFD_ACCEPT 4
|
||||||
#define AFD_RECEIVE 5
|
#define AFD_RECEIVE 5
|
||||||
#define AFD_RECEIVE_DATAGRAM 6
|
#define AFD_RECEIVE_DATAGRAM 6
|
||||||
#define AFD_SEND 7
|
#define AFD_SEND 7
|
||||||
#define AFD_SEND_DATAGRAM 8
|
#define AFD_SEND_DATAGRAM 8
|
||||||
#define AFD_POLL 9
|
#define AFD_POLL 9
|
||||||
#define AFD_PARTIAL_DISCONNECT 10
|
#define AFD_PARTIAL_DISCONNECT 10
|
||||||
|
|
||||||
#define AFD_GET_ADDRESS 11
|
#define AFD_GET_ADDRESS 11
|
||||||
#define AFD_QUERY_RECEIVE_INFO 12
|
#define AFD_QUERY_RECEIVE_INFO 12
|
||||||
#define AFD_QUERY_HANDLES 13
|
#define AFD_QUERY_HANDLES 13
|
||||||
#define AFD_SET_INFORMATION 14
|
#define AFD_SET_INFORMATION 14
|
||||||
#define AFD_GET_CONTEXT_LENGTH 15
|
#define AFD_GET_CONTEXT_LENGTH 15
|
||||||
#define AFD_GET_CONTEXT 16
|
#define AFD_GET_CONTEXT 16
|
||||||
#define AFD_SET_CONTEXT 17
|
#define AFD_SET_CONTEXT 17
|
||||||
|
|
||||||
#define AFD_SET_CONNECT_DATA 18
|
#define AFD_SET_CONNECT_DATA 18
|
||||||
#define AFD_SET_CONNECT_OPTIONS 19
|
#define AFD_SET_CONNECT_OPTIONS 19
|
||||||
#define AFD_SET_DISCONNECT_DATA 20
|
#define AFD_SET_DISCONNECT_DATA 20
|
||||||
#define AFD_SET_DISCONNECT_OPTIONS 21
|
#define AFD_SET_DISCONNECT_OPTIONS 21
|
||||||
|
|
||||||
#define AFD_GET_CONNECT_DATA 22
|
#define AFD_GET_CONNECT_DATA 22
|
||||||
#define AFD_GET_CONNECT_OPTIONS 23
|
#define AFD_GET_CONNECT_OPTIONS 23
|
||||||
#define AFD_GET_DISCONNECT_DATA 24
|
#define AFD_GET_DISCONNECT_DATA 24
|
||||||
#define AFD_GET_DISCONNECT_OPTIONS 25
|
#define AFD_GET_DISCONNECT_OPTIONS 25
|
||||||
|
|
||||||
#define AFD_SIZE_CONNECT_DATA 26
|
#define AFD_SIZE_CONNECT_DATA 26
|
||||||
#define AFD_SIZE_CONNECT_OPTIONS 27
|
#define AFD_SIZE_CONNECT_OPTIONS 27
|
||||||
#define AFD_SIZE_DISCONNECT_DATA 28
|
#define AFD_SIZE_DISCONNECT_DATA 28
|
||||||
#define AFD_SIZE_DISCONNECT_OPTIONS 29
|
#define AFD_SIZE_DISCONNECT_OPTIONS 29
|
||||||
|
|
||||||
#define AFD_GET_INFORMATION 30
|
#define AFD_GET_INFORMATION 30
|
||||||
#define AFD_TRANSMIT_FILE 31
|
#define AFD_TRANSMIT_FILE 31
|
||||||
#define AFD_SUPER_ACCEPT 32
|
#define AFD_SUPER_ACCEPT 32
|
||||||
|
|
||||||
#define AFD_EVENT_SELECT 33
|
#define AFD_EVENT_SELECT 33
|
||||||
#define AFD_ENUM_NETWORK_EVENTS 34
|
#define AFD_ENUM_NETWORK_EVENTS 34
|
||||||
|
|
||||||
#define AFD_DEFER_ACCEPT 35
|
#define AFD_DEFER_ACCEPT 35
|
||||||
#define AFD_WAIT_FOR_LISTEN_LIFO 36
|
#define AFD_WAIT_FOR_LISTEN_LIFO 36
|
||||||
#define AFD_SET_QOS 37
|
#define AFD_SET_QOS 37
|
||||||
#define AFD_GET_QOS 38
|
#define AFD_GET_QOS 38
|
||||||
#define AFD_NO_OPERATION 39
|
#define AFD_NO_OPERATION 39
|
||||||
#define AFD_VALIDATE_GROUP 40
|
#define AFD_VALIDATE_GROUP 40
|
||||||
#define AFD_GET_UNACCEPTED_CONNECT_DATA 41
|
#define AFD_GET_UNACCEPTED_CONNECT_DATA 41
|
||||||
|
|||||||
@@ -8,31 +8,31 @@ using emulator_pointer = uint64_t;
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
class object_wrapper
|
class object_wrapper
|
||||||
{
|
{
|
||||||
T* obj_;
|
T* obj_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
object_wrapper(T& obj)
|
object_wrapper(T& obj)
|
||||||
: obj_(&obj)
|
: obj_(&obj)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
T& get() const
|
T& get() const
|
||||||
{
|
{
|
||||||
return *this->obj_;
|
return *this->obj_;
|
||||||
}
|
}
|
||||||
|
|
||||||
operator T&() const
|
operator T&() const
|
||||||
{
|
{
|
||||||
return this->get();
|
return this->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(utils::buffer_serializer&) const
|
void serialize(utils::buffer_serializer&) const
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void deserialize(utils::buffer_deserializer&)
|
void deserialize(utils::buffer_deserializer&)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class windows_emulator;
|
class windows_emulator;
|
||||||
@@ -45,256 +45,250 @@ using windows_emulator_wrapper = object_wrapper<windows_emulator>;
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
class emulator_object
|
class emulator_object
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
|
|
||||||
emulator_object(const x64_emulator_wrapper& wrapper, const uint64_t address = 0)
|
emulator_object(const x64_emulator_wrapper& wrapper, const uint64_t address = 0)
|
||||||
: emulator_object(wrapper.get(), address)
|
: emulator_object(wrapper.get(), address)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
emulator_object(emulator& emu, const uint64_t address = 0)
|
emulator_object(emulator& emu, const uint64_t address = 0)
|
||||||
: emu_(&emu)
|
: emu_(&emu),
|
||||||
, address_(address)
|
address_(address)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
emulator_object(emulator& emu, const void* address)
|
emulator_object(emulator& emu, const void* address)
|
||||||
: emulator_object(emu, reinterpret_cast<uint64_t>(address))
|
: emulator_object(emu, reinterpret_cast<uint64_t>(address))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t value() const
|
uint64_t value() const
|
||||||
{
|
{
|
||||||
return this->address_;
|
return this->address_;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr uint64_t size() const
|
constexpr uint64_t size() const
|
||||||
{
|
{
|
||||||
return sizeof(T);
|
return sizeof(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t end() const
|
uint64_t end() const
|
||||||
{
|
{
|
||||||
return this->value() + this->size();
|
return this->value() + this->size();
|
||||||
}
|
}
|
||||||
|
|
||||||
T* ptr() const
|
T* ptr() const
|
||||||
{
|
{
|
||||||
return reinterpret_cast<T*>(this->address_);
|
return reinterpret_cast<T*>(this->address_);
|
||||||
}
|
}
|
||||||
|
|
||||||
operator bool() const
|
operator bool() const
|
||||||
{
|
{
|
||||||
return this->address_ != 0;
|
return this->address_ != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
T read(const size_t index = 0) const
|
T read(const size_t index = 0) const
|
||||||
{
|
{
|
||||||
T obj{};
|
T obj{};
|
||||||
this->emu_->read_memory(this->address_ + index * this->size(), &obj, sizeof(obj));
|
this->emu_->read_memory(this->address_ + index * this->size(), &obj, sizeof(obj));
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(const T& value, const size_t index = 0) const
|
void write(const T& value, const size_t index = 0) const
|
||||||
{
|
{
|
||||||
this->emu_->write_memory(this->address_ + index * this->size(), &value, sizeof(value));
|
this->emu_->write_memory(this->address_ + index * this->size(), &value, sizeof(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_if_valid(const T& value, const size_t index = 0) const
|
void write_if_valid(const T& value, const size_t index = 0) const
|
||||||
{
|
{
|
||||||
if (this->operator bool())
|
if (this->operator bool())
|
||||||
{
|
{
|
||||||
this->write(value, index);
|
this->write(value, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void access(const F& accessor, const size_t index = 0) const
|
void access(const F& accessor, const size_t index = 0) const
|
||||||
{
|
{
|
||||||
T obj{};
|
T obj{};
|
||||||
this->emu_->read_memory(this->address_ + index * this->size(), &obj, sizeof(obj));
|
this->emu_->read_memory(this->address_ + index * this->size(), &obj, sizeof(obj));
|
||||||
|
|
||||||
accessor(obj);
|
accessor(obj);
|
||||||
|
|
||||||
this->write(obj, index);
|
this->write(obj, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(utils::buffer_serializer& buffer) const
|
void serialize(utils::buffer_serializer& buffer) const
|
||||||
{
|
{
|
||||||
buffer.write(this->address_);
|
buffer.write(this->address_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void deserialize(utils::buffer_deserializer& buffer)
|
void deserialize(utils::buffer_deserializer& buffer)
|
||||||
{
|
{
|
||||||
buffer.read(this->address_);
|
buffer.read(this->address_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_address(const uint64_t address)
|
void set_address(const uint64_t address)
|
||||||
{
|
{
|
||||||
this->address_ = address;
|
this->address_ = address;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
emulator* emu_{};
|
emulator* emu_{};
|
||||||
uint64_t address_{};
|
uint64_t address_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// TODO: warning emulator_utils is hardcoded for 64bit unicode_string usage
|
// TODO: warning emulator_utils is hardcoded for 64bit unicode_string usage
|
||||||
class emulator_allocator
|
class emulator_allocator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
emulator_allocator(emulator& emu)
|
emulator_allocator(emulator& emu)
|
||||||
: emu_(&emu)
|
: emu_(&emu)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
emulator_allocator(emulator& emu, const uint64_t address, const uint64_t size)
|
emulator_allocator(emulator& emu, const uint64_t address, const uint64_t size)
|
||||||
: emu_(&emu)
|
: emu_(&emu),
|
||||||
, address_(address)
|
address_(address),
|
||||||
, size_(size)
|
size_(size),
|
||||||
, active_address_(address)
|
active_address_(address)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t reserve(const uint64_t count, const uint64_t alignment = 1)
|
uint64_t reserve(const uint64_t count, const uint64_t alignment = 1)
|
||||||
{
|
{
|
||||||
const auto potential_start = align_up(this->active_address_, alignment);
|
const auto potential_start = align_up(this->active_address_, alignment);
|
||||||
const auto potential_end = potential_start + count;
|
const auto potential_end = potential_start + count;
|
||||||
const auto total_end = this->address_ + this->size_;
|
const auto total_end = this->address_ + this->size_;
|
||||||
|
|
||||||
if (potential_end > total_end)
|
if (potential_end > total_end)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Out of memory");
|
throw std::runtime_error("Out of memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
this->active_address_ = potential_end;
|
this->active_address_ = potential_end;
|
||||||
|
|
||||||
return potential_start;
|
return potential_start;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
emulator_object<T> reserve(const size_t count = 1)
|
emulator_object<T> reserve(const size_t count = 1)
|
||||||
{
|
{
|
||||||
const auto potential_start = this->reserve(sizeof(T) * count, alignof(T));
|
const auto potential_start = this->reserve(sizeof(T) * count, alignof(T));
|
||||||
return emulator_object<T>(*this->emu_, potential_start);
|
return emulator_object<T>(*this->emu_, potential_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char16_t* copy_string(const std::u16string_view str)
|
||||||
|
{
|
||||||
|
UNICODE_STRING<EmulatorTraits<Emu64>> uc_str{};
|
||||||
|
this->make_unicode_string(uc_str, str);
|
||||||
|
return reinterpret_cast<char16_t*>(uc_str.Buffer);
|
||||||
|
}
|
||||||
|
|
||||||
char16_t* copy_string(const std::u16string_view str)
|
void make_unicode_string(UNICODE_STRING<EmulatorTraits<Emu64>>& result, const std::u16string_view str)
|
||||||
{
|
{
|
||||||
UNICODE_STRING<EmulatorTraits<Emu64>> uc_str{};
|
constexpr auto element_size = sizeof(str[0]);
|
||||||
this->make_unicode_string(uc_str, str);
|
constexpr auto required_alignment = alignof(decltype(str[0]));
|
||||||
return reinterpret_cast<char16_t*>(uc_str.Buffer);
|
const auto total_length = str.size() * element_size;
|
||||||
}
|
|
||||||
|
|
||||||
void make_unicode_string(UNICODE_STRING<EmulatorTraits<Emu64>>& result, const std::u16string_view str)
|
const auto string_buffer = this->reserve(total_length + element_size, required_alignment);
|
||||||
{
|
|
||||||
constexpr auto element_size = sizeof(str[0]);
|
|
||||||
constexpr auto required_alignment = alignof(decltype(str[0]));
|
|
||||||
const auto total_length = str.size() * element_size;
|
|
||||||
|
|
||||||
const auto string_buffer = this->reserve(total_length + element_size, required_alignment);
|
this->emu_->write_memory(string_buffer, str.data(), total_length);
|
||||||
|
|
||||||
this->emu_->write_memory(string_buffer, str.data(), total_length);
|
constexpr std::array<char, element_size> nullbyte{};
|
||||||
|
this->emu_->write_memory(string_buffer + total_length, nullbyte.data(), nullbyte.size());
|
||||||
|
|
||||||
constexpr std::array<char, element_size> nullbyte{};
|
result.Buffer = string_buffer;
|
||||||
this->emu_->write_memory(string_buffer + total_length, nullbyte.data(), nullbyte.size());
|
result.Length = static_cast<USHORT>(total_length);
|
||||||
|
result.MaximumLength = static_cast<USHORT>(total_length + element_size);
|
||||||
|
}
|
||||||
|
|
||||||
result.Buffer = string_buffer;
|
emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> make_unicode_string(const std::u16string_view str)
|
||||||
result.Length = static_cast<USHORT>(total_length);
|
{
|
||||||
result.MaximumLength = static_cast<USHORT>(total_length + element_size);
|
const auto unicode_string = this->reserve<UNICODE_STRING<EmulatorTraits<Emu64>>>();
|
||||||
}
|
|
||||||
|
|
||||||
emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> make_unicode_string(const std::u16string_view str)
|
unicode_string.access(
|
||||||
{
|
[&](UNICODE_STRING<EmulatorTraits<Emu64>>& unicode_str) { this->make_unicode_string(unicode_str, str); });
|
||||||
const auto unicode_string = this->reserve<UNICODE_STRING<EmulatorTraits<Emu64>>>();
|
|
||||||
|
|
||||||
unicode_string.access([&](UNICODE_STRING<EmulatorTraits<Emu64>>& unicode_str)
|
return unicode_string;
|
||||||
{
|
}
|
||||||
this->make_unicode_string(unicode_str, str);
|
|
||||||
});
|
|
||||||
|
|
||||||
return unicode_string;
|
uint64_t get_base() const
|
||||||
}
|
{
|
||||||
|
return this->address_;
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t get_base() const
|
uint64_t get_size() const
|
||||||
{
|
{
|
||||||
return this->address_;
|
return this->size_;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t get_size() const
|
uint64_t get_next_address() const
|
||||||
{
|
{
|
||||||
return this->size_;
|
return this->active_address_;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t get_next_address() const
|
emulator& get_emulator() const
|
||||||
{
|
{
|
||||||
return this->active_address_;
|
return *this->emu_;
|
||||||
}
|
}
|
||||||
|
|
||||||
emulator& get_emulator() const
|
void serialize(utils::buffer_serializer& buffer) const
|
||||||
{
|
{
|
||||||
return *this->emu_;
|
buffer.write(this->address_);
|
||||||
}
|
buffer.write(this->size_);
|
||||||
|
buffer.write(this->active_address_);
|
||||||
|
}
|
||||||
|
|
||||||
void serialize(utils::buffer_serializer& buffer) const
|
void deserialize(utils::buffer_deserializer& buffer)
|
||||||
{
|
{
|
||||||
buffer.write(this->address_);
|
buffer.read(this->address_);
|
||||||
buffer.write(this->size_);
|
buffer.read(this->size_);
|
||||||
buffer.write(this->active_address_);
|
buffer.read(this->active_address_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void deserialize(utils::buffer_deserializer& buffer)
|
void release()
|
||||||
{
|
{
|
||||||
buffer.read(this->address_);
|
if (this->emu_ && this->address_ && this->size_)
|
||||||
buffer.read(this->size_);
|
{
|
||||||
buffer.read(this->active_address_);
|
this->emu_->release_memory(this->address_, this->size_);
|
||||||
}
|
this->address_ = 0;
|
||||||
|
this->size_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void release()
|
private:
|
||||||
{
|
emulator* emu_{};
|
||||||
if (this->emu_ && this->address_ && this->size_)
|
uint64_t address_{};
|
||||||
{
|
uint64_t size_{};
|
||||||
this->emu_->release_memory(this->address_, this->size_);
|
uint64_t active_address_{0};
|
||||||
this->address_ = 0;
|
|
||||||
this->size_ = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
emulator* emu_{};
|
|
||||||
uint64_t address_{};
|
|
||||||
uint64_t size_{};
|
|
||||||
uint64_t active_address_{0};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
inline std::u16string read_unicode_string(const emulator& emu, const UNICODE_STRING<EmulatorTraits<Emu64>> ucs)
|
inline std::u16string read_unicode_string(const emulator& emu, const UNICODE_STRING<EmulatorTraits<Emu64>> ucs)
|
||||||
{
|
{
|
||||||
static_assert(offsetof(UNICODE_STRING<EmulatorTraits<Emu64>>, Length) == 0);
|
static_assert(offsetof(UNICODE_STRING<EmulatorTraits<Emu64>>, Length) == 0);
|
||||||
static_assert(offsetof(UNICODE_STRING<EmulatorTraits<Emu64>>, MaximumLength) == 2);
|
static_assert(offsetof(UNICODE_STRING<EmulatorTraits<Emu64>>, MaximumLength) == 2);
|
||||||
static_assert(offsetof(UNICODE_STRING<EmulatorTraits<Emu64>>, Buffer) == 8);
|
static_assert(offsetof(UNICODE_STRING<EmulatorTraits<Emu64>>, Buffer) == 8);
|
||||||
static_assert(sizeof(UNICODE_STRING<EmulatorTraits<Emu64>>) == 16);
|
static_assert(sizeof(UNICODE_STRING<EmulatorTraits<Emu64>>) == 16);
|
||||||
|
|
||||||
std::u16string result{};
|
std::u16string result{};
|
||||||
result.resize(ucs.Length / 2);
|
result.resize(ucs.Length / 2);
|
||||||
|
|
||||||
emu.read_memory(ucs.Buffer, result.data(), ucs.Length);
|
emu.read_memory(ucs.Buffer, result.data(), ucs.Length);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline std::u16string read_unicode_string(const emulator& emu,
|
inline std::u16string read_unicode_string(const emulator& emu,
|
||||||
const emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> uc_string)
|
const emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> uc_string)
|
||||||
{
|
{
|
||||||
const auto ucs = uc_string.read();
|
const auto ucs = uc_string.read();
|
||||||
return read_unicode_string(emu, ucs);
|
return read_unicode_string(emu, ucs);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::u16string read_unicode_string(emulator& emu, const UNICODE_STRING<EmulatorTraits<Emu64>>* uc_string)
|
inline std::u16string read_unicode_string(emulator& emu, const UNICODE_STRING<EmulatorTraits<Emu64>>* uc_string)
|
||||||
{
|
{
|
||||||
return read_unicode_string(emu, emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>>{emu, uc_string});
|
return read_unicode_string(emu, emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>>{emu, uc_string});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,33 +2,33 @@
|
|||||||
|
|
||||||
struct handle_types
|
struct handle_types
|
||||||
{
|
{
|
||||||
enum type : uint16_t
|
enum type : uint16_t
|
||||||
{
|
{
|
||||||
reserved = 0,
|
reserved = 0,
|
||||||
file,
|
file,
|
||||||
device,
|
device,
|
||||||
event,
|
event,
|
||||||
section,
|
section,
|
||||||
symlink,
|
symlink,
|
||||||
directory,
|
directory,
|
||||||
semaphore,
|
semaphore,
|
||||||
port,
|
port,
|
||||||
thread,
|
thread,
|
||||||
registry,
|
registry,
|
||||||
mutant,
|
mutant,
|
||||||
token,
|
token,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(push)
|
#pragma pack(push)
|
||||||
#pragma pack(1)
|
#pragma pack(1)
|
||||||
struct handle_value
|
struct handle_value
|
||||||
{
|
{
|
||||||
uint64_t id : 32;
|
uint64_t id : 32;
|
||||||
uint64_t type : 16;
|
uint64_t type : 16;
|
||||||
uint64_t padding : 14;
|
uint64_t padding : 14;
|
||||||
uint64_t is_system : 1;
|
uint64_t is_system : 1;
|
||||||
uint64_t is_pseudo : 1;
|
uint64_t is_pseudo : 1;
|
||||||
};
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
@@ -37,315 +37,315 @@ static_assert(sizeof(handle_value) == 8);
|
|||||||
// TODO: this is a concrete 64bit handle
|
// TODO: this is a concrete 64bit handle
|
||||||
union handle
|
union handle
|
||||||
{
|
{
|
||||||
handle_value value;
|
handle_value value;
|
||||||
uint64_t bits;
|
uint64_t bits;
|
||||||
std::uint64_t h;
|
std::uint64_t h;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace utils
|
namespace utils
|
||||||
{
|
{
|
||||||
inline void serialize(buffer_serializer& buffer, const handle& h)
|
inline void serialize(buffer_serializer& buffer, const handle& h)
|
||||||
{
|
{
|
||||||
buffer.write(h.bits);
|
buffer.write(h.bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void deserialize(buffer_deserializer& buffer, handle& h)
|
inline void deserialize(buffer_deserializer& buffer, handle& h)
|
||||||
{
|
{
|
||||||
buffer.read(h.bits);
|
buffer.read(h.bits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator==(const handle& h1, const handle& h2)
|
inline bool operator==(const handle& h1, const handle& h2)
|
||||||
{
|
{
|
||||||
return h1.bits == h2.bits;
|
return h1.bits == h2.bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator==(const handle& h1, const uint64_t& h2)
|
inline bool operator==(const handle& h1, const uint64_t& h2)
|
||||||
{
|
{
|
||||||
return h1.bits == h2;
|
return h1.bits == h2;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline handle_value get_handle_value(const uint64_t h)
|
inline handle_value get_handle_value(const uint64_t h)
|
||||||
{
|
{
|
||||||
handle hh{};
|
handle hh{};
|
||||||
hh.bits = h;
|
hh.bits = h;
|
||||||
return hh.value;
|
return hh.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr handle make_handle(const uint32_t id, const handle_types::type type, const bool is_pseudo)
|
constexpr handle make_handle(const uint32_t id, const handle_types::type type, const bool is_pseudo)
|
||||||
{
|
{
|
||||||
handle_value value{};
|
handle_value value{};
|
||||||
|
|
||||||
value.padding = 0;
|
value.padding = 0;
|
||||||
value.id = id;
|
value.id = id;
|
||||||
value.type = type;
|
value.type = type;
|
||||||
value.is_system = false;
|
value.is_system = false;
|
||||||
value.is_pseudo = is_pseudo;
|
value.is_pseudo = is_pseudo;
|
||||||
|
|
||||||
return {value};
|
return {value};
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr handle make_handle(const uint64_t value)
|
constexpr handle make_handle(const uint64_t value)
|
||||||
{
|
{
|
||||||
handle h{};
|
handle h{};
|
||||||
h.bits = value;
|
h.bits = value;
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr handle make_pseudo_handle(const uint32_t id, const handle_types::type type)
|
constexpr handle make_pseudo_handle(const uint32_t id, const handle_types::type type)
|
||||||
{
|
{
|
||||||
return make_handle(id, type, true);
|
return make_handle(id, type, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace handle_detail
|
namespace handle_detail
|
||||||
{
|
{
|
||||||
template <typename, typename = void>
|
template <typename, typename = void>
|
||||||
struct has_deleter_function : std::false_type
|
struct has_deleter_function : std::false_type
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct has_deleter_function<T, std::void_t<decltype(T::deleter(std::declval<T&>()))>>
|
struct has_deleter_function<T, std::void_t<decltype(T::deleter(std::declval<T&>()))>>
|
||||||
: std::is_same<decltype(T::deleter(std::declval<T&>())), bool>
|
: std::is_same<decltype(T::deleter(std::declval<T&>())), bool>
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
struct generic_handle_store
|
struct generic_handle_store
|
||||||
{
|
{
|
||||||
virtual ~generic_handle_store() = default;
|
virtual ~generic_handle_store() = default;
|
||||||
virtual bool erase(const handle h) = 0;
|
virtual bool erase(const handle h) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <handle_types::type Type, typename T, uint32_t IndexShift = 0>
|
template <handle_types::type Type, typename T, uint32_t IndexShift = 0>
|
||||||
requires(utils::Serializable<T>)
|
requires(utils::Serializable<T>)
|
||||||
class handle_store : public generic_handle_store
|
class handle_store : public generic_handle_store
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using index_type = uint32_t;
|
using index_type = uint32_t;
|
||||||
using value_map = std::map<index_type, T>;
|
using value_map = std::map<index_type, T>;
|
||||||
|
|
||||||
bool block_mutation(bool blocked)
|
bool block_mutation(bool blocked)
|
||||||
{
|
{
|
||||||
std::swap(this->block_mutation_, blocked);
|
std::swap(this->block_mutation_, blocked);
|
||||||
return blocked;
|
return blocked;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle store(T value)
|
handle store(T value)
|
||||||
{
|
{
|
||||||
if (this->block_mutation_)
|
if (this->block_mutation_)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Mutation of handle store is blocked!");
|
throw std::runtime_error("Mutation of handle store is blocked!");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto index = this->find_free_index();
|
auto index = this->find_free_index();
|
||||||
this->store_.emplace(index, std::move(value));
|
this->store_.emplace(index, std::move(value));
|
||||||
|
|
||||||
return make_handle(index);
|
return make_handle(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
handle make_handle(const index_type index) const
|
handle make_handle(const index_type index) const
|
||||||
{
|
{
|
||||||
handle h{};
|
handle h{};
|
||||||
h.bits = 0;
|
h.bits = 0;
|
||||||
h.value.is_pseudo = false;
|
h.value.is_pseudo = false;
|
||||||
h.value.type = Type;
|
h.value.type = Type;
|
||||||
h.value.id = index << IndexShift;
|
h.value.id = index << IndexShift;
|
||||||
|
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
T* get_by_index(const uint32_t index)
|
T* get_by_index(const uint32_t index)
|
||||||
{
|
{
|
||||||
return this->get(this->make_handle(index));
|
return this->get(this->make_handle(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
T* get(const handle_value h)
|
T* get(const handle_value h)
|
||||||
{
|
{
|
||||||
const auto entry = this->get_iterator(h);
|
const auto entry = this->get_iterator(h);
|
||||||
if (entry == this->store_.end())
|
if (entry == this->store_.end())
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return &entry->second;
|
return &entry->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
T* get(const handle h)
|
T* get(const handle h)
|
||||||
{
|
{
|
||||||
return this->get(h.value);
|
return this->get(h.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
T* get(const uint64_t h)
|
T* get(const uint64_t h)
|
||||||
{
|
{
|
||||||
handle hh{};
|
handle hh{};
|
||||||
hh.bits = h;
|
hh.bits = h;
|
||||||
|
|
||||||
return this->get(hh);
|
return this->get(hh);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size() const
|
size_t size() const
|
||||||
{
|
{
|
||||||
return this->store_.size();
|
return this->store_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool erase(const typename value_map::iterator& entry)
|
bool erase(const typename value_map::iterator& entry)
|
||||||
{
|
{
|
||||||
if (this->block_mutation_)
|
if (this->block_mutation_)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Mutation of handle store is blocked!");
|
throw std::runtime_error("Mutation of handle store is blocked!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry == this->store_.end())
|
if (entry == this->store_.end())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if constexpr (handle_detail::has_deleter_function<T>())
|
if constexpr (handle_detail::has_deleter_function<T>())
|
||||||
{
|
{
|
||||||
if (!T::deleter(entry->second))
|
if (!T::deleter(entry->second))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->store_.erase(entry);
|
this->store_.erase(entry);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool erase(const handle_value h)
|
bool erase(const handle_value h)
|
||||||
{
|
{
|
||||||
const auto entry = this->get_iterator(h);
|
const auto entry = this->get_iterator(h);
|
||||||
return this->erase(entry);
|
return this->erase(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool erase(const handle h) override
|
bool erase(const handle h) override
|
||||||
{
|
{
|
||||||
return this->erase(h.value);
|
return this->erase(h.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool erase(const uint64_t h)
|
bool erase(const uint64_t h)
|
||||||
{
|
{
|
||||||
handle hh{};
|
handle hh{};
|
||||||
hh.bits = h;
|
hh.bits = h;
|
||||||
|
|
||||||
return this->erase(hh);
|
return this->erase(hh);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool erase(const T& value)
|
bool erase(const T& value)
|
||||||
{
|
{
|
||||||
const auto entry = this->find(value);
|
const auto entry = this->find(value);
|
||||||
return this->erase(entry);
|
return this->erase(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(utils::buffer_serializer& buffer) const
|
void serialize(utils::buffer_serializer& buffer) const
|
||||||
{
|
{
|
||||||
buffer.write(this->block_mutation_);
|
buffer.write(this->block_mutation_);
|
||||||
buffer.write_map(this->store_);
|
buffer.write_map(this->store_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void deserialize(utils::buffer_deserializer& buffer)
|
void deserialize(utils::buffer_deserializer& buffer)
|
||||||
{
|
{
|
||||||
buffer.read(this->block_mutation_);
|
buffer.read(this->block_mutation_);
|
||||||
buffer.read_map(this->store_);
|
buffer.read_map(this->store_);
|
||||||
}
|
}
|
||||||
|
|
||||||
typename value_map::iterator find(const T& value)
|
typename value_map::iterator find(const T& value)
|
||||||
{
|
{
|
||||||
auto i = this->store_.begin();
|
auto i = this->store_.begin();
|
||||||
for (; i != this->store_.end(); ++i)
|
for (; i != this->store_.end(); ++i)
|
||||||
{
|
{
|
||||||
if (&i->second == &value)
|
if (&i->second == &value)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
typename value_map::const_iterator find(const T& value) const
|
typename value_map::const_iterator find(const T& value) const
|
||||||
{
|
{
|
||||||
auto i = this->store_.begin();
|
auto i = this->store_.begin();
|
||||||
for (; i != this->store_.end(); ++i)
|
for (; i != this->store_.end(); ++i)
|
||||||
{
|
{
|
||||||
if (&i->second == &value)
|
if (&i->second == &value)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle find_handle(const T& value) const
|
handle find_handle(const T& value) const
|
||||||
{
|
{
|
||||||
const auto entry = this->find(value);
|
const auto entry = this->find(value);
|
||||||
if (entry == this->end())
|
if (entry == this->end())
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return this->make_handle(entry->first);
|
return this->make_handle(entry->first);
|
||||||
}
|
}
|
||||||
|
|
||||||
handle find_handle(const T* value) const
|
handle find_handle(const T* value) const
|
||||||
{
|
{
|
||||||
if (!value)
|
if (!value)
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return this->find_handle(*value);
|
return this->find_handle(*value);
|
||||||
}
|
}
|
||||||
|
|
||||||
typename value_map::iterator begin()
|
typename value_map::iterator begin()
|
||||||
{
|
{
|
||||||
return this->store_.begin();
|
return this->store_.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
typename value_map::const_iterator begin() const
|
typename value_map::const_iterator begin() const
|
||||||
{
|
{
|
||||||
return this->store_.begin();
|
return this->store_.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
typename value_map::iterator end()
|
typename value_map::iterator end()
|
||||||
{
|
{
|
||||||
return this->store_.end();
|
return this->store_.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
typename value_map::const_iterator end() const
|
typename value_map::const_iterator end() const
|
||||||
{
|
{
|
||||||
return this->store_.end();
|
return this->store_.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typename value_map::iterator get_iterator(const handle_value h)
|
typename value_map::iterator get_iterator(const handle_value h)
|
||||||
{
|
{
|
||||||
if (h.type != Type || h.is_pseudo)
|
if (h.type != Type || h.is_pseudo)
|
||||||
{
|
{
|
||||||
return this->store_.end();
|
return this->store_.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this->store_.find(static_cast<uint32_t>(h.id) >> IndexShift);
|
return this->store_.find(static_cast<uint32_t>(h.id) >> IndexShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t find_free_index()
|
uint32_t find_free_index()
|
||||||
{
|
{
|
||||||
uint32_t index = 1;
|
uint32_t index = 1;
|
||||||
for (; index > 0; ++index)
|
for (; index > 0; ++index)
|
||||||
{
|
{
|
||||||
if (!this->store_.contains(index))
|
if (!this->store_.contains(index))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool block_mutation_{false};
|
bool block_mutation_{false};
|
||||||
value_map store_{};
|
value_map store_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr auto KNOWN_DLLS_DIRECTORY = make_pseudo_handle(0x1, handle_types::directory);
|
constexpr auto KNOWN_DLLS_DIRECTORY = make_pseudo_handle(0x1, handle_types::directory);
|
||||||
|
|||||||
@@ -3,30 +3,27 @@
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
struct dummy_device : stateless_device
|
struct dummy_device : stateless_device
|
||||||
{
|
{
|
||||||
NTSTATUS io_control(windows_emulator&, const io_device_context&) override
|
NTSTATUS io_control(windows_emulator&, const io_device_context&) override
|
||||||
{
|
{
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<io_device> create_device(const std::u16string_view device)
|
std::unique_ptr<io_device> create_device(const std::u16string_view device)
|
||||||
{
|
{
|
||||||
if (device == u"CNG"
|
if (device == u"CNG" || device == u"KsecDD" || device == u"PcwDrv" || device == u"DeviceApi\\CMApi" ||
|
||||||
|| device == u"KsecDD"
|
device == u"ConDrv\\Server")
|
||||||
|| device == u"PcwDrv"
|
{
|
||||||
|| device == u"DeviceApi\\CMApi"
|
return std::make_unique<dummy_device>();
|
||||||
|| device == u"ConDrv\\Server")
|
}
|
||||||
{
|
|
||||||
return std::make_unique<dummy_device>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (device == u"Afd\\Endpoint")
|
if (device == u"Afd\\Endpoint")
|
||||||
{
|
{
|
||||||
return create_afd_endpoint();
|
return create_afd_endpoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error("Unsupported device: " + u16_to_u8(device));
|
throw std::runtime_error("Unsupported device: " + u16_to_u8(device));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,190 +12,188 @@ struct process_context;
|
|||||||
|
|
||||||
struct io_device_context
|
struct io_device_context
|
||||||
{
|
{
|
||||||
handle event{};
|
handle event{};
|
||||||
emulator_pointer /*PIO_APC_ROUTINE*/ apc_routine{};
|
emulator_pointer /*PIO_APC_ROUTINE*/ apc_routine{};
|
||||||
emulator_pointer apc_context{};
|
emulator_pointer apc_context{};
|
||||||
emulator_object<IO_STATUS_BLOCK<EmulatorTraits<Emu64>>> io_status_block;
|
emulator_object<IO_STATUS_BLOCK<EmulatorTraits<Emu64>>> io_status_block;
|
||||||
ULONG io_control_code{};
|
ULONG io_control_code{};
|
||||||
emulator_pointer input_buffer{};
|
emulator_pointer input_buffer{};
|
||||||
ULONG input_buffer_length{};
|
ULONG input_buffer_length{};
|
||||||
emulator_pointer output_buffer{};
|
emulator_pointer output_buffer{};
|
||||||
ULONG output_buffer_length{};
|
ULONG output_buffer_length{};
|
||||||
|
|
||||||
io_device_context(x64_emulator& emu)
|
io_device_context(x64_emulator& emu)
|
||||||
: io_status_block(emu)
|
: io_status_block(emu)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
io_device_context(utils::buffer_deserializer& buffer)
|
io_device_context(utils::buffer_deserializer& buffer)
|
||||||
: io_device_context(buffer.read<x64_emulator_wrapper>().get())
|
: io_device_context(buffer.read<x64_emulator_wrapper>().get())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(utils::buffer_serializer& buffer) const
|
void serialize(utils::buffer_serializer& buffer) const
|
||||||
{
|
{
|
||||||
buffer.write(event);
|
buffer.write(event);
|
||||||
buffer.write(apc_routine);
|
buffer.write(apc_routine);
|
||||||
buffer.write(apc_context);
|
buffer.write(apc_context);
|
||||||
buffer.write(io_status_block);
|
buffer.write(io_status_block);
|
||||||
buffer.write(io_control_code);
|
buffer.write(io_control_code);
|
||||||
buffer.write(input_buffer);
|
buffer.write(input_buffer);
|
||||||
buffer.write(input_buffer_length);
|
buffer.write(input_buffer_length);
|
||||||
buffer.write(output_buffer);
|
buffer.write(output_buffer);
|
||||||
buffer.write(output_buffer_length);
|
buffer.write(output_buffer_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void deserialize(utils::buffer_deserializer& buffer)
|
void deserialize(utils::buffer_deserializer& buffer)
|
||||||
{
|
{
|
||||||
buffer.read(event);
|
buffer.read(event);
|
||||||
buffer.read(apc_routine);
|
buffer.read(apc_routine);
|
||||||
buffer.read(apc_context);
|
buffer.read(apc_context);
|
||||||
buffer.read(io_status_block);
|
buffer.read(io_status_block);
|
||||||
buffer.read(io_control_code);
|
buffer.read(io_control_code);
|
||||||
buffer.read(input_buffer);
|
buffer.read(input_buffer);
|
||||||
buffer.read(input_buffer_length);
|
buffer.read(input_buffer_length);
|
||||||
buffer.read(output_buffer);
|
buffer.read(output_buffer);
|
||||||
buffer.read(output_buffer_length);
|
buffer.read(output_buffer_length);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct io_device_creation_data
|
struct io_device_creation_data
|
||||||
{
|
{
|
||||||
uint64_t buffer;
|
uint64_t buffer;
|
||||||
uint32_t length;
|
uint32_t length;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void write_io_status(const emulator_object<IO_STATUS_BLOCK<EmulatorTraits<Emu64>>> io_status_block,
|
inline void write_io_status(const emulator_object<IO_STATUS_BLOCK<EmulatorTraits<Emu64>>> io_status_block,
|
||||||
const NTSTATUS status)
|
const NTSTATUS status)
|
||||||
{
|
{
|
||||||
if (io_status_block)
|
if (io_status_block)
|
||||||
{
|
{
|
||||||
io_status_block.access([&](IO_STATUS_BLOCK<EmulatorTraits<Emu64>>& status_block)
|
io_status_block.access(
|
||||||
{
|
[&](IO_STATUS_BLOCK<EmulatorTraits<Emu64>>& status_block) { status_block.Status = status; });
|
||||||
status_block.Status = status;
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct io_device
|
struct io_device
|
||||||
{
|
{
|
||||||
io_device() = default;
|
io_device() = default;
|
||||||
virtual ~io_device() = default;
|
virtual ~io_device() = default;
|
||||||
|
|
||||||
io_device(io_device&&) = default;
|
io_device(io_device&&) = default;
|
||||||
io_device& operator=(io_device&&) = default;
|
io_device& operator=(io_device&&) = default;
|
||||||
|
|
||||||
io_device(const io_device&) = delete;
|
io_device(const io_device&) = delete;
|
||||||
io_device& operator=(const io_device&) = delete;
|
io_device& operator=(const io_device&) = delete;
|
||||||
|
|
||||||
virtual NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& context) = 0;
|
virtual NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& context) = 0;
|
||||||
|
|
||||||
virtual void create(windows_emulator& win_emu, const io_device_creation_data& data)
|
virtual void create(windows_emulator& win_emu, const io_device_creation_data& data)
|
||||||
{
|
{
|
||||||
(void)win_emu;
|
(void)win_emu;
|
||||||
(void)data;
|
(void)data;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void work(windows_emulator& win_emu)
|
virtual void work(windows_emulator& win_emu)
|
||||||
{
|
{
|
||||||
(void)win_emu;
|
(void)win_emu;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void serialize(utils::buffer_serializer& buffer) const = 0;
|
virtual void serialize(utils::buffer_serializer& buffer) const = 0;
|
||||||
virtual void deserialize(utils::buffer_deserializer& buffer) = 0;
|
virtual void deserialize(utils::buffer_deserializer& buffer) = 0;
|
||||||
|
|
||||||
NTSTATUS execute_ioctl(windows_emulator& win_emu, const io_device_context& c)
|
NTSTATUS execute_ioctl(windows_emulator& win_emu, const io_device_context& c)
|
||||||
{
|
{
|
||||||
if (c.io_status_block)
|
if (c.io_status_block)
|
||||||
{
|
{
|
||||||
c.io_status_block.write({});
|
c.io_status_block.write({});
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto result = this->io_control(win_emu, c);
|
const auto result = this->io_control(win_emu, c);
|
||||||
write_io_status(c.io_status_block, result);
|
write_io_status(c.io_status_block, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct stateless_device : io_device
|
struct stateless_device : io_device
|
||||||
{
|
{
|
||||||
void create(windows_emulator&, const io_device_creation_data&) final
|
void create(windows_emulator&, const io_device_creation_data&) final
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(utils::buffer_serializer&) const override
|
void serialize(utils::buffer_serializer&) const override
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void deserialize(utils::buffer_deserializer&) override
|
void deserialize(utils::buffer_deserializer&) override
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<io_device> create_device(std::u16string_view device);
|
std::unique_ptr<io_device> create_device(std::u16string_view device);
|
||||||
|
|
||||||
class io_device_container : public io_device
|
class io_device_container : public io_device
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
io_device_container() = default;
|
io_device_container() = default;
|
||||||
|
|
||||||
io_device_container(std::u16string device, windows_emulator& win_emu, const io_device_creation_data& data)
|
io_device_container(std::u16string device, windows_emulator& win_emu, const io_device_creation_data& data)
|
||||||
: device_name_(std::move(device))
|
: device_name_(std::move(device))
|
||||||
{
|
{
|
||||||
this->setup();
|
this->setup();
|
||||||
this->device_->create(win_emu, data);
|
this->device_->create(win_emu, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& context) override
|
NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& context) override
|
||||||
{
|
{
|
||||||
this->assert_validity();
|
this->assert_validity();
|
||||||
return this->device_->io_control(win_emu, context);
|
return this->device_->io_control(win_emu, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void work(windows_emulator& win_emu) override
|
void work(windows_emulator& win_emu) override
|
||||||
{
|
{
|
||||||
this->assert_validity();
|
this->assert_validity();
|
||||||
return this->device_->work(win_emu);
|
return this->device_->work(win_emu);
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(utils::buffer_serializer& buffer) const override
|
void serialize(utils::buffer_serializer& buffer) const override
|
||||||
{
|
{
|
||||||
this->assert_validity();
|
this->assert_validity();
|
||||||
|
|
||||||
buffer.write_string(this->device_name_);
|
buffer.write_string(this->device_name_);
|
||||||
this->device_->serialize(buffer);
|
this->device_->serialize(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void deserialize(utils::buffer_deserializer& buffer) override
|
void deserialize(utils::buffer_deserializer& buffer) override
|
||||||
{
|
{
|
||||||
buffer.read_string(this->device_name_);
|
buffer.read_string(this->device_name_);
|
||||||
this->setup();
|
this->setup();
|
||||||
this->device_->deserialize(buffer);
|
this->device_->deserialize(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T = io_device>
|
template <typename T = io_device>
|
||||||
requires(std::is_base_of_v<io_device, T> || std::is_same_v<io_device, T>)
|
requires(std::is_base_of_v<io_device, T> || std::is_same_v<io_device, T>)
|
||||||
T* get_internal_device()
|
T* get_internal_device()
|
||||||
{
|
{
|
||||||
this->assert_validity();
|
this->assert_validity();
|
||||||
auto* value = this->device_.get();
|
auto* value = this->device_.get();
|
||||||
return dynamic_cast<T*>(value);
|
return dynamic_cast<T*>(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::u16string device_name_{};
|
std::u16string device_name_{};
|
||||||
std::unique_ptr<io_device> device_{};
|
std::unique_ptr<io_device> device_{};
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
this->device_ = create_device(this->device_name_);
|
this->device_ = create_device(this->device_name_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void assert_validity() const
|
void assert_validity() const
|
||||||
{
|
{
|
||||||
if (!this->device_)
|
if (!this->device_)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Device not created!");
|
throw std::runtime_error("Device not created!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,216 +10,214 @@ constexpr auto KUSD_BUFFER_SIZE = page_align_up(KUSD_SIZE);
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
void setup_kusd(KUSER_SHARED_DATA64& kusd, const bool use_relative_time)
|
void setup_kusd(KUSER_SHARED_DATA64& kusd, const bool use_relative_time)
|
||||||
{
|
{
|
||||||
memset(reinterpret_cast<void*>(&kusd), 0, sizeof(kusd));
|
memset(reinterpret_cast<void*>(&kusd), 0, sizeof(kusd));
|
||||||
|
|
||||||
kusd.TickCountMultiplier = 0x0fa00000;
|
kusd.TickCountMultiplier = 0x0fa00000;
|
||||||
kusd.InterruptTime.LowPart = 0x17bd9547;
|
kusd.InterruptTime.LowPart = 0x17bd9547;
|
||||||
kusd.InterruptTime.High1Time = 0x0000004b;
|
kusd.InterruptTime.High1Time = 0x0000004b;
|
||||||
kusd.InterruptTime.High2Time = 0x0000004b;
|
kusd.InterruptTime.High2Time = 0x0000004b;
|
||||||
kusd.SystemTime.LowPart = 0x7af9da99;
|
kusd.SystemTime.LowPart = 0x7af9da99;
|
||||||
kusd.SystemTime.High1Time = 0x01db27b9;
|
kusd.SystemTime.High1Time = 0x01db27b9;
|
||||||
kusd.SystemTime.High2Time = 0x01db27b9;
|
kusd.SystemTime.High2Time = 0x01db27b9;
|
||||||
kusd.TimeZoneBias.LowPart = 0x3c773000;
|
kusd.TimeZoneBias.LowPart = 0x3c773000;
|
||||||
kusd.TimeZoneBias.High1Time = -17;
|
kusd.TimeZoneBias.High1Time = -17;
|
||||||
kusd.TimeZoneBias.High2Time = -17;
|
kusd.TimeZoneBias.High2Time = -17;
|
||||||
kusd.TimeZoneId = 0x00000002;
|
kusd.TimeZoneId = 0x00000002;
|
||||||
kusd.LargePageMinimum = 0x00200000;
|
kusd.LargePageMinimum = 0x00200000;
|
||||||
kusd.RNGSeedVersion = 0x0000000000000013;
|
kusd.RNGSeedVersion = 0x0000000000000013;
|
||||||
kusd.TimeZoneBiasStamp = 0x00000004;
|
kusd.TimeZoneBiasStamp = 0x00000004;
|
||||||
kusd.NtBuildNumber = 0x00006c51;
|
kusd.NtBuildNumber = 0x00006c51;
|
||||||
kusd.NtProductType = NtProductWinNt;
|
kusd.NtProductType = NtProductWinNt;
|
||||||
kusd.ProductTypeIsValid = 0x01;
|
kusd.ProductTypeIsValid = 0x01;
|
||||||
kusd.NativeProcessorArchitecture = 0x0009;
|
kusd.NativeProcessorArchitecture = 0x0009;
|
||||||
kusd.NtMajorVersion = 0x0000000a;
|
kusd.NtMajorVersion = 0x0000000a;
|
||||||
kusd.BootId = 0x0000000b;
|
kusd.BootId = 0x0000000b;
|
||||||
kusd.SystemExpirationDate.QuadPart = 0x01dc26860a9ff300;
|
kusd.SystemExpirationDate.QuadPart = 0x01dc26860a9ff300;
|
||||||
kusd.SuiteMask = 0x00000110;
|
kusd.SuiteMask = 0x00000110;
|
||||||
kusd.MitigationPolicies.MitigationPolicies = 0x0a;
|
kusd.MitigationPolicies.MitigationPolicies = 0x0a;
|
||||||
kusd.MitigationPolicies.NXSupportPolicy = 0x02;
|
kusd.MitigationPolicies.NXSupportPolicy = 0x02;
|
||||||
kusd.MitigationPolicies.SEHValidationPolicy = 0x02;
|
kusd.MitigationPolicies.SEHValidationPolicy = 0x02;
|
||||||
kusd.CyclesPerYield = 0x0064;
|
kusd.CyclesPerYield = 0x0064;
|
||||||
kusd.DismountCount = 0x00000006;
|
kusd.DismountCount = 0x00000006;
|
||||||
kusd.ComPlusPackage = 0x00000001;
|
kusd.ComPlusPackage = 0x00000001;
|
||||||
kusd.LastSystemRITEventTickCount = 0x01ec1fd3;
|
kusd.LastSystemRITEventTickCount = 0x01ec1fd3;
|
||||||
kusd.NumberOfPhysicalPages = 0x00bf0958;
|
kusd.NumberOfPhysicalPages = 0x00bf0958;
|
||||||
kusd.FullNumberOfPhysicalPages = 0x0000000000bf0958;
|
kusd.FullNumberOfPhysicalPages = 0x0000000000bf0958;
|
||||||
kusd.TickCount.TickCount.LowPart = 0x001f7f05;
|
kusd.TickCount.TickCount.LowPart = 0x001f7f05;
|
||||||
kusd.TickCount.TickCountQuad = 0x00000000001f7f05;
|
kusd.TickCount.TickCountQuad = 0x00000000001f7f05;
|
||||||
kusd.Cookie = 0x1c3471da;
|
kusd.Cookie = 0x1c3471da;
|
||||||
kusd.ConsoleSessionForegroundProcessId = 0x00000000000028f4;
|
kusd.ConsoleSessionForegroundProcessId = 0x00000000000028f4;
|
||||||
kusd.TimeUpdateLock = 0x0000000002b28586;
|
kusd.TimeUpdateLock = 0x0000000002b28586;
|
||||||
kusd.BaselineSystemTimeQpc = 0x0000004b17cd596c;
|
kusd.BaselineSystemTimeQpc = 0x0000004b17cd596c;
|
||||||
kusd.BaselineInterruptTimeQpc = 0x0000004b17cd596c;
|
kusd.BaselineInterruptTimeQpc = 0x0000004b17cd596c;
|
||||||
kusd.QpcSystemTimeIncrement = 0x8000000000000000;
|
kusd.QpcSystemTimeIncrement = 0x8000000000000000;
|
||||||
kusd.QpcInterruptTimeIncrement = 0x8000000000000000;
|
kusd.QpcInterruptTimeIncrement = 0x8000000000000000;
|
||||||
kusd.QpcSystemTimeIncrementShift = 0x01;
|
kusd.QpcSystemTimeIncrementShift = 0x01;
|
||||||
kusd.QpcInterruptTimeIncrementShift = 0x01;
|
kusd.QpcInterruptTimeIncrementShift = 0x01;
|
||||||
kusd.UnparkedProcessorCount = 0x000c;
|
kusd.UnparkedProcessorCount = 0x000c;
|
||||||
kusd.TelemetryCoverageRound = 0x00000001;
|
kusd.TelemetryCoverageRound = 0x00000001;
|
||||||
kusd.LangGenerationCount = 0x00000003;
|
kusd.LangGenerationCount = 0x00000003;
|
||||||
kusd.InterruptTimeBias = 0x00000015a5d56406;
|
kusd.InterruptTimeBias = 0x00000015a5d56406;
|
||||||
kusd.QpcBias = 0x000000159530c4af;
|
kusd.QpcBias = 0x000000159530c4af;
|
||||||
kusd.ActiveProcessorCount = 0x0000000c;
|
kusd.ActiveProcessorCount = 0x0000000c;
|
||||||
kusd.ActiveGroupCount = 0x01;
|
kusd.ActiveGroupCount = 0x01;
|
||||||
kusd.QpcData.QpcData = 0x0083;
|
kusd.QpcData.QpcData = 0x0083;
|
||||||
kusd.QpcData.QpcBypassEnabled = 0x83;
|
kusd.QpcData.QpcBypassEnabled = 0x83;
|
||||||
kusd.TimeZoneBiasEffectiveStart.QuadPart = 0x01db276e654cb2ff;
|
kusd.TimeZoneBiasEffectiveStart.QuadPart = 0x01db276e654cb2ff;
|
||||||
kusd.TimeZoneBiasEffectiveEnd.QuadPart = 0x01db280b8c3b2800;
|
kusd.TimeZoneBiasEffectiveEnd.QuadPart = 0x01db280b8c3b2800;
|
||||||
kusd.XState.EnabledFeatures = 0x000000000000001f;
|
kusd.XState.EnabledFeatures = 0x000000000000001f;
|
||||||
kusd.XState.EnabledVolatileFeatures = 0x000000000000000f;
|
kusd.XState.EnabledVolatileFeatures = 0x000000000000000f;
|
||||||
kusd.XState.Size = 0x000003c0;
|
kusd.XState.Size = 0x000003c0;
|
||||||
|
|
||||||
if (use_relative_time)
|
if (use_relative_time)
|
||||||
{
|
{
|
||||||
kusd.QpcFrequency = 1000;
|
kusd.QpcFrequency = 1000;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
kusd.QpcFrequency = std::chrono::steady_clock::period::den;
|
kusd.QpcFrequency = std::chrono::steady_clock::period::den;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::wstring_view root_dir{L"C:\\WINDOWS"};
|
constexpr std::wstring_view root_dir{L"C:\\WINDOWS"};
|
||||||
memcpy(&kusd.NtSystemRoot.arr[0], root_dir.data(), root_dir.size() * 2);
|
memcpy(&kusd.NtSystemRoot.arr[0], root_dir.data(), root_dir.size() * 2);
|
||||||
|
|
||||||
kusd.ImageNumberLow = IMAGE_FILE_MACHINE_I386;
|
kusd.ImageNumberLow = IMAGE_FILE_MACHINE_I386;
|
||||||
kusd.ImageNumberHigh = IMAGE_FILE_MACHINE_AMD64;
|
kusd.ImageNumberHigh = IMAGE_FILE_MACHINE_AMD64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace utils
|
namespace utils
|
||||||
{
|
{
|
||||||
inline void serialize(buffer_serializer& buffer, const KUSER_SHARED_DATA64& kusd)
|
inline void serialize(buffer_serializer& buffer, const KUSER_SHARED_DATA64& kusd)
|
||||||
{
|
{
|
||||||
static_assert(KUSD_SIZE == sizeof(kusd));
|
static_assert(KUSD_SIZE == sizeof(kusd));
|
||||||
buffer.write(&kusd, KUSD_SIZE);
|
buffer.write(&kusd, KUSD_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void deserialize(buffer_deserializer& buffer, KUSER_SHARED_DATA64& kusd)
|
inline void deserialize(buffer_deserializer& buffer, KUSER_SHARED_DATA64& kusd)
|
||||||
{
|
{
|
||||||
buffer.read(&kusd, KUSD_SIZE);
|
buffer.read(&kusd, KUSD_SIZE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kusd_mmio::kusd_mmio(x64_emulator& emu, process_context& process)
|
kusd_mmio::kusd_mmio(x64_emulator& emu, process_context& process)
|
||||||
: emu_(&emu)
|
: emu_(&emu),
|
||||||
, process_(&process)
|
process_(&process)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
kusd_mmio::~kusd_mmio()
|
kusd_mmio::~kusd_mmio()
|
||||||
{
|
{
|
||||||
this->deregister_mmio();
|
this->deregister_mmio();
|
||||||
}
|
}
|
||||||
|
|
||||||
kusd_mmio::kusd_mmio(utils::buffer_deserializer& buffer)
|
kusd_mmio::kusd_mmio(utils::buffer_deserializer& buffer)
|
||||||
: kusd_mmio(buffer.read<x64_emulator_wrapper>(), buffer.read<process_context_wrapper>())
|
: kusd_mmio(buffer.read<x64_emulator_wrapper>(), buffer.read<process_context_wrapper>())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void kusd_mmio::setup(const bool use_relative_time)
|
void kusd_mmio::setup(const bool use_relative_time)
|
||||||
{
|
{
|
||||||
this->use_relative_time_ = use_relative_time;
|
this->use_relative_time_ = use_relative_time;
|
||||||
|
|
||||||
setup_kusd(this->kusd_, use_relative_time);
|
setup_kusd(this->kusd_, use_relative_time);
|
||||||
this->start_time_ = convert_from_ksystem_time(this->kusd_.SystemTime);
|
this->start_time_ = convert_from_ksystem_time(this->kusd_.SystemTime);
|
||||||
|
|
||||||
this->register_mmio();
|
this->register_mmio();
|
||||||
}
|
}
|
||||||
|
|
||||||
void kusd_mmio::serialize(utils::buffer_serializer& buffer) const
|
void kusd_mmio::serialize(utils::buffer_serializer& buffer) const
|
||||||
{
|
{
|
||||||
buffer.write(this->use_relative_time_);
|
buffer.write(this->use_relative_time_);
|
||||||
buffer.write(this->kusd_);
|
buffer.write(this->kusd_);
|
||||||
buffer.write(this->start_time_);
|
buffer.write(this->start_time_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void kusd_mmio::deserialize(utils::buffer_deserializer& buffer)
|
void kusd_mmio::deserialize(utils::buffer_deserializer& buffer)
|
||||||
{
|
{
|
||||||
buffer.read(this->use_relative_time_);
|
buffer.read(this->use_relative_time_);
|
||||||
buffer.read(this->kusd_);
|
buffer.read(this->kusd_);
|
||||||
buffer.read(this->start_time_);
|
buffer.read(this->start_time_);
|
||||||
|
|
||||||
this->deregister_mmio();
|
this->deregister_mmio();
|
||||||
this->register_mmio();
|
this->register_mmio();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t kusd_mmio::read(const uint64_t addr, const size_t size)
|
uint64_t kusd_mmio::read(const uint64_t addr, const size_t size)
|
||||||
{
|
{
|
||||||
uint64_t result{};
|
uint64_t result{};
|
||||||
|
|
||||||
this->update();
|
this->update();
|
||||||
|
|
||||||
if (addr >= KUSD_SIZE)
|
if (addr >= KUSD_SIZE)
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto end = addr + size;
|
const auto end = addr + size;
|
||||||
const auto valid_end = std::min(end, static_cast<uint64_t>(KUSD_SIZE));
|
const auto valid_end = std::min(end, static_cast<uint64_t>(KUSD_SIZE));
|
||||||
const auto real_size = valid_end - addr;
|
const auto real_size = valid_end - addr;
|
||||||
|
|
||||||
if (real_size > sizeof(result))
|
if (real_size > sizeof(result))
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto* kusd_buffer = reinterpret_cast<uint8_t*>(&this->kusd_);
|
const auto* kusd_buffer = reinterpret_cast<uint8_t*>(&this->kusd_);
|
||||||
memcpy(&result, kusd_buffer + addr, real_size);
|
memcpy(&result, kusd_buffer + addr, real_size);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t kusd_mmio::address()
|
uint64_t kusd_mmio::address()
|
||||||
{
|
{
|
||||||
return KUSD_ADDRESS;
|
return KUSD_ADDRESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void kusd_mmio::update()
|
void kusd_mmio::update()
|
||||||
{
|
{
|
||||||
auto time = this->start_time_;
|
auto time = this->start_time_;
|
||||||
|
|
||||||
if (this->use_relative_time_)
|
if (this->use_relative_time_)
|
||||||
{
|
{
|
||||||
const auto passed_time = this->process_->executed_instructions;
|
const auto passed_time = this->process_->executed_instructions;
|
||||||
const auto clock_frequency = static_cast<uint64_t>(this->kusd_.QpcFrequency);
|
const auto clock_frequency = static_cast<uint64_t>(this->kusd_.QpcFrequency);
|
||||||
|
|
||||||
using duration = std::chrono::system_clock::duration;
|
using duration = std::chrono::system_clock::duration;
|
||||||
time += duration(passed_time * duration::period::den / clock_frequency);
|
time += duration(passed_time * duration::period::den / clock_frequency);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
time = std::chrono::system_clock::now();
|
time = std::chrono::system_clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
convert_to_ksystem_time(&this->kusd_.SystemTime, time);
|
convert_to_ksystem_time(&this->kusd_.SystemTime, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
void kusd_mmio::register_mmio()
|
void kusd_mmio::register_mmio()
|
||||||
{
|
{
|
||||||
if (this->registered_)
|
if (this->registered_)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->registered_ = true;
|
this->registered_ = true;
|
||||||
|
|
||||||
this->emu_->allocate_mmio(KUSD_ADDRESS, KUSD_BUFFER_SIZE,
|
this->emu_->allocate_mmio(
|
||||||
[this](const uint64_t addr, const size_t size)
|
KUSD_ADDRESS, KUSD_BUFFER_SIZE,
|
||||||
{
|
[this](const uint64_t addr, const size_t size) { return this->read(addr, size); },
|
||||||
return this->read(addr, size);
|
[](const uint64_t, const size_t, const uint64_t) {
|
||||||
}, [](const uint64_t, const size_t, const uint64_t)
|
// Writing not supported!
|
||||||
{
|
});
|
||||||
// Writing not supported!
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void kusd_mmio::deregister_mmio()
|
void kusd_mmio::deregister_mmio()
|
||||||
{
|
{
|
||||||
if (this->registered_)
|
if (this->registered_)
|
||||||
{
|
{
|
||||||
this->registered_ = false;
|
this->registered_ = false;
|
||||||
this->emu_->release_memory(KUSD_ADDRESS, KUSD_BUFFER_SIZE);
|
this->emu_->release_memory(KUSD_ADDRESS, KUSD_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,48 +10,48 @@ class windows_emulator;
|
|||||||
|
|
||||||
class kusd_mmio
|
class kusd_mmio
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
kusd_mmio(x64_emulator& emu, process_context& process);
|
kusd_mmio(x64_emulator& emu, process_context& process);
|
||||||
~kusd_mmio();
|
~kusd_mmio();
|
||||||
|
|
||||||
kusd_mmio(utils::buffer_deserializer& buffer);
|
kusd_mmio(utils::buffer_deserializer& buffer);
|
||||||
|
|
||||||
kusd_mmio(kusd_mmio&&) = delete;
|
kusd_mmio(kusd_mmio&&) = delete;
|
||||||
kusd_mmio(const kusd_mmio&) = delete;
|
kusd_mmio(const kusd_mmio&) = delete;
|
||||||
kusd_mmio& operator=(kusd_mmio&& obj) = delete;
|
kusd_mmio& operator=(kusd_mmio&& obj) = delete;
|
||||||
kusd_mmio& operator=(const kusd_mmio&) = delete;
|
kusd_mmio& operator=(const kusd_mmio&) = delete;
|
||||||
|
|
||||||
void serialize(utils::buffer_serializer& buffer) const;
|
void serialize(utils::buffer_serializer& buffer) const;
|
||||||
void deserialize(utils::buffer_deserializer& buffer);
|
void deserialize(utils::buffer_deserializer& buffer);
|
||||||
|
|
||||||
KUSER_SHARED_DATA64& get()
|
KUSER_SHARED_DATA64& get()
|
||||||
{
|
{
|
||||||
return this->kusd_;
|
return this->kusd_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const KUSER_SHARED_DATA64& get() const
|
const KUSER_SHARED_DATA64& get() const
|
||||||
{
|
{
|
||||||
return this->kusd_;
|
return this->kusd_;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t address();
|
static uint64_t address();
|
||||||
|
|
||||||
void setup(bool use_relative_time);
|
void setup(bool use_relative_time);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
x64_emulator* emu_{};
|
x64_emulator* emu_{};
|
||||||
process_context* process_{};
|
process_context* process_{};
|
||||||
|
|
||||||
bool registered_{};
|
bool registered_{};
|
||||||
bool use_relative_time_{};
|
bool use_relative_time_{};
|
||||||
|
|
||||||
KUSER_SHARED_DATA64 kusd_{};
|
KUSER_SHARED_DATA64 kusd_{};
|
||||||
std::chrono::system_clock::time_point start_time_{};
|
std::chrono::system_clock::time_point start_time_{};
|
||||||
|
|
||||||
uint64_t read(uint64_t addr, size_t size);
|
uint64_t read(uint64_t addr, size_t size);
|
||||||
|
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
void register_mmio();
|
void register_mmio();
|
||||||
void deregister_mmio();
|
void deregister_mmio();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,130 +7,141 @@ namespace
|
|||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define COLOR(win, posix) win
|
#define COLOR(win, posix) win
|
||||||
using color_type = WORD;
|
using color_type = WORD;
|
||||||
#else
|
#else
|
||||||
#define COLOR(win, posix) posix
|
#define COLOR(win, posix) posix
|
||||||
using color_type = const char*;
|
using color_type = const char*;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
color_type get_reset_color()
|
color_type get_reset_color()
|
||||||
{
|
{
|
||||||
return COLOR(7, "\033[0m");
|
return COLOR(7, "\033[0m");
|
||||||
}
|
}
|
||||||
|
|
||||||
color_type get_color_type(const color c)
|
color_type get_color_type(const color c)
|
||||||
{
|
{
|
||||||
using enum color;
|
using enum color;
|
||||||
|
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
case black: return COLOR(0x8, "\033[0;90m");
|
case black:
|
||||||
case red: return COLOR(0xC, "\033[0;91m");
|
return COLOR(0x8, "\033[0;90m");
|
||||||
case green: return COLOR(0xA, "\033[0;92m");
|
case red:
|
||||||
case yellow: return COLOR(0xE, "\033[0;93m");
|
return COLOR(0xC, "\033[0;91m");
|
||||||
case blue: return COLOR(0x9, "\033[0;94m");
|
case green:
|
||||||
case cyan: return COLOR(0xB, "\033[0;96m");
|
return COLOR(0xA, "\033[0;92m");
|
||||||
case pink: return COLOR(0xD, "\033[0;95m");
|
case yellow:
|
||||||
case white: return COLOR(0xF, "\033[0;97m");
|
return COLOR(0xE, "\033[0;93m");
|
||||||
case dark_gray: return COLOR(0x8, "\033[0;97m");
|
case blue:
|
||||||
case gray:
|
return COLOR(0x9, "\033[0;94m");
|
||||||
default: return get_reset_color();
|
case cyan:
|
||||||
}
|
return COLOR(0xB, "\033[0;96m");
|
||||||
}
|
case pink:
|
||||||
|
return COLOR(0xD, "\033[0;95m");
|
||||||
|
case white:
|
||||||
|
return COLOR(0xF, "\033[0;97m");
|
||||||
|
case dark_gray:
|
||||||
|
return COLOR(0x8, "\033[0;97m");
|
||||||
|
case gray:
|
||||||
|
default:
|
||||||
|
return get_reset_color();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
HANDLE get_console_handle()
|
HANDLE get_console_handle()
|
||||||
{
|
{
|
||||||
return GetStdHandle(STD_OUTPUT_HANDLE);
|
return GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void set_color(const color_type color)
|
void set_color(const color_type color)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
SetConsoleTextAttribute(get_console_handle(), color);
|
SetConsoleTextAttribute(get_console_handle(), color);
|
||||||
#else
|
#else
|
||||||
printf("%s", color);
|
printf("%s", color);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset_color()
|
void reset_color()
|
||||||
{
|
{
|
||||||
(void)fflush(stdout);
|
(void)fflush(stdout);
|
||||||
set_color(get_reset_color());
|
set_color(get_reset_color());
|
||||||
(void)fflush(stdout);
|
(void)fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string_view format(va_list* ap, const char* message)
|
std::string_view format(va_list* ap, const char* message)
|
||||||
{
|
{
|
||||||
thread_local char buffer[0x1000];
|
thread_local char buffer[0x1000];
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
const int count = _vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer), message, *ap);
|
const int count = _vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer), message, *ap);
|
||||||
#else
|
#else
|
||||||
const int count = vsnprintf(buffer, sizeof(buffer), message, *ap);
|
const int count = vsnprintf(buffer, sizeof(buffer), message, *ap);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (count < 0) return {};
|
if (count < 0)
|
||||||
return {buffer, static_cast<size_t>(count)};
|
return {};
|
||||||
}
|
return {buffer, static_cast<size_t>(count)};
|
||||||
|
}
|
||||||
|
|
||||||
#define format_to_string(msg, str)\
|
#define format_to_string(msg, str) \
|
||||||
va_list ap;\
|
va_list ap; \
|
||||||
va_start(ap, msg);\
|
va_start(ap, msg); \
|
||||||
const auto str = format(&ap, msg);\
|
const auto str = format(&ap, msg); \
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
void print_colored(const std::string_view& line, const color_type base_color)
|
void print_colored(const std::string_view& line, const color_type base_color)
|
||||||
{
|
{
|
||||||
const auto _ = utils::finally(&reset_color);
|
const auto _ = utils::finally(&reset_color);
|
||||||
set_color(base_color);
|
set_color(base_color);
|
||||||
(void)fwrite(line.data(), 1, line.size(), stdout);
|
(void)fwrite(line.data(), 1, line.size(), stdout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void logger::print(const color c, const std::string_view message) const
|
void logger::print(const color c, const std::string_view message) const
|
||||||
{
|
{
|
||||||
if (this->disable_output_)
|
if (this->disable_output_)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
print_colored(message, get_color_type(c));
|
print_colored(message, get_color_type(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
void logger::print(const color c, const char* message, ...) const
|
void logger::print(const color c, const char* message, ...) const
|
||||||
{
|
{
|
||||||
format_to_string(message, data);
|
format_to_string(message, data);
|
||||||
this->print(c, data);
|
this->print(c, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void logger::info(const char* message, ...) const
|
void logger::info(const char* message, ...) const
|
||||||
{
|
{
|
||||||
format_to_string(message, data);
|
format_to_string(message, data);
|
||||||
this->print(color::cyan, data);
|
this->print(color::cyan, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void logger::warn(const char* message, ...) const
|
void logger::warn(const char* message, ...) const
|
||||||
{
|
{
|
||||||
format_to_string(message, data);
|
format_to_string(message, data);
|
||||||
this->print(color::yellow, data);
|
this->print(color::yellow, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void logger::error(const char* message, ...) const
|
void logger::error(const char* message, ...) const
|
||||||
{
|
{
|
||||||
format_to_string(message, data);
|
format_to_string(message, data);
|
||||||
this->print(color::red, data);
|
this->print(color::red, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void logger::success(const char* message, ...) const
|
void logger::success(const char* message, ...) const
|
||||||
{
|
{
|
||||||
format_to_string(message, data);
|
format_to_string(message, data);
|
||||||
this->print(color::green, data);
|
this->print(color::green, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void logger::log(const char* message, ...) const
|
void logger::log(const char* message, ...) const
|
||||||
{
|
{
|
||||||
format_to_string(message, data);
|
format_to_string(message, data);
|
||||||
this->print(color::gray, data);
|
this->print(color::gray, data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,44 +3,44 @@
|
|||||||
#ifdef OS_WINDOWS
|
#ifdef OS_WINDOWS
|
||||||
#define FORMAT_ATTRIBUTE(fmt_pos, var_pos)
|
#define FORMAT_ATTRIBUTE(fmt_pos, var_pos)
|
||||||
#else
|
#else
|
||||||
#define FORMAT_ATTRIBUTE(fmt_pos, var_pos) __attribute__((format( printf, fmt_pos, var_pos)))
|
#define FORMAT_ATTRIBUTE(fmt_pos, var_pos) __attribute__((format(printf, fmt_pos, var_pos)))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum class color
|
enum class color
|
||||||
{
|
{
|
||||||
black,
|
black,
|
||||||
red,
|
red,
|
||||||
green,
|
green,
|
||||||
yellow,
|
yellow,
|
||||||
blue,
|
blue,
|
||||||
cyan,
|
cyan,
|
||||||
pink,
|
pink,
|
||||||
white,
|
white,
|
||||||
gray,
|
gray,
|
||||||
dark_gray,
|
dark_gray,
|
||||||
};
|
};
|
||||||
|
|
||||||
class logger
|
class logger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void print(color c, std::string_view message) const;
|
void print(color c, std::string_view message) const;
|
||||||
void print(color c, const char* message, ...) const FORMAT_ATTRIBUTE(3, 4);
|
void print(color c, const char* message, ...) const FORMAT_ATTRIBUTE(3, 4);
|
||||||
void info(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
|
void info(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
|
||||||
void warn(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
|
void warn(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
|
||||||
void error(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
|
void error(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
|
||||||
void success(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
|
void success(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
|
||||||
void log(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
|
void log(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
|
||||||
|
|
||||||
void disable_output(const bool value)
|
void disable_output(const bool value)
|
||||||
{
|
{
|
||||||
this->disable_output_ = value;
|
this->disable_output_ = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_output_disabled() const
|
bool is_output_disabled() const
|
||||||
{
|
{
|
||||||
return this->disable_output_;
|
return this->disable_output_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool disable_output_{false};
|
bool disable_output_{false};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,69 +5,69 @@
|
|||||||
|
|
||||||
inline std::string get_permission_string(const memory_permission permission)
|
inline std::string get_permission_string(const memory_permission permission)
|
||||||
{
|
{
|
||||||
const bool has_exec = (permission & memory_permission::exec) != memory_permission::none;
|
const bool has_exec = (permission & memory_permission::exec) != memory_permission::none;
|
||||||
const bool has_read = (permission & memory_permission::read) != memory_permission::none;
|
const bool has_read = (permission & memory_permission::read) != memory_permission::none;
|
||||||
const bool has_write = (permission & memory_permission::write) != memory_permission::none;
|
const bool has_write = (permission & memory_permission::write) != memory_permission::none;
|
||||||
|
|
||||||
std::string res = {};
|
std::string res = {};
|
||||||
res.reserve(3);
|
res.reserve(3);
|
||||||
|
|
||||||
res.push_back(has_read ? 'r' : '-');
|
res.push_back(has_read ? 'r' : '-');
|
||||||
res.push_back(has_write ? 'w' : '-');
|
res.push_back(has_write ? 'w' : '-');
|
||||||
res.push_back(has_exec ? 'x' : '-');
|
res.push_back(has_exec ? 'x' : '-');
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline memory_permission map_nt_to_emulator_protection(uint32_t nt_protection)
|
inline memory_permission map_nt_to_emulator_protection(uint32_t nt_protection)
|
||||||
{
|
{
|
||||||
nt_protection &= ~static_cast<uint32_t>(PAGE_GUARD); // TODO: Implement that
|
nt_protection &= ~static_cast<uint32_t>(PAGE_GUARD); // TODO: Implement that
|
||||||
|
|
||||||
switch (nt_protection)
|
switch (nt_protection)
|
||||||
{
|
{
|
||||||
case PAGE_NOACCESS:
|
case PAGE_NOACCESS:
|
||||||
return memory_permission::none;
|
return memory_permission::none;
|
||||||
case PAGE_READONLY:
|
case PAGE_READONLY:
|
||||||
return memory_permission::read;
|
return memory_permission::read;
|
||||||
case PAGE_READWRITE:
|
case PAGE_READWRITE:
|
||||||
case PAGE_WRITECOPY:
|
case PAGE_WRITECOPY:
|
||||||
return memory_permission::read | memory_permission::write;
|
return memory_permission::read | memory_permission::write;
|
||||||
case PAGE_EXECUTE:
|
case PAGE_EXECUTE:
|
||||||
case PAGE_EXECUTE_READ:
|
case PAGE_EXECUTE_READ:
|
||||||
return memory_permission::read | memory_permission::exec;
|
return memory_permission::read | memory_permission::exec;
|
||||||
case PAGE_EXECUTE_READWRITE:
|
case PAGE_EXECUTE_READWRITE:
|
||||||
return memory_permission::all;
|
return memory_permission::all;
|
||||||
case PAGE_EXECUTE_WRITECOPY:
|
case PAGE_EXECUTE_WRITECOPY:
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Failed to map protection");
|
throw std::runtime_error("Failed to map protection");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint32_t map_emulator_to_nt_protection(const memory_permission permission)
|
inline uint32_t map_emulator_to_nt_protection(const memory_permission permission)
|
||||||
{
|
{
|
||||||
const bool has_exec = (permission & memory_permission::exec) != memory_permission::none;
|
const bool has_exec = (permission & memory_permission::exec) != memory_permission::none;
|
||||||
const bool has_read = (permission & memory_permission::read) != memory_permission::none;
|
const bool has_read = (permission & memory_permission::read) != memory_permission::none;
|
||||||
const bool has_write = (permission & memory_permission::write) != memory_permission::none;
|
const bool has_write = (permission & memory_permission::write) != memory_permission::none;
|
||||||
|
|
||||||
if (!has_read)
|
if (!has_read)
|
||||||
{
|
{
|
||||||
return PAGE_NOACCESS;
|
return PAGE_NOACCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_exec && has_write)
|
if (has_exec && has_write)
|
||||||
{
|
{
|
||||||
return PAGE_EXECUTE_READWRITE;
|
return PAGE_EXECUTE_READWRITE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_exec)
|
if (has_exec)
|
||||||
{
|
{
|
||||||
return PAGE_EXECUTE_READ;
|
return PAGE_EXECUTE_READ;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_write)
|
if (has_write)
|
||||||
{
|
{
|
||||||
return PAGE_READWRITE;
|
return PAGE_READWRITE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PAGE_READONLY;
|
return PAGE_READONLY;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
|
|
||||||
struct exported_symbol
|
struct exported_symbol
|
||||||
{
|
{
|
||||||
std::string name{};
|
std::string name{};
|
||||||
uint64_t ordinal{};
|
uint64_t ordinal{};
|
||||||
uint64_t rva{};
|
uint64_t rva{};
|
||||||
uint64_t address{};
|
uint64_t address{};
|
||||||
};
|
};
|
||||||
|
|
||||||
using exported_symbols = std::vector<exported_symbol>;
|
using exported_symbols = std::vector<exported_symbol>;
|
||||||
@@ -14,39 +14,39 @@ using address_name_mapping = std::map<uint64_t, std::string>;
|
|||||||
|
|
||||||
struct mapped_section
|
struct mapped_section
|
||||||
{
|
{
|
||||||
std::string name{};
|
std::string name{};
|
||||||
basic_memory_region region{};
|
basic_memory_region region{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mapped_module
|
struct mapped_module
|
||||||
{
|
{
|
||||||
std::string name{};
|
std::string name{};
|
||||||
std::filesystem::path path{};
|
std::filesystem::path path{};
|
||||||
|
|
||||||
uint64_t image_base{};
|
uint64_t image_base{};
|
||||||
uint64_t size_of_image{};
|
uint64_t size_of_image{};
|
||||||
uint64_t entry_point{};
|
uint64_t entry_point{};
|
||||||
|
|
||||||
exported_symbols exports{};
|
exported_symbols exports{};
|
||||||
address_name_mapping address_names{};
|
address_name_mapping address_names{};
|
||||||
|
|
||||||
std::vector<mapped_section> sections{};
|
std::vector<mapped_section> sections{};
|
||||||
|
|
||||||
bool is_within(const uint64_t address) const
|
bool is_within(const uint64_t address) const
|
||||||
{
|
{
|
||||||
return address >= this->image_base && address < (this->image_base + this->size_of_image);
|
return address >= this->image_base && address < (this->image_base + this->size_of_image);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t find_export(const std::string_view export_name) const
|
uint64_t find_export(const std::string_view export_name) const
|
||||||
{
|
{
|
||||||
for (auto& symbol : this->exports)
|
for (auto& symbol : this->exports)
|
||||||
{
|
{
|
||||||
if (symbol.name == export_name)
|
if (symbol.name == export_name)
|
||||||
{
|
{
|
||||||
return symbol.address;
|
return symbol.address;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,124 +5,124 @@
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
std::filesystem::path canonicalize_module_path(const std::filesystem::path& file)
|
std::filesystem::path canonicalize_module_path(const std::filesystem::path& file)
|
||||||
{
|
{
|
||||||
constexpr std::u16string_view nt_prefix = u"\\??\\";
|
constexpr std::u16string_view nt_prefix = u"\\??\\";
|
||||||
const auto wide_file = file.u16string();
|
const auto wide_file = file.u16string();
|
||||||
|
|
||||||
if (!wide_file.starts_with(nt_prefix))
|
if (!wide_file.starts_with(nt_prefix))
|
||||||
{
|
{
|
||||||
return canonical(absolute(file));
|
return canonical(absolute(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
return canonicalize_module_path(wide_file.substr(nt_prefix.size()));
|
return canonicalize_module_path(wide_file.substr(nt_prefix.size()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace utils
|
namespace utils
|
||||||
{
|
{
|
||||||
static void serialize(buffer_serializer& buffer, const exported_symbol& sym)
|
static void serialize(buffer_serializer& buffer, const exported_symbol& sym)
|
||||||
{
|
{
|
||||||
buffer.write(sym.name);
|
buffer.write(sym.name);
|
||||||
buffer.write(sym.ordinal);
|
buffer.write(sym.ordinal);
|
||||||
buffer.write(sym.rva);
|
buffer.write(sym.rva);
|
||||||
buffer.write(sym.address);
|
buffer.write(sym.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void deserialize(buffer_deserializer& buffer, exported_symbol& sym)
|
static void deserialize(buffer_deserializer& buffer, exported_symbol& sym)
|
||||||
{
|
{
|
||||||
buffer.read(sym.name);
|
buffer.read(sym.name);
|
||||||
buffer.read(sym.ordinal);
|
buffer.read(sym.ordinal);
|
||||||
buffer.read(sym.rva);
|
buffer.read(sym.rva);
|
||||||
buffer.read(sym.address);
|
buffer.read(sym.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void serialize(buffer_serializer& buffer, const mapped_module& mod)
|
static void serialize(buffer_serializer& buffer, const mapped_module& mod)
|
||||||
{
|
{
|
||||||
buffer.write_string(mod.name);
|
buffer.write_string(mod.name);
|
||||||
buffer.write(mod.path.u16string());
|
buffer.write(mod.path.u16string());
|
||||||
|
|
||||||
buffer.write(mod.image_base);
|
buffer.write(mod.image_base);
|
||||||
buffer.write(mod.size_of_image);
|
buffer.write(mod.size_of_image);
|
||||||
buffer.write(mod.entry_point);
|
buffer.write(mod.entry_point);
|
||||||
|
|
||||||
buffer.write_vector(mod.exports);
|
buffer.write_vector(mod.exports);
|
||||||
buffer.write_map(mod.address_names);
|
buffer.write_map(mod.address_names);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void deserialize(buffer_deserializer& buffer, mapped_module& mod)
|
static void deserialize(buffer_deserializer& buffer, mapped_module& mod)
|
||||||
{
|
{
|
||||||
mod.name = buffer.read_string();
|
mod.name = buffer.read_string();
|
||||||
mod.path = buffer.read_string<std::u16string::value_type>();
|
mod.path = buffer.read_string<std::u16string::value_type>();
|
||||||
|
|
||||||
buffer.read(mod.image_base);
|
buffer.read(mod.image_base);
|
||||||
buffer.read(mod.size_of_image);
|
buffer.read(mod.size_of_image);
|
||||||
buffer.read(mod.entry_point);
|
buffer.read(mod.entry_point);
|
||||||
|
|
||||||
buffer.read_vector(mod.exports);
|
buffer.read_vector(mod.exports);
|
||||||
buffer.read_map(mod.address_names);
|
buffer.read_map(mod.address_names);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module_manager::module_manager(emulator& emu)
|
module_manager::module_manager(emulator& emu)
|
||||||
: emu_(&emu)
|
: emu_(&emu)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
mapped_module* module_manager::map_module(const std::filesystem::path& file, logger& logger)
|
mapped_module* module_manager::map_module(const std::filesystem::path& file, logger& logger)
|
||||||
{
|
{
|
||||||
auto canonical_file = canonicalize_module_path(file);
|
auto canonical_file = canonicalize_module_path(file);
|
||||||
|
|
||||||
for (auto& mod : this->modules_)
|
for (auto& mod : this->modules_)
|
||||||
{
|
{
|
||||||
if (mod.second.path == canonical_file)
|
if (mod.second.path == canonical_file)
|
||||||
{
|
{
|
||||||
return &mod.second;
|
return &mod.second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto mod = map_module_from_file(*this->emu_, std::move(canonical_file));
|
auto mod = map_module_from_file(*this->emu_, std::move(canonical_file));
|
||||||
|
|
||||||
logger.log("Mapped %s at 0x%" PRIx64 "\n", mod.path.generic_string().c_str(), mod.image_base);
|
logger.log("Mapped %s at 0x%" PRIx64 "\n", mod.path.generic_string().c_str(), mod.image_base);
|
||||||
|
|
||||||
const auto image_base = mod.image_base;
|
const auto image_base = mod.image_base;
|
||||||
const auto entry = this->modules_.try_emplace(image_base, std::move(mod));
|
const auto entry = this->modules_.try_emplace(image_base, std::move(mod));
|
||||||
return &entry.first->second;
|
return &entry.first->second;
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
logger.error("Failed to map %s: %s\n", file.generic_string().c_str(), e.what());
|
logger.error("Failed to map %s: %s\n", file.generic_string().c_str(), e.what());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
logger.error("Failed to map %s: Unknown error\n", file.generic_string().c_str());
|
logger.error("Failed to map %s: Unknown error\n", file.generic_string().c_str());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void module_manager::serialize(utils::buffer_serializer& buffer) const
|
void module_manager::serialize(utils::buffer_serializer& buffer) const
|
||||||
{
|
{
|
||||||
buffer.write_map(this->modules_);
|
buffer.write_map(this->modules_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void module_manager::deserialize(utils::buffer_deserializer& buffer)
|
void module_manager::deserialize(utils::buffer_deserializer& buffer)
|
||||||
{
|
{
|
||||||
buffer.read_map(this->modules_);
|
buffer.read_map(this->modules_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool module_manager::unmap(const uint64_t address)
|
bool module_manager::unmap(const uint64_t address)
|
||||||
{
|
{
|
||||||
const auto mod = this->modules_.find(address);
|
const auto mod = this->modules_.find(address);
|
||||||
if (mod == this->modules_.end())
|
if (mod == this->modules_.end())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
unmap_module(*this->emu_, mod->second);
|
unmap_module(*this->emu_, mod->second);
|
||||||
this->modules_.erase(mod);
|
this->modules_.erase(mod);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,58 +6,58 @@ class logger;
|
|||||||
|
|
||||||
class module_manager
|
class module_manager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
module_manager(emulator& emu);
|
module_manager(emulator& emu);
|
||||||
|
|
||||||
mapped_module* map_module(const std::filesystem::path& file, logger& logger);
|
mapped_module* map_module(const std::filesystem::path& file, logger& logger);
|
||||||
|
|
||||||
mapped_module* find_by_address(const uint64_t address)
|
mapped_module* find_by_address(const uint64_t address)
|
||||||
{
|
{
|
||||||
const auto entry = this->get_module(address);
|
const auto entry = this->get_module(address);
|
||||||
if (entry != this->modules_.end())
|
if (entry != this->modules_.end())
|
||||||
{
|
{
|
||||||
return &entry->second;
|
return &entry->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* find_name(const uint64_t address)
|
const char* find_name(const uint64_t address)
|
||||||
{
|
{
|
||||||
const auto* mod = this->find_by_address(address);
|
const auto* mod = this->find_by_address(address);
|
||||||
if (!mod)
|
if (!mod)
|
||||||
{
|
{
|
||||||
return "<N/A>";
|
return "<N/A>";
|
||||||
}
|
}
|
||||||
|
|
||||||
return mod->name.c_str();
|
return mod->name.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(utils::buffer_serializer& buffer) const;
|
void serialize(utils::buffer_serializer& buffer) const;
|
||||||
void deserialize(utils::buffer_deserializer& buffer);
|
void deserialize(utils::buffer_deserializer& buffer);
|
||||||
|
|
||||||
bool unmap(const uint64_t address);
|
bool unmap(const uint64_t address);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
emulator* emu_{};
|
emulator* emu_{};
|
||||||
|
|
||||||
using module_map = std::map<uint64_t, mapped_module>;
|
using module_map = std::map<uint64_t, mapped_module>;
|
||||||
module_map modules_{};
|
module_map modules_{};
|
||||||
|
|
||||||
module_map::iterator get_module(const uint64_t address)
|
module_map::iterator get_module(const uint64_t address)
|
||||||
{
|
{
|
||||||
if (this->modules_.empty())
|
if (this->modules_.empty())
|
||||||
{
|
{
|
||||||
return this->modules_.end();
|
return this->modules_.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto upper_bound = this->modules_.upper_bound(address);
|
auto upper_bound = this->modules_.upper_bound(address);
|
||||||
if (upper_bound == this->modules_.begin())
|
if (upper_bound == this->modules_.begin())
|
||||||
{
|
{
|
||||||
return this->modules_.end();
|
return this->modules_.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::advance(upper_bound, -1);
|
std::advance(upper_bound, -1);
|
||||||
return upper_bound;
|
return upper_bound;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,265 +7,259 @@
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
uint64_t get_first_section_offset(const PENTHeaders_t<std::uint64_t>& nt_headers, const uint64_t nt_headers_offset)
|
uint64_t get_first_section_offset(const PENTHeaders_t<std::uint64_t>& nt_headers, const uint64_t nt_headers_offset)
|
||||||
{
|
{
|
||||||
const uint8_t* nt_headers_addr = reinterpret_cast<const uint8_t*>(&nt_headers);
|
const uint8_t* nt_headers_addr = reinterpret_cast<const uint8_t*>(&nt_headers);
|
||||||
size_t optional_header_offset = reinterpret_cast<uintptr_t>(&(nt_headers.OptionalHeader)) - reinterpret_cast<
|
size_t optional_header_offset =
|
||||||
uintptr_t>(&nt_headers);
|
reinterpret_cast<uintptr_t>(&(nt_headers.OptionalHeader)) - reinterpret_cast<uintptr_t>(&nt_headers);
|
||||||
size_t optional_header_size = nt_headers.FileHeader.SizeOfOptionalHeader;
|
size_t optional_header_size = nt_headers.FileHeader.SizeOfOptionalHeader;
|
||||||
const uint8_t* first_section_addr = nt_headers_addr + optional_header_offset + optional_header_size;
|
const uint8_t* first_section_addr = nt_headers_addr + optional_header_offset + optional_header_size;
|
||||||
|
|
||||||
const auto first_section_absolute = reinterpret_cast<uint64_t>(first_section_addr);
|
const auto first_section_absolute = reinterpret_cast<uint64_t>(first_section_addr);
|
||||||
const auto absolute_base = reinterpret_cast<uint64_t>(&nt_headers);
|
const auto absolute_base = reinterpret_cast<uint64_t>(&nt_headers);
|
||||||
return nt_headers_offset + (first_section_absolute - absolute_base);
|
return nt_headers_offset + (first_section_absolute - absolute_base);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> read_mapped_memory(const emulator& emu, const mapped_module& binary)
|
std::vector<uint8_t> read_mapped_memory(const emulator& emu, const mapped_module& binary)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> memory{};
|
std::vector<uint8_t> memory{};
|
||||||
memory.resize(binary.size_of_image);
|
memory.resize(binary.size_of_image);
|
||||||
emu.read_memory(binary.image_base, memory.data(), memory.size());
|
emu.read_memory(binary.image_base, memory.data(), memory.size());
|
||||||
|
|
||||||
return memory;
|
return memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 uint8_t> buffer,
|
||||||
const PEOptionalHeader_t<std::uint64_t>& optional_header)
|
const PEOptionalHeader_t<std::uint64_t>& optional_header)
|
||||||
{
|
{
|
||||||
auto& export_directory_entry = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
auto& export_directory_entry = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||||
if (export_directory_entry.VirtualAddress == 0 || export_directory_entry.Size == 0)
|
if (export_directory_entry.VirtualAddress == 0 || export_directory_entry.Size == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto export_directory = buffer.as<IMAGE_EXPORT_DIRECTORY>(export_directory_entry.
|
const auto export_directory = buffer.as<IMAGE_EXPORT_DIRECTORY>(export_directory_entry.VirtualAddress).get();
|
||||||
VirtualAddress).get();
|
|
||||||
|
|
||||||
const auto names_count = export_directory.NumberOfNames;
|
const auto names_count = export_directory.NumberOfNames;
|
||||||
//const auto function_count = export_directory.NumberOfFunctions;
|
// const auto function_count = export_directory.NumberOfFunctions;
|
||||||
|
|
||||||
const auto names = buffer.as<DWORD>(export_directory.AddressOfNames);
|
const auto names = buffer.as<DWORD>(export_directory.AddressOfNames);
|
||||||
const auto ordinals = buffer.as<WORD>(export_directory.AddressOfNameOrdinals);
|
const auto ordinals = buffer.as<WORD>(export_directory.AddressOfNameOrdinals);
|
||||||
const auto functions = buffer.as<DWORD>(export_directory.AddressOfFunctions);
|
const auto functions = buffer.as<DWORD>(export_directory.AddressOfFunctions);
|
||||||
|
|
||||||
binary.exports.reserve(names_count);
|
binary.exports.reserve(names_count);
|
||||||
|
|
||||||
for (DWORD i = 0; i < names_count; i++)
|
for (DWORD i = 0; i < names_count; i++)
|
||||||
{
|
{
|
||||||
const auto ordinal = ordinals.get(i);
|
const auto ordinal = ordinals.get(i);
|
||||||
|
|
||||||
exported_symbol symbol{};
|
exported_symbol symbol{};
|
||||||
symbol.ordinal = export_directory.Base + ordinal;
|
symbol.ordinal = export_directory.Base + ordinal;
|
||||||
symbol.rva = functions.get(ordinal);
|
symbol.rva = functions.get(ordinal);
|
||||||
symbol.address = binary.image_base + symbol.rva;
|
symbol.address = binary.image_base + symbol.rva;
|
||||||
symbol.name = buffer.as_string(names.get(i));
|
symbol.name = buffer.as_string(names.get(i));
|
||||||
|
|
||||||
binary.exports.push_back(std::move(symbol));
|
binary.exports.push_back(std::move(symbol));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& symbol : binary.exports)
|
for (const auto& symbol : binary.exports)
|
||||||
{
|
{
|
||||||
binary.address_names.try_emplace(symbol.address, symbol.name);
|
binary.address_names.try_emplace(symbol.address, symbol.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
requires(std::is_integral_v<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<uint8_t> buffer, const uint64_t offset,
|
||||||
const uint64_t delta)
|
const uint64_t delta)
|
||||||
{
|
{
|
||||||
const auto obj = buffer.as<T>(offset);
|
const auto obj = buffer.as<T>(offset);
|
||||||
const auto value = obj.get();
|
const auto value = obj.get();
|
||||||
const auto new_value = value + static_cast<T>(delta);
|
const auto new_value = value + static_cast<T>(delta);
|
||||||
obj.set(new_value);
|
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<uint8_t> buffer,
|
||||||
const PEOptionalHeader_t<std::uint64_t>& optional_header)
|
const PEOptionalHeader_t<std::uint64_t>& optional_header)
|
||||||
{
|
{
|
||||||
const auto delta = binary.image_base - optional_header.ImageBase;
|
const auto delta = binary.image_base - optional_header.ImageBase;
|
||||||
if (delta == 0)
|
if (delta == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto directory = &optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
const auto directory = &optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
||||||
if (directory->Size == 0)
|
if (directory->Size == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto relocation_offset = directory->VirtualAddress;
|
auto relocation_offset = directory->VirtualAddress;
|
||||||
const auto relocation_end = relocation_offset + directory->Size;
|
const auto relocation_end = relocation_offset + directory->Size;
|
||||||
|
|
||||||
while (relocation_offset < relocation_end)
|
while (relocation_offset < relocation_end)
|
||||||
{
|
{
|
||||||
const auto relocation = buffer.as<IMAGE_BASE_RELOCATION>(relocation_offset).get();
|
const auto relocation = buffer.as<IMAGE_BASE_RELOCATION>(relocation_offset).get();
|
||||||
|
|
||||||
if (relocation.VirtualAddress <= 0 || relocation.SizeOfBlock <= sizeof(IMAGE_BASE_RELOCATION))
|
if (relocation.VirtualAddress <= 0 || relocation.SizeOfBlock <= sizeof(IMAGE_BASE_RELOCATION))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto data_size = relocation.SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION);
|
const auto data_size = relocation.SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION);
|
||||||
const auto entry_count = data_size / sizeof(uint16_t);
|
const auto entry_count = data_size / sizeof(uint16_t);
|
||||||
|
|
||||||
const auto entries = buffer.as<uint16_t>(relocation_offset + sizeof(IMAGE_BASE_RELOCATION));
|
const auto entries = buffer.as<uint16_t>(relocation_offset + sizeof(IMAGE_BASE_RELOCATION));
|
||||||
|
|
||||||
relocation_offset += relocation.SizeOfBlock;
|
relocation_offset += relocation.SizeOfBlock;
|
||||||
|
|
||||||
for (size_t i = 0; i < entry_count; ++i)
|
for (size_t i = 0; i < entry_count; ++i)
|
||||||
{
|
{
|
||||||
const auto entry = entries.get(i);
|
const auto entry = entries.get(i);
|
||||||
|
|
||||||
const int type = entry >> 12;
|
const int type = entry >> 12;
|
||||||
const auto offset = static_cast<uint16_t>(entry & 0xfff);
|
const auto offset = static_cast<uint16_t>(entry & 0xfff);
|
||||||
const auto total_offset = relocation.VirtualAddress + offset;
|
const auto total_offset = relocation.VirtualAddress + offset;
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case IMAGE_REL_BASED_ABSOLUTE:
|
case IMAGE_REL_BASED_ABSOLUTE:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IMAGE_REL_BASED_HIGHLOW:
|
case IMAGE_REL_BASED_HIGHLOW:
|
||||||
apply_relocation<DWORD>(buffer, total_offset, delta);
|
apply_relocation<DWORD>(buffer, total_offset, delta);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IMAGE_REL_BASED_DIR64:
|
case IMAGE_REL_BASED_DIR64:
|
||||||
apply_relocation<ULONGLONG>(buffer, total_offset, delta);
|
apply_relocation<ULONGLONG>(buffer, total_offset, delta);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Unknown relocation type: " + std::to_string(type));
|
throw std::runtime_error("Unknown relocation type: " + std::to_string(type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void map_sections(emulator& emu, mapped_module& binary,
|
void map_sections(emulator& emu, mapped_module& binary, const utils::safe_buffer_accessor<const uint8_t> buffer,
|
||||||
const utils::safe_buffer_accessor<const uint8_t> buffer,
|
const PENTHeaders_t<std::uint64_t>& nt_headers, const uint64_t nt_headers_offset)
|
||||||
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);
|
||||||
const auto first_section_offset = get_first_section_offset(nt_headers, nt_headers_offset);
|
const auto sections = buffer.as<IMAGE_SECTION_HEADER>(first_section_offset);
|
||||||
const auto sections = buffer.as<IMAGE_SECTION_HEADER>(first_section_offset);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i)
|
for (size_t i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i)
|
||||||
{
|
{
|
||||||
const auto section = sections.get(i);
|
const auto section = sections.get(i);
|
||||||
const auto target_ptr = binary.image_base + section.VirtualAddress;
|
const auto target_ptr = binary.image_base + section.VirtualAddress;
|
||||||
|
|
||||||
if (section.SizeOfRawData > 0)
|
if (section.SizeOfRawData > 0)
|
||||||
{
|
{
|
||||||
const auto size_of_data = std::min(section.SizeOfRawData, section.Misc.VirtualSize);
|
const auto size_of_data = std::min(section.SizeOfRawData, section.Misc.VirtualSize);
|
||||||
const auto* source_ptr = buffer.get_pointer_for_range(section.PointerToRawData, size_of_data);
|
const auto* source_ptr = buffer.get_pointer_for_range(section.PointerToRawData, size_of_data);
|
||||||
emu.write_memory(target_ptr, source_ptr, size_of_data);
|
emu.write_memory(target_ptr, source_ptr, size_of_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto permissions = memory_permission::none;
|
auto permissions = memory_permission::none;
|
||||||
|
|
||||||
if (section.Characteristics & IMAGE_SCN_MEM_EXECUTE)
|
if (section.Characteristics & IMAGE_SCN_MEM_EXECUTE)
|
||||||
{
|
{
|
||||||
permissions |= memory_permission::exec;
|
permissions |= memory_permission::exec;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (section.Characteristics & IMAGE_SCN_MEM_READ)
|
if (section.Characteristics & IMAGE_SCN_MEM_READ)
|
||||||
{
|
{
|
||||||
permissions |= memory_permission::read;
|
permissions |= memory_permission::read;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (section.Characteristics & IMAGE_SCN_MEM_WRITE)
|
if (section.Characteristics & IMAGE_SCN_MEM_WRITE)
|
||||||
{
|
{
|
||||||
permissions |= memory_permission::write;
|
permissions |= memory_permission::write;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto size_of_section = page_align_up(std::max(section.SizeOfRawData, section.Misc.VirtualSize));
|
const auto size_of_section = page_align_up(std::max(section.SizeOfRawData, section.Misc.VirtualSize));
|
||||||
|
|
||||||
emu.protect_memory(target_ptr, size_of_section, permissions, nullptr);
|
emu.protect_memory(target_ptr, size_of_section, permissions, nullptr);
|
||||||
|
|
||||||
mapped_section section_info{};
|
mapped_section section_info{};
|
||||||
section_info.region.start = target_ptr;
|
section_info.region.start = target_ptr;
|
||||||
section_info.region.length = size_of_section;
|
section_info.region.length = size_of_section;
|
||||||
section_info.region.permissions = permissions;
|
section_info.region.permissions = permissions;
|
||||||
|
|
||||||
for (size_t j = 0; j < sizeof(section.Name) && section.Name[j]; ++j)
|
for (size_t j = 0; j < sizeof(section.Name) && section.Name[j]; ++j)
|
||||||
{
|
{
|
||||||
section_info.name.push_back(static_cast<char>(section.Name[j]));
|
section_info.name.push_back(static_cast<char>(section.Name[j]));
|
||||||
}
|
}
|
||||||
|
|
||||||
binary.sections.push_back(std::move(section_info));
|
binary.sections.push_back(std::move(section_info));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mapped_module map_module_from_data(emulator& emu, const std::span<const uint8_t> data,
|
mapped_module map_module_from_data(emulator& emu, const std::span<const uint8_t> data, std::filesystem::path file)
|
||||||
std::filesystem::path file)
|
|
||||||
{
|
{
|
||||||
mapped_module binary{};
|
mapped_module binary{};
|
||||||
binary.path = std::move(file);
|
binary.path = std::move(file);
|
||||||
binary.name = binary.path.filename().string();
|
binary.name = binary.path.filename().string();
|
||||||
|
|
||||||
utils::safe_buffer_accessor buffer{data};
|
utils::safe_buffer_accessor buffer{data};
|
||||||
|
|
||||||
const auto dos_header = buffer.as<PEDosHeader_t>(0).get();
|
const auto dos_header = buffer.as<PEDosHeader_t>(0).get();
|
||||||
const auto nt_headers_offset = dos_header.e_lfanew;
|
const auto nt_headers_offset = dos_header.e_lfanew;
|
||||||
|
|
||||||
const auto nt_headers = buffer.as<PENTHeaders_t<std::uint64_t>>(nt_headers_offset).get();
|
const auto nt_headers = buffer.as<PENTHeaders_t<std::uint64_t>>(nt_headers_offset).get();
|
||||||
auto& optional_header = nt_headers.OptionalHeader;
|
auto& optional_header = nt_headers.OptionalHeader;
|
||||||
|
|
||||||
if (nt_headers.FileHeader.Machine != PEMachineType::AMD64)
|
if (nt_headers.FileHeader.Machine != PEMachineType::AMD64)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Unsupported architecture!");
|
throw std::runtime_error("Unsupported architecture!");
|
||||||
}
|
}
|
||||||
|
|
||||||
binary.image_base = optional_header.ImageBase;
|
binary.image_base = optional_header.ImageBase;
|
||||||
binary.size_of_image = page_align_up(optional_header.SizeOfImage); // TODO: Sanitize
|
binary.size_of_image = page_align_up(optional_header.SizeOfImage); // TODO: Sanitize
|
||||||
|
|
||||||
if (!emu.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::read))
|
if (!emu.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::read))
|
||||||
{
|
{
|
||||||
binary.image_base = emu.find_free_allocation_base(binary.size_of_image);
|
binary.image_base = emu.find_free_allocation_base(binary.size_of_image);
|
||||||
const auto is_dll = nt_headers.FileHeader.Characteristics & IMAGE_FILE_DLL;
|
const auto is_dll = nt_headers.FileHeader.Characteristics & IMAGE_FILE_DLL;
|
||||||
const auto has_dynamic_base =
|
const auto has_dynamic_base = optional_header.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
|
||||||
optional_header.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
|
const auto is_relocatable = is_dll || has_dynamic_base;
|
||||||
const auto is_relocatable = is_dll || has_dynamic_base;
|
|
||||||
|
|
||||||
if (!is_relocatable || !emu.allocate_memory(binary.image_base, binary.size_of_image,
|
if (!is_relocatable || !emu.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::read))
|
||||||
memory_permission::read))
|
{
|
||||||
{
|
throw std::runtime_error("Memory range not allocatable");
|
||||||
throw std::runtime_error("Memory range not allocatable");
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
binary.entry_point = binary.image_base + optional_header.AddressOfEntryPoint;
|
binary.entry_point = binary.image_base + optional_header.AddressOfEntryPoint;
|
||||||
|
|
||||||
const auto* header_buffer = buffer.get_pointer_for_range(0, optional_header.SizeOfHeaders);
|
const auto* header_buffer = buffer.get_pointer_for_range(0, optional_header.SizeOfHeaders);
|
||||||
emu.write_memory(binary.image_base, header_buffer,
|
emu.write_memory(binary.image_base, header_buffer, optional_header.SizeOfHeaders);
|
||||||
optional_header.SizeOfHeaders);
|
|
||||||
|
|
||||||
map_sections(emu, binary, buffer, nt_headers, nt_headers_offset);
|
map_sections(emu, binary, buffer, nt_headers, nt_headers_offset);
|
||||||
|
|
||||||
auto mapped_memory = read_mapped_memory(emu, binary);
|
auto mapped_memory = read_mapped_memory(emu, binary);
|
||||||
utils::safe_buffer_accessor<uint8_t> mapped_buffer{mapped_memory};
|
utils::safe_buffer_accessor<uint8_t> mapped_buffer{mapped_memory};
|
||||||
|
|
||||||
apply_relocations(binary, mapped_buffer, optional_header);
|
apply_relocations(binary, mapped_buffer, optional_header);
|
||||||
collect_exports(binary, mapped_buffer, optional_header);
|
collect_exports(binary, mapped_buffer, optional_header);
|
||||||
|
|
||||||
emu.write_memory(binary.image_base, mapped_memory.data(), mapped_memory.size());
|
emu.write_memory(binary.image_base, mapped_memory.data(), mapped_memory.size());
|
||||||
|
|
||||||
return binary;
|
return binary;
|
||||||
}
|
}
|
||||||
|
|
||||||
mapped_module map_module_from_file(emulator& emu, std::filesystem::path file)
|
mapped_module map_module_from_file(emulator& emu, std::filesystem::path file)
|
||||||
{
|
{
|
||||||
const auto data = utils::io::read_file(file);
|
const auto data = utils::io::read_file(file);
|
||||||
if (data.empty())
|
if (data.empty())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Bad file data");
|
throw std::runtime_error("Bad file data");
|
||||||
}
|
}
|
||||||
|
|
||||||
return map_module_from_data(emu, data, std::move(file));
|
return map_module_from_data(emu, data, std::move(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool unmap_module(emulator& emu, const mapped_module& mod)
|
bool unmap_module(emulator& emu, const mapped_module& mod)
|
||||||
{
|
{
|
||||||
return emu.release_memory(mod.image_base, mod.size_of_image);
|
return emu.release_memory(mod.image_base, mod.size_of_image);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
#include <x64_emulator.hpp>
|
#include <x64_emulator.hpp>
|
||||||
#include "mapped_module.hpp"
|
#include "mapped_module.hpp"
|
||||||
|
|
||||||
mapped_module map_module_from_data(emulator& emu, std::span<const uint8_t> data,
|
mapped_module map_module_from_data(emulator& emu, std::span<const uint8_t> data, std::filesystem::path file);
|
||||||
std::filesystem::path file);
|
|
||||||
mapped_module map_module_from_file(emulator& emu, std::filesystem::path file);
|
mapped_module map_module_from_file(emulator& emu, std::filesystem::path file);
|
||||||
|
|
||||||
bool unmap_module(emulator& emu, const mapped_module& mod);
|
bool unmap_module(emulator& emu, const mapped_module& mod);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -5,216 +5,216 @@
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
constexpr uint64_t MAIN_ROOT_OFFSET = 0x1000;
|
constexpr uint64_t MAIN_ROOT_OFFSET = 0x1000;
|
||||||
constexpr uint64_t MAIN_KEY_BLOCK_OFFSET = MAIN_ROOT_OFFSET + 0x20;
|
constexpr uint64_t MAIN_KEY_BLOCK_OFFSET = MAIN_ROOT_OFFSET + 0x20;
|
||||||
|
|
||||||
struct offset_entry_t
|
struct offset_entry_t
|
||||||
{
|
{
|
||||||
int32_t offset;
|
int32_t offset;
|
||||||
int32_t hash;
|
int32_t hash;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct offsets_t
|
struct offsets_t
|
||||||
{
|
{
|
||||||
int32_t block_size;
|
int32_t block_size;
|
||||||
char block_type[2];
|
char block_type[2];
|
||||||
int16_t count;
|
int16_t count;
|
||||||
offset_entry_t entries[1];
|
offset_entry_t entries[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct key_block_t
|
struct key_block_t
|
||||||
{
|
{
|
||||||
int32_t block_size;
|
int32_t block_size;
|
||||||
char block_type[2];
|
char block_type[2];
|
||||||
uint8_t dummya[18];
|
uint8_t dummya[18];
|
||||||
int32_t subkey_count;
|
int32_t subkey_count;
|
||||||
uint8_t dummyb[4];
|
uint8_t dummyb[4];
|
||||||
int32_t subkeys;
|
int32_t subkeys;
|
||||||
uint8_t dummyc[4];
|
uint8_t dummyc[4];
|
||||||
int32_t value_count;
|
int32_t value_count;
|
||||||
int32_t offsets;
|
int32_t offsets;
|
||||||
uint8_t dummyd[28];
|
uint8_t dummyd[28];
|
||||||
int16_t len;
|
int16_t len;
|
||||||
int16_t du;
|
int16_t du;
|
||||||
char name[255];
|
char name[255];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct value_block_t
|
struct value_block_t
|
||||||
{
|
{
|
||||||
int32_t block_size;
|
int32_t block_size;
|
||||||
char block_type[2];
|
char block_type[2];
|
||||||
int16_t name_len;
|
int16_t name_len;
|
||||||
int32_t size;
|
int32_t size;
|
||||||
int32_t offset;
|
int32_t offset;
|
||||||
int32_t value_type;
|
int32_t value_type;
|
||||||
int16_t flags;
|
int16_t flags;
|
||||||
int16_t dummy;
|
int16_t dummy;
|
||||||
char name[255];
|
char name[255];
|
||||||
};
|
};
|
||||||
|
|
||||||
bool read_file_data_safe(std::ifstream& file, const uint64_t offset, void* buffer, const size_t size)
|
bool read_file_data_safe(std::ifstream& file, const uint64_t offset, void* buffer, const size_t size)
|
||||||
{
|
{
|
||||||
if (file.bad())
|
if (file.bad())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
file.clear();
|
file.clear();
|
||||||
|
|
||||||
if (!file.good())
|
if (!file.good())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
file.seekg(static_cast<std::streamoff>(offset));
|
file.seekg(static_cast<std::streamoff>(offset));
|
||||||
|
|
||||||
if (!file.good())
|
if (!file.good())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
file.read(static_cast<char*>(buffer), static_cast<std::streamsize>(size));
|
file.read(static_cast<char*>(buffer), static_cast<std::streamsize>(size));
|
||||||
|
|
||||||
return file.good();
|
return file.good();
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_file_data(std::ifstream& file, const uint64_t offset, void* buffer, const size_t size)
|
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))
|
if (!read_file_data_safe(file, offset, buffer, size))
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Failed to read file data");
|
throw std::runtime_error("Failed to read file data");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::byte> read_file_data(std::ifstream& file, const uint64_t offset, const size_t size)
|
std::vector<std::byte> read_file_data(std::ifstream& file, const uint64_t offset, const size_t size)
|
||||||
{
|
{
|
||||||
std::vector<std::byte> result{};
|
std::vector<std::byte> result{};
|
||||||
result.resize(size);
|
result.resize(size);
|
||||||
|
|
||||||
read_file_data(file, offset, result.data(), size);
|
read_file_data(file, offset, result.data(), size);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string read_file_data_string(std::ifstream& file, const uint64_t offset, const size_t size)
|
std::string read_file_data_string(std::ifstream& file, const uint64_t offset, const size_t size)
|
||||||
{
|
{
|
||||||
std::string result{};
|
std::string result{};
|
||||||
result.resize(size);
|
result.resize(size);
|
||||||
|
|
||||||
read_file_data(file, offset, result.data(), size);
|
read_file_data(file, offset, result.data(), size);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
requires(std::is_trivially_copyable_v<T>)
|
requires(std::is_trivially_copyable_v<T>)
|
||||||
T read_file_object(std::ifstream& file, const uint64_t offset, const size_t array_index = 0)
|
T read_file_object(std::ifstream& file, const uint64_t offset, const size_t array_index = 0)
|
||||||
{
|
{
|
||||||
T obj{};
|
T obj{};
|
||||||
read_file_data(file, offset + (array_index * sizeof(T)), &obj, sizeof(T));
|
read_file_data(file, offset + (array_index * sizeof(T)), &obj, sizeof(T));
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
hive_key parse_root_block(std::ifstream& file, const std::filesystem::path& file_path)
|
hive_key parse_root_block(std::ifstream& file, const std::filesystem::path& file_path)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (read_file_data_string(file, 0, 4) != "regf")
|
if (read_file_data_string(file, 0, 4) != "regf")
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Invalid signature");
|
throw std::runtime_error("Invalid signature");
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto key_block = read_file_object<key_block_t>(file, MAIN_KEY_BLOCK_OFFSET);
|
const auto key_block = read_file_object<key_block_t>(file, MAIN_KEY_BLOCK_OFFSET);
|
||||||
|
|
||||||
return {key_block.subkeys, key_block.value_count, key_block.offsets};
|
return {key_block.subkeys, key_block.value_count, key_block.offsets};
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Bad hive file '" + file_path.string() + "': " + e.what());
|
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)
|
const hive_value* hive_key::get_value(std::ifstream& file, const std::string_view name)
|
||||||
{
|
{
|
||||||
this->parse(file);
|
this->parse(file);
|
||||||
|
|
||||||
const auto entry = this->values_.find(name);
|
const auto entry = this->values_.find(name);
|
||||||
if (entry == this->values_.end())
|
if (entry == this->values_.end())
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& value = entry->second;
|
auto& value = entry->second;
|
||||||
|
|
||||||
if (!value.parsed)
|
if (!value.parsed)
|
||||||
{
|
{
|
||||||
value.data = read_file_data(file, MAIN_ROOT_OFFSET + value.data_offset, value.data_length);
|
value.data = read_file_data(file, MAIN_ROOT_OFFSET + value.data_offset, value.data_length);
|
||||||
value.parsed = true;
|
value.parsed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return &value;
|
return &value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hive_key::parse(std::ifstream& file)
|
void hive_key::parse(std::ifstream& file)
|
||||||
{
|
{
|
||||||
if (this->parsed_)
|
if (this->parsed_)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->parsed_ = true;
|
this->parsed_ = true;
|
||||||
|
|
||||||
// Values
|
// Values
|
||||||
|
|
||||||
for (auto i = 0; i < this->value_count_; i++)
|
for (auto i = 0; i < this->value_count_; i++)
|
||||||
{
|
{
|
||||||
const auto offset = read_file_object<int>(file, MAIN_ROOT_OFFSET + this->value_offsets_ + 4, i);
|
const auto offset = read_file_object<int>(file, MAIN_ROOT_OFFSET + this->value_offsets_ + 4, i);
|
||||||
const auto value = read_file_object<value_block_t>(file, MAIN_ROOT_OFFSET + offset);
|
const auto value = read_file_object<value_block_t>(file, MAIN_ROOT_OFFSET + offset);
|
||||||
|
|
||||||
std::string value_name(value.name, std::min(value.name_len, static_cast<short>(sizeof(value.name))));
|
std::string value_name(value.name, std::min(value.name_len, static_cast<short>(sizeof(value.name))));
|
||||||
|
|
||||||
raw_hive_value raw_value{};
|
raw_hive_value raw_value{};
|
||||||
raw_value.parsed = false;
|
raw_value.parsed = false;
|
||||||
raw_value.type = value.value_type;
|
raw_value.type = value.value_type;
|
||||||
raw_value.name = value_name;
|
raw_value.name = value_name;
|
||||||
raw_value.data_length = value.size & 0xffff;
|
raw_value.data_length = value.size & 0xffff;
|
||||||
raw_value.data_offset = value.offset + 4;
|
raw_value.data_offset = value.offset + 4;
|
||||||
|
|
||||||
if (value.size & 1 << 31)
|
if (value.size & 1 << 31)
|
||||||
{
|
{
|
||||||
raw_value.data_offset = offset + static_cast<int>(offsetof(value_block_t, offset));
|
raw_value.data_offset = offset + static_cast<int>(offsetof(value_block_t, offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::string::to_lower_inplace(value_name);
|
utils::string::to_lower_inplace(value_name);
|
||||||
this->values_[std::move(value_name)] = std::move(raw_value);
|
this->values_[std::move(value_name)] = std::move(raw_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subkeys
|
// Subkeys
|
||||||
|
|
||||||
const auto item = read_file_object<offsets_t>(file, MAIN_ROOT_OFFSET + this->subkey_block_offset_);
|
const auto item = read_file_object<offsets_t>(file, MAIN_ROOT_OFFSET + this->subkey_block_offset_);
|
||||||
|
|
||||||
if (item.block_type[1] != 'f' && item.block_type[1] != 'h')
|
if (item.block_type[1] != 'f' && item.block_type[1] != 'h')
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto entry_offsets = this->subkey_block_offset_ + offsetof(offsets_t, entries);
|
const auto entry_offsets = this->subkey_block_offset_ + offsetof(offsets_t, entries);
|
||||||
|
|
||||||
for (short i = 0; i < item.count; ++i)
|
for (short i = 0; i < item.count; ++i)
|
||||||
{
|
{
|
||||||
const auto offset_entry = read_file_object<offset_entry_t>(file, MAIN_ROOT_OFFSET + entry_offsets, i);
|
const auto offset_entry = read_file_object<offset_entry_t>(file, MAIN_ROOT_OFFSET + entry_offsets, i);
|
||||||
|
|
||||||
const auto subkey_block_offset = MAIN_ROOT_OFFSET + offset_entry.offset;
|
const auto subkey_block_offset = MAIN_ROOT_OFFSET + offset_entry.offset;
|
||||||
const auto subkey = read_file_object<key_block_t>(file, subkey_block_offset);
|
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))));
|
std::string subkey_name(subkey.name, std::min(subkey.len, static_cast<int16_t>(sizeof(subkey.name))));
|
||||||
utils::string::to_lower_inplace(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});
|
this->sub_keys_.emplace(std::move(subkey_name), hive_key{subkey.subkeys, subkey.value_count, subkey.offsets});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hive_parser::hive_parser(const std::filesystem::path& file_path)
|
hive_parser::hive_parser(const std::filesystem::path& file_path)
|
||||||
: file_(file_path, std::ios::binary)
|
: file_(file_path, std::ios::binary),
|
||||||
, root_key_(parse_root_block(file_, file_path))
|
root_key_(parse_root_block(file_, file_path))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,95 +8,95 @@
|
|||||||
|
|
||||||
struct hive_value
|
struct hive_value
|
||||||
{
|
{
|
||||||
uint32_t type{};
|
uint32_t type{};
|
||||||
std::string name{};
|
std::string name{};
|
||||||
std::vector<std::byte> data{};
|
std::vector<std::byte> data{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class hive_key
|
class hive_key
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
hive_key(const int subkey_block_offset, const int value_count, const int value_offsets)
|
hive_key(const int subkey_block_offset, const int value_count, const int value_offsets)
|
||||||
: subkey_block_offset_(subkey_block_offset)
|
: subkey_block_offset_(subkey_block_offset),
|
||||||
, value_count_(value_count)
|
value_count_(value_count),
|
||||||
, value_offsets_(value_offsets)
|
value_offsets_(value_offsets)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::unordered_string_map<hive_key>& get_sub_keys(std::ifstream& file)
|
utils::unordered_string_map<hive_key>& get_sub_keys(std::ifstream& file)
|
||||||
{
|
{
|
||||||
this->parse(file);
|
this->parse(file);
|
||||||
return this->sub_keys_;
|
return this->sub_keys_;
|
||||||
}
|
}
|
||||||
|
|
||||||
hive_key* get_sub_key(std::ifstream& file, const std::string_view name)
|
hive_key* get_sub_key(std::ifstream& file, const std::string_view name)
|
||||||
{
|
{
|
||||||
auto& sub_keys = this->get_sub_keys(file);
|
auto& sub_keys = this->get_sub_keys(file);
|
||||||
const auto entry = sub_keys.find(name);
|
const auto entry = sub_keys.find(name);
|
||||||
|
|
||||||
if (entry == sub_keys.end())
|
if (entry == sub_keys.end())
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return &entry->second;
|
return &entry->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hive_value* get_value(std::ifstream& file, const std::string_view name);
|
const hive_value* get_value(std::ifstream& file, const std::string_view name);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct raw_hive_value : hive_value
|
struct raw_hive_value : hive_value
|
||||||
{
|
{
|
||||||
bool parsed{false};
|
bool parsed{false};
|
||||||
int data_offset{};
|
int data_offset{};
|
||||||
size_t data_length{};
|
size_t data_length{};
|
||||||
};
|
};
|
||||||
|
|
||||||
bool parsed_{false};
|
bool parsed_{false};
|
||||||
utils::unordered_string_map<hive_key> sub_keys_{};
|
utils::unordered_string_map<hive_key> sub_keys_{};
|
||||||
utils::unordered_string_map<raw_hive_value> values_{};
|
utils::unordered_string_map<raw_hive_value> values_{};
|
||||||
|
|
||||||
const int subkey_block_offset_{};
|
const int subkey_block_offset_{};
|
||||||
const int value_count_{};
|
const int value_count_{};
|
||||||
const int value_offsets_{};
|
const int value_offsets_{};
|
||||||
|
|
||||||
void parse(std::ifstream& file);
|
void parse(std::ifstream& file);
|
||||||
};
|
};
|
||||||
|
|
||||||
class hive_parser
|
class hive_parser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit hive_parser(const std::filesystem::path& file_path);
|
explicit hive_parser(const std::filesystem::path& file_path);
|
||||||
|
|
||||||
[[nodiscard]] hive_key* get_sub_key(const std::filesystem::path& key)
|
[[nodiscard]] hive_key* get_sub_key(const std::filesystem::path& key)
|
||||||
{
|
{
|
||||||
hive_key* current_key = &this->root_key_;
|
hive_key* current_key = &this->root_key_;
|
||||||
|
|
||||||
for (const auto& key_part : key)
|
for (const auto& key_part : key)
|
||||||
{
|
{
|
||||||
if (!current_key)
|
if (!current_key)
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
current_key = current_key->get_sub_key(this->file_, key_part.string());
|
current_key = current_key->get_sub_key(this->file_, key_part.string());
|
||||||
}
|
}
|
||||||
|
|
||||||
return current_key;
|
return current_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] const hive_value* get_value(const std::filesystem::path& key, const std::string_view name)
|
[[nodiscard]] const hive_value* get_value(const std::filesystem::path& key, const std::string_view name)
|
||||||
{
|
{
|
||||||
auto* sub_key = this->get_sub_key(key);
|
auto* sub_key = this->get_sub_key(key);
|
||||||
if (!sub_key)
|
if (!sub_key)
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sub_key->get_value(this->file_, name);
|
return sub_key->get_value(this->file_, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::ifstream file_{};
|
std::ifstream file_{};
|
||||||
hive_key root_key_;
|
hive_key root_key_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,33 +7,33 @@
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
std::filesystem::path canonicalize_path(const std::filesystem::path& key)
|
std::filesystem::path canonicalize_path(const std::filesystem::path& key)
|
||||||
{
|
{
|
||||||
auto path = key.lexically_normal().wstring();
|
auto path = key.lexically_normal().wstring();
|
||||||
return utils::string::to_lower_consume(path);
|
return utils::string::to_lower_consume(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_subpath(const std::filesystem::path& root, const std::filesystem::path& p)
|
bool is_subpath(const std::filesystem::path& root, const std::filesystem::path& p)
|
||||||
{
|
{
|
||||||
auto root_it = root.begin();
|
auto root_it = root.begin();
|
||||||
auto p_it = p.begin();
|
auto p_it = p.begin();
|
||||||
|
|
||||||
for (; root_it != root.end(); ++root_it, ++p_it)
|
for (; root_it != root.end(); ++root_it, ++p_it)
|
||||||
{
|
{
|
||||||
if (p_it == p.end() || *root_it != *p_it)
|
if (p_it == p.end() || *root_it != *p_it)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void register_hive(registry_manager::hive_map& hives,
|
void register_hive(registry_manager::hive_map& hives, const std::filesystem::path& key,
|
||||||
const std::filesystem::path& key, const std::filesystem::path& file)
|
const std::filesystem::path& file)
|
||||||
{
|
{
|
||||||
hives[canonicalize_path(key)] = std::make_unique<hive_parser>(file);
|
hives[canonicalize_path(key)] = std::make_unique<hive_parser>(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
registry_manager::registry_manager() = default;
|
registry_manager::registry_manager() = default;
|
||||||
@@ -42,130 +42,130 @@ registry_manager::registry_manager(registry_manager&&) noexcept = default;
|
|||||||
registry_manager& registry_manager::operator=(registry_manager&&) noexcept = default;
|
registry_manager& registry_manager::operator=(registry_manager&&) noexcept = default;
|
||||||
|
|
||||||
registry_manager::registry_manager(const std::filesystem::path& hive_path)
|
registry_manager::registry_manager(const std::filesystem::path& hive_path)
|
||||||
: hive_path_(absolute(hive_path))
|
: hive_path_(absolute(hive_path))
|
||||||
{
|
{
|
||||||
this->setup();
|
this->setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void registry_manager::setup()
|
void registry_manager::setup()
|
||||||
{
|
{
|
||||||
this->path_mapping_.clear();
|
this->path_mapping_.clear();
|
||||||
this->hives_.clear();
|
this->hives_.clear();
|
||||||
|
|
||||||
const std::filesystem::path root = R"(\registry)";
|
const std::filesystem::path root = R"(\registry)";
|
||||||
const std::filesystem::path machine = root / "machine";
|
const std::filesystem::path machine = root / "machine";
|
||||||
|
|
||||||
register_hive(this->hives_, machine / "system", this->hive_path_ / "SYSTEM");
|
register_hive(this->hives_, machine / "system", this->hive_path_ / "SYSTEM");
|
||||||
register_hive(this->hives_, machine / "security", this->hive_path_ / "SECURITY");
|
register_hive(this->hives_, machine / "security", this->hive_path_ / "SECURITY");
|
||||||
register_hive(this->hives_, machine / "sam", this->hive_path_ / "SAM");
|
register_hive(this->hives_, machine / "sam", this->hive_path_ / "SAM");
|
||||||
register_hive(this->hives_, machine / "software", this->hive_path_ / "SOFTWARE");
|
register_hive(this->hives_, machine / "software", this->hive_path_ / "SOFTWARE");
|
||||||
register_hive(this->hives_, machine / "system", this->hive_path_ / "SYSTEM");
|
register_hive(this->hives_, machine / "system", this->hive_path_ / "SYSTEM");
|
||||||
register_hive(this->hives_, machine / "hardware", this->hive_path_ / "HARDWARE");
|
register_hive(this->hives_, machine / "hardware", this->hive_path_ / "HARDWARE");
|
||||||
|
|
||||||
register_hive(this->hives_, root / "user", this->hive_path_ / "NTUSER.dat");
|
register_hive(this->hives_, root / "user", this->hive_path_ / "NTUSER.dat");
|
||||||
|
|
||||||
this->add_path_mapping(machine / "system" / "CurrentControlSet", machine / "system" / "ControlSet001");
|
this->add_path_mapping(machine / "system" / "CurrentControlSet", machine / "system" / "ControlSet001");
|
||||||
}
|
}
|
||||||
|
|
||||||
void registry_manager::serialize(utils::buffer_serializer& buffer) const
|
void registry_manager::serialize(utils::buffer_serializer& buffer) const
|
||||||
{
|
{
|
||||||
buffer.write(this->hive_path_);
|
buffer.write(this->hive_path_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void registry_manager::deserialize(utils::buffer_deserializer& buffer)
|
void registry_manager::deserialize(utils::buffer_deserializer& buffer)
|
||||||
{
|
{
|
||||||
buffer.read(this->hive_path_);
|
buffer.read(this->hive_path_);
|
||||||
this->setup();
|
this->setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path registry_manager::normalize_path(const std::filesystem::path& path) const
|
std::filesystem::path registry_manager::normalize_path(const std::filesystem::path& path) const
|
||||||
{
|
{
|
||||||
auto canonical_path = canonicalize_path(path);
|
auto canonical_path = canonicalize_path(path);
|
||||||
|
|
||||||
for (const auto& mapping : this->path_mapping_)
|
for (const auto& mapping : this->path_mapping_)
|
||||||
{
|
{
|
||||||
if (is_subpath(mapping.first, canonical_path))
|
if (is_subpath(mapping.first, canonical_path))
|
||||||
{
|
{
|
||||||
return mapping.second / canonical_path.lexically_relative(mapping.first);
|
return mapping.second / canonical_path.lexically_relative(mapping.first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return canonical_path;
|
return canonical_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void registry_manager::add_path_mapping(const std::filesystem::path& key, const std::filesystem::path& value)
|
void registry_manager::add_path_mapping(const std::filesystem::path& key, const std::filesystem::path& value)
|
||||||
{
|
{
|
||||||
this->path_mapping_[canonicalize_path(key)] = canonicalize_path(value);
|
this->path_mapping_[canonicalize_path(key)] = canonicalize_path(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<registry_key> registry_manager::get_key(const std::filesystem::path& key)
|
std::optional<registry_key> registry_manager::get_key(const std::filesystem::path& key)
|
||||||
{
|
{
|
||||||
const auto normal_key = this->normalize_path(key);
|
const auto normal_key = this->normalize_path(key);
|
||||||
|
|
||||||
if (is_subpath(normal_key, "\\registry\\machine"))
|
if (is_subpath(normal_key, "\\registry\\machine"))
|
||||||
{
|
{
|
||||||
registry_key reg_key{};
|
registry_key reg_key{};
|
||||||
reg_key.hive = normal_key;
|
reg_key.hive = normal_key;
|
||||||
return {std::move(reg_key)};
|
return {std::move(reg_key)};
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto iterator = this->find_hive(normal_key);
|
const auto iterator = this->find_hive(normal_key);
|
||||||
if (iterator == this->hives_.end())
|
if (iterator == this->hives_.end())
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
registry_key reg_key{};
|
registry_key reg_key{};
|
||||||
reg_key.hive = iterator->first;
|
reg_key.hive = iterator->first;
|
||||||
reg_key.path = normal_key.lexically_relative(reg_key.hive);
|
reg_key.path = normal_key.lexically_relative(reg_key.hive);
|
||||||
|
|
||||||
if (reg_key.path.empty())
|
if (reg_key.path.empty())
|
||||||
{
|
{
|
||||||
return {std::move(reg_key)};
|
return {std::move(reg_key)};
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto entry = iterator->second->get_sub_key(reg_key.path);
|
const auto entry = iterator->second->get_sub_key(reg_key.path);
|
||||||
if (!entry)
|
if (!entry)
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {std::move(reg_key)};
|
return {std::move(reg_key)};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<registry_value> registry_manager::get_value(const registry_key& key, std::string name)
|
std::optional<registry_value> registry_manager::get_value(const registry_key& key, std::string name)
|
||||||
{
|
{
|
||||||
utils::string::to_lower_inplace(name);
|
utils::string::to_lower_inplace(name);
|
||||||
|
|
||||||
const auto iterator = this->hives_.find(key.hive);
|
const auto iterator = this->hives_.find(key.hive);
|
||||||
if (iterator == this->hives_.end())
|
if (iterator == this->hives_.end())
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* entry = iterator->second->get_value(key.path, name);
|
auto* entry = iterator->second->get_value(key.path, name);
|
||||||
if (!entry)
|
if (!entry)
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
registry_value v{};
|
registry_value v{};
|
||||||
v.type = entry->type;
|
v.type = entry->type;
|
||||||
v.name = entry->name;
|
v.name = entry->name;
|
||||||
v.data = entry->data;
|
v.data = entry->data;
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
registry_manager::hive_map::iterator registry_manager::find_hive(const std::filesystem::path& key)
|
registry_manager::hive_map::iterator registry_manager::find_hive(const std::filesystem::path& key)
|
||||||
{
|
{
|
||||||
for (auto i = this->hives_.begin(); i != this->hives_.end(); ++i)
|
for (auto i = this->hives_.begin(); i != this->hives_.end(); ++i)
|
||||||
{
|
{
|
||||||
if (is_subpath(i->first, key))
|
if (is_subpath(i->first, key))
|
||||||
{
|
{
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this->hives_.end();
|
return this->hives_.end();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,61 +6,60 @@
|
|||||||
|
|
||||||
struct registry_key
|
struct registry_key
|
||||||
{
|
{
|
||||||
std::filesystem::path hive{};
|
std::filesystem::path hive{};
|
||||||
std::filesystem::path path{};
|
std::filesystem::path path{};
|
||||||
|
|
||||||
void serialize(utils::buffer_serializer& buffer) const
|
void serialize(utils::buffer_serializer& buffer) const
|
||||||
{
|
{
|
||||||
buffer.write(this->hive);
|
buffer.write(this->hive);
|
||||||
buffer.write(this->path);
|
buffer.write(this->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void deserialize(utils::buffer_deserializer& buffer)
|
void deserialize(utils::buffer_deserializer& buffer)
|
||||||
{
|
{
|
||||||
buffer.read(this->hive);
|
buffer.read(this->hive);
|
||||||
buffer.read(this->path);
|
buffer.read(this->path);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct registry_value
|
struct registry_value
|
||||||
{
|
{
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
std::string_view name;
|
std::string_view name;
|
||||||
std::span<const std::byte> data;
|
std::span<const std::byte> data;
|
||||||
};
|
};
|
||||||
|
|
||||||
class registry_manager
|
class registry_manager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using hive_ptr = std::unique_ptr<hive_parser>;
|
using hive_ptr = std::unique_ptr<hive_parser>;
|
||||||
using hive_map = std::unordered_map<std::filesystem::path, hive_ptr>;
|
using hive_map = std::unordered_map<std::filesystem::path, hive_ptr>;
|
||||||
|
|
||||||
registry_manager();
|
registry_manager();
|
||||||
registry_manager(const std::filesystem::path& hive_path);
|
registry_manager(const std::filesystem::path& hive_path);
|
||||||
~registry_manager();
|
~registry_manager();
|
||||||
|
|
||||||
registry_manager(registry_manager&&) noexcept;
|
registry_manager(registry_manager&&) noexcept;
|
||||||
registry_manager& operator=(registry_manager&&) noexcept;
|
registry_manager& operator=(registry_manager&&) noexcept;
|
||||||
|
|
||||||
registry_manager(const registry_manager&) = delete;
|
registry_manager(const registry_manager&) = delete;
|
||||||
registry_manager& operator=(const registry_manager&) = delete;
|
registry_manager& operator=(const registry_manager&) = delete;
|
||||||
|
|
||||||
|
void serialize(utils::buffer_serializer& buffer) const;
|
||||||
|
void deserialize(utils::buffer_deserializer& buffer);
|
||||||
|
|
||||||
void serialize(utils::buffer_serializer& buffer) const;
|
std::optional<registry_key> get_key(const std::filesystem::path& key);
|
||||||
void deserialize(utils::buffer_deserializer& buffer);
|
std::optional<registry_value> get_value(const registry_key& key, std::string name);
|
||||||
|
|
||||||
std::optional<registry_key> get_key(const std::filesystem::path& key);
|
private:
|
||||||
std::optional<registry_value> get_value(const registry_key& key, std::string name);
|
std::filesystem::path hive_path_{};
|
||||||
|
hive_map hives_{};
|
||||||
|
std::unordered_map<std::filesystem::path, std::filesystem::path> path_mapping_{};
|
||||||
|
|
||||||
private:
|
std::filesystem::path normalize_path(const std::filesystem::path& path) const;
|
||||||
std::filesystem::path hive_path_{};
|
void add_path_mapping(const std::filesystem::path& key, const std::filesystem::path& value);
|
||||||
hive_map hives_{};
|
|
||||||
std::unordered_map<std::filesystem::path, std::filesystem::path> path_mapping_{};
|
|
||||||
|
|
||||||
std::filesystem::path normalize_path(const std::filesystem::path& path) const;
|
hive_map::iterator find_hive(const std::filesystem::path& key);
|
||||||
void add_path_mapping(const std::filesystem::path& key, const std::filesystem::path& value);
|
|
||||||
|
|
||||||
hive_map::iterator find_hive(const std::filesystem::path& key);
|
void setup();
|
||||||
|
|
||||||
void setup();
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,32 +2,32 @@
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
#pragma warning(disable: 4005)
|
#pragma warning(disable : 4005)
|
||||||
#pragma warning(disable: 4127)
|
#pragma warning(disable : 4127)
|
||||||
#pragma warning(disable: 4201)
|
#pragma warning(disable : 4201)
|
||||||
#pragma warning(disable: 4244)
|
#pragma warning(disable : 4244)
|
||||||
#pragma warning(disable: 4245)
|
#pragma warning(disable : 4245)
|
||||||
#pragma warning(disable: 4324)
|
#pragma warning(disable : 4324)
|
||||||
#pragma warning(disable: 4458)
|
#pragma warning(disable : 4458)
|
||||||
#pragma warning(disable: 4471)
|
#pragma warning(disable : 4471)
|
||||||
#pragma warning(disable: 4505)
|
#pragma warning(disable : 4505)
|
||||||
#pragma warning(disable: 4702)
|
#pragma warning(disable : 4702)
|
||||||
#pragma warning(disable: 4996)
|
#pragma warning(disable : 4996)
|
||||||
#pragma warning(disable: 5054)
|
#pragma warning(disable : 5054)
|
||||||
#pragma warning(disable: 6011)
|
#pragma warning(disable : 6011)
|
||||||
#pragma warning(disable: 6297)
|
#pragma warning(disable : 6297)
|
||||||
#pragma warning(disable: 6385)
|
#pragma warning(disable : 6385)
|
||||||
#pragma warning(disable: 6386)
|
#pragma warning(disable : 6386)
|
||||||
#pragma warning(disable: 6387)
|
#pragma warning(disable : 6387)
|
||||||
#pragma warning(disable: 26110)
|
#pragma warning(disable : 26110)
|
||||||
#pragma warning(disable: 26451)
|
#pragma warning(disable : 26451)
|
||||||
#pragma warning(disable: 26444)
|
#pragma warning(disable : 26444)
|
||||||
#pragma warning(disable: 26451)
|
#pragma warning(disable : 26451)
|
||||||
#pragma warning(disable: 26489)
|
#pragma warning(disable : 26489)
|
||||||
#pragma warning(disable: 26495)
|
#pragma warning(disable : 26495)
|
||||||
#pragma warning(disable: 26498)
|
#pragma warning(disable : 26498)
|
||||||
#pragma warning(disable: 26812)
|
#pragma warning(disable : 26812)
|
||||||
#pragma warning(disable: 28020)
|
#pragma warning(disable : 28020)
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -3,145 +3,138 @@
|
|||||||
|
|
||||||
static void serialize(utils::buffer_serializer& buffer, const syscall_handler_entry& obj)
|
static void serialize(utils::buffer_serializer& buffer, const syscall_handler_entry& obj)
|
||||||
{
|
{
|
||||||
buffer.write(obj.name);
|
buffer.write(obj.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void deserialize(utils::buffer_deserializer& buffer, syscall_handler_entry& obj)
|
static void deserialize(utils::buffer_deserializer& buffer, syscall_handler_entry& obj)
|
||||||
{
|
{
|
||||||
buffer.read(obj.name);
|
buffer.read(obj.name);
|
||||||
obj.handler = nullptr;
|
obj.handler = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void syscall_dispatcher::serialize(utils::buffer_serializer& buffer) const
|
void syscall_dispatcher::serialize(utils::buffer_serializer& buffer) const
|
||||||
{
|
{
|
||||||
buffer.write_map(this->handlers_);
|
buffer.write_map(this->handlers_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void syscall_dispatcher::deserialize(utils::buffer_deserializer& buffer)
|
void syscall_dispatcher::deserialize(utils::buffer_deserializer& buffer)
|
||||||
{
|
{
|
||||||
buffer.read_map(this->handlers_);
|
buffer.read_map(this->handlers_);
|
||||||
this->add_handlers();
|
this->add_handlers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
|
void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
|
||||||
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data)
|
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data)
|
||||||
{
|
{
|
||||||
this->handlers_ = {};
|
this->handlers_ = {};
|
||||||
|
|
||||||
const auto ntdll_syscalls = find_syscalls(ntdll_exports, ntdll_data);
|
const auto ntdll_syscalls = find_syscalls(ntdll_exports, ntdll_data);
|
||||||
const auto win32u_syscalls = find_syscalls(win32u_exports, win32u_data);
|
const auto win32u_syscalls = find_syscalls(win32u_exports, win32u_data);
|
||||||
|
|
||||||
map_syscalls(this->handlers_, ntdll_syscalls);
|
map_syscalls(this->handlers_, ntdll_syscalls);
|
||||||
map_syscalls(this->handlers_, win32u_syscalls);
|
map_syscalls(this->handlers_, win32u_syscalls);
|
||||||
|
|
||||||
this->add_handlers();
|
this->add_handlers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void syscall_dispatcher::add_handlers()
|
void syscall_dispatcher::add_handlers()
|
||||||
{
|
{
|
||||||
std::map<std::string, syscall_handler> handler_mapping{};
|
std::map<std::string, syscall_handler> handler_mapping{};
|
||||||
|
|
||||||
syscall_dispatcher::add_handlers(handler_mapping);
|
syscall_dispatcher::add_handlers(handler_mapping);
|
||||||
|
|
||||||
for (auto& entry : this->handlers_)
|
for (auto& entry : this->handlers_)
|
||||||
{
|
{
|
||||||
const auto handler = handler_mapping.find(entry.second.name);
|
const auto handler = handler_mapping.find(entry.second.name);
|
||||||
if (handler == handler_mapping.end())
|
if (handler == handler_mapping.end())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.second.handler = handler->second;
|
entry.second.handler = handler->second;
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
handler_mapping.erase(handler);
|
handler_mapping.erase(handler);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void syscall_dispatcher::dispatch(windows_emulator& win_emu)
|
void syscall_dispatcher::dispatch(windows_emulator& win_emu)
|
||||||
{
|
{
|
||||||
auto& emu = win_emu.emu();
|
auto& emu = win_emu.emu();
|
||||||
auto& context = win_emu.process();
|
auto& context = win_emu.process();
|
||||||
|
|
||||||
const auto address = emu.read_instruction_pointer();
|
const auto address = emu.read_instruction_pointer();
|
||||||
const auto syscall_id = emu.reg<uint32_t>(x64_register::eax);
|
const auto syscall_id = emu.reg<uint32_t>(x64_register::eax);
|
||||||
|
|
||||||
|
const syscall_context c{win_emu, emu, context, true};
|
||||||
|
|
||||||
const syscall_context c{win_emu, emu, context, true};
|
try
|
||||||
|
{
|
||||||
|
const auto entry = this->handlers_.find(syscall_id);
|
||||||
|
if (entry == this->handlers_.end())
|
||||||
|
{
|
||||||
|
printf("Unknown syscall: 0x%X\n", syscall_id);
|
||||||
|
c.emu.reg<uint64_t>(x64_register::rax, STATUS_NOT_SUPPORTED);
|
||||||
|
c.emu.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
if (!entry->second.handler)
|
||||||
{
|
{
|
||||||
const auto entry = this->handlers_.find(syscall_id);
|
printf("Unimplemented syscall: %s - 0x%X\n", entry->second.name.c_str(), syscall_id);
|
||||||
if (entry == this->handlers_.end())
|
c.emu.reg<uint64_t>(x64_register::rax, STATUS_NOT_SUPPORTED);
|
||||||
{
|
c.emu.stop();
|
||||||
printf("Unknown syscall: 0x%X\n", syscall_id);
|
return;
|
||||||
c.emu.reg<uint64_t>(x64_register::rax, STATUS_NOT_SUPPORTED);
|
}
|
||||||
c.emu.stop();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!entry->second.handler)
|
const auto* mod = context.mod_manager.find_by_address(address);
|
||||||
{
|
if (mod != context.ntdll && mod != context.win32u)
|
||||||
printf("Unimplemented syscall: %s - 0x%X\n", entry->second.name.c_str(), syscall_id);
|
{
|
||||||
c.emu.reg<uint64_t>(x64_register::rax, STATUS_NOT_SUPPORTED);
|
win_emu.log.print(color::blue, "Executing inline syscall: %s (0x%X) at 0x%" PRIx64 " (%s)\n",
|
||||||
c.emu.stop();
|
entry->second.name.c_str(), syscall_id, address, mod ? mod->name.c_str() : "<N/A>");
|
||||||
return;
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
|
if (mod->is_within(context.previous_ip))
|
||||||
|
{
|
||||||
|
const auto rsp = c.emu.read_stack_pointer();
|
||||||
|
const auto return_address = c.emu.read_memory<uint64_t>(rsp);
|
||||||
|
const auto* mod_name = context.mod_manager.find_name(return_address);
|
||||||
|
|
||||||
const auto* mod = context.mod_manager.find_by_address(address);
|
win_emu.log.print(color::dark_gray,
|
||||||
if (mod != context.ntdll && mod != context.win32u)
|
"Executing syscall: %s (0x%X) at 0x%" PRIx64 " via 0x%" PRIx64 " (%s)\n",
|
||||||
{
|
entry->second.name.c_str(), syscall_id, address, return_address, mod_name);
|
||||||
win_emu.log.print(color::blue, "Executing inline syscall: %s (0x%X) at 0x%" PRIx64 " (%s)\n",
|
}
|
||||||
entry->second.name.c_str(),
|
else
|
||||||
syscall_id,
|
{
|
||||||
address, mod ? mod->name.c_str() : "<N/A>");
|
const auto* previous_mod = context.mod_manager.find_by_address(context.previous_ip);
|
||||||
}
|
win_emu.log.print(color::blue,
|
||||||
else
|
"Crafted out-of-line syscall: %s (0x%X) at 0x%" PRIx64 " (%s) via 0x%" PRIx64
|
||||||
{
|
" (%s)\n",
|
||||||
if (mod->is_within(context.previous_ip))
|
entry->second.name.c_str(), syscall_id, address, mod ? mod->name.c_str() : "<N/A>",
|
||||||
{
|
context.previous_ip, previous_mod ? previous_mod->name.c_str() : "<N/A>");
|
||||||
const auto rsp = c.emu.read_stack_pointer();
|
}
|
||||||
const auto return_address = c.emu.read_memory<uint64_t>(rsp);
|
}
|
||||||
const auto* mod_name = context.mod_manager.find_name(return_address);
|
|
||||||
|
|
||||||
win_emu.log.print(color::dark_gray,
|
entry->second.handler(c);
|
||||||
"Executing syscall: %s (0x%X) at 0x%" PRIx64 " via 0x%" PRIx64 " (%s)\n",
|
}
|
||||||
entry->second.name.c_str(),
|
catch (std::exception& e)
|
||||||
syscall_id, address, return_address, mod_name);
|
{
|
||||||
}
|
printf("Syscall threw an exception: %X (0x%" PRIx64 ") - %s\n", syscall_id, address, e.what());
|
||||||
else
|
emu.reg<uint64_t>(x64_register::rax, STATUS_UNSUCCESSFUL);
|
||||||
{
|
emu.stop();
|
||||||
const auto* previous_mod = context.mod_manager.find_by_address(context.previous_ip);
|
}
|
||||||
win_emu.log.print(color::blue,
|
catch (...)
|
||||||
"Crafted out-of-line syscall: %s (0x%X) at 0x%" PRIx64 " (%s) via 0x%" PRIx64
|
{
|
||||||
" (%s)\n",
|
printf("Syscall threw an unknown exception: %X (0x%" PRIx64 ")\n", syscall_id, address);
|
||||||
entry->second.name.c_str(),
|
emu.reg<uint64_t>(x64_register::rax, STATUS_UNSUCCESSFUL);
|
||||||
syscall_id,
|
emu.stop();
|
||||||
address, mod ? mod->name.c_str() : "<N/A>", context.previous_ip,
|
}
|
||||||
previous_mod ? previous_mod->name.c_str() : "<N/A>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
entry->second.handler(c);
|
|
||||||
}
|
|
||||||
catch (std::exception& e)
|
|
||||||
{
|
|
||||||
printf("Syscall threw an exception: %X (0x%" PRIx64 ") - %s\n", syscall_id, address, e.what());
|
|
||||||
emu.reg<uint64_t>(x64_register::rax, STATUS_UNSUCCESSFUL);
|
|
||||||
emu.stop();
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
printf("Syscall threw an unknown exception: %X (0x%" PRIx64 ")\n", syscall_id, address);
|
|
||||||
emu.reg<uint64_t>(x64_register::rax, STATUS_UNSUCCESSFUL);
|
|
||||||
emu.stop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
syscall_dispatcher::syscall_dispatcher(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
|
syscall_dispatcher::syscall_dispatcher(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
|
||||||
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data)
|
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data)
|
||||||
{
|
{
|
||||||
this->setup(ntdll_exports, ntdll_data, win32u_exports, win32u_data);
|
this->setup(ntdll_exports, ntdll_data, win32u_exports, win32u_data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,39 +3,39 @@
|
|||||||
#include "process_context.hpp"
|
#include "process_context.hpp"
|
||||||
|
|
||||||
struct syscall_context;
|
struct syscall_context;
|
||||||
using syscall_handler = void(*)(const syscall_context& c);
|
using syscall_handler = void (*)(const syscall_context& c);
|
||||||
|
|
||||||
struct syscall_handler_entry
|
struct syscall_handler_entry
|
||||||
{
|
{
|
||||||
syscall_handler handler{};
|
syscall_handler handler{};
|
||||||
std::string name{};
|
std::string name{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class windows_emulator;
|
class windows_emulator;
|
||||||
|
|
||||||
class syscall_dispatcher
|
class syscall_dispatcher
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
syscall_dispatcher() = default;
|
syscall_dispatcher() = default;
|
||||||
syscall_dispatcher(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
|
syscall_dispatcher(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
|
||||||
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data);
|
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data);
|
||||||
|
|
||||||
void dispatch(windows_emulator& win_emu);
|
void dispatch(windows_emulator& win_emu);
|
||||||
|
|
||||||
void serialize(utils::buffer_serializer& buffer) const;
|
void serialize(utils::buffer_serializer& buffer) const;
|
||||||
void deserialize(utils::buffer_deserializer& buffer);
|
void deserialize(utils::buffer_deserializer& buffer);
|
||||||
|
|
||||||
void setup(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
|
void setup(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
|
||||||
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data);
|
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data);
|
||||||
|
|
||||||
std::string get_syscall_name(const uint64_t id)
|
std::string get_syscall_name(const uint64_t id)
|
||||||
{
|
{
|
||||||
return this->handlers_.at(id).name;
|
return this->handlers_.at(id).name;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::map<uint64_t, syscall_handler_entry> handlers_{};
|
std::map<uint64_t, syscall_handler_entry> handlers_{};
|
||||||
|
|
||||||
static void add_handlers(std::map<std::string, syscall_handler>& handler_mapping);
|
static void add_handlers(std::map<std::string, syscall_handler>& handler_mapping);
|
||||||
void add_handlers();
|
void add_handlers();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,202 +5,193 @@
|
|||||||
|
|
||||||
struct syscall_context
|
struct syscall_context
|
||||||
{
|
{
|
||||||
windows_emulator& win_emu;
|
windows_emulator& win_emu;
|
||||||
x64_emulator& emu;
|
x64_emulator& emu;
|
||||||
process_context& proc;
|
process_context& proc;
|
||||||
mutable bool write_status{true};
|
mutable bool write_status{true};
|
||||||
mutable bool retrigger_syscall{false};
|
mutable bool retrigger_syscall{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
inline uint64_t get_syscall_argument(x64_emulator& emu, const size_t index)
|
inline uint64_t get_syscall_argument(x64_emulator& emu, const size_t index)
|
||||||
{
|
{
|
||||||
switch (index)
|
switch (index)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
return emu.reg(x64_register::r10);
|
return emu.reg(x64_register::r10);
|
||||||
case 1:
|
case 1:
|
||||||
return emu.reg(x64_register::rdx);
|
return emu.reg(x64_register::rdx);
|
||||||
case 2:
|
case 2:
|
||||||
return emu.reg(x64_register::r8);
|
return emu.reg(x64_register::r8);
|
||||||
case 3:
|
case 3:
|
||||||
return emu.reg(x64_register::r9);
|
return emu.reg(x64_register::r9);
|
||||||
default:
|
default:
|
||||||
return emu.read_stack(index + 1);
|
return emu.read_stack(index + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool is_uppercase(const char character)
|
inline bool is_uppercase(const char character)
|
||||||
{
|
{
|
||||||
return toupper(character) == character;
|
return toupper(character) == character;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool is_syscall(const std::string_view name)
|
inline bool is_syscall(const std::string_view name)
|
||||||
{
|
{
|
||||||
return name.starts_with("Nt") && name.size() > 3 && is_uppercase(name[2]);
|
return name.starts_with("Nt") && name.size() > 3 && is_uppercase(name[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::optional<uint32_t> extract_syscall_id(const exported_symbol& symbol, std::span<const std::byte> data)
|
inline std::optional<uint32_t> extract_syscall_id(const exported_symbol& symbol, std::span<const std::byte> data)
|
||||||
{
|
{
|
||||||
if (!is_syscall(symbol.name))
|
if (!is_syscall(symbol.name))
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr auto instruction_size = 5;
|
constexpr auto instruction_size = 5;
|
||||||
constexpr auto instruction_offset = 3;
|
constexpr auto instruction_offset = 3;
|
||||||
constexpr auto instruction_operand_offset = 1;
|
constexpr auto instruction_operand_offset = 1;
|
||||||
constexpr auto instruction_opcode = static_cast<std::byte>(0xB8);
|
constexpr auto instruction_opcode = static_cast<std::byte>(0xB8);
|
||||||
|
|
||||||
const auto instruction_rva = symbol.rva + instruction_offset;
|
const auto instruction_rva = symbol.rva + instruction_offset;
|
||||||
|
|
||||||
if (data.size() < (instruction_rva + instruction_size) || data[instruction_rva] != instruction_opcode)
|
if (data.size() < (instruction_rva + instruction_size) || data[instruction_rva] != instruction_opcode)
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t syscall_id{0};
|
uint32_t syscall_id{0};
|
||||||
static_assert(sizeof(syscall_id) <= (instruction_size - instruction_operand_offset));
|
static_assert(sizeof(syscall_id) <= (instruction_size - instruction_operand_offset));
|
||||||
memcpy(&syscall_id, data.data() + instruction_rva + instruction_operand_offset, sizeof(syscall_id));
|
memcpy(&syscall_id, data.data() + instruction_rva + instruction_operand_offset, sizeof(syscall_id));
|
||||||
|
|
||||||
return syscall_id;
|
return syscall_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::map<uint64_t, std::string> find_syscalls(const exported_symbols& exports, std::span<const std::byte> data)
|
inline std::map<uint64_t, std::string> find_syscalls(const exported_symbols& exports, std::span<const std::byte> data)
|
||||||
{
|
{
|
||||||
std::map<uint64_t, std::string> syscalls{};
|
std::map<uint64_t, std::string> syscalls{};
|
||||||
|
|
||||||
for (const auto& symbol : exports)
|
for (const auto& symbol : exports)
|
||||||
{
|
{
|
||||||
const auto id = extract_syscall_id(symbol, data);
|
const auto id = extract_syscall_id(symbol, data);
|
||||||
if (id)
|
if (id)
|
||||||
{
|
{
|
||||||
auto& entry = syscalls[*id];
|
auto& entry = syscalls[*id];
|
||||||
|
|
||||||
if (!entry.empty())
|
if (!entry.empty())
|
||||||
{
|
{
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Syscall with id " + std::to_string(*id) + ", which is mapping to " +
|
||||||
"Syscall with id " + std::to_string(*id) + ", which is mapping to " + symbol.name +
|
symbol.name + ", was already mapped to " + entry);
|
||||||
", was already mapped to " + entry);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
entry = symbol.name;
|
entry = symbol.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return syscalls;
|
return syscalls;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void map_syscalls(std::map<uint64_t, syscall_handler_entry>& handlers,
|
inline void map_syscalls(std::map<uint64_t, syscall_handler_entry>& handlers, std::map<uint64_t, std::string> syscalls)
|
||||||
std::map<uint64_t, std::string> syscalls)
|
|
||||||
{
|
{
|
||||||
for (auto& [id, name] : syscalls)
|
for (auto& [id, name] : syscalls)
|
||||||
{
|
{
|
||||||
auto& entry = handlers[id];
|
auto& entry = handlers[id];
|
||||||
|
|
||||||
if (!entry.name.empty())
|
if (!entry.name.empty())
|
||||||
{
|
{
|
||||||
throw std::runtime_error(
|
throw std::runtime_error("Syscall with id " + std::to_string(id) + ", which is mapping to " + name +
|
||||||
"Syscall with id " + std::to_string(id) + ", which is mapping to " + name +
|
", was previously mapped to " + entry.name);
|
||||||
", was previously mapped to " + entry.name);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
entry.name = std::move(name);
|
entry.name = std::move(name);
|
||||||
entry.handler = nullptr;
|
entry.handler = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
requires(std::is_integral_v<T> || std::is_enum_v<T>)
|
requires(std::is_integral_v<T> || std::is_enum_v<T>)
|
||||||
T resolve_argument(x64_emulator& emu, const size_t index)
|
T resolve_argument(x64_emulator& emu, const size_t index)
|
||||||
{
|
{
|
||||||
const auto arg = get_syscall_argument(emu, index);
|
const auto arg = get_syscall_argument(emu, index);
|
||||||
return static_cast<T>(arg);
|
return static_cast<T>(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
requires(std::is_same_v<std::remove_cvref_t<T>, handle>)
|
requires(std::is_same_v<std::remove_cvref_t<T>, handle>)
|
||||||
handle resolve_argument(x64_emulator& emu, const size_t index)
|
handle resolve_argument(x64_emulator& emu, const size_t index)
|
||||||
{
|
{
|
||||||
handle h{};
|
handle h{};
|
||||||
h.bits = resolve_argument<uint64_t>(emu, index);
|
h.bits = resolve_argument<uint64_t>(emu, index);
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
requires(std::is_same_v<T, emulator_object<typename T::value_type>>)
|
requires(std::is_same_v<T, emulator_object<typename T::value_type>>)
|
||||||
T resolve_argument(x64_emulator& emu, const size_t index)
|
T resolve_argument(x64_emulator& emu, const size_t index)
|
||||||
{
|
{
|
||||||
const auto arg = get_syscall_argument(emu, index);
|
const auto arg = get_syscall_argument(emu, index);
|
||||||
return T(emu, arg);
|
return T(emu, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T resolve_indexed_argument(x64_emulator& emu, size_t& index)
|
T resolve_indexed_argument(x64_emulator& emu, size_t& index)
|
||||||
{
|
{
|
||||||
return resolve_argument<T>(emu, index++);
|
return resolve_argument<T>(emu, index++);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void write_status(const syscall_context& c, const NTSTATUS status, const uint64_t initial_ip)
|
inline void write_status(const syscall_context& c, const NTSTATUS status, const uint64_t initial_ip)
|
||||||
{
|
{
|
||||||
if (c.write_status && !c.retrigger_syscall)
|
if (c.write_status && !c.retrigger_syscall)
|
||||||
{
|
{
|
||||||
c.emu.reg<uint64_t>(x64_register::rax, static_cast<uint64_t>(status));
|
c.emu.reg<uint64_t>(x64_register::rax, static_cast<uint64_t>(status));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto new_ip = c.emu.read_instruction_pointer();
|
const auto new_ip = c.emu.read_instruction_pointer();
|
||||||
if (initial_ip != new_ip || c.retrigger_syscall)
|
if (initial_ip != new_ip || c.retrigger_syscall)
|
||||||
{
|
{
|
||||||
c.emu.reg(x64_register::rip, new_ip - 2);
|
c.emu.reg(x64_register::rip, new_ip - 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void forward_syscall(const syscall_context& c, NTSTATUS (*handler)())
|
inline void forward_syscall(const syscall_context& c, NTSTATUS (*handler)())
|
||||||
{
|
{
|
||||||
const auto ip = c.emu.read_instruction_pointer();
|
const auto ip = c.emu.read_instruction_pointer();
|
||||||
|
|
||||||
const auto ret = handler();
|
const auto ret = handler();
|
||||||
write_status(c, ret, ip);
|
write_status(c, ret, ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
void forward_syscall(const syscall_context& c, NTSTATUS (*handler)(const syscall_context&, Args...))
|
void forward_syscall(const syscall_context& c, NTSTATUS (*handler)(const syscall_context&, Args...))
|
||||||
{
|
{
|
||||||
const auto ip = c.emu.read_instruction_pointer();
|
const auto ip = c.emu.read_instruction_pointer();
|
||||||
|
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
std::tuple<const syscall_context&, Args...> func_args
|
std::tuple<const syscall_context&, Args...> func_args{
|
||||||
{
|
c, resolve_indexed_argument<std::remove_cv_t<std::remove_reference_t<Args>>>(c.emu, index)...};
|
||||||
c,
|
|
||||||
resolve_indexed_argument<std::remove_cv_t<std::remove_reference_t<Args>>>(c.emu, index)...
|
|
||||||
};
|
|
||||||
|
|
||||||
(void)index;
|
(void)index;
|
||||||
|
|
||||||
const auto ret = std::apply(handler, std::move(func_args));
|
const auto ret = std::apply(handler, std::move(func_args));
|
||||||
write_status(c, ret, ip);
|
write_status(c, ret, ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <auto Handler>
|
template <auto Handler>
|
||||||
syscall_handler make_syscall_handler()
|
syscall_handler make_syscall_handler()
|
||||||
{
|
{
|
||||||
return +[](const syscall_context& c)
|
return +[](const syscall_context& c) { forward_syscall(c, Handler); };
|
||||||
{
|
|
||||||
forward_syscall(c, Handler);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Traits>
|
template <typename T, typename Traits>
|
||||||
void write_attribute(emulator& emu, const PS_ATTRIBUTE<Traits>& attribute, const T& value)
|
void write_attribute(emulator& emu, const PS_ATTRIBUTE<Traits>& attribute, const T& value)
|
||||||
{
|
{
|
||||||
if (attribute.ReturnLength)
|
if (attribute.ReturnLength)
|
||||||
{
|
{
|
||||||
emulator_object<typename Traits::SIZE_T>{emu, attribute.ReturnLength}.write(sizeof(T));
|
emulator_object<typename Traits::SIZE_T>{emu, attribute.ReturnLength}.write(sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attribute.Size >= sizeof(T))
|
if (attribute.Size >= sizeof(T))
|
||||||
{
|
{
|
||||||
emulator_object<T>{emu, attribute.Value}.write(value);
|
emulator_object<T>{emu, attribute.Value}.write(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr auto HUNDRED_NANOSECONDS_IN_ONE_SECOND = 10000000LL;
|
constexpr auto HUNDRED_NANOSECONDS_IN_ONE_SECOND = 10000000LL;
|
||||||
@@ -209,67 +200,65 @@ constexpr auto WINDOWS_EPOCH_DIFFERENCE = EPOCH_DIFFERENCE_1601_TO_1970_SECONDS
|
|||||||
|
|
||||||
inline std::chrono::steady_clock::time_point convert_delay_interval_to_time_point(const LARGE_INTEGER delay_interval)
|
inline std::chrono::steady_clock::time_point convert_delay_interval_to_time_point(const LARGE_INTEGER delay_interval)
|
||||||
{
|
{
|
||||||
if (delay_interval.QuadPart <= 0)
|
if (delay_interval.QuadPart <= 0)
|
||||||
{
|
{
|
||||||
const auto relative_time = -delay_interval.QuadPart;
|
const auto relative_time = -delay_interval.QuadPart;
|
||||||
const auto relative_ticks_in_ms = relative_time / 10;
|
const auto relative_ticks_in_ms = relative_time / 10;
|
||||||
const auto relative_fraction_ns = (relative_time % 10) * 100;
|
const auto relative_fraction_ns = (relative_time % 10) * 100;
|
||||||
const auto relative_duration = std::chrono::microseconds(relative_ticks_in_ms) +
|
const auto relative_duration =
|
||||||
std::chrono::nanoseconds(relative_fraction_ns);
|
std::chrono::microseconds(relative_ticks_in_ms) + std::chrono::nanoseconds(relative_fraction_ns);
|
||||||
|
|
||||||
return std::chrono::steady_clock::now() + relative_duration;
|
return std::chrono::steady_clock::now() + relative_duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto delay_seconds_since_1601 = delay_interval.QuadPart / HUNDRED_NANOSECONDS_IN_ONE_SECOND;
|
const auto delay_seconds_since_1601 = delay_interval.QuadPart / HUNDRED_NANOSECONDS_IN_ONE_SECOND;
|
||||||
const auto delay_fraction_ns = (delay_interval.QuadPart % HUNDRED_NANOSECONDS_IN_ONE_SECOND) * 100;
|
const auto delay_fraction_ns = (delay_interval.QuadPart % HUNDRED_NANOSECONDS_IN_ONE_SECOND) * 100;
|
||||||
|
|
||||||
const auto delay_seconds_since_1970 = delay_seconds_since_1601 - EPOCH_DIFFERENCE_1601_TO_1970_SECONDS;
|
const auto delay_seconds_since_1970 = delay_seconds_since_1601 - EPOCH_DIFFERENCE_1601_TO_1970_SECONDS;
|
||||||
|
|
||||||
const auto target_time =
|
const auto target_time =
|
||||||
std::chrono::system_clock::from_time_t(delay_seconds_since_1970) +
|
std::chrono::system_clock::from_time_t(delay_seconds_since_1970) + std::chrono::nanoseconds(delay_fraction_ns);
|
||||||
std::chrono::nanoseconds(delay_fraction_ns);
|
|
||||||
|
|
||||||
const auto now_system = std::chrono::system_clock::now();
|
const auto now_system = std::chrono::system_clock::now();
|
||||||
|
|
||||||
const auto duration_until_target = std::chrono::duration_cast<
|
const auto duration_until_target = std::chrono::duration_cast<std::chrono::microseconds>(target_time - now_system);
|
||||||
std::chrono::microseconds>(target_time - now_system);
|
|
||||||
|
|
||||||
return std::chrono::steady_clock::now() + duration_until_target;
|
return std::chrono::steady_clock::now() + duration_until_target;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline KSYSTEM_TIME convert_to_ksystem_time(const std::chrono::system_clock::time_point& tp)
|
inline KSYSTEM_TIME convert_to_ksystem_time(const std::chrono::system_clock::time_point& tp)
|
||||||
{
|
{
|
||||||
const auto duration = tp.time_since_epoch();
|
const auto duration = tp.time_since_epoch();
|
||||||
const auto ns_duration = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
|
const auto ns_duration = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
|
||||||
|
|
||||||
const auto total_ticks = ns_duration.count() / 100 + WINDOWS_EPOCH_DIFFERENCE;
|
const auto total_ticks = ns_duration.count() / 100 + WINDOWS_EPOCH_DIFFERENCE;
|
||||||
|
|
||||||
KSYSTEM_TIME time{};
|
KSYSTEM_TIME time{};
|
||||||
time.LowPart = static_cast<uint32_t>(total_ticks);
|
time.LowPart = static_cast<uint32_t>(total_ticks);
|
||||||
time.High1Time = static_cast<int32_t>(total_ticks >> 32);
|
time.High1Time = static_cast<int32_t>(total_ticks >> 32);
|
||||||
time.High2Time = time.High1Time;
|
time.High2Time = time.High1Time;
|
||||||
|
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void convert_to_ksystem_time(volatile KSYSTEM_TIME* dest, const std::chrono::system_clock::time_point& tp)
|
inline void convert_to_ksystem_time(volatile KSYSTEM_TIME* dest, const std::chrono::system_clock::time_point& tp)
|
||||||
{
|
{
|
||||||
const auto time = convert_to_ksystem_time(tp);
|
const auto time = convert_to_ksystem_time(tp);
|
||||||
memcpy(const_cast<KSYSTEM_TIME*>(dest), &time, sizeof(*dest));
|
memcpy(const_cast<KSYSTEM_TIME*>(dest), &time, sizeof(*dest));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::chrono::system_clock::time_point convert_from_ksystem_time(const KSYSTEM_TIME& time)
|
inline std::chrono::system_clock::time_point convert_from_ksystem_time(const KSYSTEM_TIME& time)
|
||||||
{
|
{
|
||||||
auto totalTicks = (static_cast<int64_t>(time.High1Time) << 32) | time.LowPart;
|
auto totalTicks = (static_cast<int64_t>(time.High1Time) << 32) | time.LowPart;
|
||||||
totalTicks -= WINDOWS_EPOCH_DIFFERENCE;
|
totalTicks -= WINDOWS_EPOCH_DIFFERENCE;
|
||||||
|
|
||||||
const auto duration = std::chrono::system_clock::duration(totalTicks * 100);
|
const auto duration = std::chrono::system_clock::duration(totalTicks * 100);
|
||||||
return std::chrono::system_clock::time_point(duration);
|
return std::chrono::system_clock::time_point(duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::chrono::system_clock::time_point convert_from_ksystem_time(const volatile KSYSTEM_TIME& time)
|
inline std::chrono::system_clock::time_point convert_from_ksystem_time(const volatile KSYSTEM_TIME& time)
|
||||||
{
|
{
|
||||||
return convert_from_ksystem_time(*const_cast<const KSYSTEM_TIME*>(&time));
|
return convert_from_ksystem_time(*const_cast<const KSYSTEM_TIME*>(&time));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef OS_WINDOWS
|
#ifndef OS_WINDOWS
|
||||||
@@ -278,7 +267,7 @@ using __time64_t = int64_t;
|
|||||||
|
|
||||||
inline LARGE_INTEGER convert_unix_to_windows_time(const __time64_t unix_time)
|
inline LARGE_INTEGER convert_unix_to_windows_time(const __time64_t unix_time)
|
||||||
{
|
{
|
||||||
LARGE_INTEGER windows_time{};
|
LARGE_INTEGER windows_time{};
|
||||||
windows_time.QuadPart = (unix_time + EPOCH_DIFFERENCE_1601_TO_1970_SECONDS) * HUNDRED_NANOSECONDS_IN_ONE_SECOND;
|
windows_time.QuadPart = (unix_time + EPOCH_DIFFERENCE_1601_TO_1970_SECONDS) * HUNDRED_NANOSECONDS_IN_ONE_SECOND;
|
||||||
return windows_time;
|
return windows_time;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -12,120 +12,119 @@ std::unique_ptr<x64_emulator> create_default_x64_emulator();
|
|||||||
// TODO: Split up into application and emulator settings
|
// TODO: Split up into application and emulator settings
|
||||||
struct emulator_settings
|
struct emulator_settings
|
||||||
{
|
{
|
||||||
std::filesystem::path application{};
|
std::filesystem::path application{};
|
||||||
std::filesystem::path working_directory{};
|
std::filesystem::path working_directory{};
|
||||||
std::filesystem::path registry_directory{"./registry"};
|
std::filesystem::path registry_directory{"./registry"};
|
||||||
std::vector<std::u16string> arguments{};
|
std::vector<std::u16string> arguments{};
|
||||||
std::function<void(std::string_view)> stdout_callback{};
|
std::function<void(std::string_view)> stdout_callback{};
|
||||||
bool disable_logging{false};
|
bool disable_logging{false};
|
||||||
bool silent_until_main{false};
|
bool silent_until_main{false};
|
||||||
bool use_relative_time{false};
|
bool use_relative_time{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
class windows_emulator
|
class windows_emulator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
windows_emulator(std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
|
windows_emulator(std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
|
||||||
windows_emulator(emulator_settings settings,
|
windows_emulator(emulator_settings settings, std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
|
||||||
std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
|
|
||||||
|
|
||||||
windows_emulator(windows_emulator&&) = delete;
|
windows_emulator(windows_emulator&&) = delete;
|
||||||
windows_emulator(const windows_emulator&) = delete;
|
windows_emulator(const windows_emulator&) = delete;
|
||||||
windows_emulator& operator=(windows_emulator&&) = delete;
|
windows_emulator& operator=(windows_emulator&&) = delete;
|
||||||
windows_emulator& operator=(const windows_emulator&) = delete;
|
windows_emulator& operator=(const windows_emulator&) = delete;
|
||||||
|
|
||||||
~windows_emulator() = default;
|
~windows_emulator() = default;
|
||||||
|
|
||||||
x64_emulator& emu()
|
x64_emulator& emu()
|
||||||
{
|
{
|
||||||
return *this->emu_;
|
return *this->emu_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const x64_emulator& emu() const
|
const x64_emulator& emu() const
|
||||||
{
|
{
|
||||||
return *this->emu_;
|
return *this->emu_;
|
||||||
}
|
}
|
||||||
|
|
||||||
process_context& process()
|
process_context& process()
|
||||||
{
|
{
|
||||||
return this->process_;
|
return this->process_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const process_context& process() const
|
const process_context& process() const
|
||||||
{
|
{
|
||||||
return this->process_;
|
return this->process_;
|
||||||
}
|
}
|
||||||
|
|
||||||
syscall_dispatcher& dispatcher()
|
syscall_dispatcher& dispatcher()
|
||||||
{
|
{
|
||||||
return this->dispatcher_;
|
return this->dispatcher_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const syscall_dispatcher& dispatcher() const
|
const syscall_dispatcher& dispatcher() const
|
||||||
{
|
{
|
||||||
return this->dispatcher_;
|
return this->dispatcher_;
|
||||||
}
|
}
|
||||||
|
|
||||||
emulator_thread& current_thread() const
|
emulator_thread& current_thread() const
|
||||||
{
|
{
|
||||||
if (!this->process_.active_thread)
|
if (!this->process_.active_thread)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("No active thread!");
|
throw std::runtime_error("No active thread!");
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this->process_.active_thread;
|
return *this->process_.active_thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
void start(std::chrono::nanoseconds timeout = {}, size_t count = 0);
|
void start(std::chrono::nanoseconds timeout = {}, size_t count = 0);
|
||||||
|
|
||||||
void serialize(utils::buffer_serializer& buffer) const;
|
void serialize(utils::buffer_serializer& buffer) const;
|
||||||
void deserialize(utils::buffer_deserializer& buffer);
|
void deserialize(utils::buffer_deserializer& buffer);
|
||||||
|
|
||||||
void save_snapshot();
|
void save_snapshot();
|
||||||
void restore_snapshot();
|
void restore_snapshot();
|
||||||
|
|
||||||
void add_syscall_hook(instruction_hook_callback callback)
|
void add_syscall_hook(instruction_hook_callback callback)
|
||||||
{
|
{
|
||||||
this->syscall_hooks_.push_back(std::move(callback));
|
this->syscall_hooks_.push_back(std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_stdout(const std::string_view data) const
|
void on_stdout(const std::string_view data) const
|
||||||
{
|
{
|
||||||
if (this->stdout_callback_)
|
if (this->stdout_callback_)
|
||||||
{
|
{
|
||||||
this->stdout_callback_(data);
|
this->stdout_callback_(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger log{};
|
logger log{};
|
||||||
bool verbose{false};
|
bool verbose{false};
|
||||||
bool verbose_calls{false};
|
bool verbose_calls{false};
|
||||||
bool buffer_stdout{false};
|
bool buffer_stdout{false};
|
||||||
bool fuzzing{false};
|
bool fuzzing{false};
|
||||||
bool switch_thread{false};
|
bool switch_thread{false};
|
||||||
|
|
||||||
void yield_thread();
|
void yield_thread();
|
||||||
void perform_thread_switch();
|
void perform_thread_switch();
|
||||||
|
|
||||||
bool time_is_relative() const
|
bool time_is_relative() const
|
||||||
{
|
{
|
||||||
return this->use_relative_time_;
|
return this->use_relative_time_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool use_relative_time_{false};
|
bool use_relative_time_{false};
|
||||||
bool silent_until_main_{false};
|
bool silent_until_main_{false};
|
||||||
std::unique_ptr<x64_emulator> emu_{};
|
std::unique_ptr<x64_emulator> emu_{};
|
||||||
std::vector<instruction_hook_callback> syscall_hooks_{};
|
std::vector<instruction_hook_callback> syscall_hooks_{};
|
||||||
std::function<void(std::string_view)> stdout_callback_{};
|
std::function<void(std::string_view)> stdout_callback_{};
|
||||||
|
|
||||||
process_context process_;
|
process_context process_;
|
||||||
syscall_dispatcher dispatcher_;
|
syscall_dispatcher dispatcher_;
|
||||||
|
|
||||||
std::vector<std::byte> process_snapshot_{};
|
std::vector<std::byte> process_snapshot_{};
|
||||||
//std::optional<process_context> process_snapshot_{};
|
// std::optional<process_context> process_snapshot_{};
|
||||||
|
|
||||||
void setup_hooks();
|
void setup_hooks();
|
||||||
void setup_process(const emulator_settings& settings);
|
void setup_process(const emulator_settings& settings);
|
||||||
void on_instruction_execution(uint64_t address);
|
void on_instruction_execution(uint64_t address);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user