mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-11 08:36:16 +00:00
26
.clang-format
Normal file
26
.clang-format
Normal file
@@ -0,0 +1,26 @@
|
||||
# Format Style Options - Created with Clang Power Tools
|
||||
---
|
||||
BasedOnStyle: Microsoft
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
DerivePointerAlignment: false
|
||||
FixNamespaceComments: false
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
Language: Cpp
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: All
|
||||
PointerAlignment: Left
|
||||
SortIncludes: false
|
||||
AlignEscapedNewlines: Left
|
||||
PackConstructorInitializers: Never
|
||||
IndentPPDirectives: None
|
||||
AlignConsecutiveMacros:
|
||||
Enabled: true
|
||||
AcrossEmptyLines: true
|
||||
AcrossComments: true
|
||||
AlignCompound: true
|
||||
PadOperators: true
|
||||
BraceWrapping:
|
||||
AfterStruct: true
|
||||
AfterUnion: true
|
||||
...
|
||||
13
.github/workflows/build.yml
vendored
13
.github/workflows/build.yml
vendored
@@ -15,6 +15,19 @@ on:
|
||||
# cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
verify-formatting:
|
||||
name: Verify Formatting
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Verify Formatting
|
||||
uses: jidicula/clang-format-action@v4.14.0
|
||||
with:
|
||||
clang-format-version: '19'
|
||||
check-path: 'src'
|
||||
|
||||
dump-registry:
|
||||
name: Dump Registry
|
||||
runs-on: windows-latest
|
||||
|
||||
@@ -7,254 +7,247 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
struct analysis_options
|
||||
{
|
||||
bool use_gdb{false};
|
||||
bool concise_logging{false};
|
||||
};
|
||||
struct analysis_options
|
||||
{
|
||||
bool use_gdb{false};
|
||||
bool concise_logging{false};
|
||||
};
|
||||
|
||||
void watch_system_objects(windows_emulator& win_emu, const bool cache_logging)
|
||||
{
|
||||
(void)win_emu;
|
||||
(void)cache_logging;
|
||||
void watch_system_objects(windows_emulator& win_emu, const bool cache_logging)
|
||||
{
|
||||
(void)win_emu;
|
||||
(void)cache_logging;
|
||||
|
||||
#ifdef OS_WINDOWS
|
||||
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, emulator_object<KUSER_SHARED_DATA64>{win_emu.emu(), kusd_mmio::address()}, 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, 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,
|
||||
[&, cache_logging](const uint64_t address, size_t, const uint64_t value)
|
||||
{
|
||||
const auto target_address = win_emu.process().peb.value() + offsetof(
|
||||
PEB64, ProcessParameters);
|
||||
win_emu.emu().hook_memory_write(
|
||||
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(PEB64, ProcessParameters);
|
||||
|
||||
if (address == target_address)
|
||||
{
|
||||
const emulator_object<RTL_USER_PROCESS_PARAMETERS64> obj{
|
||||
win_emu.emu(), value
|
||||
};
|
||||
if (address == target_address)
|
||||
{
|
||||
const emulator_object<RTL_USER_PROCESS_PARAMETERS64> obj{win_emu.emu(), value};
|
||||
|
||||
win_emu.emu().delete_hook(params_hook);
|
||||
params_hook = watch_object(win_emu, obj, cache_logging);
|
||||
}
|
||||
});
|
||||
win_emu.emu().delete_hook(params_hook);
|
||||
params_hook = watch_object(win_emu, obj, cache_logging);
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void run_emulation(windows_emulator& win_emu, const analysis_options& options)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (options.use_gdb)
|
||||
{
|
||||
const auto* address = "127.0.0.1:28960";
|
||||
win_emu.log.print(color::pink, "Waiting for GDB connection on %s...\n", address);
|
||||
void run_emulation(windows_emulator& win_emu, const analysis_options& options)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (options.use_gdb)
|
||||
{
|
||||
const auto* address = "127.0.0.1:28960";
|
||||
win_emu.log.print(color::pink, "Waiting for GDB connection on %s...\n", address);
|
||||
|
||||
win_x64_gdb_stub_handler handler{win_emu};
|
||||
run_gdb_stub(handler, "i386:x86-64", gdb_registers.size(), address);
|
||||
}
|
||||
else
|
||||
{
|
||||
win_emu.start();
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 " - %s\n",
|
||||
win_emu.emu().read_instruction_pointer(), e.what());
|
||||
throw;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 "\n",
|
||||
win_emu.emu().read_instruction_pointer());
|
||||
throw;
|
||||
}
|
||||
win_x64_gdb_stub_handler handler{win_emu};
|
||||
run_gdb_stub(handler, "i386:x86-64", gdb_registers.size(), address);
|
||||
}
|
||||
else
|
||||
{
|
||||
win_emu.start();
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 " - %s\n",
|
||||
win_emu.emu().read_instruction_pointer(), e.what());
|
||||
throw;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 "\n",
|
||||
win_emu.emu().read_instruction_pointer());
|
||||
throw;
|
||||
}
|
||||
|
||||
const auto exit_status = win_emu.process().exit_status;
|
||||
if (exit_status.has_value())
|
||||
{
|
||||
win_emu.log.print(color::red, "Emulation terminated with status: %X\n", *exit_status);
|
||||
}
|
||||
else
|
||||
{
|
||||
win_emu.log.print(color::red, "Emulation terminated without status!\n");
|
||||
}
|
||||
}
|
||||
const auto exit_status = win_emu.process().exit_status;
|
||||
if (exit_status.has_value())
|
||||
{
|
||||
win_emu.log.print(color::red, "Emulation terminated with status: %X\n", *exit_status);
|
||||
}
|
||||
else
|
||||
{
|
||||
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> wide_args{};
|
||||
wide_args.reserve(args.size() - 1);
|
||||
std::vector<std::u16string> parse_arguments(const std::span<const std::string_view> args)
|
||||
{
|
||||
std::vector<std::u16string> wide_args{};
|
||||
wide_args.reserve(args.size() - 1);
|
||||
|
||||
for (size_t i = 1; i < args.size(); ++i)
|
||||
{
|
||||
const auto& arg = args[i];
|
||||
wide_args.emplace_back(arg.begin(), arg.end());
|
||||
}
|
||||
for (size_t i = 1; i < args.size(); ++i)
|
||||
{
|
||||
const auto& arg = args[i];
|
||||
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)
|
||||
{
|
||||
if (args.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
void run(const analysis_options& options, const std::span<const std::string_view> args)
|
||||
{
|
||||
if (args.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
emulator_settings settings{
|
||||
.application = args[0],
|
||||
.arguments = parse_arguments(args),
|
||||
.silent_until_main = options.concise_logging,
|
||||
};
|
||||
emulator_settings settings{
|
||||
.application = args[0],
|
||||
.arguments = parse_arguments(args),
|
||||
.silent_until_main = options.concise_logging,
|
||||
};
|
||||
|
||||
windows_emulator win_emu{std::move(settings)};
|
||||
windows_emulator win_emu{std::move(settings)};
|
||||
|
||||
(void)&watch_system_objects;
|
||||
watch_system_objects(win_emu, options.concise_logging);
|
||||
win_emu.buffer_stdout = true;
|
||||
//win_emu.verbose_calls = true;
|
||||
(void)&watch_system_objects;
|
||||
watch_system_objects(win_emu, options.concise_logging);
|
||||
win_emu.buffer_stdout = 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)
|
||||
{
|
||||
if ((section.region.permissions & memory_permission::exec) != memory_permission::exec)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (const auto& section : exe.sections)
|
||||
{
|
||||
if ((section.region.permissions & memory_permission::exec) != memory_permission::exec)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto read_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t)
|
||||
{
|
||||
const auto rip = win_emu.emu().read_instruction_pointer();
|
||||
if (win_emu.process().mod_manager.find_by_address(rip) != win_emu.process().executable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto read_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t) {
|
||||
const auto rip = win_emu.emu().read_instruction_pointer();
|
||||
if (win_emu.process().mod_manager.find_by_address(rip) != win_emu.process().executable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (concise_logging)
|
||||
{
|
||||
static uint64_t count{0};
|
||||
++count;
|
||||
if (count > 100 && count % 10000 != 0) return;
|
||||
}
|
||||
if (concise_logging)
|
||||
{
|
||||
static uint64_t count{0};
|
||||
++count;
|
||||
if (count > 100 && count % 10000 != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
win_emu.log.print(
|
||||
color::green,
|
||||
"Reading from executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n",
|
||||
section.name.c_str(), address, rip);
|
||||
};
|
||||
win_emu.log.print(color::green,
|
||||
"Reading from executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n",
|
||||
section.name.c_str(), address, rip);
|
||||
};
|
||||
|
||||
const auto write_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t)
|
||||
{
|
||||
const auto rip = win_emu.emu().read_instruction_pointer();
|
||||
if (win_emu.process().mod_manager.find_by_address(rip) != win_emu.process().executable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const auto write_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t) {
|
||||
const auto rip = win_emu.emu().read_instruction_pointer();
|
||||
if (win_emu.process().mod_manager.find_by_address(rip) != win_emu.process().executable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (concise_logging)
|
||||
{
|
||||
static uint64_t count{0};
|
||||
++count;
|
||||
if (count > 100 && count % 10000 != 0) return;
|
||||
}
|
||||
if (concise_logging)
|
||||
{
|
||||
static uint64_t count{0};
|
||||
++count;
|
||||
if (count > 100 && count % 10000 != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
win_emu.log.print(
|
||||
color::blue,
|
||||
"Writing to executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n",
|
||||
section.name.c_str(), address, rip);
|
||||
};
|
||||
win_emu.log.print(color::blue, "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_write(section.region.start, section.region.length, std::move(write_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));
|
||||
}
|
||||
|
||||
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> args{};
|
||||
std::vector<std::string_view> bundle_arguments(const int argc, char** argv)
|
||||
{
|
||||
std::vector<std::string_view> args{};
|
||||
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
args.push_back(argv[i]);
|
||||
}
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
args.push_back(argv[i]);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
analysis_options parse_options(std::vector<std::string_view>& args)
|
||||
{
|
||||
analysis_options options{};
|
||||
analysis_options parse_options(std::vector<std::string_view>& args)
|
||||
{
|
||||
analysis_options options{};
|
||||
|
||||
while (!args.empty())
|
||||
{
|
||||
auto arg_it = args.begin();
|
||||
const auto& arg = *arg_it;
|
||||
while (!args.empty())
|
||||
{
|
||||
auto arg_it = args.begin();
|
||||
const auto& arg = *arg_it;
|
||||
|
||||
if (arg == "-d")
|
||||
{
|
||||
options.use_gdb = true;
|
||||
}
|
||||
else if (arg == "-c")
|
||||
{
|
||||
options.concise_logging = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (arg == "-d")
|
||||
{
|
||||
options.use_gdb = true;
|
||||
}
|
||||
else if (arg == "-c")
|
||||
{
|
||||
options.concise_logging = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
args.erase(arg_it);
|
||||
}
|
||||
args.erase(arg_it);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
int main(const int argc, char** argv)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto args = bundle_arguments(argc, argv);
|
||||
const auto options = parse_options(args);
|
||||
try
|
||||
{
|
||||
auto args = bundle_arguments(argc, argv);
|
||||
const auto options = parse_options(args);
|
||||
|
||||
if (args.empty())
|
||||
{
|
||||
throw std::runtime_error("Application not specified!");
|
||||
}
|
||||
if (args.empty())
|
||||
{
|
||||
throw std::runtime_error("Application not specified!");
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
run(options, args);
|
||||
}
|
||||
while (options.use_gdb);
|
||||
do
|
||||
{
|
||||
run(options, args);
|
||||
} while (options.use_gdb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
puts(e.what());
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
puts(e.what());
|
||||
|
||||
#if defined(_WIN32) && 0
|
||||
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
|
||||
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int)
|
||||
{
|
||||
return main(__argc, __argv);
|
||||
return main(__argc, __argv);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5,36 +5,32 @@
|
||||
template <typename T>
|
||||
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(),
|
||||
[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* mod = emu.process().mod_manager.find_by_address(rip);
|
||||
const auto is_main_access = mod == emu.process().executable;
|
||||
return emu.emu().hook_memory_read(
|
||||
object.value(), object.size(),
|
||||
[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* mod = emu.process().mod_manager.find_by_address(rip);
|
||||
const auto is_main_access = mod == emu.process().executable;
|
||||
|
||||
if (!emu.verbose_calls && !is_main_access)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!emu.verbose_calls && !is_main_access)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (cache_logging)
|
||||
{
|
||||
static std::unordered_set<uint64_t> logged_addresses{};
|
||||
if (is_main_access && !logged_addresses.insert(address).second)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (cache_logging)
|
||||
{
|
||||
static std::unordered_set<uint64_t> logged_addresses{};
|
||||
if (is_main_access && !logged_addresses.insert(address).second)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto offset = address - object.value();
|
||||
emu.log.print(is_main_access ? color::green : color::dark_gray,
|
||||
"Object access: %s - 0x%llX (%s) at 0x%llX (%s)\n",
|
||||
i.get_type_name().c_str(),
|
||||
offset,
|
||||
i.get_member_name(offset).c_str(), rip,
|
||||
mod ? mod->name.c_str() : "<N/A>");
|
||||
});
|
||||
const auto offset = address - object.value();
|
||||
emu.log.print(is_main_access ? color::green : color::dark_gray,
|
||||
"Object access: %s - 0x%llX (%s) at 0x%llX (%s)\n", i.get_type_name().c_str(), 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>
|
||||
class reflect_type_info
|
||||
{
|
||||
public:
|
||||
reflect_type_info()
|
||||
{
|
||||
this->type_name_ = reflect::type_name<T>();
|
||||
public:
|
||||
reflect_type_info()
|
||||
{
|
||||
this->type_name_ = reflect::type_name<T>();
|
||||
|
||||
reflect::for_each<T>([this](auto I)
|
||||
{
|
||||
const auto member_name = reflect::member_name<I, T>();
|
||||
const auto member_offset = reflect::offset_of<I, T>();
|
||||
reflect::for_each<T>([this](auto I) {
|
||||
const auto member_name = reflect::member_name<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
|
||||
{
|
||||
size_t last_offset{};
|
||||
std::string_view last_member{};
|
||||
std::string get_member_name(const size_t offset) const
|
||||
{
|
||||
size_t last_offset{};
|
||||
std::string_view last_member{};
|
||||
|
||||
for (const auto& member : this->members_)
|
||||
{
|
||||
if (offset == member.first)
|
||||
{
|
||||
return member.second;
|
||||
}
|
||||
for (const auto& member : this->members_)
|
||||
{
|
||||
if (offset == member.first)
|
||||
{
|
||||
return member.second;
|
||||
}
|
||||
|
||||
if (offset < member.first)
|
||||
{
|
||||
const auto diff = offset - last_offset;
|
||||
return std::string(last_member) + "+" + std::to_string(diff);
|
||||
}
|
||||
if (offset < member.first)
|
||||
{
|
||||
const auto diff = offset - last_offset;
|
||||
return std::string(last_member) + "+" + std::to_string(diff);
|
||||
}
|
||||
|
||||
last_offset = member.first;
|
||||
last_member = member.second;
|
||||
}
|
||||
last_offset = member.first;
|
||||
last_member = member.second;
|
||||
}
|
||||
|
||||
return "<N/A>";
|
||||
}
|
||||
return "<N/A>";
|
||||
}
|
||||
|
||||
const std::string& get_type_name() const
|
||||
{
|
||||
return this->type_name_;
|
||||
}
|
||||
const std::string& get_type_name() const
|
||||
{
|
||||
return this->type_name_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string type_name_{};
|
||||
std::map<size_t, std::string> members_{};
|
||||
private:
|
||||
std::string type_name_{};
|
||||
std::map<size_t, std::string> members_{};
|
||||
};
|
||||
|
||||
@@ -5,61 +5,59 @@
|
||||
|
||||
#define THE_SIZE 30
|
||||
|
||||
extern "C" NO_INLINE EXPORT_SYMBOL
|
||||
void vulnerable(const uint8_t* data, const size_t size)
|
||||
extern "C" NO_INLINE EXPORT_SYMBOL void vulnerable(const uint8_t* data, const size_t size)
|
||||
{
|
||||
if (size < 10)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (size < 10)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[9] != 'A')
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (data[9] != 'A')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[8] != 'B')
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (data[8] != 'B')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[7] != 'C')
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (data[7] != 'C')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[2] != 'V')
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (data[2] != 'V')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[4] != 'H')
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (data[4] != 'H')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (size < 100)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (size < 100)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
*(int*)1 = 1;
|
||||
*(int*)1 = 1;
|
||||
}
|
||||
|
||||
uint8_t buffer[THE_SIZE] = {};
|
||||
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
const void* input = buffer;
|
||||
auto size = sizeof(buffer);
|
||||
const void* input = buffer;
|
||||
auto size = sizeof(buffer);
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
input = argv[1];
|
||||
size = strlen(argv[1]);
|
||||
}
|
||||
if (argc > 1)
|
||||
{
|
||||
input = argv[1];
|
||||
size = strlen(argv[1]);
|
||||
}
|
||||
|
||||
vulnerable((uint8_t*)input, size);
|
||||
return 0;
|
||||
vulnerable((uint8_t*)input, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -8,376 +8,365 @@ using namespace std::literals;
|
||||
|
||||
namespace network
|
||||
{
|
||||
void initialize_wsa()
|
||||
{
|
||||
void initialize_wsa()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
static struct wsa_initializer
|
||||
{
|
||||
public:
|
||||
wsa_initializer()
|
||||
{
|
||||
WSADATA wsa_data;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsa_data))
|
||||
{
|
||||
throw std::runtime_error("Unable to initialize WSA");
|
||||
}
|
||||
}
|
||||
static struct wsa_initializer
|
||||
{
|
||||
wsa_initializer()
|
||||
{
|
||||
WSADATA wsa_data;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsa_data))
|
||||
{
|
||||
throw std::runtime_error("Unable to initialize WSA");
|
||||
}
|
||||
}
|
||||
|
||||
~wsa_initializer()
|
||||
{
|
||||
WSACleanup();
|
||||
}
|
||||
} _;
|
||||
~wsa_initializer()
|
||||
{
|
||||
WSACleanup();
|
||||
}
|
||||
} _;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
address::address()
|
||||
{
|
||||
initialize_wsa();
|
||||
ZeroMemory(&this->storage_, this->get_max_size());
|
||||
address::address()
|
||||
{
|
||||
initialize_wsa();
|
||||
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()
|
||||
{
|
||||
this->parse(addr, family);
|
||||
}
|
||||
address::address(const std::string& addr, const std::optional<int>& family)
|
||||
: address()
|
||||
{
|
||||
this->parse(addr, family);
|
||||
}
|
||||
|
||||
address::address(const sockaddr_in6& addr)
|
||||
: address()
|
||||
{
|
||||
this->address6_ = addr;
|
||||
}
|
||||
address::address(const sockaddr_in6& addr)
|
||||
: address()
|
||||
{
|
||||
this->address6_ = addr;
|
||||
}
|
||||
|
||||
address::address(const sockaddr_in& addr)
|
||||
: address()
|
||||
{
|
||||
this->address4_ = addr;
|
||||
}
|
||||
address::address(const sockaddr_in& addr)
|
||||
: address()
|
||||
{
|
||||
this->address4_ = addr;
|
||||
}
|
||||
|
||||
address::address(const sockaddr* addr, const socklen_t length)
|
||||
: address()
|
||||
{
|
||||
this->set_address(addr, length);
|
||||
}
|
||||
address::address(const sockaddr* addr, const socklen_t length)
|
||||
: address()
|
||||
{
|
||||
this->set_address(addr, length);
|
||||
}
|
||||
|
||||
void address::set_ipv4(const uint32_t ip)
|
||||
{
|
||||
in_addr addr{};
|
||||
addr.s_addr = ip;
|
||||
this->set_ipv4(addr);
|
||||
}
|
||||
void address::set_ipv4(const uint32_t ip)
|
||||
{
|
||||
in_addr addr{};
|
||||
addr.s_addr = ip;
|
||||
this->set_ipv4(addr);
|
||||
}
|
||||
|
||||
bool address::operator==(const address& obj) const
|
||||
{
|
||||
if (this->address_.sa_family != obj.address_.sa_family)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool address::operator==(const address& obj) const
|
||||
{
|
||||
if (this->address_.sa_family != obj.address_.sa_family)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->get_port() != obj.get_port())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (this->get_port() != obj.get_port())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->address_.sa_family == AF_INET)
|
||||
{
|
||||
return this->address4_.sin_addr.s_addr == obj.address4_.sin_addr.s_addr;
|
||||
}
|
||||
else if (this->address_.sa_family == AF_INET6)
|
||||
{
|
||||
return !memcmp(this->address6_.sin6_addr.s6_addr, obj.address6_.sin6_addr.s6_addr,
|
||||
sizeof(obj.address6_.sin6_addr.s6_addr));
|
||||
}
|
||||
if (this->address_.sa_family == AF_INET)
|
||||
{
|
||||
return this->address4_.sin_addr.s_addr == obj.address4_.sin_addr.s_addr;
|
||||
}
|
||||
else if (this->address_.sa_family == AF_INET6)
|
||||
{
|
||||
return !memcmp(this->address6_.sin6_addr.s6_addr, 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)
|
||||
{
|
||||
ZeroMemory(&this->address4_, sizeof(this->address4_));
|
||||
this->address4_.sin_family = AF_INET;
|
||||
this->address4_.sin_addr = addr;
|
||||
}
|
||||
void address::set_ipv4(const in_addr& addr)
|
||||
{
|
||||
ZeroMemory(&this->address4_, sizeof(this->address4_));
|
||||
this->address4_.sin_family = AF_INET;
|
||||
this->address4_.sin_addr = addr;
|
||||
}
|
||||
|
||||
void address::set_ipv6(const in6_addr& addr)
|
||||
{
|
||||
ZeroMemory(&this->address6_, sizeof(this->address6_));
|
||||
this->address6_.sin6_family = AF_INET6;
|
||||
this->address6_.sin6_addr = addr;
|
||||
}
|
||||
void address::set_ipv6(const in6_addr& addr)
|
||||
{
|
||||
ZeroMemory(&this->address6_, sizeof(this->address6_));
|
||||
this->address6_.sin6_family = AF_INET6;
|
||||
this->address6_.sin6_addr = addr;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
this->address4_ = *reinterpret_cast<const sockaddr_in*>(addr);
|
||||
}
|
||||
else if (static_cast<size_t>(length) == sizeof(sockaddr_in6) && addr->sa_family == AF_INET6)
|
||||
{
|
||||
this->address6_ = *reinterpret_cast<const sockaddr_in6*>(addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Invalid network address");
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
this->address4_ = *reinterpret_cast<const sockaddr_in*>(addr);
|
||||
}
|
||||
else if (static_cast<size_t>(length) == sizeof(sockaddr_in6) && addr->sa_family == AF_INET6)
|
||||
{
|
||||
this->address6_ = *reinterpret_cast<const sockaddr_in6*>(addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Invalid network address");
|
||||
}
|
||||
}
|
||||
|
||||
void address::set_port(const unsigned short port)
|
||||
{
|
||||
switch (this->address_.sa_family)
|
||||
{
|
||||
case AF_INET:
|
||||
this->address4_.sin_port = htons(port);
|
||||
break;
|
||||
case AF_INET6:
|
||||
this->address6_.sin6_port = htons(port);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Invalid address family");
|
||||
}
|
||||
}
|
||||
void address::set_port(const unsigned short port)
|
||||
{
|
||||
switch (this->address_.sa_family)
|
||||
{
|
||||
case AF_INET:
|
||||
this->address4_.sin_port = htons(port);
|
||||
break;
|
||||
case AF_INET6:
|
||||
this->address6_.sin6_port = htons(port);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Invalid address family");
|
||||
}
|
||||
}
|
||||
|
||||
unsigned short address::get_port() const
|
||||
{
|
||||
switch (this->address_.sa_family)
|
||||
{
|
||||
case AF_INET:
|
||||
return ntohs(this->address4_.sin_port);
|
||||
case AF_INET6:
|
||||
return ntohs(this->address6_.sin6_port);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
unsigned short address::get_port() const
|
||||
{
|
||||
switch (this->address_.sa_family)
|
||||
{
|
||||
case AF_INET:
|
||||
return ntohs(this->address4_.sin_port);
|
||||
case AF_INET6:
|
||||
return ntohs(this->address6_.sin6_port);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::string address::to_string() const
|
||||
{
|
||||
char buffer[1000] = {0};
|
||||
std::string addr;
|
||||
std::string address::to_string() const
|
||||
{
|
||||
char buffer[1000] = {0};
|
||||
std::string addr;
|
||||
|
||||
switch (this->address_.sa_family)
|
||||
{
|
||||
case AF_INET:
|
||||
inet_ntop(this->address_.sa_family, &this->address4_.sin_addr, buffer, sizeof(buffer));
|
||||
addr = std::string(buffer);
|
||||
break;
|
||||
case AF_INET6:
|
||||
inet_ntop(this->address_.sa_family, &this->address6_.sin6_addr, buffer, sizeof(buffer));
|
||||
addr = "[" + std::string(buffer) + "]";
|
||||
break;
|
||||
default:
|
||||
buffer[0] = '?';
|
||||
buffer[1] = 0;
|
||||
addr = std::string(buffer);
|
||||
break;
|
||||
}
|
||||
switch (this->address_.sa_family)
|
||||
{
|
||||
case AF_INET:
|
||||
inet_ntop(this->address_.sa_family, &this->address4_.sin_addr, buffer, sizeof(buffer));
|
||||
addr = std::string(buffer);
|
||||
break;
|
||||
case AF_INET6:
|
||||
inet_ntop(this->address_.sa_family, &this->address6_.sin6_addr, buffer, sizeof(buffer));
|
||||
addr = "[" + std::string(buffer) + "]";
|
||||
break;
|
||||
default:
|
||||
buffer[0] = '?';
|
||||
buffer[1] = 0;
|
||||
addr = std::string(buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
return addr + ":"s + std::to_string(this->get_port());
|
||||
}
|
||||
return addr + ":"s + std::to_string(this->get_port());
|
||||
}
|
||||
|
||||
bool address::is_local() const
|
||||
{
|
||||
if (this->address_.sa_family != AF_INET)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool address::is_local() const
|
||||
{
|
||||
if (this->address_.sa_family != AF_INET)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// According to: https://en.wikipedia.org/wiki/Private_network
|
||||
// According to: https://en.wikipedia.org/wiki/Private_network
|
||||
|
||||
uint8_t bytes[4];
|
||||
*reinterpret_cast<uint32_t*>(&bytes) = this->address4_.sin_addr.s_addr;
|
||||
uint8_t bytes[4];
|
||||
*reinterpret_cast<uint32_t*>(&bytes) = this->address4_.sin_addr.s_addr;
|
||||
|
||||
// 10.X.X.X
|
||||
if (bytes[0] == 10)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// 10.X.X.X
|
||||
if (bytes[0] == 10)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 192.168.X.X
|
||||
if (bytes[0] == 192
|
||||
&& bytes[1] == 168)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// 192.168.X.X
|
||||
if (bytes[0] == 192 && bytes[1] == 168)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 172.16.X.X - 172.31.X.X
|
||||
if (bytes[0] == 172
|
||||
&& bytes[1] >= 16
|
||||
&& bytes[1] < 32)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// 172.16.X.X - 172.31.X.X
|
||||
if (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] < 32)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 127.0.0.1
|
||||
if (this->address4_.sin_addr.s_addr == 0x0100007F)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// 127.0.0.1
|
||||
if (this->address4_.sin_addr.s_addr == 0x0100007F)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
sockaddr& address::get_addr()
|
||||
{
|
||||
return this->address_;
|
||||
}
|
||||
sockaddr& address::get_addr()
|
||||
{
|
||||
return this->address_;
|
||||
}
|
||||
|
||||
const sockaddr& address::get_addr() const
|
||||
{
|
||||
return this->address_;
|
||||
}
|
||||
const sockaddr& address::get_addr() const
|
||||
{
|
||||
return this->address_;
|
||||
}
|
||||
|
||||
sockaddr_in& address::get_in_addr()
|
||||
{
|
||||
return this->address4_;
|
||||
}
|
||||
sockaddr_in& address::get_in_addr()
|
||||
{
|
||||
return this->address4_;
|
||||
}
|
||||
|
||||
sockaddr_in6& address::get_in6_addr()
|
||||
{
|
||||
return this->address6_;
|
||||
}
|
||||
sockaddr_in6& address::get_in6_addr()
|
||||
{
|
||||
return this->address6_;
|
||||
}
|
||||
|
||||
const sockaddr_in& address::get_in_addr() const
|
||||
{
|
||||
return this->address4_;
|
||||
}
|
||||
const sockaddr_in& address::get_in_addr() const
|
||||
{
|
||||
return this->address4_;
|
||||
}
|
||||
|
||||
const sockaddr_in6& address::get_in6_addr() const
|
||||
{
|
||||
return this->address6_;
|
||||
}
|
||||
const sockaddr_in6& address::get_in6_addr() const
|
||||
{
|
||||
return this->address6_;
|
||||
}
|
||||
|
||||
socklen_t address::get_size() const
|
||||
{
|
||||
switch (this->address_.sa_family)
|
||||
{
|
||||
case AF_INET:
|
||||
return static_cast<socklen_t>(sizeof(this->address4_));
|
||||
case AF_INET6:
|
||||
return static_cast<socklen_t>(sizeof(this->address6_));
|
||||
default:
|
||||
return static_cast<socklen_t>(sizeof(this->address_));
|
||||
}
|
||||
}
|
||||
socklen_t address::get_size() const
|
||||
{
|
||||
switch (this->address_.sa_family)
|
||||
{
|
||||
case AF_INET:
|
||||
return static_cast<socklen_t>(sizeof(this->address4_));
|
||||
case AF_INET6:
|
||||
return static_cast<socklen_t>(sizeof(this->address6_));
|
||||
default:
|
||||
return static_cast<socklen_t>(sizeof(this->address_));
|
||||
}
|
||||
}
|
||||
|
||||
socklen_t address::get_max_size() const
|
||||
{
|
||||
constexpr auto s = sizeof(this->address_);
|
||||
constexpr auto s4 = sizeof(this->address4_);
|
||||
constexpr auto s6 = sizeof(this->address6_);
|
||||
constexpr auto sstore = sizeof(this->storage_);
|
||||
constexpr auto max_size = std::max(sstore, std::max(s, std::max(s4, s6)));
|
||||
static_assert(max_size == sstore);
|
||||
socklen_t address::get_max_size() const
|
||||
{
|
||||
constexpr auto s = sizeof(this->address_);
|
||||
constexpr auto s4 = sizeof(this->address4_);
|
||||
constexpr auto s6 = sizeof(this->address6_);
|
||||
constexpr auto sstore = sizeof(this->storage_);
|
||||
constexpr auto max_size = std::max(sstore, std::max(s, std::max(s4, s6)));
|
||||
static_assert(max_size == sstore);
|
||||
|
||||
return static_cast<socklen_t>(max_size);
|
||||
}
|
||||
return static_cast<socklen_t>(max_size);
|
||||
}
|
||||
|
||||
bool address::is_ipv4() const
|
||||
{
|
||||
return this->address_.sa_family == AF_INET;
|
||||
}
|
||||
bool address::is_ipv4() const
|
||||
{
|
||||
return this->address_.sa_family == AF_INET;
|
||||
}
|
||||
|
||||
bool address::is_ipv6() const
|
||||
{
|
||||
return this->address_.sa_family == AF_INET6;
|
||||
}
|
||||
bool address::is_ipv6() const
|
||||
{
|
||||
return this->address_.sa_family == AF_INET6;
|
||||
}
|
||||
|
||||
bool address::is_supported() const
|
||||
{
|
||||
return is_ipv4() || is_ipv6();
|
||||
}
|
||||
bool address::is_supported() const
|
||||
{
|
||||
return is_ipv4() || is_ipv6();
|
||||
}
|
||||
|
||||
void address::parse(std::string addr, const std::optional<int>& family)
|
||||
{
|
||||
std::optional<uint16_t> port_value{};
|
||||
void address::parse(std::string addr, const std::optional<int>& family)
|
||||
{
|
||||
std::optional<uint16_t> port_value{};
|
||||
|
||||
const auto pos = addr.find_last_of(':');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
auto port = addr.substr(pos + 1);
|
||||
port_value = uint16_t(atoi(port.data()));
|
||||
addr = addr.substr(0, pos);
|
||||
}
|
||||
const auto pos = addr.find_last_of(':');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
auto port = addr.substr(pos + 1);
|
||||
port_value = uint16_t(atoi(port.data()));
|
||||
addr = addr.substr(0, pos);
|
||||
}
|
||||
|
||||
this->resolve(addr, family);
|
||||
this->resolve(addr, family);
|
||||
|
||||
if (port_value)
|
||||
{
|
||||
this->set_port(*port_value);
|
||||
}
|
||||
}
|
||||
if (port_value)
|
||||
{
|
||||
this->set_port(*port_value);
|
||||
}
|
||||
}
|
||||
|
||||
void address::resolve(const std::string& hostname, const std::optional<int>& family)
|
||||
{
|
||||
const auto port = this->get_port();
|
||||
auto port_reset_action = utils::finally([this, port]()
|
||||
{
|
||||
this->set_port(port);
|
||||
});
|
||||
void address::resolve(const std::string& hostname, const std::optional<int>& family)
|
||||
{
|
||||
const auto port = this->get_port();
|
||||
auto port_reset_action = utils::finally([this, port]() { this->set_port(port); });
|
||||
|
||||
const auto result = resolve_multiple(hostname);
|
||||
for (const auto& addr : result)
|
||||
{
|
||||
if (addr.is_supported() && (!family || addr.get_addr().sa_family == *family))
|
||||
{
|
||||
this->set_address(&addr.get_addr(), addr.get_size());
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto result = resolve_multiple(hostname);
|
||||
for (const auto& addr : result)
|
||||
{
|
||||
if (addr.is_supported() && (!family || addr.get_addr().sa_family == *family))
|
||||
{
|
||||
this->set_address(&addr.get_addr(), addr.get_size());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
port_reset_action.cancel();
|
||||
throw std::runtime_error{"Unable to resolve hostname: " + hostname};
|
||||
}
|
||||
port_reset_action.cancel();
|
||||
throw std::runtime_error{"Unable to resolve hostname: " + hostname};
|
||||
}
|
||||
|
||||
std::vector<address> address::resolve_multiple(const std::string& hostname)
|
||||
{
|
||||
std::vector<address> results{};
|
||||
std::vector<address> address::resolve_multiple(const std::string& hostname)
|
||||
{
|
||||
std::vector<address> results{};
|
||||
|
||||
addrinfo* result = nullptr;
|
||||
if (!getaddrinfo(hostname.data(), nullptr, nullptr, &result))
|
||||
{
|
||||
const auto _2 = utils::finally([&result]
|
||||
{
|
||||
freeaddrinfo(result);
|
||||
});
|
||||
addrinfo* result = nullptr;
|
||||
if (!getaddrinfo(hostname.data(), nullptr, nullptr, &result))
|
||||
{
|
||||
const auto _2 = utils::finally([&result] { freeaddrinfo(result); });
|
||||
|
||||
for (auto* i = result; i; i = i->ai_next)
|
||||
{
|
||||
if (i->ai_family == AF_INET || i->ai_family == AF_INET6)
|
||||
{
|
||||
address a{};
|
||||
a.set_address(i->ai_addr, static_cast<socklen_t>(i->ai_addrlen));
|
||||
results.emplace_back(std::move(a));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto* i = result; i; i = i->ai_next)
|
||||
{
|
||||
if (i->ai_family == AF_INET || i->ai_family == AF_INET6)
|
||||
{
|
||||
address a{};
|
||||
a.set_address(i->ai_addr, static_cast<socklen_t>(i->ai_addrlen));
|
||||
results.emplace_back(std::move(a));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
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 port = a.get_port();
|
||||
const uint32_t family = a.get_addr().sa_family;
|
||||
const uint32_t port = a.get_port();
|
||||
|
||||
std::size_t hash = std::hash<uint32_t>{}(family);
|
||||
hash ^= std::hash<uint32_t>{}(port);
|
||||
switch (a.get_addr().sa_family)
|
||||
{
|
||||
case AF_INET:
|
||||
hash ^= std::hash<decltype(a.get_in_addr().sin_addr.s_addr)>{}(a.get_in_addr().sin_addr.s_addr);
|
||||
break;
|
||||
case AF_INET6:
|
||||
hash ^= std::hash<std::string_view>{}(std::string_view{
|
||||
reinterpret_cast<const char*>(a.get_in6_addr().sin6_addr.s6_addr),
|
||||
sizeof(a.get_in6_addr().sin6_addr.s6_addr)
|
||||
});
|
||||
break;
|
||||
}
|
||||
std::size_t hash = std::hash<uint32_t>{}(family);
|
||||
hash ^= std::hash<uint32_t>{}(port);
|
||||
switch (a.get_addr().sa_family)
|
||||
{
|
||||
case AF_INET:
|
||||
hash ^= std::hash<decltype(a.get_in_addr().sin_addr.s_addr)>{}(a.get_in_addr().sin_addr.s_addr);
|
||||
break;
|
||||
case AF_INET6:
|
||||
hash ^= std::hash<std::string_view>{}(
|
||||
std::string_view{reinterpret_cast<const char*>(a.get_in6_addr().sin6_addr.s6_addr),
|
||||
sizeof(a.get_in6_addr().sin6_addr.s6_addr)});
|
||||
break;
|
||||
}
|
||||
|
||||
return hash;
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -36,71 +36,71 @@ using socklen_t = int;
|
||||
|
||||
namespace network
|
||||
{
|
||||
void initialize_wsa();
|
||||
void initialize_wsa();
|
||||
|
||||
class address
|
||||
{
|
||||
public:
|
||||
address();
|
||||
address(const std::string& addr, const std::optional<int>& family = {});
|
||||
address(const sockaddr_in& addr);
|
||||
address(const sockaddr_in6& addr);
|
||||
address(const sockaddr* addr, socklen_t length);
|
||||
class address
|
||||
{
|
||||
public:
|
||||
address();
|
||||
address(const std::string& addr, const std::optional<int>& family = {});
|
||||
address(const sockaddr_in& addr);
|
||||
address(const sockaddr_in6& addr);
|
||||
address(const sockaddr* addr, socklen_t length);
|
||||
|
||||
void set_ipv4(uint32_t ip);
|
||||
void set_ipv4(const in_addr& addr);
|
||||
void set_ipv6(const in6_addr& addr);
|
||||
void set_address(const sockaddr* addr, socklen_t length);
|
||||
void set_ipv4(uint32_t ip);
|
||||
void set_ipv4(const in_addr& addr);
|
||||
void set_ipv6(const in6_addr& addr);
|
||||
void set_address(const sockaddr* addr, socklen_t length);
|
||||
|
||||
void set_port(unsigned short port);
|
||||
[[nodiscard]] unsigned short get_port() const;
|
||||
void set_port(unsigned short port);
|
||||
[[nodiscard]] unsigned short get_port() const;
|
||||
|
||||
sockaddr& get_addr();
|
||||
sockaddr_in& get_in_addr();
|
||||
sockaddr_in6& get_in6_addr();
|
||||
sockaddr& get_addr();
|
||||
sockaddr_in& get_in_addr();
|
||||
sockaddr_in6& get_in6_addr();
|
||||
|
||||
const sockaddr& get_addr() const;
|
||||
const sockaddr_in& get_in_addr() const;
|
||||
const sockaddr_in6& get_in6_addr() const;
|
||||
const sockaddr& get_addr() const;
|
||||
const sockaddr_in& get_in_addr() const;
|
||||
const sockaddr_in6& get_in6_addr() const;
|
||||
|
||||
socklen_t get_size() const;
|
||||
socklen_t get_max_size() const;
|
||||
socklen_t get_size() const;
|
||||
socklen_t get_max_size() const;
|
||||
|
||||
bool is_ipv4() const;
|
||||
bool is_ipv6() const;
|
||||
bool is_supported() const;
|
||||
bool is_ipv4() const;
|
||||
bool is_ipv6() const;
|
||||
bool is_supported() const;
|
||||
|
||||
[[nodiscard]] bool is_local() const;
|
||||
[[nodiscard]] std::string to_string() const;
|
||||
[[nodiscard]] bool is_local() 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
|
||||
{
|
||||
return !(*this == obj);
|
||||
}
|
||||
bool operator!=(const address& obj) const
|
||||
{
|
||||
return !(*this == obj);
|
||||
}
|
||||
|
||||
static std::vector<address> resolve_multiple(const std::string& hostname);
|
||||
static std::vector<address> resolve_multiple(const std::string& hostname);
|
||||
|
||||
private:
|
||||
union
|
||||
{
|
||||
sockaddr address_;
|
||||
sockaddr_in address4_;
|
||||
sockaddr_in6 address6_;
|
||||
sockaddr_storage storage_;
|
||||
};
|
||||
private:
|
||||
union
|
||||
{
|
||||
sockaddr address_;
|
||||
sockaddr_in address4_;
|
||||
sockaddr_in6 address6_;
|
||||
sockaddr_storage storage_;
|
||||
};
|
||||
|
||||
void parse(std::string addr, const std::optional<int>& family = {});
|
||||
void resolve(const std::string& hostname, 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 = {});
|
||||
};
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash<network::address>
|
||||
{
|
||||
std::size_t operator()(const network::address& a) const noexcept;
|
||||
};
|
||||
template <>
|
||||
struct hash<network::address>
|
||||
{
|
||||
std::size_t operator()(const network::address& a) const noexcept;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,226 +6,225 @@ using namespace std::literals;
|
||||
|
||||
namespace network
|
||||
{
|
||||
socket::socket(const int af)
|
||||
: address_family_(af)
|
||||
{
|
||||
initialize_wsa();
|
||||
this->socket_ = ::socket(af, SOCK_DGRAM, IPPROTO_UDP);
|
||||
socket::socket(const int af)
|
||||
: address_family_(af)
|
||||
{
|
||||
initialize_wsa();
|
||||
this->socket_ = ::socket(af, SOCK_DGRAM, IPPROTO_UDP);
|
||||
|
||||
if (af == AF_INET6)
|
||||
{
|
||||
int i = 1;
|
||||
setsockopt(this->socket_, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&i),
|
||||
static_cast<int>(sizeof(i)));
|
||||
}
|
||||
}
|
||||
if (af == AF_INET6)
|
||||
{
|
||||
int i = 1;
|
||||
setsockopt(this->socket_, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&i),
|
||||
static_cast<int>(sizeof(i)));
|
||||
}
|
||||
}
|
||||
|
||||
socket::~socket()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
socket::~socket()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
socket::socket(socket&& obj) noexcept
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
socket::socket(socket&& obj) noexcept
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
|
||||
socket& socket::operator=(socket&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->release();
|
||||
this->socket_ = obj.socket_;
|
||||
this->port_ = obj.port_;
|
||||
this->address_family_ = obj.address_family_;
|
||||
socket& socket::operator=(socket&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->release();
|
||||
this->socket_ = obj.socket_;
|
||||
this->port_ = obj.port_;
|
||||
this->address_family_ = obj.address_family_;
|
||||
|
||||
obj.socket_ = INVALID_SOCKET;
|
||||
obj.address_family_ = AF_UNSPEC;
|
||||
}
|
||||
obj.socket_ = INVALID_SOCKET;
|
||||
obj.address_family_ = AF_UNSPEC;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void socket::release()
|
||||
{
|
||||
if (this->socket_ != INVALID_SOCKET)
|
||||
{
|
||||
closesocket(this->socket_);
|
||||
this->socket_ = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
void socket::release()
|
||||
{
|
||||
if (this->socket_ != INVALID_SOCKET)
|
||||
{
|
||||
closesocket(this->socket_);
|
||||
this->socket_ = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
|
||||
bool socket::bind_port(const address& target)
|
||||
{
|
||||
const auto result = bind(this->socket_, &target.get_addr(), target.get_size()) == 0;
|
||||
if (result)
|
||||
{
|
||||
this->port_ = target.get_port();
|
||||
}
|
||||
bool socket::bind_port(const address& target)
|
||||
{
|
||||
const auto result = bind(this->socket_, &target.get_addr(), target.get_size()) == 0;
|
||||
if (result)
|
||||
{
|
||||
this->port_ = target.get_port();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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,
|
||||
&target.get_addr(),
|
||||
target.get_size());
|
||||
return static_cast<size_t>(res) == size;
|
||||
}
|
||||
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,
|
||||
&target.get_addr(), target.get_size());
|
||||
return static_cast<size_t>(res) == size;
|
||||
}
|
||||
|
||||
bool socket::send(const address& target, const std::string& data) const
|
||||
{
|
||||
return this->send(target, data.data(), data.size());
|
||||
}
|
||||
bool socket::send(const address& target, const std::string& data) const
|
||||
{
|
||||
return this->send(target, data.data(), data.size());
|
||||
}
|
||||
|
||||
bool socket::receive(address& source, std::string& data) const
|
||||
{
|
||||
char buffer[0x2000];
|
||||
auto len = source.get_max_size();
|
||||
bool socket::receive(address& source, std::string& data) const
|
||||
{
|
||||
char buffer[0x2000];
|
||||
auto len = source.get_max_size();
|
||||
|
||||
const auto result = recvfrom(this->socket_, buffer, static_cast<int>(sizeof(buffer)), 0, &source.get_addr(),
|
||||
&len);
|
||||
if (result == SOCKET_ERROR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto result =
|
||||
recvfrom(this->socket_, buffer, static_cast<int>(sizeof(buffer)), 0, &source.get_addr(), &len);
|
||||
if (result == SOCKET_ERROR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
data.assign(buffer, buffer + result);
|
||||
return true;
|
||||
}
|
||||
data.assign(buffer, buffer + result);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool socket::set_blocking(const bool blocking)
|
||||
{
|
||||
return socket::set_blocking(this->socket_, blocking);
|
||||
}
|
||||
bool socket::set_blocking(const bool 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
|
||||
unsigned long mode = blocking ? 0 : 1;
|
||||
return ioctlsocket(s, FIONBIO, &mode) == 0;
|
||||
unsigned long mode = blocking ? 0 : 1;
|
||||
return ioctlsocket(s, FIONBIO, &mode) == 0;
|
||||
#else
|
||||
int flags = fcntl(s, F_GETFL, 0);
|
||||
if (flags == -1) return false;
|
||||
flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
|
||||
return fcntl(s, F_SETFL, flags) == 0;
|
||||
int flags = fcntl(s, F_GETFL, 0);
|
||||
if (flags == -1)
|
||||
return false;
|
||||
flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
|
||||
return fcntl(s, F_SETFL, flags) == 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool socket::sleep(const std::chrono::milliseconds timeout) const
|
||||
{
|
||||
/*fd_set fdr;
|
||||
FD_ZERO(&fdr);
|
||||
FD_SET(this->socket_, &fdr);
|
||||
bool socket::sleep(const std::chrono::milliseconds timeout) const
|
||||
{
|
||||
/*fd_set fdr;
|
||||
FD_ZERO(&fdr);
|
||||
FD_SET(this->socket_, &fdr);
|
||||
|
||||
const auto msec = timeout.count();
|
||||
const auto msec = timeout.count();
|
||||
|
||||
timeval tv{};
|
||||
tv.tv_sec = static_cast<long>(msec / 1000ll);
|
||||
tv.tv_usec = static_cast<long>((msec % 1000) * 1000);
|
||||
timeval tv{};
|
||||
tv.tv_sec = static_cast<long>(msec / 1000ll);
|
||||
tv.tv_usec = static_cast<long>((msec % 1000) * 1000);
|
||||
|
||||
const auto retval = select(static_cast<int>(this->socket_) + 1, &fdr, nullptr, nullptr, &tv);
|
||||
if (retval == SOCKET_ERROR)
|
||||
{
|
||||
std::this_thread::sleep_for(1ms);
|
||||
return socket_is_ready;
|
||||
}
|
||||
const auto retval = select(static_cast<int>(this->socket_) + 1, &fdr, nullptr, nullptr, &tv);
|
||||
if (retval == SOCKET_ERROR)
|
||||
{
|
||||
std::this_thread::sleep_for(1ms);
|
||||
return socket_is_ready;
|
||||
}
|
||||
|
||||
if (retval > 0)
|
||||
{
|
||||
return socket_is_ready;
|
||||
}
|
||||
if (retval > 0)
|
||||
{
|
||||
return socket_is_ready;
|
||||
}
|
||||
|
||||
return !socket_is_ready;*/
|
||||
return !socket_is_ready;*/
|
||||
|
||||
std::vector<const socket*> sockets{};
|
||||
sockets.push_back(this);
|
||||
std::vector<const socket*> sockets{};
|
||||
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
|
||||
{
|
||||
const auto duration = time_point - std::chrono::high_resolution_clock::now();
|
||||
return this->sleep(std::chrono::duration_cast<std::chrono::milliseconds>(duration));
|
||||
}
|
||||
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();
|
||||
return this->sleep(std::chrono::duration_cast<std::chrono::milliseconds>(duration));
|
||||
}
|
||||
|
||||
SOCKET socket::get_socket() const
|
||||
{
|
||||
return this->socket_;
|
||||
}
|
||||
SOCKET socket::get_socket() const
|
||||
{
|
||||
return this->socket_;
|
||||
}
|
||||
|
||||
uint16_t socket::get_port() const
|
||||
{
|
||||
return this->port_;
|
||||
}
|
||||
uint16_t socket::get_port() const
|
||||
{
|
||||
return this->port_;
|
||||
}
|
||||
|
||||
int socket::get_address_family() const
|
||||
{
|
||||
return this->address_family_;
|
||||
}
|
||||
int socket::get_address_family() const
|
||||
{
|
||||
return this->address_family_;
|
||||
}
|
||||
|
||||
bool socket::sleep_sockets(const std::span<const socket*>& sockets, const std::chrono::milliseconds timeout)
|
||||
{
|
||||
std::vector<pollfd> pfds{};
|
||||
pfds.resize(sockets.size());
|
||||
bool socket::sleep_sockets(const std::span<const socket*>& sockets, const std::chrono::milliseconds timeout)
|
||||
{
|
||||
std::vector<pollfd> pfds{};
|
||||
pfds.resize(sockets.size());
|
||||
|
||||
for (size_t i = 0; i < sockets.size(); ++i)
|
||||
{
|
||||
auto& pfd = pfds.at(i);
|
||||
const auto& socket = sockets[i];
|
||||
for (size_t i = 0; i < sockets.size(); ++i)
|
||||
{
|
||||
auto& pfd = pfds.at(i);
|
||||
const auto& socket = sockets[i];
|
||||
|
||||
pfd.fd = socket->get_socket();
|
||||
pfd.events = POLLIN;
|
||||
pfd.revents = 0;
|
||||
}
|
||||
pfd.fd = socket->get_socket();
|
||||
pfd.events = POLLIN;
|
||||
pfd.revents = 0;
|
||||
}
|
||||
|
||||
const auto retval = poll(pfds.data(), static_cast<uint32_t>(pfds.size()),
|
||||
static_cast<int>(timeout.count()));
|
||||
const auto retval = poll(pfds.data(), static_cast<uint32_t>(pfds.size()), static_cast<int>(timeout.count()));
|
||||
|
||||
if (retval == SOCKET_ERROR)
|
||||
{
|
||||
std::this_thread::sleep_for(1ms);
|
||||
return socket_is_ready;
|
||||
}
|
||||
if (retval == SOCKET_ERROR)
|
||||
{
|
||||
std::this_thread::sleep_for(1ms);
|
||||
return socket_is_ready;
|
||||
}
|
||||
|
||||
if (retval > 0)
|
||||
{
|
||||
return socket_is_ready;
|
||||
}
|
||||
if (retval > 0)
|
||||
{
|
||||
return socket_is_ready;
|
||||
}
|
||||
|
||||
return !socket_is_ready;
|
||||
}
|
||||
return !socket_is_ready;
|
||||
}
|
||||
|
||||
bool socket::is_socket_ready(const SOCKET s, const bool in_poll)
|
||||
{
|
||||
pollfd pfd{};
|
||||
bool socket::is_socket_ready(const SOCKET s, const bool in_poll)
|
||||
{
|
||||
pollfd pfd{};
|
||||
|
||||
pfd.fd = s;
|
||||
pfd.events = in_poll ? POLLIN : POLLOUT;
|
||||
pfd.revents = 0;
|
||||
pfd.fd = s;
|
||||
pfd.events = in_poll ? POLLIN : POLLOUT;
|
||||
pfd.revents = 0;
|
||||
|
||||
const auto retval = poll(&pfd, 1, 0);
|
||||
const auto retval = poll(&pfd, 1, 0);
|
||||
|
||||
if (retval == SOCKET_ERROR)
|
||||
{
|
||||
std::this_thread::sleep_for(1ms);
|
||||
return socket_is_ready;
|
||||
}
|
||||
if (retval == SOCKET_ERROR)
|
||||
{
|
||||
std::this_thread::sleep_for(1ms);
|
||||
return socket_is_ready;
|
||||
}
|
||||
|
||||
if (retval > 0)
|
||||
{
|
||||
return socket_is_ready;
|
||||
}
|
||||
if (retval > 0)
|
||||
{
|
||||
return socket_is_ready;
|
||||
}
|
||||
|
||||
return !socket_is_ready;
|
||||
}
|
||||
return !socket_is_ready;
|
||||
}
|
||||
|
||||
bool socket::sleep_sockets_until(const std::span<const socket*>& sockets,
|
||||
const std::chrono::high_resolution_clock::time_point time_point)
|
||||
{
|
||||
const auto duration = time_point - std::chrono::high_resolution_clock::now();
|
||||
return sleep_sockets(sockets, std::chrono::duration_cast<std::chrono::milliseconds>(duration));
|
||||
}
|
||||
bool socket::sleep_sockets_until(const std::span<const socket*>& sockets,
|
||||
const std::chrono::high_resolution_clock::time_point time_point)
|
||||
{
|
||||
const auto duration = time_point - std::chrono::high_resolution_clock::now();
|
||||
return sleep_sockets(sockets, std::chrono::duration_cast<std::chrono::milliseconds>(duration));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,63 +8,63 @@
|
||||
#ifdef _WIN32
|
||||
using send_size = int;
|
||||
#define GET_SOCKET_ERROR() (WSAGetLastError())
|
||||
#define poll WSAPoll
|
||||
#define SOCK_WOULDBLOCK WSAEWOULDBLOCK
|
||||
#define poll WSAPoll
|
||||
#define SOCK_WOULDBLOCK WSAEWOULDBLOCK
|
||||
#else
|
||||
using SOCKET = int;
|
||||
using send_size = size_t;
|
||||
#define INVALID_SOCKET (SOCKET)(~0)
|
||||
#define SOCKET_ERROR (-1)
|
||||
#define INVALID_SOCKET (SOCKET)(~0)
|
||||
#define SOCKET_ERROR (-1)
|
||||
#define GET_SOCKET_ERROR() (errno)
|
||||
#define closesocket close
|
||||
#define SOCK_WOULDBLOCK EWOULDBLOCK
|
||||
#define closesocket close
|
||||
#define SOCK_WOULDBLOCK EWOULDBLOCK
|
||||
#endif
|
||||
|
||||
namespace network
|
||||
{
|
||||
class socket
|
||||
{
|
||||
public:
|
||||
socket() = default;
|
||||
class socket
|
||||
{
|
||||
public:
|
||||
socket() = default;
|
||||
|
||||
socket(int af);
|
||||
~socket();
|
||||
socket(int af);
|
||||
~socket();
|
||||
|
||||
socket(const socket& obj) = delete;
|
||||
socket& operator=(const socket& obj) = delete;
|
||||
socket(const socket& obj) = delete;
|
||||
socket& operator=(const socket& obj) = delete;
|
||||
|
||||
socket(socket&& obj) noexcept;
|
||||
socket& operator=(socket&& obj) noexcept;
|
||||
socket(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 std::string& data) const;
|
||||
bool receive(address& source, std::string& data) 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;
|
||||
bool receive(address& source, std::string& data) const;
|
||||
|
||||
bool set_blocking(bool blocking);
|
||||
static bool set_blocking(SOCKET s, bool blocking);
|
||||
bool set_blocking(bool blocking);
|
||||
static bool set_blocking(SOCKET s, bool blocking);
|
||||
|
||||
static constexpr bool socket_is_ready = true;
|
||||
bool sleep(std::chrono::milliseconds timeout) const;
|
||||
bool sleep_until(std::chrono::high_resolution_clock::time_point time_point) const;
|
||||
static constexpr bool socket_is_ready = true;
|
||||
bool sleep(std::chrono::milliseconds timeout) const;
|
||||
bool sleep_until(std::chrono::high_resolution_clock::time_point time_point) const;
|
||||
|
||||
SOCKET get_socket() const;
|
||||
uint16_t get_port() const;
|
||||
SOCKET get_socket() 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_until(const std::span<const socket*>& sockets,
|
||||
std::chrono::high_resolution_clock::time_point time_point);
|
||||
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,
|
||||
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:
|
||||
int address_family_{AF_UNSPEC};
|
||||
uint16_t port_ = 0;
|
||||
SOCKET socket_ = INVALID_SOCKET;
|
||||
private:
|
||||
int address_family_{AF_UNSPEC};
|
||||
uint16_t port_ = 0;
|
||||
SOCKET socket_ = INVALID_SOCKET;
|
||||
|
||||
void release();
|
||||
};
|
||||
void release();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#define OS_WINDOWS
|
||||
#elif defined(__APPLE__) || defined(__MACH__)
|
||||
#define OS_MAC
|
||||
#define OS_MAC
|
||||
#elif defined(__linux__)
|
||||
#define OS_LINUX
|
||||
#else
|
||||
@@ -11,9 +11,9 @@
|
||||
#endif
|
||||
|
||||
#ifdef OS_WINDOWS
|
||||
#define EXPORT_SYMBOL __declspec(dllexport)
|
||||
#define IMPORT_SYMBOL __declspec(dllimport)
|
||||
#define NO_INLINE __declspec(noinline)
|
||||
#define EXPORT_SYMBOL __declspec(dllexport)
|
||||
#define IMPORT_SYMBOL __declspec(dllimport)
|
||||
#define NO_INLINE __declspec(noinline)
|
||||
|
||||
#define DECLSPEC_ALIGN(n) __declspec(align(n))
|
||||
|
||||
@@ -24,21 +24,21 @@
|
||||
|
||||
#define EXPORT_SYMBOL __attribute__((visibility("default")))
|
||||
#define IMPORT_SYMBOL
|
||||
#define NO_INLINE __attribute__((noinline))
|
||||
#define NO_INLINE __attribute__((noinline))
|
||||
|
||||
#define DECLSPEC_ALIGN(n) alignas(n)
|
||||
#define fopen_s fopen
|
||||
#define DECLSPEC_ALIGN(n) alignas(n)
|
||||
#define fopen_s fopen
|
||||
|
||||
#define RESTRICTED_POINTER __restrict
|
||||
|
||||
#ifdef OS_MAC
|
||||
#define _fseeki64 fseeko
|
||||
#define _ftelli64 ftello
|
||||
#define _stat64 stat
|
||||
#define _stat64 stat
|
||||
#else
|
||||
#define _fseeki64 fseeko64
|
||||
#define _ftelli64 ftello64
|
||||
#define _stat64 stat64
|
||||
#define _stat64 stat64
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,246 +1,250 @@
|
||||
#pragma once
|
||||
|
||||
#define ACCESS_MASK DWORD
|
||||
#define DEVICE_TYPE DWORD
|
||||
#define ACCESS_MASK DWORD
|
||||
#define DEVICE_TYPE DWORD
|
||||
|
||||
#define FILE_DEVICE_DISK 0x00000007
|
||||
#define FILE_DEVICE_CONSOLE 0x00000050
|
||||
#define FILE_DEVICE_DISK 0x00000007
|
||||
#define FILE_DEVICE_CONSOLE 0x00000050
|
||||
|
||||
#define FILE_SUPERSEDE 0x00000000
|
||||
#define FILE_OPEN 0x00000001
|
||||
#define FILE_CREATE 0x00000002
|
||||
#define FILE_OPEN_IF 0x00000003
|
||||
#define FILE_OVERWRITE 0x00000004
|
||||
#define FILE_OVERWRITE_IF 0x00000005
|
||||
#define FILE_MAXIMUM_DISPOSITION 0x00000005
|
||||
#define FILE_SUPERSEDE 0x00000000
|
||||
#define FILE_OPEN 0x00000001
|
||||
#define FILE_CREATE 0x00000002
|
||||
#define FILE_OPEN_IF 0x00000003
|
||||
#define FILE_OVERWRITE 0x00000004
|
||||
#define FILE_OVERWRITE_IF 0x00000005
|
||||
#define FILE_MAXIMUM_DISPOSITION 0x00000005
|
||||
|
||||
#ifndef OS_WINDOWS
|
||||
#define GENERIC_READ 0x80000000
|
||||
#define GENERIC_WRITE 0x40000000
|
||||
#define GENERIC_EXECUTE 0x20000000
|
||||
#define GENERIC_ALL 0x10000000
|
||||
#define GENERIC_READ 0x80000000
|
||||
#define GENERIC_WRITE 0x40000000
|
||||
#define GENERIC_EXECUTE 0x20000000
|
||||
#define GENERIC_ALL 0x10000000
|
||||
|
||||
#undef DELETE
|
||||
#define DELETE 0x00010000
|
||||
#define READ_CONTROL 0x00020000
|
||||
#define WRITE_DAC 0x00040000
|
||||
#define WRITE_OWNER 0x00080000
|
||||
#define SYNCHRONIZE 0x00100000
|
||||
#define STANDARD_RIGHTS_REQUIRED 0x000f0000
|
||||
#undef DELETE
|
||||
#define DELETE 0x00010000
|
||||
#define READ_CONTROL 0x00020000
|
||||
#define WRITE_DAC 0x00040000
|
||||
#define WRITE_OWNER 0x00080000
|
||||
#define SYNCHRONIZE 0x00100000
|
||||
#define STANDARD_RIGHTS_REQUIRED 0x000f0000
|
||||
|
||||
#define FILE_READ_DATA 0x0001 /* file & pipe */
|
||||
#define FILE_LIST_DIRECTORY 0x0001 /* directory */
|
||||
#define FILE_WRITE_DATA 0x0002 /* file & pipe */
|
||||
#define FILE_ADD_FILE 0x0002 /* directory */
|
||||
#define FILE_APPEND_DATA 0x0004 /* file */
|
||||
#define FILE_ADD_SUBDIRECTORY 0x0004 /* directory */
|
||||
#define FILE_CREATE_PIPE_INSTANCE 0x0004 /* named pipe */
|
||||
#define FILE_READ_EA 0x0008 /* file & directory */
|
||||
#define FILE_READ_DATA 0x0001 /* file & pipe */
|
||||
#define FILE_LIST_DIRECTORY 0x0001 /* directory */
|
||||
#define FILE_WRITE_DATA 0x0002 /* file & pipe */
|
||||
#define FILE_ADD_FILE 0x0002 /* directory */
|
||||
#define FILE_APPEND_DATA 0x0004 /* file */
|
||||
#define FILE_ADD_SUBDIRECTORY 0x0004 /* directory */
|
||||
#define FILE_CREATE_PIPE_INSTANCE 0x0004 /* named pipe */
|
||||
#define FILE_READ_EA 0x0008 /* file & directory */
|
||||
#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_EXECUTE 0x0020 /* file */
|
||||
#define FILE_TRAVERSE 0x0020 /* directory */
|
||||
#define FILE_DELETE_CHILD 0x0040 /* directory */
|
||||
#define FILE_READ_ATTRIBUTES 0x0080 /* all */
|
||||
#define FILE_WRITE_ATTRIBUTES 0x0100 /* all */
|
||||
#define FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x1ff)
|
||||
#define FILE_EXECUTE 0x0020 /* file */
|
||||
#define FILE_TRAVERSE 0x0020 /* directory */
|
||||
#define FILE_DELETE_CHILD 0x0040 /* directory */
|
||||
#define FILE_READ_ATTRIBUTES 0x0080 /* all */
|
||||
#define FILE_WRITE_ATTRIBUTES 0x0100 /* all */
|
||||
#define FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1ff)
|
||||
|
||||
#endif
|
||||
|
||||
#define FILE_DIRECTORY_FILE 0x00000001
|
||||
#define FILE_WRITE_THROUGH 0x00000002
|
||||
#define FILE_SEQUENTIAL_ONLY 0x00000004
|
||||
#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008
|
||||
#define FILE_DIRECTORY_FILE 0x00000001
|
||||
#define FILE_WRITE_THROUGH 0x00000002
|
||||
#define FILE_SEQUENTIAL_ONLY 0x00000004
|
||||
#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008
|
||||
|
||||
#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010
|
||||
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
|
||||
#define FILE_NON_DIRECTORY_FILE 0x00000040
|
||||
#define FILE_CREATE_TREE_CONNECTION 0x00000080
|
||||
#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010
|
||||
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
|
||||
#define FILE_NON_DIRECTORY_FILE 0x00000040
|
||||
#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_THREAD 0x00010000 // may be used with thread creation
|
||||
#define PS_ATTRIBUTE_INPUT 0x00020000 // input only
|
||||
#define PS_ATTRIBUTE_ADDITIVE 0x00040000 // "accumulated" e.g. bitmasks, counters, etc.
|
||||
#define PS_ATTRIBUTE_NUMBER_MASK 0x0000ffff
|
||||
#define PS_ATTRIBUTE_THREAD 0x00010000 // may be used with thread creation
|
||||
#define PS_ATTRIBUTE_INPUT 0x00020000 // input only
|
||||
#define PS_ATTRIBUTE_ADDITIVE 0x00040000 // "accumulated" e.g. bitmasks, counters, etc.
|
||||
|
||||
#define SL_RESTART_SCAN 0x01
|
||||
#define SL_RETURN_SINGLE_ENTRY 0x02
|
||||
#define SL_NO_CURSOR_UPDATE 0x10
|
||||
#define SL_RESTART_SCAN 0x01
|
||||
#define SL_RETURN_SINGLE_ENTRY 0x02
|
||||
#define SL_NO_CURSOR_UPDATE 0x10
|
||||
|
||||
#define SEC_IMAGE 0x01000000
|
||||
#define SEC_IMAGE 0x01000000
|
||||
|
||||
typedef enum _FSINFOCLASS
|
||||
{
|
||||
FileFsVolumeInformation = 1, // q: FILE_FS_VOLUME_INFORMATION
|
||||
FileFsLabelInformation, // s: FILE_FS_LABEL_INFORMATION (requires FILE_WRITE_DATA to volume)
|
||||
FileFsSizeInformation, // q: FILE_FS_SIZE_INFORMATION
|
||||
FileFsDeviceInformation, // q: FILE_FS_DEVICE_INFORMATION
|
||||
FileFsAttributeInformation, // q: FILE_FS_ATTRIBUTE_INFORMATION
|
||||
FileFsControlInformation,
|
||||
// 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
|
||||
FileFsObjectIdInformation, // q; s: FILE_FS_OBJECTID_INFORMATION (s: requires FILE_WRITE_DATA to volume)
|
||||
FileFsDriverPathInformation, // q: FILE_FS_DRIVER_PATH_INFORMATION
|
||||
FileFsVolumeFlagsInformation,
|
||||
// q; s: FILE_FS_VOLUME_FLAGS_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES to volume) // 10
|
||||
FileFsSectorSizeInformation, // q: FILE_FS_SECTOR_SIZE_INFORMATION // since WIN8
|
||||
FileFsDataCopyInformation, // q: FILE_FS_DATA_COPY_INFORMATION
|
||||
FileFsMetadataSizeInformation, // q: FILE_FS_METADATA_SIZE_INFORMATION // since THRESHOLD
|
||||
FileFsFullSizeInformationEx, // q: FILE_FS_FULL_SIZE_INFORMATION_EX // since REDSTONE5
|
||||
FileFsGuidInformation, // q: FILE_FS_GUID_INFORMATION // since 23H2
|
||||
FileFsMaximumInformation
|
||||
FileFsVolumeInformation = 1, // q: FILE_FS_VOLUME_INFORMATION
|
||||
FileFsLabelInformation, // s: FILE_FS_LABEL_INFORMATION (requires FILE_WRITE_DATA to volume)
|
||||
FileFsSizeInformation, // q: FILE_FS_SIZE_INFORMATION
|
||||
FileFsDeviceInformation, // q: FILE_FS_DEVICE_INFORMATION
|
||||
FileFsAttributeInformation, // q: FILE_FS_ATTRIBUTE_INFORMATION
|
||||
FileFsControlInformation,
|
||||
// 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
|
||||
FileFsObjectIdInformation, // q; s: FILE_FS_OBJECTID_INFORMATION (s: requires FILE_WRITE_DATA to volume)
|
||||
FileFsDriverPathInformation, // q: FILE_FS_DRIVER_PATH_INFORMATION
|
||||
FileFsVolumeFlagsInformation,
|
||||
// q; s: FILE_FS_VOLUME_FLAGS_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES to
|
||||
// volume) // 10
|
||||
FileFsSectorSizeInformation, // q: FILE_FS_SECTOR_SIZE_INFORMATION // since WIN8
|
||||
FileFsDataCopyInformation, // q: FILE_FS_DATA_COPY_INFORMATION
|
||||
FileFsMetadataSizeInformation, // q: FILE_FS_METADATA_SIZE_INFORMATION // since THRESHOLD
|
||||
FileFsFullSizeInformationEx, // q: FILE_FS_FULL_SIZE_INFORMATION_EX // since REDSTONE5
|
||||
FileFsGuidInformation, // q: FILE_FS_GUID_INFORMATION // since 23H2
|
||||
FileFsMaximumInformation
|
||||
} FSINFOCLASS, *PFSINFOCLASS;
|
||||
|
||||
typedef enum _FSINFOCLASS FS_INFORMATION_CLASS;
|
||||
|
||||
typedef enum _FILE_INFORMATION_CLASS
|
||||
{
|
||||
FileDirectoryInformation = 1,
|
||||
// q: FILE_DIRECTORY_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileFullDirectoryInformation,
|
||||
// q: FILE_FULL_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileBothDirectoryInformation,
|
||||
// q: FILE_BOTH_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileBasicInformation,
|
||||
// q; s: FILE_BASIC_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
|
||||
FileStandardInformation, // q: FILE_STANDARD_INFORMATION, FILE_STANDARD_INFORMATION_EX
|
||||
FileInternalInformation, // q: FILE_INTERNAL_INFORMATION
|
||||
FileEaInformation, // q: FILE_EA_INFORMATION
|
||||
FileAccessInformation, // q: FILE_ACCESS_INFORMATION
|
||||
FileNameInformation, // q: FILE_NAME_INFORMATION
|
||||
FileRenameInformation, // s: FILE_RENAME_INFORMATION (requires DELETE) // 10
|
||||
FileLinkInformation, // s: FILE_LINK_INFORMATION
|
||||
FileNamesInformation, // q: FILE_NAMES_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileDispositionInformation, // s: FILE_DISPOSITION_INFORMATION (requires DELETE)
|
||||
FilePositionInformation, // q; s: FILE_POSITION_INFORMATION
|
||||
FileFullEaInformation, // FILE_FULL_EA_INFORMATION
|
||||
FileModeInformation, // q; s: FILE_MODE_INFORMATION
|
||||
FileAlignmentInformation, // q: FILE_ALIGNMENT_INFORMATION
|
||||
FileAllInformation, // q: FILE_ALL_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||
FileAllocationInformation, // s: FILE_ALLOCATION_INFORMATION (requires FILE_WRITE_DATA)
|
||||
FileEndOfFileInformation, // s: FILE_END_OF_FILE_INFORMATION (requires FILE_WRITE_DATA) // 20
|
||||
FileAlternateNameInformation, // q: FILE_NAME_INFORMATION
|
||||
FileStreamInformation, // q: FILE_STREAM_INFORMATION
|
||||
FilePipeInformation,
|
||||
// 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)
|
||||
FilePipeRemoteInformation,
|
||||
// q; s: FILE_PIPE_REMOTE_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
|
||||
FileMailslotQueryInformation, // q: FILE_MAILSLOT_QUERY_INFORMATION
|
||||
FileMailslotSetInformation, // s: FILE_MAILSLOT_SET_INFORMATION
|
||||
FileCompressionInformation, // q: FILE_COMPRESSION_INFORMATION
|
||||
FileObjectIdInformation, // q: FILE_OBJECTID_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileCompletionInformation, // s: FILE_COMPLETION_INFORMATION // 30
|
||||
FileMoveClusterInformation, // s: FILE_MOVE_CLUSTER_INFORMATION (requires FILE_WRITE_DATA)
|
||||
FileQuotaInformation, // q: FILE_QUOTA_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileReparsePointInformation,
|
||||
// q: FILE_REPARSE_POINT_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileNetworkOpenInformation, // q: FILE_NETWORK_OPEN_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||
FileAttributeTagInformation, // q: FILE_ATTRIBUTE_TAG_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||
FileTrackingInformation, // s: FILE_TRACKING_INFORMATION (requires FILE_WRITE_DATA)
|
||||
FileIdBothDirectoryInformation,
|
||||
// q: FILE_ID_BOTH_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileIdFullDirectoryInformation,
|
||||
// q: FILE_ID_FULL_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileValidDataLengthInformation,
|
||||
// s: FILE_VALID_DATA_LENGTH_INFORMATION (requires FILE_WRITE_DATA and/or SeManageVolumePrivilege)
|
||||
FileShortNameInformation, // s: FILE_NAME_INFORMATION (requires DELETE) // 40
|
||||
FileIoCompletionNotificationInformation,
|
||||
// q; s: FILE_IO_COMPLETION_NOTIFICATION_INFORMATION (q: requires FILE_READ_ATTRIBUTES) // since VISTA
|
||||
FileIoStatusBlockRangeInformation, // s: FILE_IOSTATUSBLOCK_RANGE_INFORMATION (requires SeLockMemoryPrivilege)
|
||||
FileIoPriorityHintInformation,
|
||||
// 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)
|
||||
FileSfioVolumeInformation, // q: FILE_SFIO_VOLUME_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||
FileHardLinkInformation, // q: FILE_LINKS_INFORMATION
|
||||
FileProcessIdsUsingFileInformation, // q: FILE_PROCESS_IDS_USING_FILE_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||
FileNormalizedNameInformation, // q: FILE_NAME_INFORMATION
|
||||
FileNetworkPhysicalNameInformation, // q: FILE_NETWORK_PHYSICAL_NAME_INFORMATION
|
||||
FileIdGlobalTxDirectoryInformation,
|
||||
// q: FILE_ID_GLOBAL_TX_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // since WIN7 // 50
|
||||
FileIsRemoteDeviceInformation, // q: FILE_IS_REMOTE_DEVICE_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||
FileUnusedInformation,
|
||||
FileNumaNodeInformation, // q: FILE_NUMA_NODE_INFORMATION
|
||||
FileStandardLinkInformation, // q: FILE_STANDARD_LINK_INFORMATION
|
||||
FileRemoteProtocolInformation, // q: FILE_REMOTE_PROTOCOL_INFORMATION
|
||||
FileRenameInformationBypassAccessCheck, // (kernel-mode only); s: FILE_RENAME_INFORMATION // since WIN8
|
||||
FileLinkInformationBypassAccessCheck, // (kernel-mode only); s: FILE_LINK_INFORMATION
|
||||
FileVolumeNameInformation, // q: FILE_VOLUME_NAME_INFORMATION
|
||||
FileIdInformation, // q: FILE_ID_INFORMATION
|
||||
FileIdExtdDirectoryInformation,
|
||||
// q: FILE_ID_EXTD_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // 60
|
||||
FileReplaceCompletionInformation, // s: FILE_COMPLETION_INFORMATION // since WINBLUE
|
||||
FileHardLinkFullIdInformation, // q: FILE_LINK_ENTRY_FULL_ID_INFORMATION // FILE_LINKS_FULL_ID_INFORMATION
|
||||
FileIdExtdBothDirectoryInformation,
|
||||
// q: FILE_ID_EXTD_BOTH_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // since THRESHOLD
|
||||
FileDispositionInformationEx, // s: FILE_DISPOSITION_INFO_EX (requires DELETE) // since REDSTONE
|
||||
FileRenameInformationEx, // s: FILE_RENAME_INFORMATION_EX
|
||||
FileRenameInformationExBypassAccessCheck, // (kernel-mode only); s: FILE_RENAME_INFORMATION_EX
|
||||
FileDesiredStorageClassInformation,
|
||||
// q; s: FILE_DESIRED_STORAGE_CLASS_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES) // since REDSTONE2
|
||||
FileStatInformation, // q: FILE_STAT_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||
FileMemoryPartitionInformation, // s: FILE_MEMORY_PARTITION_INFORMATION // since REDSTONE3
|
||||
FileStatLxInformation,
|
||||
// q: FILE_STAT_LX_INFORMATION (requires FILE_READ_ATTRIBUTES and FILE_READ_EA) // since REDSTONE4 // 70
|
||||
FileCaseSensitiveInformation,
|
||||
// q; s: FILE_CASE_SENSITIVE_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
|
||||
FileLinkInformationEx, // s: FILE_LINK_INFORMATION_EX // since REDSTONE5
|
||||
FileLinkInformationExBypassAccessCheck, // (kernel-mode only); s: FILE_LINK_INFORMATION_EX
|
||||
FileStorageReserveIdInformation,
|
||||
// q; s: FILE_STORAGE_RESERVE_ID_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
|
||||
FileCaseSensitiveInformationForceAccessCheck, // q; s: FILE_CASE_SENSITIVE_INFORMATION
|
||||
FileKnownFolderInformation,
|
||||
// q; s: FILE_KNOWN_FOLDER_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES) // since WIN11
|
||||
FileStatBasicInformation, // since 23H2
|
||||
FileId64ExtdDirectoryInformation, // FILE_ID_64_EXTD_DIR_INFORMATION
|
||||
FileId64ExtdBothDirectoryInformation, // FILE_ID_64_EXTD_BOTH_DIR_INFORMATION
|
||||
FileIdAllExtdDirectoryInformation, // FILE_ID_ALL_EXTD_DIR_INFORMATION
|
||||
FileIdAllExtdBothDirectoryInformation, // FILE_ID_ALL_EXTD_BOTH_DIR_INFORMATION
|
||||
FileStreamReservationInformation, // FILE_STREAM_RESERVATION_INFORMATION // since 24H2
|
||||
FileMupProviderInfo, // MUP_PROVIDER_INFORMATION
|
||||
FileMaximumInformation
|
||||
FileDirectoryInformation = 1,
|
||||
// q: FILE_DIRECTORY_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileFullDirectoryInformation,
|
||||
// q: FILE_FULL_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileBothDirectoryInformation,
|
||||
// q: FILE_BOTH_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileBasicInformation,
|
||||
// q; s: FILE_BASIC_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
|
||||
FileStandardInformation, // q: FILE_STANDARD_INFORMATION, FILE_STANDARD_INFORMATION_EX
|
||||
FileInternalInformation, // q: FILE_INTERNAL_INFORMATION
|
||||
FileEaInformation, // q: FILE_EA_INFORMATION
|
||||
FileAccessInformation, // q: FILE_ACCESS_INFORMATION
|
||||
FileNameInformation, // q: FILE_NAME_INFORMATION
|
||||
FileRenameInformation, // s: FILE_RENAME_INFORMATION (requires DELETE) // 10
|
||||
FileLinkInformation, // s: FILE_LINK_INFORMATION
|
||||
FileNamesInformation, // q: FILE_NAMES_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileDispositionInformation, // s: FILE_DISPOSITION_INFORMATION (requires DELETE)
|
||||
FilePositionInformation, // q; s: FILE_POSITION_INFORMATION
|
||||
FileFullEaInformation, // FILE_FULL_EA_INFORMATION
|
||||
FileModeInformation, // q; s: FILE_MODE_INFORMATION
|
||||
FileAlignmentInformation, // q: FILE_ALIGNMENT_INFORMATION
|
||||
FileAllInformation, // q: FILE_ALL_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||
FileAllocationInformation, // s: FILE_ALLOCATION_INFORMATION (requires FILE_WRITE_DATA)
|
||||
FileEndOfFileInformation, // s: FILE_END_OF_FILE_INFORMATION (requires FILE_WRITE_DATA) // 20
|
||||
FileAlternateNameInformation, // q: FILE_NAME_INFORMATION
|
||||
FileStreamInformation, // q: FILE_STREAM_INFORMATION
|
||||
FilePipeInformation,
|
||||
// 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)
|
||||
FilePipeRemoteInformation,
|
||||
// q; s: FILE_PIPE_REMOTE_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
|
||||
FileMailslotQueryInformation, // q: FILE_MAILSLOT_QUERY_INFORMATION
|
||||
FileMailslotSetInformation, // s: FILE_MAILSLOT_SET_INFORMATION
|
||||
FileCompressionInformation, // q: FILE_COMPRESSION_INFORMATION
|
||||
FileObjectIdInformation, // q: FILE_OBJECTID_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileCompletionInformation, // s: FILE_COMPLETION_INFORMATION // 30
|
||||
FileMoveClusterInformation, // s: FILE_MOVE_CLUSTER_INFORMATION (requires FILE_WRITE_DATA)
|
||||
FileQuotaInformation, // q: FILE_QUOTA_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileReparsePointInformation,
|
||||
// q: FILE_REPARSE_POINT_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileNetworkOpenInformation, // q: FILE_NETWORK_OPEN_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||
FileAttributeTagInformation, // q: FILE_ATTRIBUTE_TAG_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||
FileTrackingInformation, // s: FILE_TRACKING_INFORMATION (requires FILE_WRITE_DATA)
|
||||
FileIdBothDirectoryInformation,
|
||||
// q: FILE_ID_BOTH_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileIdFullDirectoryInformation,
|
||||
// q: FILE_ID_FULL_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
|
||||
FileValidDataLengthInformation,
|
||||
// s: FILE_VALID_DATA_LENGTH_INFORMATION (requires FILE_WRITE_DATA and/or SeManageVolumePrivilege)
|
||||
FileShortNameInformation, // s: FILE_NAME_INFORMATION (requires DELETE) // 40
|
||||
FileIoCompletionNotificationInformation,
|
||||
// q; s: FILE_IO_COMPLETION_NOTIFICATION_INFORMATION (q: requires FILE_READ_ATTRIBUTES) // since VISTA
|
||||
FileIoStatusBlockRangeInformation, // s: FILE_IOSTATUSBLOCK_RANGE_INFORMATION (requires SeLockMemoryPrivilege)
|
||||
FileIoPriorityHintInformation,
|
||||
// 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)
|
||||
FileSfioVolumeInformation, // q: FILE_SFIO_VOLUME_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||
FileHardLinkInformation, // q: FILE_LINKS_INFORMATION
|
||||
FileProcessIdsUsingFileInformation, // q: FILE_PROCESS_IDS_USING_FILE_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||
FileNormalizedNameInformation, // q: FILE_NAME_INFORMATION
|
||||
FileNetworkPhysicalNameInformation, // q: FILE_NETWORK_PHYSICAL_NAME_INFORMATION
|
||||
FileIdGlobalTxDirectoryInformation,
|
||||
// q: FILE_ID_GLOBAL_TX_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // since WIN7 //
|
||||
// 50
|
||||
FileIsRemoteDeviceInformation, // q: FILE_IS_REMOTE_DEVICE_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||
FileUnusedInformation,
|
||||
FileNumaNodeInformation, // q: FILE_NUMA_NODE_INFORMATION
|
||||
FileStandardLinkInformation, // q: FILE_STANDARD_LINK_INFORMATION
|
||||
FileRemoteProtocolInformation, // q: FILE_REMOTE_PROTOCOL_INFORMATION
|
||||
FileRenameInformationBypassAccessCheck, // (kernel-mode only); s: FILE_RENAME_INFORMATION // since WIN8
|
||||
FileLinkInformationBypassAccessCheck, // (kernel-mode only); s: FILE_LINK_INFORMATION
|
||||
FileVolumeNameInformation, // q: FILE_VOLUME_NAME_INFORMATION
|
||||
FileIdInformation, // q: FILE_ID_INFORMATION
|
||||
FileIdExtdDirectoryInformation,
|
||||
// q: FILE_ID_EXTD_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // 60
|
||||
FileReplaceCompletionInformation, // s: FILE_COMPLETION_INFORMATION // since WINBLUE
|
||||
FileHardLinkFullIdInformation, // q: FILE_LINK_ENTRY_FULL_ID_INFORMATION // FILE_LINKS_FULL_ID_INFORMATION
|
||||
FileIdExtdBothDirectoryInformation,
|
||||
// q: FILE_ID_EXTD_BOTH_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // since THRESHOLD
|
||||
FileDispositionInformationEx, // s: FILE_DISPOSITION_INFO_EX (requires DELETE) // since REDSTONE
|
||||
FileRenameInformationEx, // s: FILE_RENAME_INFORMATION_EX
|
||||
FileRenameInformationExBypassAccessCheck, // (kernel-mode only); s: FILE_RENAME_INFORMATION_EX
|
||||
FileDesiredStorageClassInformation,
|
||||
// q; s: FILE_DESIRED_STORAGE_CLASS_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires
|
||||
// FILE_WRITE_ATTRIBUTES) // since REDSTONE2
|
||||
FileStatInformation, // q: FILE_STAT_INFORMATION (requires FILE_READ_ATTRIBUTES)
|
||||
FileMemoryPartitionInformation, // s: FILE_MEMORY_PARTITION_INFORMATION // since REDSTONE3
|
||||
FileStatLxInformation,
|
||||
// q: FILE_STAT_LX_INFORMATION (requires FILE_READ_ATTRIBUTES and FILE_READ_EA) // since REDSTONE4 // 70
|
||||
FileCaseSensitiveInformation,
|
||||
// q; s: FILE_CASE_SENSITIVE_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
|
||||
FileLinkInformationEx, // s: FILE_LINK_INFORMATION_EX // since REDSTONE5
|
||||
FileLinkInformationExBypassAccessCheck, // (kernel-mode only); s: FILE_LINK_INFORMATION_EX
|
||||
FileStorageReserveIdInformation,
|
||||
// q; s: FILE_STORAGE_RESERVE_ID_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
|
||||
FileCaseSensitiveInformationForceAccessCheck, // q; s: FILE_CASE_SENSITIVE_INFORMATION
|
||||
FileKnownFolderInformation,
|
||||
// q; s: FILE_KNOWN_FOLDER_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES) //
|
||||
// since WIN11
|
||||
FileStatBasicInformation, // since 23H2
|
||||
FileId64ExtdDirectoryInformation, // FILE_ID_64_EXTD_DIR_INFORMATION
|
||||
FileId64ExtdBothDirectoryInformation, // FILE_ID_64_EXTD_BOTH_DIR_INFORMATION
|
||||
FileIdAllExtdDirectoryInformation, // FILE_ID_ALL_EXTD_DIR_INFORMATION
|
||||
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;
|
||||
|
||||
typedef enum _OBJECT_INFORMATION_CLASS
|
||||
{
|
||||
ObjectBasicInformation, // q: OBJECT_BASIC_INFORMATION
|
||||
ObjectNameInformation, // q: OBJECT_NAME_INFORMATION
|
||||
ObjectTypeInformation, // q: OBJECT_TYPE_INFORMATION
|
||||
ObjectTypesInformation, // q: OBJECT_TYPES_INFORMATION
|
||||
ObjectHandleFlagInformation, // qs: OBJECT_HANDLE_FLAG_INFORMATION
|
||||
ObjectSessionInformation, // s: void // change object session // (requires SeTcbPrivilege)
|
||||
ObjectSessionObjectInformation, // s: void // change object session // (requires SeTcbPrivilege)
|
||||
MaxObjectInfoClass
|
||||
ObjectBasicInformation, // q: OBJECT_BASIC_INFORMATION
|
||||
ObjectNameInformation, // q: OBJECT_NAME_INFORMATION
|
||||
ObjectTypeInformation, // q: OBJECT_TYPE_INFORMATION
|
||||
ObjectTypesInformation, // q: OBJECT_TYPES_INFORMATION
|
||||
ObjectHandleFlagInformation, // qs: OBJECT_HANDLE_FLAG_INFORMATION
|
||||
ObjectSessionInformation, // s: void // change object session // (requires SeTcbPrivilege)
|
||||
ObjectSessionObjectInformation, // s: void // change object session // (requires SeTcbPrivilege)
|
||||
MaxObjectInfoClass
|
||||
} OBJECT_INFORMATION_CLASS;
|
||||
|
||||
typedef enum _HARDERROR_RESPONSE_OPTION
|
||||
{
|
||||
OptionAbortRetryIgnore,
|
||||
OptionOk,
|
||||
OptionOkCancel,
|
||||
OptionRetryCancel,
|
||||
OptionYesNo,
|
||||
OptionYesNoCancel,
|
||||
OptionShutdownSystem,
|
||||
OptionOkNoWait,
|
||||
OptionCancelTryContinue
|
||||
OptionAbortRetryIgnore,
|
||||
OptionOk,
|
||||
OptionOkCancel,
|
||||
OptionRetryCancel,
|
||||
OptionYesNo,
|
||||
OptionYesNoCancel,
|
||||
OptionShutdownSystem,
|
||||
OptionOkNoWait,
|
||||
OptionCancelTryContinue
|
||||
} HARDERROR_RESPONSE_OPTION;
|
||||
|
||||
typedef enum _HARDERROR_RESPONSE
|
||||
{
|
||||
ResponseReturnToCaller,
|
||||
ResponseNotHandled,
|
||||
ResponseAbort,
|
||||
ResponseCancel,
|
||||
ResponseIgnore,
|
||||
ResponseNo,
|
||||
ResponseOk,
|
||||
ResponseRetry,
|
||||
ResponseYes,
|
||||
ResponseTryAgain,
|
||||
ResponseContinue
|
||||
ResponseReturnToCaller,
|
||||
ResponseNotHandled,
|
||||
ResponseAbort,
|
||||
ResponseCancel,
|
||||
ResponseIgnore,
|
||||
ResponseNo,
|
||||
ResponseOk,
|
||||
ResponseRetry,
|
||||
ResponseYes,
|
||||
ResponseTryAgain,
|
||||
ResponseContinue
|
||||
} HARDERROR_RESPONSE;
|
||||
|
||||
typedef USHORT RTL_ATOM;
|
||||
@@ -248,113 +252,112 @@ typedef USHORT RTL_ATOM;
|
||||
template <typename Traits>
|
||||
struct IO_STATUS_BLOCK
|
||||
{
|
||||
union
|
||||
{
|
||||
NTSTATUS Status;
|
||||
typename Traits::PVOID Pointer;
|
||||
};
|
||||
union
|
||||
{
|
||||
NTSTATUS Status;
|
||||
typename Traits::PVOID Pointer;
|
||||
};
|
||||
|
||||
typename Traits::ULONG_PTR Information;
|
||||
typename Traits::ULONG_PTR Information;
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
struct OBJECT_ATTRIBUTES
|
||||
{
|
||||
ULONG Length;
|
||||
typename Traits::HANDLE RootDirectory;
|
||||
EMULATOR_CAST(typename Traits::PVOID, UNICODE_STRING*) ObjectName;
|
||||
ULONG Attributes;
|
||||
typename Traits::PVOID SecurityDescriptor; // PSECURITY_DESCRIPTOR;
|
||||
typename Traits::PVOID SecurityQualityOfService; // PSECURITY_QUALITY_OF_SERVICE
|
||||
ULONG Length;
|
||||
typename Traits::HANDLE RootDirectory;
|
||||
EMULATOR_CAST(typename Traits::PVOID, UNICODE_STRING*) ObjectName;
|
||||
ULONG Attributes;
|
||||
typename Traits::PVOID SecurityDescriptor; // PSECURITY_DESCRIPTOR;
|
||||
typename Traits::PVOID SecurityQualityOfService; // PSECURITY_QUALITY_OF_SERVICE
|
||||
};
|
||||
|
||||
typedef struct _FILE_FS_DEVICE_INFORMATION
|
||||
{
|
||||
DEVICE_TYPE DeviceType;
|
||||
ULONG Characteristics;
|
||||
DEVICE_TYPE DeviceType;
|
||||
ULONG Characteristics;
|
||||
} FILE_FS_DEVICE_INFORMATION, *PFILE_FS_DEVICE_INFORMATION;
|
||||
|
||||
typedef struct _FILE_POSITION_INFORMATION
|
||||
{
|
||||
LARGE_INTEGER CurrentByteOffset;
|
||||
LARGE_INTEGER CurrentByteOffset;
|
||||
} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION;
|
||||
|
||||
typedef struct _FILE_STANDARD_INFORMATION
|
||||
{
|
||||
LARGE_INTEGER AllocationSize;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
ULONG NumberOfLinks;
|
||||
BOOLEAN DeletePending;
|
||||
BOOLEAN Directory;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
ULONG NumberOfLinks;
|
||||
BOOLEAN DeletePending;
|
||||
BOOLEAN Directory;
|
||||
} FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION;
|
||||
|
||||
typedef struct _FILE_NAME_INFORMATION
|
||||
{
|
||||
ULONG FileNameLength;
|
||||
char16_t FileName[1];
|
||||
ULONG FileNameLength;
|
||||
char16_t FileName[1];
|
||||
} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION;
|
||||
|
||||
typedef struct _FILE_BASIC_INFORMATION
|
||||
{
|
||||
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 LastWriteTime; // Specifies the time that the file was last written to.
|
||||
LARGE_INTEGER ChangeTime; // Specifies the last time the file was changed.
|
||||
ULONG FileAttributes; // Specifies one or more FILE_ATTRIBUTE_XXX flags.
|
||||
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 LastWriteTime; // Specifies the time that the file was last written to.
|
||||
LARGE_INTEGER ChangeTime; // Specifies the last time the file was changed.
|
||||
ULONG FileAttributes; // Specifies one or more FILE_ATTRIBUTE_XXX flags.
|
||||
} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
|
||||
|
||||
typedef struct _FILE_DIRECTORY_INFORMATION
|
||||
{
|
||||
ULONG NextEntryOffset;
|
||||
ULONG FileIndex;
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
ULONG FileAttributes;
|
||||
ULONG FileNameLength;
|
||||
char16_t FileName[1];
|
||||
ULONG NextEntryOffset;
|
||||
ULONG FileIndex;
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
ULONG FileAttributes;
|
||||
ULONG FileNameLength;
|
||||
char16_t FileName[1];
|
||||
} FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION;
|
||||
|
||||
typedef struct _FILE_FULL_DIR_INFORMATION
|
||||
{
|
||||
ULONG NextEntryOffset;
|
||||
ULONG FileIndex;
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
ULONG FileAttributes;
|
||||
ULONG FileNameLength;
|
||||
ULONG EaSize;
|
||||
char16_t FileName[1];
|
||||
ULONG NextEntryOffset;
|
||||
ULONG FileIndex;
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
ULONG FileAttributes;
|
||||
ULONG FileNameLength;
|
||||
ULONG EaSize;
|
||||
char16_t FileName[1];
|
||||
} FILE_FULL_DIR_INFORMATION, *PFILE_FULL_DIR_INFORMATION;
|
||||
|
||||
typedef struct _FILE_BOTH_DIR_INFORMATION
|
||||
{
|
||||
ULONG NextEntryOffset;
|
||||
ULONG FileIndex;
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
ULONG FileAttributes;
|
||||
ULONG FileNameLength;
|
||||
ULONG EaSize;
|
||||
char ShortNameLength;
|
||||
char16_t ShortName[12];
|
||||
char16_t FileName[1];
|
||||
ULONG NextEntryOffset;
|
||||
ULONG FileIndex;
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
ULONG FileAttributes;
|
||||
ULONG FileNameLength;
|
||||
ULONG EaSize;
|
||||
char ShortNameLength;
|
||||
char16_t ShortName[12];
|
||||
char16_t FileName[1];
|
||||
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;
|
||||
|
||||
#ifndef OS_WINDOWS
|
||||
typedef BOOLEAN SECURITY_CONTEXT_TRACKING_MODE,
|
||||
* PSECURITY_CONTEXT_TRACKING_MODE;
|
||||
typedef BOOLEAN SECURITY_CONTEXT_TRACKING_MODE, *PSECURITY_CONTEXT_TRACKING_MODE;
|
||||
typedef struct _SECURITY_QUALITY_OF_SERVICE
|
||||
{
|
||||
DWORD Length;
|
||||
@@ -367,17 +370,17 @@ typedef struct _SECURITY_QUALITY_OF_SERVICE
|
||||
|
||||
typedef struct _PORT_VIEW64
|
||||
{
|
||||
ULONG Length;
|
||||
EMULATOR_CAST(std::uint64_t, HANDLE) SectionHandle;
|
||||
ULONG SectionOffset;
|
||||
EMULATOR_CAST(std::int64_t, SIZE_T) ViewSize;
|
||||
EmulatorTraits<Emu64>::PVOID ViewBase;
|
||||
EmulatorTraits<Emu64>::PVOID ViewRemoteBase;
|
||||
ULONG Length;
|
||||
EMULATOR_CAST(std::uint64_t, HANDLE) SectionHandle;
|
||||
ULONG SectionOffset;
|
||||
EMULATOR_CAST(std::int64_t, SIZE_T) ViewSize;
|
||||
EmulatorTraits<Emu64>::PVOID ViewBase;
|
||||
EmulatorTraits<Emu64>::PVOID ViewRemoteBase;
|
||||
} PORT_VIEW64, *PPORT_VIEW64;
|
||||
|
||||
typedef struct _REMOTE_PORT_VIEW64
|
||||
{
|
||||
ULONG Length;
|
||||
EMULATOR_CAST(std::int64_t, SIZE_T) ViewSize;
|
||||
EmulatorTraits<Emu64>::PVOID ViewBase;
|
||||
ULONG Length;
|
||||
EMULATOR_CAST(std::int64_t, SIZE_T) ViewSize;
|
||||
EmulatorTraits<Emu64>::PVOID ViewBase;
|
||||
} REMOTE_PORT_VIEW64, *PREMOTE_PORT_VIEW64;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#define PAGE_EXECUTE 0x10
|
||||
#define PAGE_EXECUTE_READ 0x20
|
||||
#define PAGE_EXECUTE_READWRITE 0x40
|
||||
#define PAGE_EXECUTE_WRITECOPY 0x80
|
||||
#define PAGE_EXECUTE 0x10
|
||||
#define PAGE_EXECUTE_READ 0x20
|
||||
#define PAGE_EXECUTE_READWRITE 0x40
|
||||
#define PAGE_EXECUTE_WRITECOPY 0x80
|
||||
|
||||
#define PAGE_NOACCESS 0x01
|
||||
#define PAGE_READONLY 0x02
|
||||
#define PAGE_READWRITE 0x04
|
||||
#define PAGE_WRITECOPY 0x08
|
||||
#define PAGE_NOACCESS 0x01
|
||||
#define PAGE_READONLY 0x02
|
||||
#define PAGE_READWRITE 0x04
|
||||
#define PAGE_WRITECOPY 0x08
|
||||
|
||||
#define PAGE_TARGETS_INVALID 0x40000000
|
||||
#define PAGE_TARGETS_NO_UPDATE 0x40000000
|
||||
#define PAGE_TARGETS_INVALID 0x40000000
|
||||
#define PAGE_TARGETS_NO_UPDATE 0x40000000
|
||||
|
||||
#define PAGE_GUARD 0x100
|
||||
#define PAGE_NOCACHE 0x200
|
||||
#define PAGE_WRITECOMBINE 0x400
|
||||
#define PAGE_GUARD 0x100
|
||||
#define PAGE_NOCACHE 0x200
|
||||
#define PAGE_WRITECOMBINE 0x400
|
||||
|
||||
#define MEM_COMMIT 0x00001000
|
||||
#define MEM_RESERVE 0x00002000
|
||||
@@ -38,94 +38,92 @@
|
||||
|
||||
typedef enum _MEMORY_INFORMATION_CLASS
|
||||
{
|
||||
MemoryBasicInformation, // q: MEMORY_BASIC_INFORMATION
|
||||
MemoryWorkingSetInformation, // q: MEMORY_WORKING_SET_INFORMATION
|
||||
MemoryMappedFilenameInformation, // q: UNICODE_STRING
|
||||
MemoryRegionInformation, // q: MEMORY_REGION_INFORMATION
|
||||
MemoryWorkingSetExInformation, // q: MEMORY_WORKING_SET_EX_INFORMATION // since VISTA
|
||||
MemorySharedCommitInformation, // q: MEMORY_SHARED_COMMIT_INFORMATION // since WIN8
|
||||
MemoryImageInformation, // q: MEMORY_IMAGE_INFORMATION
|
||||
MemoryRegionInformationEx, // MEMORY_REGION_INFORMATION
|
||||
MemoryPrivilegedBasicInformation, // MEMORY_BASIC_INFORMATION
|
||||
MemoryEnclaveImageInformation, // MEMORY_ENCLAVE_IMAGE_INFORMATION // since REDSTONE3
|
||||
MemoryBasicInformationCapped, // 10
|
||||
MemoryPhysicalContiguityInformation, // MEMORY_PHYSICAL_CONTIGUITY_INFORMATION // since 20H1
|
||||
MemoryBadInformation, // since WIN11
|
||||
MemoryBadInformationAllProcesses, // since 22H1
|
||||
MemoryImageExtensionInformation, // since 24H2
|
||||
MaxMemoryInfoClass
|
||||
MemoryBasicInformation, // q: MEMORY_BASIC_INFORMATION
|
||||
MemoryWorkingSetInformation, // q: MEMORY_WORKING_SET_INFORMATION
|
||||
MemoryMappedFilenameInformation, // q: UNICODE_STRING
|
||||
MemoryRegionInformation, // q: MEMORY_REGION_INFORMATION
|
||||
MemoryWorkingSetExInformation, // q: MEMORY_WORKING_SET_EX_INFORMATION // since VISTA
|
||||
MemorySharedCommitInformation, // q: MEMORY_SHARED_COMMIT_INFORMATION // since WIN8
|
||||
MemoryImageInformation, // q: MEMORY_IMAGE_INFORMATION
|
||||
MemoryRegionInformationEx, // MEMORY_REGION_INFORMATION
|
||||
MemoryPrivilegedBasicInformation, // MEMORY_BASIC_INFORMATION
|
||||
MemoryEnclaveImageInformation, // MEMORY_ENCLAVE_IMAGE_INFORMATION // since REDSTONE3
|
||||
MemoryBasicInformationCapped, // 10
|
||||
MemoryPhysicalContiguityInformation, // MEMORY_PHYSICAL_CONTIGUITY_INFORMATION // since 20H1
|
||||
MemoryBadInformation, // since WIN11
|
||||
MemoryBadInformationAllProcesses, // since 22H1
|
||||
MemoryImageExtensionInformation, // since 24H2
|
||||
MaxMemoryInfoClass
|
||||
} MEMORY_INFORMATION_CLASS;
|
||||
|
||||
typedef enum _SECTION_INHERIT
|
||||
{
|
||||
ViewShare = 1,
|
||||
ViewUnmap = 2
|
||||
ViewShare = 1,
|
||||
ViewUnmap = 2
|
||||
} SECTION_INHERIT;
|
||||
|
||||
|
||||
typedef struct DECLSPEC_ALIGN(16) _EMU_MEMORY_BASIC_INFORMATION64
|
||||
{
|
||||
void* BaseAddress;
|
||||
void* AllocationBase;
|
||||
DWORD AllocationProtect;
|
||||
WORD PartitionId;
|
||||
std::int64_t RegionSize;
|
||||
DWORD State;
|
||||
DWORD Protect;
|
||||
DWORD Type;
|
||||
void* BaseAddress;
|
||||
void* AllocationBase;
|
||||
DWORD AllocationProtect;
|
||||
WORD PartitionId;
|
||||
std::int64_t RegionSize;
|
||||
DWORD State;
|
||||
DWORD Protect;
|
||||
DWORD Type;
|
||||
} EMU_MEMORY_BASIC_INFORMATION64, *PEMU_MEMORY_BASIC_INFORMATION64;
|
||||
|
||||
|
||||
typedef struct _MEMORY_IMAGE_INFORMATION64
|
||||
{
|
||||
void* ImageBase;
|
||||
std::int64_t SizeOfImage;
|
||||
void* ImageBase;
|
||||
std::int64_t SizeOfImage;
|
||||
|
||||
union
|
||||
{
|
||||
ULONG ImageFlags;
|
||||
union
|
||||
{
|
||||
ULONG ImageFlags;
|
||||
|
||||
struct
|
||||
{
|
||||
ULONG ImagePartialMap : 1;
|
||||
ULONG ImageNotExecutable : 1;
|
||||
ULONG ImageSigningLevel : 4; // REDSTONE3
|
||||
ULONG ImageExtensionPresent : 1; // since 24H2
|
||||
ULONG Reserved : 25;
|
||||
};
|
||||
};
|
||||
struct
|
||||
{
|
||||
ULONG ImagePartialMap : 1;
|
||||
ULONG ImageNotExecutable : 1;
|
||||
ULONG ImageSigningLevel : 4; // REDSTONE3
|
||||
ULONG ImageExtensionPresent : 1; // since 24H2
|
||||
ULONG Reserved : 25;
|
||||
};
|
||||
};
|
||||
} MEMORY_IMAGE_INFORMATION64, *PMEMORY_IMAGE_INFORMATION64;
|
||||
|
||||
typedef struct _MEMORY_REGION_INFORMATION
|
||||
{
|
||||
void* AllocationBase;
|
||||
ULONG AllocationProtect;
|
||||
void* AllocationBase;
|
||||
ULONG AllocationProtect;
|
||||
|
||||
union
|
||||
{
|
||||
ULONG RegionType;
|
||||
union
|
||||
{
|
||||
ULONG RegionType;
|
||||
|
||||
struct
|
||||
{
|
||||
ULONG Private : 1;
|
||||
ULONG MappedDataFile : 1;
|
||||
ULONG MappedImage : 1;
|
||||
ULONG MappedPageFile : 1;
|
||||
ULONG MappedPhysical : 1;
|
||||
ULONG DirectMapped : 1;
|
||||
ULONG SoftwareEnclave : 1; // REDSTONE3
|
||||
ULONG PageSize64K : 1;
|
||||
ULONG PlaceholderReservation : 1; // REDSTONE4
|
||||
ULONG MappedAwe : 1; // 21H1
|
||||
ULONG MappedWriteWatch : 1;
|
||||
ULONG PageSizeLarge : 1;
|
||||
ULONG PageSizeHuge : 1;
|
||||
ULONG Reserved : 19;
|
||||
};
|
||||
};
|
||||
struct
|
||||
{
|
||||
ULONG Private : 1;
|
||||
ULONG MappedDataFile : 1;
|
||||
ULONG MappedImage : 1;
|
||||
ULONG MappedPageFile : 1;
|
||||
ULONG MappedPhysical : 1;
|
||||
ULONG DirectMapped : 1;
|
||||
ULONG SoftwareEnclave : 1; // REDSTONE3
|
||||
ULONG PageSize64K : 1;
|
||||
ULONG PlaceholderReservation : 1; // REDSTONE4
|
||||
ULONG MappedAwe : 1; // 21H1
|
||||
ULONG MappedWriteWatch : 1;
|
||||
ULONG PageSizeLarge : 1;
|
||||
ULONG PageSizeHuge : 1;
|
||||
ULONG Reserved : 19;
|
||||
};
|
||||
};
|
||||
|
||||
std::int64_t RegionSize;
|
||||
std::int64_t CommitSize;
|
||||
DWORD64 PartitionId; // 19H1
|
||||
DWORD64 NodePreference; // 20H1
|
||||
std::int64_t RegionSize;
|
||||
std::int64_t CommitSize;
|
||||
DWORD64 PartitionId; // 19H1
|
||||
DWORD64 NodePreference; // 20H1
|
||||
} MEMORY_REGION_INFORMATION64, *PMEMORY_REGION_INFORMATION64;
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
template <typename Traits>
|
||||
struct EMU_WSABUF
|
||||
{
|
||||
ULONG len;
|
||||
EMULATOR_CAST(typename Traits::PVOID, CHAR*) buf;
|
||||
ULONG len;
|
||||
EMULATOR_CAST(typename Traits::PVOID, CHAR*) buf;
|
||||
};
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4201) // nameless struct/union
|
||||
#pragma warning(disable: 4702) // unreachable code
|
||||
#pragma warning(disable : 4201) // nameless struct/union
|
||||
#pragma warning(disable : 4702) // unreachable code
|
||||
#else
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||
@@ -27,18 +27,12 @@
|
||||
#ifdef OS_WINDOWS
|
||||
#pragma comment(lib, "ntdll")
|
||||
|
||||
extern "C" {
|
||||
NTSYSCALLAPI
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
NtQuerySystemInformationEx(
|
||||
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
|
||||
_In_reads_bytes_(InputBufferLength) PVOID InputBuffer,
|
||||
_In_ ULONG InputBufferLength,
|
||||
_Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation,
|
||||
_In_ ULONG SystemInformationLength,
|
||||
_Out_opt_ PULONG ReturnLength
|
||||
);
|
||||
extern "C"
|
||||
{
|
||||
NTSYSCALLAPI NTSTATUS NTAPI NtQuerySystemInformationEx(
|
||||
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _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)
|
||||
#else
|
||||
|
||||
@@ -20,31 +20,30 @@ using LONGLONG = std::int64_t;
|
||||
|
||||
typedef union _ULARGE_INTEGER
|
||||
{
|
||||
struct
|
||||
{
|
||||
DWORD LowPart;
|
||||
DWORD HighPart;
|
||||
};
|
||||
struct
|
||||
{
|
||||
DWORD LowPart;
|
||||
DWORD HighPart;
|
||||
};
|
||||
|
||||
ULONGLONG QuadPart;
|
||||
ULONGLONG QuadPart;
|
||||
} ULARGE_INTEGER;
|
||||
|
||||
typedef union _LARGE_INTEGER
|
||||
{
|
||||
struct
|
||||
{
|
||||
DWORD LowPart;
|
||||
LONG HighPart;
|
||||
};
|
||||
struct
|
||||
{
|
||||
DWORD LowPart;
|
||||
LONG HighPart;
|
||||
};
|
||||
|
||||
LONGLONG QuadPart;
|
||||
LONGLONG QuadPart;
|
||||
} LARGE_INTEGER;
|
||||
|
||||
using BYTE = std::uint8_t;
|
||||
#define CHAR BYTE
|
||||
#define CHAR BYTE
|
||||
#endif
|
||||
|
||||
|
||||
using WORD = std::uint16_t;
|
||||
|
||||
#define UCHAR unsigned char
|
||||
@@ -53,7 +52,6 @@ using WORD = std::uint16_t;
|
||||
using CSHORT = short;
|
||||
using USHORT = WORD;
|
||||
|
||||
|
||||
#define DUMMYSTRUCTNAME
|
||||
|
||||
#ifndef TRUE
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,63 +2,63 @@
|
||||
|
||||
typedef enum _KEY_INFORMATION_CLASS
|
||||
{
|
||||
KeyBasicInformation, // KEY_BASIC_INFORMATION
|
||||
KeyNodeInformation, // KEY_NODE_INFORMATION
|
||||
KeyFullInformation, // KEY_FULL_INFORMATION
|
||||
KeyNameInformation, // KEY_NAME_INFORMATION
|
||||
KeyCachedInformation, // KEY_CACHED_INFORMATION
|
||||
KeyFlagsInformation, // KEY_FLAGS_INFORMATION
|
||||
KeyVirtualizationInformation, // KEY_VIRTUALIZATION_INFORMATION
|
||||
KeyHandleTagsInformation, // KEY_HANDLE_TAGS_INFORMATION
|
||||
KeyTrustInformation, // KEY_TRUST_INFORMATION
|
||||
KeyLayerInformation, // KEY_LAYER_INFORMATION
|
||||
MaxKeyInfoClass
|
||||
KeyBasicInformation, // KEY_BASIC_INFORMATION
|
||||
KeyNodeInformation, // KEY_NODE_INFORMATION
|
||||
KeyFullInformation, // KEY_FULL_INFORMATION
|
||||
KeyNameInformation, // KEY_NAME_INFORMATION
|
||||
KeyCachedInformation, // KEY_CACHED_INFORMATION
|
||||
KeyFlagsInformation, // KEY_FLAGS_INFORMATION
|
||||
KeyVirtualizationInformation, // KEY_VIRTUALIZATION_INFORMATION
|
||||
KeyHandleTagsInformation, // KEY_HANDLE_TAGS_INFORMATION
|
||||
KeyTrustInformation, // KEY_TRUST_INFORMATION
|
||||
KeyLayerInformation, // KEY_LAYER_INFORMATION
|
||||
MaxKeyInfoClass
|
||||
} KEY_INFORMATION_CLASS;
|
||||
|
||||
typedef enum _KEY_VALUE_INFORMATION_CLASS
|
||||
{
|
||||
KeyValueBasicInformation, // KEY_VALUE_BASIC_INFORMATION
|
||||
KeyValueFullInformation, // KEY_VALUE_FULL_INFORMATION
|
||||
KeyValuePartialInformation, // KEY_VALUE_PARTIAL_INFORMATION
|
||||
KeyValueFullInformationAlign64,
|
||||
KeyValuePartialInformationAlign64, // KEY_VALUE_PARTIAL_INFORMATION_ALIGN64
|
||||
KeyValueLayerInformation, // KEY_VALUE_LAYER_INFORMATION
|
||||
MaxKeyValueInfoClass
|
||||
KeyValueBasicInformation, // KEY_VALUE_BASIC_INFORMATION
|
||||
KeyValueFullInformation, // KEY_VALUE_FULL_INFORMATION
|
||||
KeyValuePartialInformation, // KEY_VALUE_PARTIAL_INFORMATION
|
||||
KeyValueFullInformationAlign64,
|
||||
KeyValuePartialInformationAlign64, // KEY_VALUE_PARTIAL_INFORMATION_ALIGN64
|
||||
KeyValueLayerInformation, // KEY_VALUE_LAYER_INFORMATION
|
||||
MaxKeyValueInfoClass
|
||||
} KEY_VALUE_INFORMATION_CLASS;
|
||||
|
||||
struct KEY_NAME_INFORMATION
|
||||
{
|
||||
std::uint32_t NameLength;
|
||||
char16_t Name[1];
|
||||
std::uint32_t NameLength;
|
||||
char16_t Name[1];
|
||||
};
|
||||
|
||||
struct KEY_HANDLE_TAGS_INFORMATION
|
||||
{
|
||||
ULONG HandleTags;
|
||||
ULONG HandleTags;
|
||||
};
|
||||
|
||||
struct KEY_VALUE_BASIC_INFORMATION
|
||||
{
|
||||
ULONG TitleIndex;
|
||||
ULONG Type;
|
||||
ULONG NameLength;
|
||||
char16_t Name[1];
|
||||
ULONG TitleIndex;
|
||||
ULONG Type;
|
||||
ULONG NameLength;
|
||||
char16_t Name[1];
|
||||
};
|
||||
|
||||
struct KEY_VALUE_PARTIAL_INFORMATION
|
||||
{
|
||||
ULONG TitleIndex;
|
||||
ULONG Type;
|
||||
ULONG DataLength;
|
||||
UCHAR Data[1];
|
||||
ULONG TitleIndex;
|
||||
ULONG Type;
|
||||
ULONG DataLength;
|
||||
UCHAR Data[1];
|
||||
};
|
||||
|
||||
struct KEY_VALUE_FULL_INFORMATION
|
||||
{
|
||||
ULONG TitleIndex;
|
||||
ULONG Type;
|
||||
ULONG DataOffset;
|
||||
ULONG DataLength;
|
||||
ULONG NameLength;
|
||||
char16_t Name[1];
|
||||
ULONG TitleIndex;
|
||||
ULONG Type;
|
||||
ULONG DataOffset;
|
||||
ULONG DataLength;
|
||||
ULONG NameLength;
|
||||
char16_t Name[1];
|
||||
};
|
||||
|
||||
@@ -5,42 +5,41 @@
|
||||
using NTSTATUS = std::uint32_t;
|
||||
|
||||
#ifndef OS_WINDOWS
|
||||
#define STATUS_WAIT_0 ((NTSTATUS)0x00000000L)
|
||||
#define STATUS_TIMEOUT ((NTSTATUS)0x00000102L)
|
||||
#define STATUS_WAIT_0 ((NTSTATUS)0x00000000L)
|
||||
#define STATUS_TIMEOUT ((NTSTATUS)0x00000102L)
|
||||
|
||||
#define STATUS_ACCESS_VIOLATION ((NTSTATUS)0xC0000005L)
|
||||
#define STATUS_INVALID_HANDLE ((NTSTATUS)0xC0000008L)
|
||||
#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000DL)
|
||||
#define STATUS_ACCESS_VIOLATION ((NTSTATUS)0xC0000005L)
|
||||
#define STATUS_INVALID_HANDLE ((NTSTATUS)0xC0000008L)
|
||||
#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000DL)
|
||||
#define STATUS_ILLEGAL_INSTRUCTION ((NTSTATUS)0xC000001DL)
|
||||
|
||||
#define STATUS_PENDING ((DWORD)0x00000103L)
|
||||
#define STATUS_PENDING ((NTSTATUS)0x00000103L)
|
||||
#endif
|
||||
|
||||
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
||||
#define STATUS_WAIT_1 ((NTSTATUS)0x00000001L)
|
||||
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
||||
#define STATUS_WAIT_1 ((NTSTATUS)0x00000001L)
|
||||
|
||||
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0x00000001L)
|
||||
#define STATUS_ALERTED ((NTSTATUS)0x00000101L)
|
||||
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0x00000001L)
|
||||
#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_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
|
||||
#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
|
||||
#define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL)
|
||||
#define STATUS_FILE_INVALID ((NTSTATUS)0xC0000098L)
|
||||
#define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS)0xC00000A0L)
|
||||
#define STATUS_FILE_IS_A_DIRECTORY ((NTSTATUS)0xC00000BAL)
|
||||
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL)
|
||||
#define STATUS_INVALID_ADDRESS ((NTSTATUS)0xC0000141L)
|
||||
#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L)
|
||||
#define STATUS_CONNECTION_REFUSED ((NTSTATUS)0xC0000236L)
|
||||
#define STATUS_ADDRESS_ALREADY_ASSOCIATED ((NTSTATUS)0xC0000328L)
|
||||
#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
|
||||
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
|
||||
#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
|
||||
#define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL)
|
||||
#define STATUS_FILE_INVALID ((NTSTATUS)0xC0000098L)
|
||||
#define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS)0xC00000A0L)
|
||||
#define STATUS_FILE_IS_A_DIRECTORY ((NTSTATUS)0xC00000BAL)
|
||||
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL)
|
||||
#define STATUS_INVALID_ADDRESS ((NTSTATUS)0xC0000141L)
|
||||
#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L)
|
||||
#define STATUS_CONNECTION_REFUSED ((NTSTATUS)0xC0000236L)
|
||||
#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 FSCTL_AFD_BASE FILE_DEVICE_NETWORK
|
||||
#define FILE_DEVICE_NETWORK 0x00000012
|
||||
#define FSCTL_AFD_BASE FILE_DEVICE_NETWORK
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
typedef enum _EVENT_TYPE
|
||||
{
|
||||
NotificationEvent,
|
||||
SynchronizationEvent
|
||||
NotificationEvent,
|
||||
SynchronizationEvent
|
||||
} EVENT_TYPE;
|
||||
|
||||
typedef enum _WAIT_TYPE
|
||||
{
|
||||
WaitAll,
|
||||
WaitAny,
|
||||
WaitNotification,
|
||||
WaitDequeue,
|
||||
WaitDpc,
|
||||
WaitAll,
|
||||
WaitAny,
|
||||
WaitNotification,
|
||||
WaitDequeue,
|
||||
WaitDpc,
|
||||
} WAIT_TYPE;
|
||||
|
||||
@@ -2,70 +2,69 @@
|
||||
|
||||
typedef enum _THREADINFOCLASS
|
||||
{
|
||||
ThreadBasicInformation, // q: THREAD_BASIC_INFORMATION
|
||||
ThreadTimes, // q: KERNEL_USER_TIMES
|
||||
ThreadPriority, // s: KPRIORITY (requires SeIncreaseBasePriorityPrivilege)
|
||||
ThreadBasePriority, // s: KPRIORITY
|
||||
ThreadAffinityMask, // s: KAFFINITY
|
||||
ThreadImpersonationToken, // s: HANDLE
|
||||
ThreadDescriptorTableEntry, // q: DESCRIPTOR_TABLE_ENTRY (or WOW64_DESCRIPTOR_TABLE_ENTRY)
|
||||
ThreadBasicInformation, // q: THREAD_BASIC_INFORMATION
|
||||
ThreadTimes, // q: KERNEL_USER_TIMES
|
||||
ThreadPriority, // s: KPRIORITY (requires SeIncreaseBasePriorityPrivilege)
|
||||
ThreadBasePriority, // s: KPRIORITY
|
||||
ThreadAffinityMask, // s: KAFFINITY
|
||||
ThreadImpersonationToken, // s: HANDLE
|
||||
ThreadDescriptorTableEntry, // q: DESCRIPTOR_TABLE_ENTRY (or WOW64_DESCRIPTOR_TABLE_ENTRY)
|
||||
ThreadEnableAlignmentFaultFixup, // s: BOOLEAN
|
||||
ThreadEventPair,
|
||||
ThreadQuerySetWin32StartAddress, // q: ULONG_PTR
|
||||
ThreadZeroTlsCell, // s: ULONG // TlsIndex // 10
|
||||
ThreadPerformanceCount, // q: LARGE_INTEGER
|
||||
ThreadAmILastThread, // q: ULONG
|
||||
ThreadIdealProcessor, // s: ULONG
|
||||
ThreadPriorityBoost, // qs: ULONG
|
||||
ThreadSetTlsArrayAddress, // s: ULONG_PTR // Obsolete
|
||||
ThreadIsIoPending, // q: ULONG
|
||||
ThreadHideFromDebugger, // q: BOOLEAN; s: void
|
||||
ThreadBreakOnTermination, // qs: ULONG
|
||||
ThreadSwitchLegacyState, // s: void // NtCurrentThread // NPX/FPU
|
||||
ThreadIsTerminated, // q: ULONG // 20
|
||||
ThreadLastSystemCall, // q: THREAD_LAST_SYSCALL_INFORMATION
|
||||
ThreadIoPriority, // qs: IO_PRIORITY_HINT (requires SeIncreaseBasePriorityPrivilege)
|
||||
ThreadCycleTime, // q: THREAD_CYCLE_TIME_INFORMATION
|
||||
ThreadPagePriority, // qs: PAGE_PRIORITY_INFORMATION
|
||||
ThreadActualBasePriority, // s: LONG (requires SeIncreaseBasePriorityPrivilege)
|
||||
ThreadTebInformation, // q: THREAD_TEB_INFORMATION (requires THREAD_GET_CONTEXT + THREAD_SET_CONTEXT)
|
||||
ThreadCSwitchMon, // Obsolete
|
||||
ThreadZeroTlsCell, // s: ULONG // TlsIndex // 10
|
||||
ThreadPerformanceCount, // q: LARGE_INTEGER
|
||||
ThreadAmILastThread, // q: ULONG
|
||||
ThreadIdealProcessor, // s: ULONG
|
||||
ThreadPriorityBoost, // qs: ULONG
|
||||
ThreadSetTlsArrayAddress, // s: ULONG_PTR // Obsolete
|
||||
ThreadIsIoPending, // q: ULONG
|
||||
ThreadHideFromDebugger, // q: BOOLEAN; s: void
|
||||
ThreadBreakOnTermination, // qs: ULONG
|
||||
ThreadSwitchLegacyState, // s: void // NtCurrentThread // NPX/FPU
|
||||
ThreadIsTerminated, // q: ULONG // 20
|
||||
ThreadLastSystemCall, // q: THREAD_LAST_SYSCALL_INFORMATION
|
||||
ThreadIoPriority, // qs: IO_PRIORITY_HINT (requires SeIncreaseBasePriorityPrivilege)
|
||||
ThreadCycleTime, // q: THREAD_CYCLE_TIME_INFORMATION
|
||||
ThreadPagePriority, // qs: PAGE_PRIORITY_INFORMATION
|
||||
ThreadActualBasePriority, // s: LONG (requires SeIncreaseBasePriorityPrivilege)
|
||||
ThreadTebInformation, // q: THREAD_TEB_INFORMATION (requires THREAD_GET_CONTEXT + THREAD_SET_CONTEXT)
|
||||
ThreadCSwitchMon, // Obsolete
|
||||
ThreadCSwitchPmu,
|
||||
ThreadWow64Context, // qs: WOW64_CONTEXT, ARM_NT_CONTEXT since 20H1
|
||||
ThreadGroupInformation, // qs: GROUP_AFFINITY // 30
|
||||
ThreadUmsInformation, // q: THREAD_UMS_INFORMATION // Obsolete
|
||||
ThreadCounterProfiling, // q: BOOLEAN; s: THREAD_PROFILING_INFORMATION?
|
||||
ThreadIdealProcessorEx, // qs: PROCESSOR_NUMBER; s: previous PROCESSOR_NUMBER on return
|
||||
ThreadWow64Context, // qs: WOW64_CONTEXT, ARM_NT_CONTEXT since 20H1
|
||||
ThreadGroupInformation, // qs: GROUP_AFFINITY // 30
|
||||
ThreadUmsInformation, // q: THREAD_UMS_INFORMATION // Obsolete
|
||||
ThreadCounterProfiling, // q: BOOLEAN; s: THREAD_PROFILING_INFORMATION?
|
||||
ThreadIdealProcessorEx, // qs: PROCESSOR_NUMBER; s: previous PROCESSOR_NUMBER on return
|
||||
ThreadCpuAccountingInformation, // q: BOOLEAN; s: HANDLE (NtOpenSession) // NtCurrentThread // since WIN8
|
||||
ThreadSuspendCount, // q: ULONG // since WINBLUE
|
||||
ThreadHeterogeneousCpuPolicy, // q: KHETERO_CPU_POLICY // since THRESHOLD
|
||||
ThreadContainerId, // q: GUID
|
||||
ThreadNameInformation, // qs: THREAD_NAME_INFORMATION
|
||||
ThreadSuspendCount, // q: ULONG // since WINBLUE
|
||||
ThreadHeterogeneousCpuPolicy, // q: KHETERO_CPU_POLICY // since THRESHOLD
|
||||
ThreadContainerId, // q: GUID
|
||||
ThreadNameInformation, // qs: THREAD_NAME_INFORMATION
|
||||
ThreadSelectedCpuSets,
|
||||
ThreadSystemThreadInformation, // q: SYSTEM_THREAD_INFORMATION // 40
|
||||
ThreadActualGroupAffinity, // q: GROUP_AFFINITY // since THRESHOLD2
|
||||
ThreadDynamicCodePolicyInfo, // q: ULONG; s: ULONG (NtCurrentThread)
|
||||
ThreadExplicitCaseSensitivity, // qs: ULONG; s: 0 disables, otherwise enables
|
||||
ThreadWorkOnBehalfTicket, // RTL_WORK_ON_BEHALF_TICKET_EX
|
||||
ThreadSubsystemInformation, // q: SUBSYSTEM_INFORMATION_TYPE // since REDSTONE2
|
||||
ThreadDbgkWerReportActive, // s: ULONG; s: 0 disables, otherwise enables
|
||||
ThreadAttachContainer, // s: HANDLE (job object) // NtCurrentThread
|
||||
ThreadSystemThreadInformation, // q: SYSTEM_THREAD_INFORMATION // 40
|
||||
ThreadActualGroupAffinity, // q: GROUP_AFFINITY // since THRESHOLD2
|
||||
ThreadDynamicCodePolicyInfo, // q: ULONG; s: ULONG (NtCurrentThread)
|
||||
ThreadExplicitCaseSensitivity, // qs: ULONG; s: 0 disables, otherwise enables
|
||||
ThreadWorkOnBehalfTicket, // RTL_WORK_ON_BEHALF_TICKET_EX
|
||||
ThreadSubsystemInformation, // q: SUBSYSTEM_INFORMATION_TYPE // since REDSTONE2
|
||||
ThreadDbgkWerReportActive, // s: ULONG; s: 0 disables, otherwise enables
|
||||
ThreadAttachContainer, // s: HANDLE (job object) // NtCurrentThread
|
||||
ThreadManageWritesToExecutableMemory, // MANAGE_WRITES_TO_EXECUTABLE_MEMORY // since REDSTONE3
|
||||
ThreadPowerThrottlingState, // POWER_THROTTLING_THREAD_STATE // since REDSTONE3 (set), WIN11 22H2 (query)
|
||||
ThreadWorkloadClass, // THREAD_WORKLOAD_CLASS // since REDSTONE5 // 50
|
||||
ThreadCreateStateChange, // since WIN11
|
||||
ThreadPowerThrottlingState, // POWER_THROTTLING_THREAD_STATE // since REDSTONE3 (set), WIN11 22H2 (query)
|
||||
ThreadWorkloadClass, // THREAD_WORKLOAD_CLASS // since REDSTONE5 // 50
|
||||
ThreadCreateStateChange, // since WIN11
|
||||
ThreadApplyStateChange,
|
||||
ThreadStrongerBadHandleChecks, // since 22H1
|
||||
ThreadEffectiveIoPriority, // q: IO_PRIORITY_HINT
|
||||
ThreadEffectivePagePriority, // q: ULONG
|
||||
ThreadUpdateLockOwnership, // since 24H2
|
||||
ThreadEffectiveIoPriority, // q: IO_PRIORITY_HINT
|
||||
ThreadEffectivePagePriority, // q: ULONG
|
||||
ThreadUpdateLockOwnership, // since 24H2
|
||||
ThreadSchedulerSharedDataSlot, // SCHEDULER_SHARED_DATA_SLOT_INFORMATION
|
||||
ThreadTebInformationAtomic, // THREAD_TEB_INFORMATION
|
||||
ThreadIndexInformation, // THREAD_INDEX_INFORMATION
|
||||
ThreadTebInformationAtomic, // THREAD_TEB_INFORMATION
|
||||
ThreadIndexInformation, // THREAD_INDEX_INFORMATION
|
||||
MaxThreadInfoClass
|
||||
} THREADINFOCLASS;
|
||||
|
||||
|
||||
template <typename Traits>
|
||||
struct THREAD_NAME_INFORMATION
|
||||
{
|
||||
@@ -80,4 +79,4 @@ typedef struct _THREAD_BASIC_INFORMATION64
|
||||
EMULATOR_CAST(std::uint64_t, KAFFINITY) AffinityMask;
|
||||
EMULATOR_CAST(std::uint32_t, KPRIORITY) Priority;
|
||||
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 <>
|
||||
struct EmulatorTraits<Emu32>
|
||||
{
|
||||
using PVOID = std::uint32_t;
|
||||
using ULONG_PTR = std::uint32_t;
|
||||
using SIZE_T = std::uint32_t;
|
||||
using UNICODE = char16_t;
|
||||
using HANDLE = std::uint32_t;
|
||||
using PVOID = std::uint32_t;
|
||||
using ULONG_PTR = std::uint32_t;
|
||||
using SIZE_T = std::uint32_t;
|
||||
using UNICODE = char16_t;
|
||||
using HANDLE = std::uint32_t;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct EmulatorTraits<Emu64>
|
||||
{
|
||||
using PVOID = std::uint64_t;
|
||||
using ULONG_PTR = std::uint64_t;
|
||||
using SIZE_T = std::uint64_t;
|
||||
using UNICODE = char16_t;
|
||||
using HANDLE = std::uint64_t;
|
||||
using PVOID = std::uint64_t;
|
||||
using ULONG_PTR = std::uint64_t;
|
||||
using SIZE_T = std::uint64_t;
|
||||
using UNICODE = char16_t;
|
||||
using HANDLE = std::uint64_t;
|
||||
};
|
||||
|
||||
@@ -5,76 +5,76 @@
|
||||
template <typename Traits>
|
||||
struct UNICODE_STRING
|
||||
{
|
||||
USHORT Length;
|
||||
USHORT MaximumLength;
|
||||
EMULATOR_CAST(typename Traits::PVOID, char16_t*) Buffer;
|
||||
USHORT Length;
|
||||
USHORT MaximumLength;
|
||||
EMULATOR_CAST(typename Traits::PVOID, char16_t*) Buffer;
|
||||
};
|
||||
|
||||
inline std::string u16_to_u8(const std::u16string_view u16_view)
|
||||
{
|
||||
std::string utf8_str;
|
||||
utf8_str.reserve(u16_view.size() * 2);
|
||||
for (const char16_t ch : u16_view)
|
||||
{
|
||||
if (ch <= 0x7F)
|
||||
{
|
||||
utf8_str.push_back(static_cast<char>(ch));
|
||||
}
|
||||
else if (ch <= 0x7FF)
|
||||
{
|
||||
utf8_str.push_back(static_cast<char>(0xC0 | (ch >> 6)));
|
||||
utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
|
||||
}
|
||||
else
|
||||
{
|
||||
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 & 0x3F)));
|
||||
}
|
||||
}
|
||||
return utf8_str;
|
||||
std::string utf8_str;
|
||||
utf8_str.reserve(u16_view.size() * 2);
|
||||
for (const char16_t ch : u16_view)
|
||||
{
|
||||
if (ch <= 0x7F)
|
||||
{
|
||||
utf8_str.push_back(static_cast<char>(ch));
|
||||
}
|
||||
else if (ch <= 0x7FF)
|
||||
{
|
||||
utf8_str.push_back(static_cast<char>(0xC0 | (ch >> 6)));
|
||||
utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
|
||||
}
|
||||
else
|
||||
{
|
||||
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 & 0x3F)));
|
||||
}
|
||||
}
|
||||
return utf8_str;
|
||||
}
|
||||
|
||||
inline std::string w_to_u8(const std::wstring_view w_view)
|
||||
{
|
||||
std::string utf8_str;
|
||||
utf8_str.reserve(w_view.size() * 2);
|
||||
for (const wchar_t w_ch : w_view)
|
||||
{
|
||||
const auto ch = static_cast<char16_t>(w_ch);
|
||||
if (ch <= 0x7F)
|
||||
{
|
||||
utf8_str.push_back(static_cast<char>(ch));
|
||||
}
|
||||
else if (ch <= 0x7FF)
|
||||
{
|
||||
utf8_str.push_back(static_cast<char>(0xC0 | (ch >> 6)));
|
||||
utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
|
||||
}
|
||||
else
|
||||
{
|
||||
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 & 0x3F)));
|
||||
}
|
||||
}
|
||||
return utf8_str;
|
||||
std::string utf8_str;
|
||||
utf8_str.reserve(w_view.size() * 2);
|
||||
for (const wchar_t w_ch : w_view)
|
||||
{
|
||||
const auto ch = static_cast<char16_t>(w_ch);
|
||||
if (ch <= 0x7F)
|
||||
{
|
||||
utf8_str.push_back(static_cast<char>(ch));
|
||||
}
|
||||
else if (ch <= 0x7FF)
|
||||
{
|
||||
utf8_str.push_back(static_cast<char>(0xC0 | (ch >> 6)));
|
||||
utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
|
||||
}
|
||||
else
|
||||
{
|
||||
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 & 0x3F)));
|
||||
}
|
||||
}
|
||||
return utf8_str;
|
||||
}
|
||||
|
||||
#ifndef OS_WINDOWS
|
||||
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());
|
||||
return errno;
|
||||
*handle = fopen(u16_to_u8(fileName).c_str(), u16_to_u8(mode).c_str());
|
||||
return errno;
|
||||
}
|
||||
#else
|
||||
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)
|
||||
{
|
||||
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
|
||||
|
||||
@@ -2,96 +2,95 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
|
||||
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
|
||||
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
|
||||
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
|
||||
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
|
||||
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
|
||||
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
|
||||
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
|
||||
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
|
||||
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
|
||||
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
|
||||
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
|
||||
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
|
||||
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
|
||||
// IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage)
|
||||
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data
|
||||
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP
|
||||
#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS 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_IAT 12 // Import Address Table
|
||||
#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_ARCHITECTURE 7 // Architecture Specific Data
|
||||
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP
|
||||
#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS 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_IAT 12 // Import Address Table
|
||||
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors
|
||||
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
|
||||
|
||||
#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_NOT_CACHED 0x04000000 // Section is not cachable.
|
||||
#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 // Section is not pageable.
|
||||
#define IMAGE_SCN_MEM_SHARED 0x10000000 // Section is shareable.
|
||||
#define IMAGE_SCN_MEM_EXECUTE 0x20000000 // Section is executable.
|
||||
#define IMAGE_SCN_MEM_READ 0x40000000 // Section is readable.
|
||||
#define IMAGE_SCN_MEM_WRITE 0x80000000 // Section is writeable.
|
||||
#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_NOT_CACHED 0x04000000 // Section is not cachable.
|
||||
#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 // Section is not pageable.
|
||||
#define IMAGE_SCN_MEM_SHARED 0x10000000 // Section is shareable.
|
||||
#define IMAGE_SCN_MEM_EXECUTE 0x20000000 // Section is executable.
|
||||
#define IMAGE_SCN_MEM_READ 0x40000000 // Section is readable.
|
||||
#define IMAGE_SCN_MEM_WRITE 0x80000000 // Section is writeable.
|
||||
|
||||
#define IMAGE_SCN_CNT_CODE 0x00000020 // Section contains code.
|
||||
#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_CODE 0x00000020 // Section contains code.
|
||||
#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 // Section contains initialized data.
|
||||
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 // Section contains uninitialized data.
|
||||
|
||||
#define IMAGE_REL_BASED_ABSOLUTE 0
|
||||
#define IMAGE_REL_BASED_HIGH 1
|
||||
#define IMAGE_REL_BASED_LOW 2
|
||||
#define IMAGE_REL_BASED_HIGHLOW 3
|
||||
#define IMAGE_REL_BASED_HIGHADJ 4
|
||||
#define IMAGE_REL_BASED_MIPS_JMPADDR 5
|
||||
#define IMAGE_REL_BASED_ARM_MOV32A 5
|
||||
#define IMAGE_REL_BASED_ARM_MOV32 5
|
||||
#define IMAGE_REL_BASED_SECTION 6
|
||||
#define IMAGE_REL_BASED_REL 7
|
||||
#define IMAGE_REL_BASED_ARM_MOV32T 7
|
||||
#define IMAGE_REL_BASED_THUMB_MOV32 7
|
||||
#define IMAGE_REL_BASED_MIPS_JMPADDR16 9
|
||||
#define IMAGE_REL_BASED_IA64_IMM64 9
|
||||
#define IMAGE_REL_BASED_DIR64 10
|
||||
#define IMAGE_REL_BASED_HIGH3ADJ 11
|
||||
#define IMAGE_REL_BASED_ABSOLUTE 0
|
||||
#define IMAGE_REL_BASED_HIGH 1
|
||||
#define IMAGE_REL_BASED_LOW 2
|
||||
#define IMAGE_REL_BASED_HIGHLOW 3
|
||||
#define IMAGE_REL_BASED_HIGHADJ 4
|
||||
#define IMAGE_REL_BASED_MIPS_JMPADDR 5
|
||||
#define IMAGE_REL_BASED_ARM_MOV32A 5
|
||||
#define IMAGE_REL_BASED_ARM_MOV32 5
|
||||
#define IMAGE_REL_BASED_SECTION 6
|
||||
#define IMAGE_REL_BASED_REL 7
|
||||
#define IMAGE_REL_BASED_ARM_MOV32T 7
|
||||
#define IMAGE_REL_BASED_THUMB_MOV32 7
|
||||
#define IMAGE_REL_BASED_MIPS_JMPADDR16 9
|
||||
#define IMAGE_REL_BASED_IA64_IMM64 9
|
||||
#define IMAGE_REL_BASED_DIR64 10
|
||||
#define IMAGE_REL_BASED_HIGH3ADJ 11
|
||||
|
||||
#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040
|
||||
#define IMAGE_FILE_DLL 0x2000
|
||||
#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040
|
||||
#define IMAGE_FILE_DLL 0x2000
|
||||
|
||||
#define IMAGE_FILE_MACHINE_I386 0x014c
|
||||
#define IMAGE_FILE_MACHINE_AMD64 0x8664
|
||||
#define IMAGE_FILE_MACHINE_I386 0x014c
|
||||
#define IMAGE_FILE_MACHINE_AMD64 0x8664
|
||||
|
||||
#define PROCESSOR_ARCHITECTURE_AMD64 9
|
||||
#define PROCESSOR_ARCHITECTURE_AMD64 9
|
||||
|
||||
enum class PEMachineType : std::uint16_t
|
||||
{
|
||||
UNKNOWN = 0,
|
||||
I386 = 0x014c, // Intel 386.
|
||||
R3000 = 0x0162, // MIPS little-endian, 0x160 big-endian
|
||||
R4000 = 0x0166, // MIPS little-endian
|
||||
R10000 = 0x0168, // MIPS little-endian
|
||||
WCEMIPSV2 = 0x0169, // MIPS little-endian WCE v2
|
||||
ALPHA = 0x0184, // Alpha_AXP
|
||||
SH3 = 0x01a2, // SH3 little-endian
|
||||
SH3DSP = 0x01a3,
|
||||
SH3E = 0x01a4, // SH3E little-endian
|
||||
SH4 = 0x01a6, // SH4 little-endian
|
||||
SH5 = 0x01a8, // SH5
|
||||
ARM = 0x01c0, // ARM Little-Endian
|
||||
THUMB = 0x01c2, // ARM Thumb/Thumb-2 Little-Endian
|
||||
ARMNT = 0x01c4, // ARM Thumb-2 Little-Endian
|
||||
AM33 = 0x01d3,
|
||||
POWERPC = 0x01F0, // IBM PowerPC Little-Endian
|
||||
POWERPCFP = 0x01f1,
|
||||
IA64 = 0x0200, // Intel 64
|
||||
MIPS16 = 0x0266, // MIPS
|
||||
ALPHA64 = 0x0284, // ALPHA64
|
||||
MIPSFPU = 0x0366, // MIPS
|
||||
MIPSFPU16 = 0x0466, // MIPS
|
||||
AXP64 = ALPHA64,
|
||||
TRICORE = 0x0520, // Infineon
|
||||
CEF = 0x0CEF,
|
||||
EBC = 0x0EBC, // EFI Byte Code
|
||||
AMD64 = 0x8664, // AMD64 (K8)
|
||||
M32R = 0x9041, // M32R little-endian
|
||||
CEE = 0xC0EE,
|
||||
UNKNOWN = 0,
|
||||
I386 = 0x014c, // Intel 386.
|
||||
R3000 = 0x0162, // MIPS little-endian, 0x160 big-endian
|
||||
R4000 = 0x0166, // MIPS little-endian
|
||||
R10000 = 0x0168, // MIPS little-endian
|
||||
WCEMIPSV2 = 0x0169, // MIPS little-endian WCE v2
|
||||
ALPHA = 0x0184, // Alpha_AXP
|
||||
SH3 = 0x01a2, // SH3 little-endian
|
||||
SH3DSP = 0x01a3,
|
||||
SH3E = 0x01a4, // SH3E little-endian
|
||||
SH4 = 0x01a6, // SH4 little-endian
|
||||
SH5 = 0x01a8, // SH5
|
||||
ARM = 0x01c0, // ARM Little-Endian
|
||||
THUMB = 0x01c2, // ARM Thumb/Thumb-2 Little-Endian
|
||||
ARMNT = 0x01c4, // ARM Thumb-2 Little-Endian
|
||||
AM33 = 0x01d3,
|
||||
POWERPC = 0x01F0, // IBM PowerPC Little-Endian
|
||||
POWERPCFP = 0x01f1,
|
||||
IA64 = 0x0200, // Intel 64
|
||||
MIPS16 = 0x0266, // MIPS
|
||||
ALPHA64 = 0x0284, // ALPHA64
|
||||
MIPSFPU = 0x0366, // MIPS
|
||||
MIPSFPU16 = 0x0466, // MIPS
|
||||
AXP64 = ALPHA64,
|
||||
TRICORE = 0x0520, // Infineon
|
||||
CEF = 0x0CEF,
|
||||
EBC = 0x0EBC, // EFI Byte Code
|
||||
AMD64 = 0x8664, // AMD64 (K8)
|
||||
M32R = 0x9041, // M32R little-endian
|
||||
CEE = 0xC0EE,
|
||||
};
|
||||
|
||||
|
||||
#pragma pack(push, 4)
|
||||
|
||||
template <typename T>
|
||||
@@ -102,65 +101,64 @@ struct PEOptionalHeaderBasePart2_t
|
||||
template <>
|
||||
struct PEOptionalHeaderBasePart2_t<std::uint32_t>
|
||||
{
|
||||
std::uint32_t BaseOfData;
|
||||
std::uint32_t ImageBase;
|
||||
std::uint32_t BaseOfData;
|
||||
std::uint32_t ImageBase;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct PEOptionalHeaderBasePart2_t<std::uint64_t>
|
||||
{
|
||||
std::uint64_t ImageBase;
|
||||
std::uint64_t ImageBase;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct PEOptionalHeaderBasePart1_t
|
||||
{
|
||||
enum
|
||||
{
|
||||
k_NumberOfDataDirectors = 16
|
||||
};
|
||||
enum
|
||||
{
|
||||
k_NumberOfDataDirectors = 16
|
||||
};
|
||||
|
||||
uint16_t Magic;
|
||||
uint8_t MajorLinkerVersion;
|
||||
uint8_t MinorLinkerVersion;
|
||||
uint32_t SizeOfCode;
|
||||
uint32_t SizeOfInitializedData;
|
||||
uint32_t SizeOfUninitializedData;
|
||||
uint32_t AddressOfEntryPoint;
|
||||
uint32_t BaseOfCode;
|
||||
uint16_t Magic;
|
||||
uint8_t MajorLinkerVersion;
|
||||
uint8_t MinorLinkerVersion;
|
||||
uint32_t SizeOfCode;
|
||||
uint32_t SizeOfInitializedData;
|
||||
uint32_t SizeOfUninitializedData;
|
||||
uint32_t AddressOfEntryPoint;
|
||||
uint32_t BaseOfCode;
|
||||
};
|
||||
|
||||
|
||||
struct PEDirectory_t2
|
||||
{
|
||||
std::uint32_t VirtualAddress;
|
||||
std::uint32_t Size;
|
||||
std::uint32_t VirtualAddress;
|
||||
std::uint32_t Size;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct PEOptionalHeaderBasePart3_t : PEOptionalHeaderBasePart1_t<T>, PEOptionalHeaderBasePart2_t<T>
|
||||
{
|
||||
uint32_t SectionAlignment;
|
||||
uint32_t FileAlignment;
|
||||
uint16_t MajorOperatingSystemVersion;
|
||||
uint16_t MinorOperatingSystemVersion;
|
||||
uint16_t MajorImageVersion;
|
||||
uint16_t MinorImageVersion;
|
||||
uint16_t MajorSubsystemVersion;
|
||||
uint16_t MinorSubsystemVersion;
|
||||
uint32_t Win32VersionValue;
|
||||
uint32_t SizeOfImage;
|
||||
uint32_t SizeOfHeaders;
|
||||
uint32_t CheckSum;
|
||||
uint16_t Subsystem;
|
||||
uint16_t DllCharacteristics;
|
||||
T SizeOfStackReserve;
|
||||
T SizeOfStackCommit;
|
||||
T SizeOfHeapReserve;
|
||||
T SizeOfHeapCommit;
|
||||
uint32_t LoaderFlags;
|
||||
uint32_t NumberOfRvaAndSizes;
|
||||
PEDirectory_t2 DataDirectory[PEOptionalHeaderBasePart1_t<T>::k_NumberOfDataDirectors];
|
||||
uint32_t SectionAlignment;
|
||||
uint32_t FileAlignment;
|
||||
uint16_t MajorOperatingSystemVersion;
|
||||
uint16_t MinorOperatingSystemVersion;
|
||||
uint16_t MajorImageVersion;
|
||||
uint16_t MinorImageVersion;
|
||||
uint16_t MajorSubsystemVersion;
|
||||
uint16_t MinorSubsystemVersion;
|
||||
uint32_t Win32VersionValue;
|
||||
uint32_t SizeOfImage;
|
||||
uint32_t SizeOfHeaders;
|
||||
uint32_t CheckSum;
|
||||
uint16_t Subsystem;
|
||||
uint16_t DllCharacteristics;
|
||||
T SizeOfStackReserve;
|
||||
T SizeOfStackCommit;
|
||||
T SizeOfHeapReserve;
|
||||
T SizeOfHeapCommit;
|
||||
uint32_t LoaderFlags;
|
||||
uint32_t NumberOfRvaAndSizes;
|
||||
PEDirectory_t2 DataDirectory[PEOptionalHeaderBasePart1_t<T>::k_NumberOfDataDirectors];
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@@ -171,113 +169,116 @@ struct PEOptionalHeader_t
|
||||
template <>
|
||||
struct PEOptionalHeader_t<std::uint32_t> : PEOptionalHeaderBasePart3_t<std::uint32_t>
|
||||
{
|
||||
enum
|
||||
{
|
||||
k_Magic = 0x10b, // IMAGE_NT_OPTIONAL_HDR32_MAGIC
|
||||
};
|
||||
enum
|
||||
{
|
||||
k_Magic = 0x10b, // IMAGE_NT_OPTIONAL_HDR32_MAGIC
|
||||
};
|
||||
};
|
||||
|
||||
template <>
|
||||
struct PEOptionalHeader_t<std::uint64_t> : PEOptionalHeaderBasePart3_t<std::uint64_t>
|
||||
{
|
||||
enum
|
||||
{
|
||||
k_Magic = 0x20b, // IMAGE_NT_OPTIONAL_HDR64_MAGIC
|
||||
};
|
||||
enum
|
||||
{
|
||||
k_Magic = 0x20b, // IMAGE_NT_OPTIONAL_HDR64_MAGIC
|
||||
};
|
||||
};
|
||||
|
||||
struct PEFileHeader_t
|
||||
{
|
||||
PEMachineType Machine;
|
||||
std::uint16_t NumberOfSections;
|
||||
std::uint32_t TimeDateStamp;
|
||||
std::uint32_t PointerToSymbolTable;
|
||||
std::uint32_t NumberOfSymbols;
|
||||
std::uint16_t SizeOfOptionalHeader;
|
||||
std::uint16_t Characteristics;
|
||||
PEMachineType Machine;
|
||||
std::uint16_t NumberOfSections;
|
||||
std::uint32_t TimeDateStamp;
|
||||
std::uint32_t PointerToSymbolTable;
|
||||
std::uint32_t NumberOfSymbols;
|
||||
std::uint16_t SizeOfOptionalHeader;
|
||||
std::uint16_t Characteristics;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct PENTHeaders_t
|
||||
{
|
||||
enum
|
||||
{
|
||||
k_Signature = 0x00004550, // IMAGE_NT_SIGNATURE
|
||||
};
|
||||
enum
|
||||
{
|
||||
k_Signature = 0x00004550, // IMAGE_NT_SIGNATURE
|
||||
};
|
||||
|
||||
uint32_t Signature;
|
||||
PEFileHeader_t FileHeader;
|
||||
PEOptionalHeader_t<T> OptionalHeader;
|
||||
uint32_t Signature;
|
||||
PEFileHeader_t FileHeader;
|
||||
PEOptionalHeader_t<T> OptionalHeader;
|
||||
};
|
||||
|
||||
struct PEDosHeader_t
|
||||
{
|
||||
enum
|
||||
{
|
||||
k_Magic = 0x5A4D
|
||||
};
|
||||
enum
|
||||
{
|
||||
k_Magic = 0x5A4D
|
||||
};
|
||||
|
||||
std::uint16_t e_magic; // Magic number ( k_Magic )
|
||||
std::uint16_t e_cblp; // Bytes on last page of file
|
||||
std::uint16_t e_cp; // Pages in file
|
||||
std::uint16_t e_crlc; // Relocations
|
||||
std::uint16_t e_cparhdr; // Size of header in paragraphs
|
||||
std::uint16_t e_minalloc; // Minimum 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_sp; // Initial SP value
|
||||
std::uint16_t e_csum; // Checksum
|
||||
std::uint16_t e_ip; // Initial IP value
|
||||
std::uint16_t e_cs; // Initial (relative) CS value
|
||||
std::uint16_t e_lfarlc; // File address of relocation table
|
||||
std::uint16_t e_ovno; // Overlay number
|
||||
std::uint16_t e_res[4]; // Reserved words
|
||||
std::uint16_t e_oemid; // OEM identifier (for e_oeminfo)
|
||||
std::uint16_t e_oeminfo; // OEM information; e_oemid specific
|
||||
std::uint16_t e_res2[10]; // Reserved words
|
||||
std::uint32_t e_lfanew; // File address of new exe header
|
||||
std::uint16_t e_magic; // Magic number ( k_Magic )
|
||||
std::uint16_t e_cblp; // Bytes on last page of file
|
||||
std::uint16_t e_cp; // Pages in file
|
||||
std::uint16_t e_crlc; // Relocations
|
||||
std::uint16_t e_cparhdr; // Size of header in paragraphs
|
||||
std::uint16_t e_minalloc; // Minimum 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_sp; // Initial SP value
|
||||
std::uint16_t e_csum; // Checksum
|
||||
std::uint16_t e_ip; // Initial IP value
|
||||
std::uint16_t e_cs; // Initial (relative) CS value
|
||||
std::uint16_t e_lfarlc; // File address of relocation table
|
||||
std::uint16_t e_ovno; // Overlay number
|
||||
std::uint16_t e_res[4]; // Reserved words
|
||||
std::uint16_t e_oemid; // OEM identifier (for e_oeminfo)
|
||||
std::uint16_t e_oeminfo; // OEM information; e_oemid specific
|
||||
std::uint16_t e_res2[10]; // Reserved words
|
||||
std::uint32_t e_lfanew; // File address of new exe header
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#define IMAGE_SIZEOF_SHORT_NAME 8
|
||||
#define IMAGE_SIZEOF_SHORT_NAME 8
|
||||
|
||||
#ifndef OS_WINDOWS
|
||||
typedef struct _IMAGE_SECTION_HEADER
|
||||
{
|
||||
std::uint8_t Name[IMAGE_SIZEOF_SHORT_NAME];
|
||||
union {
|
||||
std:: uint32_t PhysicalAddress;
|
||||
std::uint32_t VirtualSize;
|
||||
std::uint8_t Name[IMAGE_SIZEOF_SHORT_NAME];
|
||||
union
|
||||
{
|
||||
std::uint32_t PhysicalAddress;
|
||||
std::uint32_t VirtualSize;
|
||||
} Misc;
|
||||
std::uint32_t VirtualAddress;
|
||||
std::uint32_t SizeOfRawData;
|
||||
std::uint32_t PointerToRawData;
|
||||
std::uint32_t PointerToRelocations;
|
||||
std::uint32_t PointerToLinenumbers;
|
||||
std::uint16_t NumberOfRelocations;
|
||||
std::uint16_t NumberOfLinenumbers;
|
||||
std::uint32_t Characteristics;
|
||||
std::uint32_t VirtualAddress;
|
||||
std::uint32_t SizeOfRawData;
|
||||
std::uint32_t PointerToRawData;
|
||||
std::uint32_t PointerToRelocations;
|
||||
std::uint32_t PointerToLinenumbers;
|
||||
std::uint16_t NumberOfRelocations;
|
||||
std::uint16_t NumberOfLinenumbers;
|
||||
std::uint32_t Characteristics;
|
||||
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
|
||||
|
||||
typedef struct _IMAGE_EXPORT_DIRECTORY {
|
||||
DWORD Characteristics;
|
||||
DWORD TimeDateStamp;
|
||||
WORD MajorVersion;
|
||||
WORD MinorVersion;
|
||||
DWORD Name;
|
||||
DWORD Base;
|
||||
DWORD NumberOfFunctions;
|
||||
DWORD NumberOfNames;
|
||||
DWORD AddressOfFunctions;
|
||||
DWORD AddressOfNames;
|
||||
DWORD AddressOfNameOrdinals;
|
||||
typedef struct _IMAGE_EXPORT_DIRECTORY
|
||||
{
|
||||
DWORD Characteristics;
|
||||
DWORD TimeDateStamp;
|
||||
WORD MajorVersion;
|
||||
WORD MinorVersion;
|
||||
DWORD Name;
|
||||
DWORD Base;
|
||||
DWORD NumberOfFunctions;
|
||||
DWORD NumberOfNames;
|
||||
DWORD AddressOfFunctions;
|
||||
DWORD AddressOfNames;
|
||||
DWORD AddressOfNameOrdinals;
|
||||
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
|
||||
|
||||
typedef struct _IMAGE_BASE_RELOCATION {
|
||||
DWORD VirtualAddress;
|
||||
DWORD SizeOfBlock;
|
||||
WORD TypeOffset[1];
|
||||
typedef struct _IMAGE_BASE_RELOCATION
|
||||
{
|
||||
DWORD VirtualAddress;
|
||||
DWORD SizeOfBlock;
|
||||
WORD TypeOffset[1];
|
||||
} IMAGE_BASE_RELOCATION, *PIMAGE_BASE_RELOCATION;
|
||||
|
||||
#endif
|
||||
@@ -285,56 +286,56 @@ typedef struct _IMAGE_BASE_RELOCATION {
|
||||
template <typename Traits>
|
||||
struct SECTION_IMAGE_INFORMATION
|
||||
{
|
||||
typename Traits::PVOID TransferAddress;
|
||||
ULONG ZeroBits;
|
||||
typename Traits::SIZE_T MaximumStackSize;
|
||||
typename Traits::SIZE_T CommittedStackSize;
|
||||
ULONG SubSystemType;
|
||||
typename Traits::PVOID TransferAddress;
|
||||
ULONG ZeroBits;
|
||||
typename Traits::SIZE_T MaximumStackSize;
|
||||
typename Traits::SIZE_T CommittedStackSize;
|
||||
ULONG SubSystemType;
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
USHORT SubSystemMinorVersion;
|
||||
USHORT SubSystemMajorVersion;
|
||||
};
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
USHORT SubSystemMinorVersion;
|
||||
USHORT SubSystemMajorVersion;
|
||||
};
|
||||
|
||||
ULONG SubSystemVersion;
|
||||
};
|
||||
ULONG SubSystemVersion;
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
USHORT MajorOperatingSystemVersion;
|
||||
USHORT MinorOperatingSystemVersion;
|
||||
};
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
USHORT MajorOperatingSystemVersion;
|
||||
USHORT MinorOperatingSystemVersion;
|
||||
};
|
||||
|
||||
ULONG OperatingSystemVersion;
|
||||
};
|
||||
ULONG OperatingSystemVersion;
|
||||
};
|
||||
|
||||
USHORT ImageCharacteristics;
|
||||
USHORT DllCharacteristics;
|
||||
PEMachineType Machine;
|
||||
BOOLEAN ImageContainsCode;
|
||||
USHORT ImageCharacteristics;
|
||||
USHORT DllCharacteristics;
|
||||
PEMachineType Machine;
|
||||
BOOLEAN ImageContainsCode;
|
||||
|
||||
union
|
||||
{
|
||||
UCHAR ImageFlags;
|
||||
union
|
||||
{
|
||||
UCHAR ImageFlags;
|
||||
|
||||
struct
|
||||
{
|
||||
UCHAR ComPlusNativeReady : 1;
|
||||
UCHAR ComPlusILOnly : 1;
|
||||
UCHAR ImageDynamicallyRelocated : 1;
|
||||
UCHAR ImageMappedFlat : 1;
|
||||
UCHAR BaseBelow4gb : 1;
|
||||
UCHAR ComPlusPrefer32bit : 1;
|
||||
UCHAR Reserved : 2;
|
||||
};
|
||||
};
|
||||
struct
|
||||
{
|
||||
UCHAR ComPlusNativeReady : 1;
|
||||
UCHAR ComPlusILOnly : 1;
|
||||
UCHAR ImageDynamicallyRelocated : 1;
|
||||
UCHAR ImageMappedFlat : 1;
|
||||
UCHAR BaseBelow4gb : 1;
|
||||
UCHAR ComPlusPrefer32bit : 1;
|
||||
UCHAR Reserved : 2;
|
||||
};
|
||||
};
|
||||
|
||||
ULONG LoaderFlags;
|
||||
ULONG ImageFileSize;
|
||||
ULONG CheckSum;
|
||||
ULONG LoaderFlags;
|
||||
ULONG ImageFileSize;
|
||||
ULONG CheckSum;
|
||||
};
|
||||
|
||||
@@ -5,114 +5,114 @@
|
||||
|
||||
namespace utils
|
||||
{
|
||||
template <typename T, typename S = const uint8_t>
|
||||
requires(std::is_trivially_copyable_v<T> && std::is_same_v<uint8_t, std::remove_cv_t<S>>)
|
||||
class safe_object_accessor
|
||||
{
|
||||
public:
|
||||
safe_object_accessor(const std::span<S> buffer, const size_t offset)
|
||||
: buffer_(buffer)
|
||||
, offset_(offset)
|
||||
{
|
||||
}
|
||||
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>>)
|
||||
class safe_object_accessor
|
||||
{
|
||||
public:
|
||||
safe_object_accessor(const std::span<S> buffer, const size_t offset)
|
||||
: buffer_(buffer),
|
||||
offset_(offset)
|
||||
{
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Object is copied to make sure platform-dependent alignment requirements
|
||||
* are respected
|
||||
****************************************************************************/
|
||||
/*****************************************************************************
|
||||
* Object is copied to make sure platform-dependent alignment requirements
|
||||
* are respected
|
||||
****************************************************************************/
|
||||
|
||||
T get(const size_t element_index = 0) const
|
||||
{
|
||||
T value{};
|
||||
memcpy(&value, get_valid_pointer(element_index), size);
|
||||
return value;
|
||||
}
|
||||
T get(const size_t element_index = 0) const
|
||||
{
|
||||
T value{};
|
||||
memcpy(&value, get_valid_pointer(element_index), size);
|
||||
return value;
|
||||
}
|
||||
|
||||
void set(const T value, const size_t element_index = 0) const
|
||||
{
|
||||
memcpy(get_valid_pointer(element_index), &value, size);
|
||||
}
|
||||
void set(const T value, const size_t element_index = 0) const
|
||||
{
|
||||
memcpy(get_valid_pointer(element_index), &value, size);
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr auto size = sizeof(T);
|
||||
private:
|
||||
static constexpr auto size = sizeof(T);
|
||||
|
||||
std::span<S> buffer_{};
|
||||
size_t offset_{};
|
||||
std::span<S> buffer_{};
|
||||
size_t offset_{};
|
||||
|
||||
S* get_valid_pointer(const size_t element_index) const
|
||||
{
|
||||
const auto start_offset = offset_ + (size * element_index);
|
||||
const auto end_offset = start_offset + size;
|
||||
if (end_offset > buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Buffer accessor overflow");
|
||||
}
|
||||
S* get_valid_pointer(const size_t element_index) const
|
||||
{
|
||||
const auto start_offset = offset_ + (size * element_index);
|
||||
const auto end_offset = start_offset + size;
|
||||
if (end_offset > buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Buffer accessor overflow");
|
||||
}
|
||||
|
||||
return buffer_.data() + start_offset;
|
||||
}
|
||||
};
|
||||
return buffer_.data() + start_offset;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
requires(std::is_same_v<uint8_t, std::remove_cv_t<T>>)
|
||||
class safe_buffer_accessor
|
||||
{
|
||||
public:
|
||||
safe_buffer_accessor(const std::span<T> buffer)
|
||||
: buffer_(buffer)
|
||||
{
|
||||
}
|
||||
template <typename T>
|
||||
requires(std::is_same_v<uint8_t, std::remove_cv_t<T>>)
|
||||
class safe_buffer_accessor
|
||||
{
|
||||
public:
|
||||
safe_buffer_accessor(const std::span<T> buffer)
|
||||
: buffer_(buffer)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
safe_buffer_accessor(const safe_buffer_accessor<S>& obj)
|
||||
: buffer_(obj.get_buffer())
|
||||
{
|
||||
}
|
||||
template <typename S>
|
||||
safe_buffer_accessor(const safe_buffer_accessor<S>& obj)
|
||||
: buffer_(obj.get_buffer())
|
||||
{
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
safe_object_accessor<S, T> as(const size_t offset) const
|
||||
{
|
||||
return {this->buffer_, offset};
|
||||
}
|
||||
template <typename S>
|
||||
safe_object_accessor<S, T> as(const size_t offset) const
|
||||
{
|
||||
return {this->buffer_, offset};
|
||||
}
|
||||
|
||||
T* get_pointer_for_range(const size_t offset, const size_t size) const
|
||||
{
|
||||
this->validate(offset, size);
|
||||
return this->buffer_.data() + offset;
|
||||
}
|
||||
T* get_pointer_for_range(const size_t offset, const size_t size) const
|
||||
{
|
||||
this->validate(offset, size);
|
||||
return this->buffer_.data() + offset;
|
||||
}
|
||||
|
||||
void validate(const size_t offset, const size_t size) const
|
||||
{
|
||||
const auto end = offset + size;
|
||||
if (end > buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Buffer accessor overflow");
|
||||
}
|
||||
}
|
||||
void validate(const size_t offset, const size_t size) const
|
||||
{
|
||||
const auto end = offset + size;
|
||||
if (end > buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Buffer accessor overflow");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename S = char>
|
||||
std::basic_string<S> as_string(const size_t offset) const
|
||||
{
|
||||
safe_object_accessor<S> string_accessor{this->buffer_, offset};
|
||||
std::basic_string<S> result{};
|
||||
template <typename S = char>
|
||||
std::basic_string<S> as_string(const size_t offset) const
|
||||
{
|
||||
safe_object_accessor<S> string_accessor{this->buffer_, offset};
|
||||
std::basic_string<S> result{};
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto value = string_accessor.get(result.size());
|
||||
if (!value)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
auto value = string_accessor.get(result.size());
|
||||
if (!value)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result.push_back(std::move(value));
|
||||
}
|
||||
}
|
||||
result.push_back(std::move(value));
|
||||
}
|
||||
}
|
||||
|
||||
std::span<T> get_buffer() const
|
||||
{
|
||||
return this->buffer_;
|
||||
}
|
||||
std::span<T> get_buffer() const
|
||||
{
|
||||
return this->buffer_;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::span<T> buffer_{};
|
||||
};
|
||||
private:
|
||||
const std::span<T> buffer_{};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,54 +4,60 @@
|
||||
|
||||
namespace utils::concurrency
|
||||
{
|
||||
template <typename T, typename MutexType = std::mutex>
|
||||
class container
|
||||
{
|
||||
public:
|
||||
template <typename R = void, typename F>
|
||||
R access(F&& accessor) const
|
||||
{
|
||||
std::lock_guard<MutexType> _{mutex_};
|
||||
return accessor(object_);
|
||||
}
|
||||
template <typename T, typename MutexType = std::mutex>
|
||||
class container
|
||||
{
|
||||
public:
|
||||
template <typename R = void, typename F>
|
||||
R access(F&& accessor) const
|
||||
{
|
||||
std::lock_guard<MutexType> _{mutex_};
|
||||
return accessor(object_);
|
||||
}
|
||||
|
||||
template <typename R = void, typename F>
|
||||
R access(F&& accessor)
|
||||
{
|
||||
std::lock_guard<MutexType> _{mutex_};
|
||||
return accessor(object_);
|
||||
}
|
||||
template <typename R = void, typename F>
|
||||
R access(F&& accessor)
|
||||
{
|
||||
std::lock_guard<MutexType> _{mutex_};
|
||||
return accessor(object_);
|
||||
}
|
||||
|
||||
template <typename R = void, typename F>
|
||||
R access_with_lock(F&& accessor) const
|
||||
{
|
||||
std::unique_lock<MutexType> lock{mutex_};
|
||||
return accessor(object_, lock);
|
||||
}
|
||||
template <typename R = void, typename F>
|
||||
R access_with_lock(F&& accessor) const
|
||||
{
|
||||
std::unique_lock<MutexType> lock{mutex_};
|
||||
return accessor(object_, lock);
|
||||
}
|
||||
|
||||
template <typename R = void, typename F>
|
||||
R access_with_lock(F&& accessor)
|
||||
{
|
||||
std::unique_lock<MutexType> lock{mutex_};
|
||||
return accessor(object_, lock);
|
||||
}
|
||||
template <typename R = void, typename F>
|
||||
R access_with_lock(F&& accessor)
|
||||
{
|
||||
std::unique_lock<MutexType> lock{mutex_};
|
||||
return accessor(object_, lock);
|
||||
}
|
||||
|
||||
T& get_raw() { return object_; }
|
||||
const T& get_raw() const { return object_; }
|
||||
T& get_raw()
|
||||
{
|
||||
return object_;
|
||||
}
|
||||
const T& get_raw() const
|
||||
{
|
||||
return object_;
|
||||
}
|
||||
|
||||
T copy() const
|
||||
{
|
||||
std::unique_lock<MutexType> lock{mutex_};
|
||||
return object_;
|
||||
}
|
||||
T copy() const
|
||||
{
|
||||
std::unique_lock<MutexType> lock{mutex_};
|
||||
return object_;
|
||||
}
|
||||
|
||||
std::unique_lock<MutexType> acquire_lock()
|
||||
{
|
||||
return std::unique_lock<MutexType>{mutex_};
|
||||
}
|
||||
std::unique_lock<MutexType> acquire_lock()
|
||||
{
|
||||
return std::unique_lock<MutexType>{mutex_};
|
||||
}
|
||||
|
||||
private:
|
||||
mutable MutexType mutex_{};
|
||||
T object_{};
|
||||
};
|
||||
private:
|
||||
mutable MutexType mutex_{};
|
||||
T object_{};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,19 +7,19 @@
|
||||
|
||||
namespace utils
|
||||
{
|
||||
struct string_hash
|
||||
{
|
||||
using is_transparent = void;
|
||||
struct string_hash
|
||||
{
|
||||
using is_transparent = void;
|
||||
|
||||
size_t operator()(const std::string_view str) const
|
||||
{
|
||||
constexpr std::hash<std::string_view> hasher{};
|
||||
return hasher(str);
|
||||
}
|
||||
};
|
||||
size_t operator()(const std::string_view str) const
|
||||
{
|
||||
constexpr std::hash<std::string_view> hasher{};
|
||||
return hasher(str);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using unordered_string_map = std::unordered_map<std::string, T, string_hash, std::equal_to<>>;
|
||||
template <typename T>
|
||||
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
|
||||
{
|
||||
class file_handle
|
||||
{
|
||||
public:
|
||||
file_handle() = default;
|
||||
class file_handle
|
||||
{
|
||||
public:
|
||||
file_handle() = default;
|
||||
|
||||
file_handle(FILE* file)
|
||||
: file_(file)
|
||||
{
|
||||
}
|
||||
file_handle(FILE* file)
|
||||
: file_(file)
|
||||
{
|
||||
}
|
||||
|
||||
~file_handle()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
~file_handle()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
file_handle(const file_handle&) = delete;
|
||||
file_handle& operator=(const file_handle&) = delete;
|
||||
file_handle(const file_handle&) = delete;
|
||||
file_handle& operator=(const file_handle&) = delete;
|
||||
|
||||
file_handle(file_handle&& obj) noexcept
|
||||
: file_handle()
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
file_handle(file_handle&& obj) noexcept
|
||||
: file_handle()
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
|
||||
file_handle& operator=(file_handle&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->release();
|
||||
this->file_ = obj.file_;
|
||||
obj.file_ = {};
|
||||
}
|
||||
file_handle& operator=(file_handle&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->release();
|
||||
this->file_ = obj.file_;
|
||||
obj.file_ = {};
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
file_handle& operator=(FILE* file) noexcept
|
||||
{
|
||||
this->release();
|
||||
this->file_ = file;
|
||||
file_handle& operator=(FILE* file) noexcept
|
||||
{
|
||||
this->release();
|
||||
this->file_ = file;
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] operator bool() const
|
||||
{
|
||||
return this->file_;
|
||||
}
|
||||
[[nodiscard]] operator bool() const
|
||||
{
|
||||
return this->file_;
|
||||
}
|
||||
|
||||
[[nodiscard]] operator FILE*() const
|
||||
{
|
||||
return this->file_;
|
||||
}
|
||||
[[nodiscard]] operator FILE*() const
|
||||
{
|
||||
return this->file_;
|
||||
}
|
||||
|
||||
[[nodiscard]] int64_t size() const
|
||||
{
|
||||
const auto current_position = this->tell();
|
||||
[[nodiscard]] int64_t size() const
|
||||
{
|
||||
const auto current_position = this->tell();
|
||||
|
||||
this->seek_to(0, SEEK_END);
|
||||
const auto size = this->tell();
|
||||
this->seek_to(current_position);
|
||||
this->seek_to(0, SEEK_END);
|
||||
const auto size = this->tell();
|
||||
this->seek_to(current_position);
|
||||
|
||||
return size;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
bool seek_to(const int64_t position, const int origin = SEEK_SET) const
|
||||
{
|
||||
return _fseeki64(this->file_, position, origin) == 0;
|
||||
}
|
||||
bool seek_to(const int64_t position, const int origin = SEEK_SET) const
|
||||
{
|
||||
return _fseeki64(this->file_, position, origin) == 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] int64_t tell() const
|
||||
{
|
||||
return _ftelli64(this->file_);
|
||||
}
|
||||
[[nodiscard]] int64_t tell() const
|
||||
{
|
||||
return _ftelli64(this->file_);
|
||||
}
|
||||
|
||||
private:
|
||||
FILE* file_{};
|
||||
private:
|
||||
FILE* file_{};
|
||||
|
||||
void release()
|
||||
{
|
||||
if (this->file_)
|
||||
{
|
||||
(void)fclose(this->file_);
|
||||
this->file_ = {};
|
||||
}
|
||||
}
|
||||
};
|
||||
void release()
|
||||
{
|
||||
if (this->file_)
|
||||
{
|
||||
(void)fclose(this->file_);
|
||||
this->file_ = {};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,52 +4,53 @@
|
||||
|
||||
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>
|
||||
class final_action
|
||||
{
|
||||
public:
|
||||
static_assert(!std::is_reference<F>::value && !std::is_const<F>::value &&
|
||||
!std::is_volatile<F>::value,
|
||||
"Final_action should store its callable by value");
|
||||
template <class F>
|
||||
class final_action
|
||||
{
|
||||
public:
|
||||
static_assert(!std::is_reference<F>::value && !std::is_const<F>::value && !std::is_volatile<F>::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
|
||||
: f_(std::move(other.f_)), invoke_(std::exchange(other.invoke_, false))
|
||||
{
|
||||
}
|
||||
final_action(final_action&& other) noexcept
|
||||
: f_(std::move(other.f_)),
|
||||
invoke_(std::exchange(other.invoke_, false))
|
||||
{
|
||||
}
|
||||
|
||||
final_action(const final_action&) = delete;
|
||||
final_action& operator=(const final_action&) = delete;
|
||||
final_action& operator=(final_action&&) = delete;
|
||||
final_action(const final_action&) = delete;
|
||||
final_action& operator=(const final_action&) = delete;
|
||||
final_action& operator=(final_action&&) = delete;
|
||||
|
||||
~final_action() noexcept
|
||||
{
|
||||
if (invoke_) f_();
|
||||
}
|
||||
~final_action() noexcept
|
||||
{
|
||||
if (invoke_)
|
||||
f_();
|
||||
}
|
||||
|
||||
// Added by momo5502
|
||||
void cancel()
|
||||
{
|
||||
invoke_ = false;
|
||||
}
|
||||
// Added by momo5502
|
||||
void cancel()
|
||||
{
|
||||
invoke_ = false;
|
||||
}
|
||||
|
||||
private:
|
||||
F f_;
|
||||
bool invoke_{true};
|
||||
};
|
||||
private:
|
||||
F f_;
|
||||
bool invoke_{true};
|
||||
};
|
||||
|
||||
template <class F>
|
||||
final_action<typename std::remove_cv<typename std::remove_reference<F>::type>::type>
|
||||
finally(F&& f) noexcept
|
||||
{
|
||||
return final_action<typename std::remove_cv<typename std::remove_reference<F>::type>::type>(
|
||||
std::forward<F>(f));
|
||||
}
|
||||
template <class F>
|
||||
final_action<typename std::remove_cv<typename std::remove_reference<F>::type>::type> finally(F&& f) noexcept
|
||||
{
|
||||
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
|
||||
{
|
||||
bool remove_file(const std::filesystem::path& file)
|
||||
{
|
||||
std::error_code ec{};
|
||||
return std::filesystem::remove(file, ec) && !ec;
|
||||
}
|
||||
bool remove_file(const std::filesystem::path& file)
|
||||
{
|
||||
std::error_code ec{};
|
||||
return std::filesystem::remove(file, ec) && !ec;
|
||||
}
|
||||
|
||||
bool move_file(const std::filesystem::path& src, const std::filesystem::path& target)
|
||||
{
|
||||
copy_folder(src, target);
|
||||
return remove_file(src);
|
||||
}
|
||||
bool move_file(const std::filesystem::path& src, const std::filesystem::path& target)
|
||||
{
|
||||
copy_folder(src, target);
|
||||
return remove_file(src);
|
||||
}
|
||||
|
||||
bool file_exists(const std::filesystem::path& file)
|
||||
{
|
||||
return std::ifstream(file).good();
|
||||
}
|
||||
bool file_exists(const std::filesystem::path& file)
|
||||
{
|
||||
return std::ifstream(file).good();
|
||||
}
|
||||
|
||||
bool write_file(const std::filesystem::path& file, const std::vector<uint8_t>& data, const bool append)
|
||||
{
|
||||
if (file.has_parent_path())
|
||||
{
|
||||
io::create_directory(file.parent_path());
|
||||
}
|
||||
bool write_file(const std::filesystem::path& file, const std::vector<uint8_t>& data, const bool append)
|
||||
{
|
||||
if (file.has_parent_path())
|
||||
{
|
||||
io::create_directory(file.parent_path());
|
||||
}
|
||||
|
||||
std::ofstream stream(
|
||||
file, std::ios::binary | std::ofstream::out | (append ? std::ofstream::app : std::ofstream::out));
|
||||
std::ofstream stream(file, std::ios::binary | std::ofstream::out |
|
||||
(append ? std::ofstream::app : std::ofstream::out));
|
||||
|
||||
if (stream.is_open())
|
||||
{
|
||||
stream.write(reinterpret_cast<const char*>(data.data()), static_cast<std::streamsize>(data.size()));
|
||||
stream.close();
|
||||
return true;
|
||||
}
|
||||
if (stream.is_open())
|
||||
{
|
||||
stream.write(reinterpret_cast<const char*>(data.data()), static_cast<std::streamsize>(data.size()));
|
||||
stream.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> read_file(const std::filesystem::path& file)
|
||||
{
|
||||
std::vector<uint8_t> data;
|
||||
read_file(file, &data);
|
||||
return data;
|
||||
}
|
||||
std::vector<uint8_t> read_file(const std::filesystem::path& file)
|
||||
{
|
||||
std::vector<uint8_t> data;
|
||||
read_file(file, &data);
|
||||
return data;
|
||||
}
|
||||
|
||||
bool read_file(const std::filesystem::path& file, std::vector<uint8_t>* data)
|
||||
{
|
||||
if (!data) return false;
|
||||
data->clear();
|
||||
bool read_file(const std::filesystem::path& file, std::vector<uint8_t>* data)
|
||||
{
|
||||
if (!data)
|
||||
return false;
|
||||
data->clear();
|
||||
|
||||
std::ifstream stream(file, std::ios::binary);
|
||||
if (!stream) return false;
|
||||
std::ifstream stream(file, std::ios::binary);
|
||||
if (!stream)
|
||||
return false;
|
||||
|
||||
*data = std::vector<uint8_t>{(std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>()};
|
||||
return true;
|
||||
}
|
||||
*data = std::vector<uint8_t>{(std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>()};
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t file_size(const std::filesystem::path& file)
|
||||
{
|
||||
std::ifstream stream(file, std::ios::binary);
|
||||
std::size_t file_size(const std::filesystem::path& file)
|
||||
{
|
||||
std::ifstream stream(file, std::ios::binary);
|
||||
|
||||
if (stream)
|
||||
{
|
||||
stream.seekg(0, std::ios::end);
|
||||
return static_cast<std::size_t>(stream.tellg());
|
||||
}
|
||||
if (stream)
|
||||
{
|
||||
stream.seekg(0, std::ios::end);
|
||||
return static_cast<std::size_t>(stream.tellg());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool create_directory(const std::filesystem::path& directory)
|
||||
{
|
||||
std::error_code ec{};
|
||||
return std::filesystem::create_directories(directory, ec) && !ec;
|
||||
}
|
||||
bool create_directory(const std::filesystem::path& directory)
|
||||
{
|
||||
std::error_code ec{};
|
||||
return std::filesystem::create_directories(directory, ec) && !ec;
|
||||
}
|
||||
|
||||
bool directory_exists(const std::filesystem::path& directory)
|
||||
{
|
||||
std::error_code ec{};
|
||||
return std::filesystem::is_directory(directory, ec) && !ec;
|
||||
}
|
||||
bool directory_exists(const std::filesystem::path& directory)
|
||||
{
|
||||
std::error_code ec{};
|
||||
return std::filesystem::is_directory(directory, ec) && !ec;
|
||||
}
|
||||
|
||||
bool directory_is_empty(const std::filesystem::path& directory)
|
||||
{
|
||||
std::error_code ec{};
|
||||
return std::filesystem::is_empty(directory, ec) && !ec;
|
||||
}
|
||||
bool directory_is_empty(const std::filesystem::path& directory)
|
||||
{
|
||||
std::error_code ec{};
|
||||
return std::filesystem::is_empty(directory, ec) && !ec;
|
||||
}
|
||||
|
||||
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target)
|
||||
{
|
||||
std::error_code ec{};
|
||||
std::filesystem::copy(src, target,
|
||||
std::filesystem::copy_options::overwrite_existing |
|
||||
std::filesystem::copy_options::recursive, ec);
|
||||
}
|
||||
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target)
|
||||
{
|
||||
std::error_code ec{};
|
||||
std::filesystem::copy(
|
||||
src, target, std::filesystem::copy_options::overwrite_existing | std::filesystem::copy_options::recursive,
|
||||
ec);
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> list_files(const std::filesystem::path& directory, const bool recursive)
|
||||
{
|
||||
std::error_code code{};
|
||||
std::vector<std::filesystem::path> files;
|
||||
std::vector<std::filesystem::path> list_files(const std::filesystem::path& directory, const bool recursive)
|
||||
{
|
||||
std::error_code code{};
|
||||
std::vector<std::filesystem::path> files;
|
||||
|
||||
if (recursive)
|
||||
{
|
||||
for (auto& file : std::filesystem::recursive_directory_iterator(directory, code))
|
||||
{
|
||||
files.push_back(file.path());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& file : std::filesystem::directory_iterator(directory, code))
|
||||
{
|
||||
files.push_back(file.path());
|
||||
}
|
||||
}
|
||||
if (recursive)
|
||||
{
|
||||
for (auto& file : std::filesystem::recursive_directory_iterator(directory, code))
|
||||
{
|
||||
files.push_back(file.path());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& file : std::filesystem::directory_iterator(directory, code))
|
||||
{
|
||||
files.push_back(file.path());
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
return files;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,17 +6,17 @@
|
||||
|
||||
namespace utils::io
|
||||
{
|
||||
bool remove_file(const std::filesystem::path& file);
|
||||
bool move_file(const std::filesystem::path& src, const std::filesystem::path& target);
|
||||
bool file_exists(const std::filesystem::path& file);
|
||||
bool write_file(const std::filesystem::path& file, const std::vector<uint8_t>& data, bool append = false);
|
||||
bool read_file(const std::filesystem::path& file, std::vector<uint8_t>* data);
|
||||
std::vector<uint8_t> read_file(const std::filesystem::path& file);
|
||||
size_t file_size(const std::filesystem::path& file);
|
||||
bool create_directory(const std::filesystem::path& directory);
|
||||
bool directory_exists(const std::filesystem::path& directory);
|
||||
bool directory_is_empty(const std::filesystem::path& directory);
|
||||
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target);
|
||||
bool remove_file(const std::filesystem::path& file);
|
||||
bool move_file(const std::filesystem::path& src, const std::filesystem::path& target);
|
||||
bool file_exists(const std::filesystem::path& file);
|
||||
bool write_file(const std::filesystem::path& file, const std::vector<uint8_t>& data, bool append = false);
|
||||
bool read_file(const std::filesystem::path& file, std::vector<uint8_t>* data);
|
||||
std::vector<uint8_t> read_file(const std::filesystem::path& file);
|
||||
size_t file_size(const std::filesystem::path& file);
|
||||
bool create_directory(const std::filesystem::path& directory);
|
||||
bool directory_exists(const std::filesystem::path& directory);
|
||||
bool directory_is_empty(const std::filesystem::path& directory);
|
||||
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
|
||||
{
|
||||
using HandleFunction = HANDLE();
|
||||
using HandleFunction = HANDLE();
|
||||
|
||||
inline HANDLE null_handle()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
inline HANDLE null_handle()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline HANDLE invalid_handle()
|
||||
{
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
inline HANDLE invalid_handle()
|
||||
{
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
template <HandleFunction InvalidHandleFunction = null_handle>
|
||||
class handle
|
||||
{
|
||||
public:
|
||||
handle() = default;
|
||||
template <HandleFunction InvalidHandleFunction = null_handle>
|
||||
class handle
|
||||
{
|
||||
public:
|
||||
handle() = default;
|
||||
|
||||
handle(const HANDLE h)
|
||||
: handle_(h)
|
||||
{
|
||||
}
|
||||
handle(const HANDLE h)
|
||||
: handle_(h)
|
||||
{
|
||||
}
|
||||
|
||||
~handle()
|
||||
{
|
||||
if (*this)
|
||||
{
|
||||
CloseHandle(this->handle_);
|
||||
this->handle_ = InvalidHandleFunction();
|
||||
}
|
||||
}
|
||||
~handle()
|
||||
{
|
||||
if (*this)
|
||||
{
|
||||
CloseHandle(this->handle_);
|
||||
this->handle_ = InvalidHandleFunction();
|
||||
}
|
||||
}
|
||||
|
||||
handle(const handle&) = delete;
|
||||
handle& operator=(const handle&) = delete;
|
||||
handle(const handle&) = delete;
|
||||
handle& operator=(const handle&) = delete;
|
||||
|
||||
handle(handle&& obj) noexcept
|
||||
: handle()
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
handle(handle&& obj) noexcept
|
||||
: handle()
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
|
||||
handle& operator=(handle&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->~handle();
|
||||
this->handle_ = obj.handle_;
|
||||
obj.handle_ = InvalidHandleFunction();
|
||||
}
|
||||
handle& operator=(handle&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->~handle();
|
||||
this->handle_ = obj.handle_;
|
||||
obj.handle_ = InvalidHandleFunction();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
handle& operator=(HANDLE h) noexcept
|
||||
{
|
||||
this->~handle();
|
||||
this->handle_ = h;
|
||||
handle& operator=(HANDLE h) noexcept
|
||||
{
|
||||
this->~handle();
|
||||
this->handle_ = h;
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] operator bool() const
|
||||
{
|
||||
return this->handle_ != InvalidHandleFunction();
|
||||
}
|
||||
[[nodiscard]] operator bool() const
|
||||
{
|
||||
return this->handle_ != InvalidHandleFunction();
|
||||
}
|
||||
|
||||
[[nodiscard]] operator HANDLE() const
|
||||
{
|
||||
return this->handle_;
|
||||
}
|
||||
[[nodiscard]] operator HANDLE() const
|
||||
{
|
||||
return this->handle_;
|
||||
}
|
||||
|
||||
private:
|
||||
HANDLE handle_{InvalidHandleFunction()};
|
||||
};
|
||||
private:
|
||||
HANDLE handle_{InvalidHandleFunction()};
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,45 +6,42 @@
|
||||
|
||||
namespace utils::string
|
||||
{
|
||||
inline char char_to_lower(const char val)
|
||||
{
|
||||
return static_cast<char>(std::tolower(static_cast<unsigned char>(val)));
|
||||
}
|
||||
inline char char_to_lower(const char val)
|
||||
{
|
||||
return static_cast<char>(std::tolower(static_cast<unsigned char>(val)));
|
||||
}
|
||||
|
||||
inline char16_t char_to_lower(const char16_t val)
|
||||
{
|
||||
if (val >= u'A' && val <= u'Z')
|
||||
{
|
||||
return val + 32;
|
||||
}
|
||||
inline char16_t char_to_lower(const char16_t val)
|
||||
{
|
||||
if (val >= u'A' && val <= u'Z')
|
||||
{
|
||||
return val + 32;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
inline wchar_t char_to_lower(const wchar_t val)
|
||||
{
|
||||
return std::towlower(val);
|
||||
}
|
||||
inline wchar_t char_to_lower(const wchar_t val)
|
||||
{
|
||||
return std::towlower(val);
|
||||
}
|
||||
|
||||
template <class Elem, class Traits, class Alloc>
|
||||
void to_lower_inplace(std::basic_string<Elem, Traits, Alloc>& str)
|
||||
{
|
||||
std::ranges::transform(str, str.begin(), [](const Elem e)
|
||||
{
|
||||
return char_to_lower(e);
|
||||
});
|
||||
}
|
||||
template <class Elem, class Traits, class Alloc>
|
||||
void to_lower_inplace(std::basic_string<Elem, Traits, Alloc>& str)
|
||||
{
|
||||
std::ranges::transform(str, str.begin(), [](const Elem e) { return char_to_lower(e); });
|
||||
}
|
||||
|
||||
template <class Elem, class Traits, class Alloc>
|
||||
std::basic_string<Elem, Traits, Alloc> to_lower(std::basic_string<Elem, Traits, Alloc> str)
|
||||
{
|
||||
to_lower_inplace(str);
|
||||
return str;
|
||||
}
|
||||
template <class Elem, class Traits, class Alloc>
|
||||
std::basic_string<Elem, Traits, Alloc> to_lower(std::basic_string<Elem, Traits, Alloc> str)
|
||||
{
|
||||
to_lower_inplace(str);
|
||||
return str;
|
||||
}
|
||||
|
||||
template <class Elem, class Traits, class Alloc>
|
||||
std::basic_string<Elem, Traits, Alloc> to_lower_consume(std::basic_string<Elem, Traits, Alloc>& str)
|
||||
{
|
||||
return to_lower(std::move(str));
|
||||
}
|
||||
template <class Elem, class Traits, class Alloc>
|
||||
std::basic_string<Elem, Traits, Alloc> to_lower_consume(std::basic_string<Elem, Traits, Alloc>& str)
|
||||
{
|
||||
return to_lower(std::move(str));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,23 +4,23 @@
|
||||
|
||||
namespace utils
|
||||
{
|
||||
template <typename Clock = std::chrono::high_resolution_clock>
|
||||
class timer
|
||||
{
|
||||
public:
|
||||
void update()
|
||||
{
|
||||
this->point_ = Clock::now();
|
||||
}
|
||||
template <typename Clock = std::chrono::high_resolution_clock>
|
||||
class timer
|
||||
{
|
||||
public:
|
||||
void update()
|
||||
{
|
||||
this->point_ = Clock::now();
|
||||
}
|
||||
|
||||
bool has_elapsed(typename Clock::duration duration) const
|
||||
{
|
||||
const auto now = Clock::now();
|
||||
const auto diff = now - this->point_;
|
||||
return diff > duration;
|
||||
}
|
||||
bool has_elapsed(typename Clock::duration duration) const
|
||||
{
|
||||
const auto now = Clock::now();
|
||||
const auto diff = now - this->point_;
|
||||
return diff > duration;
|
||||
}
|
||||
|
||||
private:
|
||||
typename Clock::time_point point_{Clock::now()};
|
||||
};
|
||||
private:
|
||||
typename Clock::time_point point_{Clock::now()};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,52 +4,52 @@
|
||||
template <typename T>
|
||||
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>
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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,
|
||||
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)
|
||||
{
|
||||
return value & ~(alignment - 1);
|
||||
return value & ~(alignment - 1);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
run_instruction = false,
|
||||
skip_instruction = true,
|
||||
run_instruction = false,
|
||||
skip_instruction = true,
|
||||
};
|
||||
|
||||
enum class memory_violation_continuation : bool
|
||||
{
|
||||
stop = false,
|
||||
resume = true,
|
||||
stop = false,
|
||||
resume = true,
|
||||
};
|
||||
|
||||
enum class memory_violation_type : uint8_t
|
||||
{
|
||||
unmapped,
|
||||
protection,
|
||||
unmapped,
|
||||
protection,
|
||||
};
|
||||
|
||||
struct basic_block
|
||||
{
|
||||
uint64_t address;
|
||||
size_t instruction_count;
|
||||
size_t size;
|
||||
uint64_t address;
|
||||
size_t instruction_count;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
using edge_generation_hook_callback = std::function<void(const basic_block& current_block,
|
||||
const basic_block& previous_block)>;
|
||||
using edge_generation_hook_callback =
|
||||
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 instruction_hook_callback = std::function<instruction_hook_continuation()>;
|
||||
|
||||
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 complex_memory_hook_callback = std::function<void(uint64_t address, size_t size, uint64_t value,
|
||||
memory_operation operation)>;
|
||||
using complex_memory_hook_callback =
|
||||
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(
|
||||
uint64_t address, size_t size, memory_operation operation,
|
||||
memory_violation_type type)>;
|
||||
uint64_t address, size_t size, memory_operation operation, memory_violation_type type)>;
|
||||
|
||||
class emulator : public memory_manager
|
||||
{
|
||||
public:
|
||||
emulator() = default;
|
||||
public:
|
||||
emulator() = default;
|
||||
|
||||
emulator(const emulator&) = delete;
|
||||
emulator& operator=(const emulator&) = delete;
|
||||
emulator(const emulator&) = delete;
|
||||
emulator& operator=(const emulator&) = delete;
|
||||
|
||||
emulator(emulator&&) = delete;
|
||||
emulator& operator=(emulator&&) = delete;
|
||||
emulator(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 stop() = 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 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 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 std::vector<std::byte> save_registers() = 0;
|
||||
virtual void restore_registers(const std::vector<std::byte>& register_data) = 0;
|
||||
virtual std::vector<std::byte> save_registers() = 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,
|
||||
memory_violation_hook_callback callback) = 0;
|
||||
virtual emulator_hook* hook_memory_violation(uint64_t address, size_t size,
|
||||
memory_violation_hook_callback callback) = 0;
|
||||
|
||||
virtual emulator_hook* hook_memory_access(uint64_t address, size_t size, memory_operation filter,
|
||||
complex_memory_hook_callback callback) = 0;
|
||||
virtual emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) = 0;
|
||||
virtual emulator_hook* hook_memory_access(uint64_t address, size_t size, memory_operation filter,
|
||||
complex_memory_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_basic_block(basic_block_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 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)
|
||||
{
|
||||
return this->hook_memory_violation(0, std::numeric_limits<size_t>::max(), std::move(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));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
emulator_hook* hook_memory_execution(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::exec);
|
||||
}
|
||||
emulator_hook* hook_memory_execution(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::exec);
|
||||
}
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
this->perform_serialization(buffer, false);
|
||||
}
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
this->perform_serialization(buffer, false);
|
||||
}
|
||||
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
this->perform_deserialization(buffer, false);
|
||||
}
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
this->perform_deserialization(buffer, false);
|
||||
}
|
||||
|
||||
void save_snapshot()
|
||||
{
|
||||
utils::buffer_serializer serializer{};
|
||||
this->perform_serialization(serializer, true);
|
||||
this->last_snapshot_data_ = serializer.move_buffer();
|
||||
}
|
||||
void save_snapshot()
|
||||
{
|
||||
utils::buffer_serializer serializer{};
|
||||
this->perform_serialization(serializer, true);
|
||||
this->last_snapshot_data_ = serializer.move_buffer();
|
||||
}
|
||||
|
||||
void restore_snapshot()
|
||||
{
|
||||
if (this->last_snapshot_data_.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
void restore_snapshot()
|
||||
{
|
||||
if (this->last_snapshot_data_.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
utils::buffer_deserializer deserializer{this->last_snapshot_data_};
|
||||
this->perform_deserialization(deserializer, true);
|
||||
}
|
||||
utils::buffer_deserializer deserializer{this->last_snapshot_data_};
|
||||
this->perform_deserialization(deserializer, true);
|
||||
}
|
||||
|
||||
virtual bool has_violation() const = 0;
|
||||
virtual bool has_violation() const = 0;
|
||||
|
||||
private:
|
||||
std::vector<std::byte> last_snapshot_data_{};
|
||||
private:
|
||||
std::vector<std::byte> last_snapshot_data_{};
|
||||
|
||||
emulator_hook* hook_simple_memory_access(const uint64_t address, const size_t size,
|
||||
simple_memory_hook_callback callback, const memory_operation operation)
|
||||
{
|
||||
assert((static_cast<uint8_t>(operation) & (static_cast<uint8_t>(operation) - 1)) == 0);
|
||||
return this->hook_memory_access(address, size, operation,
|
||||
[c = std::move(callback)](const uint64_t a, const size_t s,
|
||||
const uint64_t value,
|
||||
memory_operation)
|
||||
{
|
||||
c(a, s, value);
|
||||
});
|
||||
}
|
||||
emulator_hook* hook_simple_memory_access(const uint64_t address, const size_t size,
|
||||
simple_memory_hook_callback callback, const memory_operation operation)
|
||||
{
|
||||
assert((static_cast<uint8_t>(operation) & (static_cast<uint8_t>(operation) - 1)) == 0);
|
||||
return this->hook_memory_access(address, size, operation,
|
||||
[c = std::move(callback)](const uint64_t a, const size_t s,
|
||||
const uint64_t value,
|
||||
memory_operation) { c(a, s, value); });
|
||||
}
|
||||
|
||||
void perform_serialization(utils::buffer_serializer& buffer, const bool is_snapshot) const
|
||||
{
|
||||
this->serialize_state(buffer, is_snapshot);
|
||||
this->serialize_memory_state(buffer, is_snapshot);
|
||||
}
|
||||
void perform_serialization(utils::buffer_serializer& buffer, const bool is_snapshot) const
|
||||
{
|
||||
this->serialize_state(buffer, is_snapshot);
|
||||
this->serialize_memory_state(buffer, is_snapshot);
|
||||
}
|
||||
|
||||
void perform_deserialization(utils::buffer_deserializer& buffer, const bool is_snapshot)
|
||||
{
|
||||
this->deserialize_state(buffer, is_snapshot);
|
||||
this->deserialize_memory_state(buffer, is_snapshot);
|
||||
}
|
||||
void perform_deserialization(utils::buffer_deserializer& buffer, const bool is_snapshot)
|
||||
{
|
||||
this->deserialize_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 deserialize_state(utils::buffer_deserializer& buffer, bool is_snapshot) = 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;
|
||||
};
|
||||
|
||||
@@ -10,541 +10,547 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr auto MIN_ALLOCATION_ADDRESS = 0x0000000000010000ULL;
|
||||
constexpr auto MAX_ALLOCATION_ADDRESS = 0x00007ffffffeffffULL;
|
||||
constexpr auto MIN_ALLOCATION_ADDRESS = 0x0000000000010000ULL;
|
||||
constexpr auto MAX_ALLOCATION_ADDRESS = 0x00007ffffffeffffULL;
|
||||
|
||||
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 (const auto split_point : split_points)
|
||||
{
|
||||
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 second_length = i->second.length - first_length;
|
||||
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 (const auto split_point : split_points)
|
||||
{
|
||||
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 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)
|
||||
{
|
||||
for (auto i = regions.begin(); i != regions.end();)
|
||||
{
|
||||
assert(i->second.length > 0);
|
||||
void merge_regions(memory_manager::committed_region_map& regions)
|
||||
{
|
||||
for (auto i = regions.begin(); i != regions.end();)
|
||||
{
|
||||
assert(i->second.length > 0);
|
||||
|
||||
auto next = i;
|
||||
std::advance(next, 1);
|
||||
auto next = i;
|
||||
std::advance(next, 1);
|
||||
|
||||
if (next == regions.end())
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (next == regions.end())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
assert(next->second.length > 0);
|
||||
assert(next->second.length > 0);
|
||||
|
||||
const auto end = i->first + i->second.length;
|
||||
assert(end <= next->first);
|
||||
const auto end = i->first + i->second.length;
|
||||
assert(end <= next->first);
|
||||
|
||||
if (end != next->first || i->second.pemissions != next->second.pemissions)
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
if (end != next->first || i->second.pemissions != next->second.pemissions)
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
i->second.length += next->second.length;
|
||||
regions.erase(next);
|
||||
}
|
||||
}
|
||||
i->second.length += next->second.length;
|
||||
regions.erase(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace utils
|
||||
{
|
||||
static void serialize(buffer_serializer& buffer, const memory_manager::committed_region& region)
|
||||
{
|
||||
buffer.write<uint64_t>(region.length);
|
||||
buffer.write(region.pemissions);
|
||||
}
|
||||
static void serialize(buffer_serializer& buffer, const memory_manager::committed_region& region)
|
||||
{
|
||||
buffer.write<uint64_t>(region.length);
|
||||
buffer.write(region.pemissions);
|
||||
}
|
||||
|
||||
static void deserialize(buffer_deserializer& buffer, memory_manager::committed_region& region)
|
||||
{
|
||||
region.length = static_cast<size_t>(buffer.read<uint64_t>());
|
||||
region.pemissions = buffer.read<memory_permission>();
|
||||
}
|
||||
static void deserialize(buffer_deserializer& buffer, memory_manager::committed_region& region)
|
||||
{
|
||||
region.length = static_cast<size_t>(buffer.read<uint64_t>());
|
||||
region.pemissions = buffer.read<memory_permission>();
|
||||
}
|
||||
|
||||
static void serialize(buffer_serializer& buffer, const memory_manager::reserved_region& region)
|
||||
{
|
||||
buffer.write(region.is_mmio);
|
||||
buffer.write<uint64_t>(region.length);
|
||||
buffer.write_map(region.committed_regions);
|
||||
}
|
||||
static void serialize(buffer_serializer& buffer, const memory_manager::reserved_region& region)
|
||||
{
|
||||
buffer.write(region.is_mmio);
|
||||
buffer.write<uint64_t>(region.length);
|
||||
buffer.write_map(region.committed_regions);
|
||||
}
|
||||
|
||||
static void deserialize(buffer_deserializer& buffer, memory_manager::reserved_region& region)
|
||||
{
|
||||
buffer.read(region.is_mmio);
|
||||
region.length = static_cast<size_t>(buffer.read<uint64_t>());
|
||||
buffer.read_map(region.committed_regions);
|
||||
}
|
||||
static void deserialize(buffer_deserializer& buffer, memory_manager::reserved_region& region)
|
||||
{
|
||||
buffer.read(region.is_mmio);
|
||||
region.length = static_cast<size_t>(buffer.read<uint64_t>());
|
||||
buffer.read_map(region.committed_regions);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (is_snapshot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> data{};
|
||||
std::vector<uint8_t> data{};
|
||||
|
||||
for (const auto& reserved_region : this->reserved_regions_)
|
||||
{
|
||||
if (reserved_region.second.is_mmio)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (const auto& reserved_region : this->reserved_regions_)
|
||||
{
|
||||
if (reserved_region.second.is_mmio)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& region : reserved_region.second.committed_regions)
|
||||
{
|
||||
data.resize(region.second.length);
|
||||
for (const auto& region : reserved_region.second.committed_regions)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (!is_snapshot)
|
||||
{
|
||||
for (const auto& reserved_region : this->reserved_regions_)
|
||||
{
|
||||
for (const auto& region : reserved_region.second.committed_regions)
|
||||
{
|
||||
this->unmap_memory(region.first, region.second.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!is_snapshot)
|
||||
{
|
||||
for (const auto& reserved_region : this->reserved_regions_)
|
||||
{
|
||||
for (const auto& region : reserved_region.second.committed_regions)
|
||||
{
|
||||
this->unmap_memory(region.first, region.second.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer.read_map(this->reserved_regions_);
|
||||
buffer.read_map(this->reserved_regions_);
|
||||
|
||||
if (is_snapshot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (is_snapshot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> data{};
|
||||
std::vector<uint8_t> data{};
|
||||
|
||||
for (auto i = this->reserved_regions_.begin(); i != this->reserved_regions_.end();)
|
||||
{
|
||||
auto& reserved_region = i->second;
|
||||
if (reserved_region.is_mmio)
|
||||
{
|
||||
i = this->reserved_regions_.erase(i);
|
||||
continue;
|
||||
}
|
||||
for (auto i = this->reserved_regions_.begin(); i != this->reserved_regions_.end();)
|
||||
{
|
||||
auto& reserved_region = i->second;
|
||||
if (reserved_region.is_mmio)
|
||||
{
|
||||
i = this->reserved_regions_.erase(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
++i;
|
||||
++i;
|
||||
|
||||
for (const auto& region : reserved_region.committed_regions)
|
||||
{
|
||||
data.resize(region.second.length);
|
||||
for (const auto& region : reserved_region.committed_regions)
|
||||
{
|
||||
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->write_memory(region.first, data.data(), region.second.length);
|
||||
}
|
||||
}
|
||||
this->map_memory(region.first, region.second.length, region.second.pemissions);
|
||||
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,
|
||||
memory_permission* old_permissions)
|
||||
{
|
||||
const auto entry = this->find_reserved_region(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto entry = this->find_reserved_region(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto end = address + size;
|
||||
const auto region_end = entry->first + entry->second.length;
|
||||
const auto end = address + size;
|
||||
const auto region_end = entry->first + entry->second.length;
|
||||
|
||||
if (region_end < end)
|
||||
{
|
||||
throw std::runtime_error("Cross region protect not supported yet!");
|
||||
}
|
||||
if (region_end < end)
|
||||
{
|
||||
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;
|
||||
split_regions(committed_regions, {address, end});
|
||||
auto& committed_regions = entry->second.committed_regions;
|
||||
split_regions(committed_regions, {address, end});
|
||||
|
||||
for (auto& sub_region : committed_regions)
|
||||
{
|
||||
if (sub_region.first >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
for (auto& sub_region : committed_regions)
|
||||
{
|
||||
if (sub_region.first >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const auto sub_region_end = sub_region.first + sub_region.second.length;
|
||||
if (sub_region.first >= address && sub_region_end <= end)
|
||||
{
|
||||
if (!old_first_permissions.has_value())
|
||||
{
|
||||
old_first_permissions = sub_region.second.pemissions;
|
||||
}
|
||||
const auto sub_region_end = sub_region.first + sub_region.second.length;
|
||||
if (sub_region.first >= address && sub_region_end <= end)
|
||||
{
|
||||
if (!old_first_permissions.has_value())
|
||||
{
|
||||
old_first_permissions = sub_region.second.pemissions;
|
||||
}
|
||||
|
||||
this->apply_memory_protection(sub_region.first, sub_region.second.length, permissions);
|
||||
sub_region.second.pemissions = permissions;
|
||||
}
|
||||
}
|
||||
this->apply_memory_protection(sub_region.first, sub_region.second.length, permissions);
|
||||
sub_region.second.pemissions = permissions;
|
||||
}
|
||||
}
|
||||
|
||||
if (old_permissions)
|
||||
{
|
||||
*old_permissions = old_first_permissions.value_or(memory_permission::none);
|
||||
}
|
||||
if (old_permissions)
|
||||
{
|
||||
*old_permissions = old_first_permissions.value_or(memory_permission::none);
|
||||
}
|
||||
|
||||
merge_regions(committed_regions);
|
||||
return true;
|
||||
merge_regions(committed_regions);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool memory_manager::allocate_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb,
|
||||
mmio_write_callback write_cb)
|
||||
{
|
||||
if (this->overlaps_reserved_region(address, size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (this->overlaps_reserved_region(address, size))
|
||||
{
|
||||
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,
|
||||
reserved_region{
|
||||
.length = size,
|
||||
.is_mmio = true,
|
||||
}).first;
|
||||
const auto entry = this->reserved_regions_
|
||||
.try_emplace(address,
|
||||
reserved_region{
|
||||
.length = size,
|
||||
.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,
|
||||
const bool reserve_only)
|
||||
{
|
||||
if (this->overlaps_reserved_region(address, size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (this->overlaps_reserved_region(address, size))
|
||||
{
|
||||
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)
|
||||
{
|
||||
this->map_memory(address, size, permissions);
|
||||
entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write};
|
||||
}
|
||||
if (!reserve_only)
|
||||
{
|
||||
this->map_memory(address, size, permissions);
|
||||
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)
|
||||
{
|
||||
const auto entry = this->find_reserved_region(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto entry = this->find_reserved_region(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto end = address + size;
|
||||
const auto region_end = entry->first + entry->second.length;
|
||||
const auto end = address + size;
|
||||
const auto region_end = entry->first + entry->second.length;
|
||||
|
||||
if (region_end < end)
|
||||
{
|
||||
throw std::runtime_error("Cross region commit not supported yet!");
|
||||
}
|
||||
if (region_end < end)
|
||||
{
|
||||
throw std::runtime_error("Cross region commit not supported yet!");
|
||||
}
|
||||
|
||||
auto& committed_regions = entry->second.committed_regions;
|
||||
split_regions(committed_regions, {address, end});
|
||||
auto& committed_regions = entry->second.committed_regions;
|
||||
split_regions(committed_regions, {address, end});
|
||||
|
||||
uint64_t last_region_start{};
|
||||
const committed_region* last_region{nullptr};
|
||||
uint64_t last_region_start{};
|
||||
const committed_region* last_region{nullptr};
|
||||
|
||||
for (auto& sub_region : committed_regions)
|
||||
{
|
||||
if (sub_region.first >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
for (auto& sub_region : committed_regions)
|
||||
{
|
||||
if (sub_region.first >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const auto sub_region_end = sub_region.first + sub_region.second.length;
|
||||
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_length = sub_region.first - map_start;
|
||||
const auto sub_region_end = sub_region.first + sub_region.second.length;
|
||||
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_length = sub_region.first - map_start;
|
||||
|
||||
if (map_length > 0)
|
||||
{
|
||||
this->map_memory(map_start, map_length, permissions);
|
||||
committed_regions[map_start] = committed_region{map_length, permissions};
|
||||
}
|
||||
if (map_length > 0)
|
||||
{
|
||||
this->map_memory(map_start, map_length, permissions);
|
||||
committed_regions[map_start] = committed_region{map_length, permissions};
|
||||
}
|
||||
|
||||
last_region_start = sub_region.first;
|
||||
last_region = &sub_region.second;
|
||||
}
|
||||
}
|
||||
last_region_start = sub_region.first;
|
||||
last_region = &sub_region.second;
|
||||
}
|
||||
}
|
||||
|
||||
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_length = end - map_start;
|
||||
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_length = end - map_start;
|
||||
|
||||
this->map_memory(map_start, map_length, permissions);
|
||||
committed_regions[map_start] = committed_region{map_length, permissions};
|
||||
}
|
||||
this->map_memory(map_start, map_length, permissions);
|
||||
committed_regions[map_start] = committed_region{map_length, permissions};
|
||||
}
|
||||
|
||||
merge_regions(committed_regions);
|
||||
return true;
|
||||
merge_regions(committed_regions);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool memory_manager::decommit_memory(const uint64_t address, const size_t size)
|
||||
{
|
||||
const auto entry = this->find_reserved_region(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto entry = this->find_reserved_region(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry->second.is_mmio)
|
||||
{
|
||||
throw std::runtime_error("Not allowed to decommit MMIO!");
|
||||
}
|
||||
if (entry->second.is_mmio)
|
||||
{
|
||||
throw std::runtime_error("Not allowed to decommit MMIO!");
|
||||
}
|
||||
|
||||
const auto end = address + size;
|
||||
const auto region_end = entry->first + entry->second.length;
|
||||
const auto end = address + size;
|
||||
const auto region_end = entry->first + entry->second.length;
|
||||
|
||||
if (region_end < end)
|
||||
{
|
||||
throw std::runtime_error("Cross region decommit not supported yet!");
|
||||
}
|
||||
if (region_end < end)
|
||||
{
|
||||
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();)
|
||||
{
|
||||
if (i->first >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
for (auto i = committed_regions.begin(); i != committed_regions.end();)
|
||||
{
|
||||
if (i->first >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const auto sub_region_end = i->first + i->second.length;
|
||||
if (i->first >= address && sub_region_end <= end)
|
||||
{
|
||||
this->unmap_memory(i->first, i->second.length);
|
||||
i = committed_regions.erase(i);
|
||||
continue;
|
||||
}
|
||||
const auto sub_region_end = i->first + i->second.length;
|
||||
if (i->first >= address && sub_region_end <= end)
|
||||
{
|
||||
this->unmap_memory(i->first, i->second.length);
|
||||
i = committed_regions.erase(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool memory_manager::release_memory(const uint64_t address, size_t size)
|
||||
{
|
||||
const auto entry = this->reserved_regions_.find(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto entry = this->reserved_regions_.find(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!size)
|
||||
{
|
||||
size = entry->second.length;
|
||||
}
|
||||
if (!size)
|
||||
{
|
||||
size = entry->second.length;
|
||||
}
|
||||
|
||||
if (size > entry->second.length)
|
||||
{
|
||||
throw std::runtime_error("Cross region release not supported yet!");
|
||||
}
|
||||
if (size > entry->second.length)
|
||||
{
|
||||
throw std::runtime_error("Cross region release not supported yet!");
|
||||
}
|
||||
|
||||
const auto end = address + size;
|
||||
auto& committed_regions = entry->second.committed_regions;
|
||||
const auto end = address + size;
|
||||
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();)
|
||||
{
|
||||
if (i->first >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
for (auto i = committed_regions.begin(); i != committed_regions.end();)
|
||||
{
|
||||
if (i->first >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const auto sub_region_end = i->first + i->second.length;
|
||||
if (i->first >= address && sub_region_end <= end)
|
||||
{
|
||||
this->unmap_memory(i->first, i->second.length);
|
||||
i = committed_regions.erase(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
const auto sub_region_end = i->first + i->second.length;
|
||||
if (i->first >= address && sub_region_end <= end)
|
||||
{
|
||||
this->unmap_memory(i->first, i->second.length);
|
||||
i = committed_regions.erase(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
entry->second.length -= size;
|
||||
if (entry->second.length > 0)
|
||||
{
|
||||
this->reserved_regions_[address + size] = std::move(entry->second);
|
||||
}
|
||||
entry->second.length -= size;
|
||||
if (entry->second.length > 0)
|
||||
{
|
||||
this->reserved_regions_[address + size] = std::move(entry->second);
|
||||
}
|
||||
|
||||
this->reserved_regions_.erase(entry);
|
||||
return true;
|
||||
this->reserved_regions_.erase(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t memory_manager::find_free_allocation_base(const size_t size, const uint64_t start) const
|
||||
{
|
||||
uint64_t start_address =
|
||||
std::max(MIN_ALLOCATION_ADDRESS, start ? start : 0x100000000ULL);
|
||||
uint64_t start_address = std::max(MIN_ALLOCATION_ADDRESS, start ? start : 0x100000000ULL);
|
||||
|
||||
for (const auto& region : this->reserved_regions_)
|
||||
{
|
||||
const auto region_end = region.first + region.second.length;
|
||||
if (region_end < start_address)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (const auto& region : this->reserved_regions_)
|
||||
{
|
||||
const auto region_end = region.first + region.second.length;
|
||||
if (region_end < start_address)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!regions_with_length_intersect(start_address, size, region.first, region.second.length))
|
||||
{
|
||||
return start_address;
|
||||
}
|
||||
if (!regions_with_length_intersect(start_address, size, region.first, region.second.length))
|
||||
{
|
||||
return start_address;
|
||||
}
|
||||
|
||||
start_address = page_align_up(region_end);
|
||||
}
|
||||
start_address = page_align_up(region_end);
|
||||
}
|
||||
|
||||
if (start_address + size <= MAX_ALLOCATION_ADDRESS)
|
||||
{
|
||||
return start_address;
|
||||
}
|
||||
if (start_address + size <= MAX_ALLOCATION_ADDRESS)
|
||||
{
|
||||
return start_address;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
region_info memory_manager::get_region_info(const uint64_t address)
|
||||
{
|
||||
region_info result{};
|
||||
result.start = MIN_ALLOCATION_ADDRESS;
|
||||
result.length = MAX_ALLOCATION_ADDRESS - result.start;
|
||||
result.permissions = memory_permission::none;
|
||||
result.allocation_base = {};
|
||||
result.allocation_length = result.length;
|
||||
result.is_committed = false;
|
||||
result.is_reserved = false;
|
||||
region_info result{};
|
||||
result.start = MIN_ALLOCATION_ADDRESS;
|
||||
result.length = MAX_ALLOCATION_ADDRESS - result.start;
|
||||
result.permissions = memory_permission::none;
|
||||
result.allocation_base = {};
|
||||
result.allocation_length = result.length;
|
||||
result.is_committed = false;
|
||||
result.is_reserved = false;
|
||||
|
||||
if (this->reserved_regions_.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (this->reserved_regions_.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
auto upper_bound = this->reserved_regions_.upper_bound(address);
|
||||
if (upper_bound == this->reserved_regions_.begin())
|
||||
{
|
||||
result.length = upper_bound->first - result.start;
|
||||
return result;
|
||||
}
|
||||
auto upper_bound = this->reserved_regions_.upper_bound(address);
|
||||
if (upper_bound == this->reserved_regions_.begin())
|
||||
{
|
||||
result.length = upper_bound->first - result.start;
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto entry = --upper_bound;
|
||||
const auto lower_end = entry->first + entry->second.length;
|
||||
if (lower_end <= address)
|
||||
{
|
||||
result.start = lower_end;
|
||||
result.length = MAX_ALLOCATION_ADDRESS - result.start;
|
||||
return result;
|
||||
}
|
||||
const auto entry = --upper_bound;
|
||||
const auto lower_end = entry->first + entry->second.length;
|
||||
if (lower_end <= address)
|
||||
{
|
||||
result.start = lower_end;
|
||||
result.length = MAX_ALLOCATION_ADDRESS - result.start;
|
||||
return result;
|
||||
}
|
||||
|
||||
// We have a reserved region
|
||||
const auto& reserved_region = entry->second;
|
||||
const auto& committed_regions = reserved_region.committed_regions;
|
||||
// We have a reserved region
|
||||
const auto& reserved_region = entry->second;
|
||||
const auto& committed_regions = reserved_region.committed_regions;
|
||||
|
||||
result.is_reserved = true;
|
||||
result.allocation_base = entry->first;
|
||||
result.allocation_length = reserved_region.length;
|
||||
result.start = result.allocation_base;
|
||||
result.length = result.allocation_length;
|
||||
result.is_reserved = true;
|
||||
result.allocation_base = entry->first;
|
||||
result.allocation_length = reserved_region.length;
|
||||
result.start = result.allocation_base;
|
||||
result.length = result.allocation_length;
|
||||
|
||||
if (committed_regions.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (committed_regions.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
auto committed_bound = committed_regions.upper_bound(address);
|
||||
if (committed_bound == committed_regions.begin())
|
||||
{
|
||||
result.length = committed_bound->first - result.start;
|
||||
return result;
|
||||
}
|
||||
auto committed_bound = committed_regions.upper_bound(address);
|
||||
if (committed_bound == committed_regions.begin())
|
||||
{
|
||||
result.length = committed_bound->first - result.start;
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto committed_entry = --committed_bound;
|
||||
const auto committed_lower_end = committed_entry->first + committed_entry->second.length;
|
||||
if (committed_lower_end <= address)
|
||||
{
|
||||
result.start = committed_lower_end;
|
||||
result.length = lower_end - result.start;
|
||||
return result;
|
||||
}
|
||||
const auto committed_entry = --committed_bound;
|
||||
const auto committed_lower_end = committed_entry->first + committed_entry->second.length;
|
||||
if (committed_lower_end <= address)
|
||||
{
|
||||
result.start = committed_lower_end;
|
||||
result.length = lower_end - result.start;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.is_committed = true;
|
||||
result.start = committed_entry->first;
|
||||
result.length = committed_entry->second.length;
|
||||
result.permissions = committed_entry->second.pemissions;
|
||||
result.is_committed = true;
|
||||
result.start = committed_entry->first;
|
||||
result.length = committed_entry->second.length;
|
||||
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)
|
||||
{
|
||||
if (this->reserved_regions_.empty())
|
||||
{
|
||||
return this->reserved_regions_.end();
|
||||
}
|
||||
if (this->reserved_regions_.empty())
|
||||
{
|
||||
return this->reserved_regions_.end();
|
||||
}
|
||||
|
||||
auto upper_bound = this->reserved_regions_.upper_bound(address);
|
||||
if (upper_bound == this->reserved_regions_.begin())
|
||||
{
|
||||
return this->reserved_regions_.end();
|
||||
}
|
||||
auto upper_bound = this->reserved_regions_.upper_bound(address);
|
||||
if (upper_bound == this->reserved_regions_.begin())
|
||||
{
|
||||
return this->reserved_regions_.end();
|
||||
}
|
||||
|
||||
const auto entry = --upper_bound;
|
||||
if (entry->first + entry->second.length <= address)
|
||||
{
|
||||
return this->reserved_regions_.end();
|
||||
}
|
||||
const auto entry = --upper_bound;
|
||||
if (entry->first + entry->second.length <= address)
|
||||
{
|
||||
return this->reserved_regions_.end();
|
||||
}
|
||||
|
||||
return entry;
|
||||
return entry;
|
||||
}
|
||||
|
||||
bool memory_manager::overlaps_reserved_region(const uint64_t address, const size_t size) const
|
||||
{
|
||||
for (const auto& region : this->reserved_regions_)
|
||||
{
|
||||
if (regions_with_length_intersect(address, size, region.first, region.second.length))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (const auto& region : this->reserved_regions_)
|
||||
{
|
||||
if (regions_with_length_intersect(address, size, region.first, region.second.length))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
|
||||
struct region_info : basic_memory_region
|
||||
{
|
||||
uint64_t allocation_base{};
|
||||
size_t allocation_length{};
|
||||
bool is_reserved{};
|
||||
bool is_committed{};
|
||||
uint64_t allocation_base{};
|
||||
size_t allocation_length{};
|
||||
bool is_reserved{};
|
||||
bool is_committed{};
|
||||
};
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
struct committed_region
|
||||
{
|
||||
size_t length{};
|
||||
memory_permission pemissions{};
|
||||
};
|
||||
public:
|
||||
struct committed_region
|
||||
{
|
||||
size_t length{};
|
||||
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
|
||||
{
|
||||
size_t length{};
|
||||
committed_region_map committed_regions{};
|
||||
bool is_mmio{false};
|
||||
};
|
||||
struct reserved_region
|
||||
{
|
||||
size_t length{};
|
||||
committed_region_map committed_regions{};
|
||||
bool is_mmio{false};
|
||||
};
|
||||
|
||||
virtual ~memory_manager() = default;
|
||||
virtual ~memory_manager() = default;
|
||||
|
||||
template <typename T>
|
||||
T read_memory(const uint64_t address) const
|
||||
{
|
||||
T value{};
|
||||
this->read_memory(address, &value, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
template <typename T>
|
||||
T read_memory(const uint64_t address) const
|
||||
{
|
||||
T value{};
|
||||
this->read_memory(address, &value, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T read_memory(const void* address) const
|
||||
{
|
||||
return this->read_memory<T>(reinterpret_cast<uint64_t>(address));
|
||||
}
|
||||
template <typename T>
|
||||
T read_memory(const void* address) const
|
||||
{
|
||||
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> data{};
|
||||
data.resize(size);
|
||||
std::vector<std::byte> read_memory(const uint64_t address, const size_t size) const
|
||||
{
|
||||
std::vector<std::byte> data{};
|
||||
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
|
||||
{
|
||||
return this->read_memory(reinterpret_cast<uint64_t>(address), size);
|
||||
}
|
||||
std::vector<std::byte> read_memory(const void* address, const size_t size) const
|
||||
{
|
||||
return this->read_memory(reinterpret_cast<uint64_t>(address), size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_memory(const uint64_t address, const T& value)
|
||||
{
|
||||
this->write_memory(address, &value, sizeof(value));
|
||||
}
|
||||
template <typename T>
|
||||
void write_memory(const uint64_t address, const T& value)
|
||||
{
|
||||
this->write_memory(address, &value, sizeof(value));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_memory(void* address, const T& value)
|
||||
{
|
||||
this->write_memory(reinterpret_cast<uint64_t>(address), &value, sizeof(value));
|
||||
}
|
||||
template <typename T>
|
||||
void write_memory(void* address, const T& value)
|
||||
{
|
||||
this->write_memory(reinterpret_cast<uint64_t>(address), &value, sizeof(value));
|
||||
}
|
||||
|
||||
void write_memory(void* address, const void* data, const size_t size)
|
||||
{
|
||||
this->write_memory(reinterpret_cast<uint64_t>(address), data, size);
|
||||
}
|
||||
void write_memory(void* address, const void* data, const size_t 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 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 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;
|
||||
|
||||
bool protect_memory(uint64_t address, size_t size, memory_permission permissions,
|
||||
memory_permission* old_permissions = nullptr);
|
||||
bool protect_memory(uint64_t address, size_t size, memory_permission permissions,
|
||||
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_memory(uint64_t address, size_t size, memory_permission permissions,
|
||||
bool reserve_only = false);
|
||||
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 reserve_only = false);
|
||||
|
||||
bool commit_memory(uint64_t address, size_t size, memory_permission permissions);
|
||||
bool decommit_memory(uint64_t address, size_t size);
|
||||
bool commit_memory(uint64_t address, size_t size, memory_permission permissions);
|
||||
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)
|
||||
{
|
||||
const auto allocation_base = this->find_free_allocation_base(size);
|
||||
if (!allocate_memory(allocation_base, size, permissions, reserve_only))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
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);
|
||||
if (!allocate_memory(allocation_base, size, permissions, reserve_only))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return allocation_base;
|
||||
}
|
||||
return allocation_base;
|
||||
}
|
||||
|
||||
private:
|
||||
using reserved_region_map = std::map<uint64_t, reserved_region>;
|
||||
reserved_region_map reserved_regions_{};
|
||||
private:
|
||||
using reserved_region_map = std::map<uint64_t, reserved_region>;
|
||||
reserved_region_map reserved_regions_{};
|
||||
|
||||
reserved_region_map::iterator find_reserved_region(uint64_t address);
|
||||
bool overlaps_reserved_region(uint64_t address, size_t size) const;
|
||||
reserved_region_map::iterator find_reserved_region(uint64_t address);
|
||||
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_memory(uint64_t address, size_t size, memory_permission permissions) = 0;
|
||||
virtual void unmap_memory(uint64_t address, size_t size) = 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 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:
|
||||
void serialize_memory_state(utils::buffer_serializer& buffer, bool is_snapshot) const;
|
||||
void deserialize_memory_state(utils::buffer_deserializer& buffer, bool is_snapshot);
|
||||
protected:
|
||||
void serialize_memory_state(utils::buffer_serializer& buffer, bool is_snapshot) const;
|
||||
void deserialize_memory_state(utils::buffer_deserializer& buffer, bool is_snapshot);
|
||||
};
|
||||
|
||||
@@ -3,62 +3,52 @@
|
||||
|
||||
enum class memory_permission : uint8_t
|
||||
{
|
||||
none = 0,
|
||||
read = 1 << 0,
|
||||
write = 1 << 1,
|
||||
exec = 1 << 2,
|
||||
read_write = read | write,
|
||||
all = read | write | exec
|
||||
none = 0,
|
||||
read = 1 << 0,
|
||||
write = 1 << 1,
|
||||
exec = 1 << 2,
|
||||
read_write = read | write,
|
||||
all = read | write | exec
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
inline constexpr memory_permission
|
||||
operator&(const memory_permission x, const memory_permission y)
|
||||
inline constexpr memory_permission operator&(const memory_permission x, const memory_permission y)
|
||||
{
|
||||
return static_cast<memory_permission>
|
||||
(static_cast<uint8_t>(x) & static_cast<uint8_t>(y));
|
||||
return static_cast<memory_permission>(static_cast<uint8_t>(x) & static_cast<uint8_t>(y));
|
||||
}
|
||||
|
||||
inline constexpr memory_permission
|
||||
operator|(const memory_permission x, const memory_permission y)
|
||||
inline constexpr memory_permission operator|(const memory_permission x, const memory_permission y)
|
||||
{
|
||||
return static_cast<memory_permission>
|
||||
(static_cast<uint8_t>(x) | static_cast<uint8_t>(y));
|
||||
return static_cast<memory_permission>(static_cast<uint8_t>(x) | static_cast<uint8_t>(y));
|
||||
}
|
||||
|
||||
inline constexpr memory_permission
|
||||
operator^(const memory_permission x, const memory_permission y)
|
||||
inline constexpr memory_permission operator^(const memory_permission x, const memory_permission y)
|
||||
{
|
||||
return static_cast<memory_permission>
|
||||
(static_cast<uint8_t>(x) ^ static_cast<uint8_t>(y));
|
||||
return static_cast<memory_permission>(static_cast<uint8_t>(x) ^ static_cast<uint8_t>(y));
|
||||
}
|
||||
|
||||
inline constexpr memory_permission
|
||||
operator~(memory_permission x)
|
||||
inline constexpr memory_permission 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&
|
||||
operator&=(memory_permission& x, const memory_permission y)
|
||||
inline memory_permission& operator&=(memory_permission& x, const memory_permission y)
|
||||
{
|
||||
x = x & y;
|
||||
return x;
|
||||
x = x & y;
|
||||
return x;
|
||||
}
|
||||
|
||||
inline memory_permission&
|
||||
operator|=(memory_permission& x, const memory_permission y)
|
||||
inline memory_permission& operator|=(memory_permission& x, const memory_permission y)
|
||||
{
|
||||
x = x | y;
|
||||
return x;
|
||||
x = x | y;
|
||||
return x;
|
||||
}
|
||||
|
||||
inline memory_permission&
|
||||
operator^=(memory_permission& x, const memory_permission y)
|
||||
inline memory_permission& operator^=(memory_permission& x, const memory_permission y)
|
||||
{
|
||||
x = x ^ y;
|
||||
return x;
|
||||
x = x ^ y;
|
||||
return x;
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
|
||||
struct basic_memory_region
|
||||
{
|
||||
uint64_t start{};
|
||||
size_t length{};
|
||||
memory_permission permissions{};
|
||||
uint64_t start{};
|
||||
size_t length{};
|
||||
memory_permission permissions{};
|
||||
};
|
||||
|
||||
struct memory_region : basic_memory_region
|
||||
{
|
||||
bool committed{};
|
||||
bool committed{};
|
||||
};
|
||||
|
||||
@@ -3,52 +3,52 @@
|
||||
|
||||
class scoped_hook
|
||||
{
|
||||
public:
|
||||
scoped_hook() = default;
|
||||
public:
|
||||
scoped_hook() = default;
|
||||
|
||||
scoped_hook(emulator& emu, emulator_hook* hook)
|
||||
: emu_(&emu)
|
||||
, hook_(hook)
|
||||
{
|
||||
}
|
||||
scoped_hook(emulator& emu, emulator_hook* hook)
|
||||
: emu_(&emu),
|
||||
hook_(hook)
|
||||
{
|
||||
}
|
||||
|
||||
~scoped_hook()
|
||||
{
|
||||
this->remove();
|
||||
}
|
||||
~scoped_hook()
|
||||
{
|
||||
this->remove();
|
||||
}
|
||||
|
||||
scoped_hook(const scoped_hook&) = delete;
|
||||
scoped_hook& operator=(const scoped_hook&) = delete;
|
||||
scoped_hook(const scoped_hook&) = delete;
|
||||
scoped_hook& operator=(const scoped_hook&) = delete;
|
||||
|
||||
scoped_hook(scoped_hook&& obj) noexcept
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
scoped_hook(scoped_hook&& obj) noexcept
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
|
||||
scoped_hook& operator=(scoped_hook&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->remove();
|
||||
this->emu_ = obj.emu_;
|
||||
this->hook_ = obj.hook_;
|
||||
scoped_hook& operator=(scoped_hook&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->remove();
|
||||
this->emu_ = obj.emu_;
|
||||
this->hook_ = obj.hook_;
|
||||
|
||||
obj.hook_ = {};
|
||||
}
|
||||
obj.hook_ = {};
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void remove()
|
||||
{
|
||||
if (this->hook_)
|
||||
{
|
||||
this->emu_->delete_hook(this->hook_);
|
||||
this->hook_ = {};
|
||||
}
|
||||
}
|
||||
void remove()
|
||||
{
|
||||
if (this->hook_)
|
||||
{
|
||||
this->emu_->delete_hook(this->hook_);
|
||||
this->hook_ = {};
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
emulator* emu_{};
|
||||
emulator_hook* hook_{};
|
||||
private:
|
||||
emulator* emu_{};
|
||||
emulator_hook* hook_{};
|
||||
};
|
||||
|
||||
@@ -11,488 +11,483 @@
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class buffer_serializer;
|
||||
class buffer_deserializer;
|
||||
class buffer_serializer;
|
||||
class buffer_deserializer;
|
||||
|
||||
template <typename T>
|
||||
concept Serializable = requires(T a, const T ac, buffer_serializer& serializer, buffer_deserializer& deserializer)
|
||||
{
|
||||
{ ac.serialize(serializer) } -> std::same_as<void>;
|
||||
{ a.deserialize(deserializer) } -> std::same_as<void>;
|
||||
};
|
||||
template <typename T>
|
||||
concept Serializable = requires(T a, const T ac, buffer_serializer& serializer, buffer_deserializer& deserializer) {
|
||||
{ ac.serialize(serializer) } -> std::same_as<void>;
|
||||
{ a.deserialize(deserializer) } -> std::same_as<void>;
|
||||
};
|
||||
|
||||
/* Use concept instead, to prevent overhead of virtual function calls
|
||||
struct serializable
|
||||
{
|
||||
virtual ~serializable() = default;
|
||||
virtual void serialize(buffer_serializer& buffer) const = 0;
|
||||
virtual void deserialize(buffer_deserializer& buffer) = 0;
|
||||
};
|
||||
*/
|
||||
/* Use concept instead, to prevent overhead of virtual function calls
|
||||
struct serializable
|
||||
{
|
||||
virtual ~serializable() = default;
|
||||
virtual void serialize(buffer_serializer& buffer) const = 0;
|
||||
virtual void deserialize(buffer_deserializer& buffer) = 0;
|
||||
};
|
||||
*/
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename, typename = void>
|
||||
struct has_serialize_function : std::false_type
|
||||
{
|
||||
};
|
||||
namespace detail
|
||||
{
|
||||
template <typename, typename = void>
|
||||
struct has_serialize_function : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct has_serialize_function<T, std::void_t<decltype(serialize(std::declval<buffer_serializer&>(),
|
||||
std::declval<const std::remove_cvref_t<T>&>())
|
||||
)>>
|
||||
: std::true_type
|
||||
{
|
||||
};
|
||||
template <typename T>
|
||||
struct has_serialize_function<T,
|
||||
std::void_t<decltype(serialize(std::declval<buffer_serializer&>(),
|
||||
std::declval<const std::remove_cvref_t<T>&>()))>>
|
||||
: std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename, typename = void>
|
||||
struct has_deserialize_function : std::false_type
|
||||
{
|
||||
};
|
||||
template <typename, typename = void>
|
||||
struct has_deserialize_function : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct has_deserialize_function<T, std::void_t<decltype(deserialize(
|
||||
std::declval<buffer_deserializer&>(),
|
||||
std::declval<std::remove_cvref_t<T>&>()))>>
|
||||
: std::true_type
|
||||
{
|
||||
};
|
||||
template <typename T>
|
||||
struct has_deserialize_function<T, std::void_t<decltype(deserialize(std::declval<buffer_deserializer&>(),
|
||||
std::declval<std::remove_cvref_t<T>&>()))>>
|
||||
: std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct has_deserializer_constructor
|
||||
: std::bool_constant<std::is_constructible_v<T, buffer_deserializer&>>
|
||||
{
|
||||
};
|
||||
}
|
||||
template <typename T>
|
||||
struct has_deserializer_constructor : std::bool_constant<std::is_constructible_v<T, buffer_deserializer&>>
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
class buffer_deserializer
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
buffer_deserializer(const std::span<T> buffer, bool no_debugging = false)
|
||||
: no_debugging_(no_debugging)
|
||||
, 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");
|
||||
}
|
||||
class buffer_deserializer
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
buffer_deserializer(const std::span<T> buffer, bool no_debugging = false)
|
||||
: no_debugging_(no_debugging),
|
||||
buffer_(reinterpret_cast<const std::byte*>(buffer.data()), buffer.size() * sizeof(T))
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Type must be trivially copyable");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
buffer_deserializer(const std::vector<T>& buffer, bool no_debugging = false)
|
||||
: buffer_deserializer(std::span(buffer), no_debugging)
|
||||
{
|
||||
}
|
||||
template <typename T>
|
||||
buffer_deserializer(const std::vector<T>& buffer, bool no_debugging = false)
|
||||
: 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
|
||||
const uint64_t real_old_size = this->offset_;
|
||||
(void)real_old_size;
|
||||
const uint64_t real_old_size = this->offset_;
|
||||
(void)real_old_size;
|
||||
#endif
|
||||
|
||||
if (this->offset_ + length > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
if (this->offset_ + length > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
|
||||
const std::span result(this->buffer_.data() + this->offset_, length);
|
||||
this->offset_ += length;
|
||||
const std::span result(this->buffer_.data() + this->offset_, length);
|
||||
this->offset_ += length;
|
||||
|
||||
(void)this->no_debugging_;
|
||||
(void)this->no_debugging_;
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (!this->no_debugging_)
|
||||
{
|
||||
uint64_t old_size{};
|
||||
if (this->offset_ + sizeof(old_size) > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
if (!this->no_debugging_)
|
||||
{
|
||||
uint64_t old_size{};
|
||||
if (this->offset_ + sizeof(old_size) > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
|
||||
memcpy(&old_size, this->buffer_.data() + this->offset_, sizeof(old_size));
|
||||
if (old_size != real_old_size)
|
||||
{
|
||||
throw std::runtime_error("Reading from serialized buffer mismatches written data!");
|
||||
}
|
||||
memcpy(&old_size, this->buffer_.data() + this->offset_, sizeof(old_size));
|
||||
if (old_size != real_old_size)
|
||||
{
|
||||
throw std::runtime_error("Reading from serialized buffer mismatches written data!");
|
||||
}
|
||||
|
||||
this->offset_ += sizeof(old_size);
|
||||
}
|
||||
this->offset_ += sizeof(old_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void read(void* data, const size_t length)
|
||||
{
|
||||
const auto span = this->read_data(length);
|
||||
memcpy(data, span.data(), length);
|
||||
}
|
||||
void read(void* data, const size_t length)
|
||||
{
|
||||
const auto span = this->read_data(length);
|
||||
memcpy(data, span.data(), length);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read(T& object)
|
||||
{
|
||||
constexpr auto is_trivially_copyable = std::is_trivially_copyable_v<T>;
|
||||
template <typename T>
|
||||
void read(T& object)
|
||||
{
|
||||
constexpr auto is_trivially_copyable = std::is_trivially_copyable_v<T>;
|
||||
|
||||
if constexpr (Serializable<T>)
|
||||
{
|
||||
object.deserialize(*this);
|
||||
}
|
||||
else if constexpr (detail::has_deserialize_function<T>::value)
|
||||
{
|
||||
deserialize(*this, object);
|
||||
}
|
||||
else if constexpr (is_trivially_copyable)
|
||||
{
|
||||
union
|
||||
{
|
||||
T* type_{};
|
||||
void* void_;
|
||||
} pointers;
|
||||
if constexpr (Serializable<T>)
|
||||
{
|
||||
object.deserialize(*this);
|
||||
}
|
||||
else if constexpr (detail::has_deserialize_function<T>::value)
|
||||
{
|
||||
deserialize(*this, object);
|
||||
}
|
||||
else if constexpr (is_trivially_copyable)
|
||||
{
|
||||
union
|
||||
{
|
||||
T* type_{};
|
||||
void* void_;
|
||||
} pointers;
|
||||
|
||||
pointers.type_ = &object;
|
||||
pointers.type_ = &object;
|
||||
|
||||
this->read(pointers.void_, sizeof(object));
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(!is_trivially_copyable, "Key must be trivially copyable or implement serializable!");
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
this->read(pointers.void_, sizeof(object));
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(!is_trivially_copyable, "Key must be trivially copyable or implement serializable!");
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T read()
|
||||
{
|
||||
auto object = this->construct_object<T>();
|
||||
this->read(object);
|
||||
return object;
|
||||
}
|
||||
template <typename T>
|
||||
T read()
|
||||
{
|
||||
auto object = this->construct_object<T>();
|
||||
this->read(object);
|
||||
return object;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read_optional(std::optional<T>& val)
|
||||
{
|
||||
if (this->read<bool>())
|
||||
{
|
||||
val.emplace(this->read<T>());
|
||||
}
|
||||
else
|
||||
{
|
||||
val = std::nullopt;
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
void read_optional(std::optional<T>& val)
|
||||
{
|
||||
if (this->read<bool>())
|
||||
{
|
||||
val.emplace(this->read<T>());
|
||||
}
|
||||
else
|
||||
{
|
||||
val = std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename F>
|
||||
requires(std::is_invocable_r_v<T, F>)
|
||||
void read_optional(std::optional<T>& val, const F& factory)
|
||||
{
|
||||
if (this->read<bool>())
|
||||
{
|
||||
val.emplace(factory());
|
||||
this->read<T>(*val);
|
||||
}
|
||||
else
|
||||
{
|
||||
val = {};
|
||||
}
|
||||
}
|
||||
template <typename T, typename F>
|
||||
requires(std::is_invocable_r_v<T, F>)
|
||||
void read_optional(std::optional<T>& val, const F& factory)
|
||||
{
|
||||
if (this->read<bool>())
|
||||
{
|
||||
val.emplace(factory());
|
||||
this->read<T>(*val);
|
||||
}
|
||||
else
|
||||
{
|
||||
val = {};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read_vector(std::vector<T>& result)
|
||||
{
|
||||
const auto size = this->read<uint64_t>();
|
||||
result.clear();
|
||||
result.reserve(size);
|
||||
template <typename T>
|
||||
void read_vector(std::vector<T>& result)
|
||||
{
|
||||
const auto size = this->read<uint64_t>();
|
||||
result.clear();
|
||||
result.reserve(size);
|
||||
|
||||
for (uint64_t i = 0; i < size; ++i)
|
||||
{
|
||||
result.emplace_back(this->read<T>());
|
||||
}
|
||||
}
|
||||
for (uint64_t i = 0; i < size; ++i)
|
||||
{
|
||||
result.emplace_back(this->read<T>());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> read_vector()
|
||||
{
|
||||
std::vector<T> result{};
|
||||
this->read_vector(result);
|
||||
return result;
|
||||
}
|
||||
template <typename T>
|
||||
std::vector<T> read_vector()
|
||||
{
|
||||
std::vector<T> result{};
|
||||
this->read_vector(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Map>
|
||||
void read_map(Map& map)
|
||||
{
|
||||
using key_type = typename Map::key_type;
|
||||
using value_type = typename Map::mapped_type;
|
||||
template <typename Map>
|
||||
void read_map(Map& map)
|
||||
{
|
||||
using key_type = typename Map::key_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)
|
||||
{
|
||||
auto key = this->read<key_type>();
|
||||
auto value = this->read<value_type>();
|
||||
for (uint64_t i = 0; i < size; ++i)
|
||||
{
|
||||
auto key = this->read<key_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>
|
||||
Map read_map()
|
||||
{
|
||||
Map map{};
|
||||
this->read_map(map);
|
||||
return map;
|
||||
}
|
||||
template <typename Map>
|
||||
Map read_map()
|
||||
{
|
||||
Map map{};
|
||||
this->read_map(map);
|
||||
return map;
|
||||
}
|
||||
|
||||
template <typename T = char>
|
||||
void read_string(std::basic_string<T>& result)
|
||||
{
|
||||
const auto size = this->read<uint64_t>();
|
||||
template <typename T = char>
|
||||
void read_string(std::basic_string<T>& result)
|
||||
{
|
||||
const auto size = this->read<uint64_t>();
|
||||
|
||||
result.clear();
|
||||
result.reserve(size);
|
||||
result.clear();
|
||||
result.reserve(size);
|
||||
|
||||
for (uint64_t i = 0; i < size; ++i)
|
||||
{
|
||||
result.push_back(this->read<T>());
|
||||
}
|
||||
}
|
||||
for (uint64_t i = 0; i < size; ++i)
|
||||
{
|
||||
result.push_back(this->read<T>());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T= char>
|
||||
std::basic_string<T> read_string()
|
||||
{
|
||||
std::basic_string<T> result{};
|
||||
this->read_string(result);
|
||||
return result;
|
||||
}
|
||||
template <typename T = char>
|
||||
std::basic_string<T> read_string()
|
||||
{
|
||||
std::basic_string<T> result{};
|
||||
this->read_string(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t get_remaining_size() const
|
||||
{
|
||||
return this->buffer_.size() - offset_;
|
||||
}
|
||||
size_t get_remaining_size() const
|
||||
{
|
||||
return this->buffer_.size() - offset_;
|
||||
}
|
||||
|
||||
std::span<const std::byte> get_remaining_data()
|
||||
{
|
||||
return this->read_data(this->get_remaining_size());
|
||||
}
|
||||
std::span<const std::byte> get_remaining_data()
|
||||
{
|
||||
return this->read_data(this->get_remaining_size());
|
||||
}
|
||||
|
||||
size_t get_offset() const
|
||||
{
|
||||
return this->offset_;
|
||||
}
|
||||
size_t get_offset() const
|
||||
{
|
||||
return this->offset_;
|
||||
}
|
||||
|
||||
template <typename T, typename F>
|
||||
requires(std::is_invocable_r_v<T, F>)
|
||||
void register_factory(F factory)
|
||||
{
|
||||
this->factories_[std::type_index(typeid(T))] = [f = std::move(factory)]() -> T* {
|
||||
return new T(f());
|
||||
};
|
||||
}
|
||||
template <typename T, typename F>
|
||||
requires(std::is_invocable_r_v<T, F>)
|
||||
void register_factory(F factory)
|
||||
{
|
||||
this->factories_[std::type_index(typeid(T))] = [f = std::move(factory)]() -> T* { return new T(f()); };
|
||||
}
|
||||
|
||||
private:
|
||||
bool no_debugging_{false};
|
||||
size_t offset_{0};
|
||||
std::span<const std::byte> buffer_{};
|
||||
std::unordered_map<std::type_index, std::function<void*()>> factories_{};
|
||||
private:
|
||||
bool no_debugging_{false};
|
||||
size_t offset_{0};
|
||||
std::span<const std::byte> buffer_{};
|
||||
std::unordered_map<std::type_index, std::function<void*()>> factories_{};
|
||||
|
||||
template <typename T>
|
||||
T construct_object()
|
||||
{
|
||||
if constexpr (detail::has_deserializer_constructor<T>::value)
|
||||
{
|
||||
return T(*this);
|
||||
}
|
||||
else if constexpr (std::is_default_constructible_v<T>)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto factory = this->factories_.find(std::type_index(typeid(T)));
|
||||
if (factory == this->factories_.end())
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Object construction failed. Missing factory for type: " + std::string(typeid(T).name()));
|
||||
}
|
||||
template <typename T>
|
||||
T construct_object()
|
||||
{
|
||||
if constexpr (detail::has_deserializer_constructor<T>::value)
|
||||
{
|
||||
return T(*this);
|
||||
}
|
||||
else if constexpr (std::is_default_constructible_v<T>)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto factory = this->factories_.find(std::type_index(typeid(T)));
|
||||
if (factory == this->factories_.end())
|
||||
{
|
||||
throw std::runtime_error("Object construction failed. Missing factory for type: " +
|
||||
std::string(typeid(T).name()));
|
||||
}
|
||||
|
||||
auto* object = static_cast<T*>(factory->second());
|
||||
auto obj = std::move(*object);
|
||||
delete object;
|
||||
auto* object = static_cast<T*>(factory->second());
|
||||
auto obj = std::move(*object);
|
||||
delete object;
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
};
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class buffer_serializer
|
||||
{
|
||||
public:
|
||||
buffer_serializer() = default;
|
||||
class buffer_serializer
|
||||
{
|
||||
public:
|
||||
buffer_serializer() = default;
|
||||
|
||||
void write(const void* buffer, const size_t length)
|
||||
{
|
||||
void write(const void* buffer, const size_t length)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
const uint64_t old_size = this->buffer_.size();
|
||||
const uint64_t old_size = this->buffer_.size();
|
||||
#endif
|
||||
|
||||
const auto* byte_buffer = static_cast<const std::byte*>(buffer);
|
||||
this->buffer_.insert(this->buffer_.end(), byte_buffer, byte_buffer + length);
|
||||
const auto* byte_buffer = static_cast<const std::byte*>(buffer);
|
||||
this->buffer_.insert(this->buffer_.end(), byte_buffer, byte_buffer + length);
|
||||
|
||||
#ifndef NDEBUG
|
||||
const auto* security_buffer = reinterpret_cast<const std::byte*>(&old_size);
|
||||
this->buffer_.insert(this->buffer_.end(), security_buffer, security_buffer + sizeof(old_size));
|
||||
const auto* security_buffer = reinterpret_cast<const std::byte*>(&old_size);
|
||||
this->buffer_.insert(this->buffer_.end(), security_buffer, security_buffer + sizeof(old_size));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void write(const buffer_serializer& object)
|
||||
{
|
||||
const auto& buffer = object.get_buffer();
|
||||
this->write(buffer.data(), buffer.size());
|
||||
}
|
||||
void write(const buffer_serializer& object)
|
||||
{
|
||||
const auto& buffer = object.get_buffer();
|
||||
this->write(buffer.data(), buffer.size());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write(const T& object)
|
||||
{
|
||||
constexpr auto is_trivially_copyable = std::is_trivially_copyable_v<T>;
|
||||
template <typename T>
|
||||
void write(const T& object)
|
||||
{
|
||||
constexpr auto is_trivially_copyable = std::is_trivially_copyable_v<T>;
|
||||
|
||||
if constexpr (Serializable<T>)
|
||||
{
|
||||
object.serialize(*this);
|
||||
}
|
||||
else if constexpr (detail::has_serialize_function<T>::value)
|
||||
{
|
||||
serialize(*this, object);
|
||||
}
|
||||
else if constexpr (is_trivially_copyable)
|
||||
{
|
||||
union
|
||||
{
|
||||
const T* type_{};
|
||||
const void* void_;
|
||||
} pointers;
|
||||
if constexpr (Serializable<T>)
|
||||
{
|
||||
object.serialize(*this);
|
||||
}
|
||||
else if constexpr (detail::has_serialize_function<T>::value)
|
||||
{
|
||||
serialize(*this, object);
|
||||
}
|
||||
else if constexpr (is_trivially_copyable)
|
||||
{
|
||||
union
|
||||
{
|
||||
const T* type_{};
|
||||
const void* void_;
|
||||
} pointers;
|
||||
|
||||
pointers.type_ = &object;
|
||||
pointers.type_ = &object;
|
||||
|
||||
this->write(pointers.void_, sizeof(object));
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(!is_trivially_copyable, "Key must be trivially copyable or implement serializable!");
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
this->write(pointers.void_, sizeof(object));
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(!is_trivially_copyable, "Key must be trivially copyable or implement serializable!");
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_optional(const std::optional<T>& val)
|
||||
{
|
||||
this->write(val.has_value());
|
||||
template <typename T>
|
||||
void write_optional(const std::optional<T>& val)
|
||||
{
|
||||
this->write(val.has_value());
|
||||
|
||||
if (val.has_value())
|
||||
{
|
||||
this->write(*val);
|
||||
}
|
||||
}
|
||||
if (val.has_value())
|
||||
{
|
||||
this->write(*val);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_span(const std::span<T> vec)
|
||||
{
|
||||
this->write(static_cast<uint64_t>(vec.size()));
|
||||
template <typename T>
|
||||
void write_span(const std::span<T> vec)
|
||||
{
|
||||
this->write(static_cast<uint64_t>(vec.size()));
|
||||
|
||||
for (const auto& v : vec)
|
||||
{
|
||||
this->write(v);
|
||||
}
|
||||
}
|
||||
for (const auto& v : vec)
|
||||
{
|
||||
this->write(v);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_vector(const std::vector<T> vec)
|
||||
{
|
||||
this->write_span(std::span(vec));
|
||||
}
|
||||
template <typename T>
|
||||
void write_vector(const std::vector<T> vec)
|
||||
{
|
||||
this->write_span(std::span(vec));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_string(const std::basic_string_view<T> str)
|
||||
{
|
||||
this->write_span<const T>(str);
|
||||
}
|
||||
template <typename T>
|
||||
void write_string(const std::basic_string_view<T> str)
|
||||
{
|
||||
this->write_span<const T>(str);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_string(const std::basic_string<T>& str)
|
||||
{
|
||||
this->write_string(std::basic_string_view<T>(str));
|
||||
}
|
||||
template <typename T>
|
||||
void write_string(const std::basic_string<T>& str)
|
||||
{
|
||||
this->write_string(std::basic_string_view<T>(str));
|
||||
}
|
||||
|
||||
template <typename Map>
|
||||
void write_map(const Map& map)
|
||||
{
|
||||
this->write<uint64_t>(map.size());
|
||||
template <typename Map>
|
||||
void write_map(const Map& map)
|
||||
{
|
||||
this->write<uint64_t>(map.size());
|
||||
|
||||
for (const auto& entry : map)
|
||||
{
|
||||
this->write(entry.first);
|
||||
this->write(entry.second);
|
||||
}
|
||||
}
|
||||
for (const auto& entry : map)
|
||||
{
|
||||
this->write(entry.first);
|
||||
this->write(entry.second);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<std::byte>& get_buffer() const
|
||||
{
|
||||
return this->buffer_;
|
||||
}
|
||||
const std::vector<std::byte>& get_buffer() const
|
||||
{
|
||||
return this->buffer_;
|
||||
}
|
||||
|
||||
std::vector<std::byte> move_buffer()
|
||||
{
|
||||
return std::move(this->buffer_);
|
||||
}
|
||||
std::vector<std::byte> move_buffer()
|
||||
{
|
||||
return std::move(this->buffer_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::byte> buffer_{};
|
||||
};
|
||||
private:
|
||||
std::vector<std::byte> buffer_{};
|
||||
};
|
||||
|
||||
template <>
|
||||
inline void buffer_deserializer::read<bool>(bool& object)
|
||||
{
|
||||
object = this->read<uint8_t>() != 0;
|
||||
}
|
||||
template <>
|
||||
inline void buffer_deserializer::read<bool>(bool& object)
|
||||
{
|
||||
object = this->read<uint8_t>() != 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void buffer_deserializer::read<std::string>(std::string& object)
|
||||
{
|
||||
object = this->read_string<char>();
|
||||
}
|
||||
template <>
|
||||
inline void buffer_deserializer::read<std::string>(std::string& object)
|
||||
{
|
||||
object = this->read_string<char>();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void buffer_deserializer::read<std::wstring>(std::wstring& object)
|
||||
{
|
||||
object = this->read_string<wchar_t>();
|
||||
}
|
||||
template <>
|
||||
inline void buffer_deserializer::read<std::wstring>(std::wstring& object)
|
||||
{
|
||||
object = this->read_string<wchar_t>();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void buffer_deserializer::read<std::u16string>(std::u16string& object)
|
||||
{
|
||||
object = this->read_string<char16_t>();
|
||||
}
|
||||
template <>
|
||||
inline void buffer_deserializer::read<std::u16string>(std::u16string& object)
|
||||
{
|
||||
object = this->read_string<char16_t>();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void buffer_serializer::write<bool>(const bool& object)
|
||||
{
|
||||
this->write<uint8_t>(object ? 1 : 0);
|
||||
}
|
||||
template <>
|
||||
inline void buffer_serializer::write<bool>(const bool& object)
|
||||
{
|
||||
this->write<uint8_t>(object ? 1 : 0);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void buffer_serializer::write<std::string>(const std::string& object)
|
||||
{
|
||||
this->write_string(object);
|
||||
}
|
||||
template <>
|
||||
inline void buffer_serializer::write<std::string>(const std::string& object)
|
||||
{
|
||||
this->write_string(object);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void buffer_serializer::write<std::wstring>(const std::wstring& object)
|
||||
{
|
||||
this->write_string(object);
|
||||
}
|
||||
template <>
|
||||
inline void buffer_serializer::write<std::wstring>(const std::wstring& object)
|
||||
{
|
||||
this->write_string(object);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void buffer_serializer::write<std::u16string>(const std::u16string& object)
|
||||
{
|
||||
this->write_string(object);
|
||||
}
|
||||
template <>
|
||||
inline void buffer_serializer::write<std::u16string>(const std::u16string& object)
|
||||
{
|
||||
this->write_string(object);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,41 +7,41 @@
|
||||
|
||||
namespace utils
|
||||
{
|
||||
inline void serialize(buffer_serializer& buffer, const std::chrono::steady_clock::time_point& tp)
|
||||
{
|
||||
buffer.write(tp.time_since_epoch().count());
|
||||
}
|
||||
inline void serialize(buffer_serializer& buffer, const std::chrono::steady_clock::time_point& tp)
|
||||
{
|
||||
buffer.write(tp.time_since_epoch().count());
|
||||
}
|
||||
|
||||
inline void deserialize(buffer_deserializer& buffer, std::chrono::steady_clock::time_point& tp)
|
||||
{
|
||||
using time_point = std::chrono::steady_clock::time_point;
|
||||
using duration = time_point::duration;
|
||||
inline void deserialize(buffer_deserializer& buffer, std::chrono::steady_clock::time_point& tp)
|
||||
{
|
||||
using time_point = std::chrono::steady_clock::time_point;
|
||||
using duration = time_point::duration;
|
||||
|
||||
const auto count = buffer.read<duration::rep>();
|
||||
tp = time_point{duration{count}};
|
||||
}
|
||||
const auto count = buffer.read<duration::rep>();
|
||||
tp = time_point{duration{count}};
|
||||
}
|
||||
|
||||
inline void serialize(buffer_serializer& buffer, const std::chrono::system_clock::time_point& tp)
|
||||
{
|
||||
buffer.write(tp.time_since_epoch().count());
|
||||
}
|
||||
inline void serialize(buffer_serializer& buffer, const std::chrono::system_clock::time_point& tp)
|
||||
{
|
||||
buffer.write(tp.time_since_epoch().count());
|
||||
}
|
||||
|
||||
inline void deserialize(buffer_deserializer& buffer, std::chrono::system_clock::time_point& tp)
|
||||
{
|
||||
using time_point = std::chrono::system_clock::time_point;
|
||||
using duration = time_point::duration;
|
||||
inline void deserialize(buffer_deserializer& buffer, std::chrono::system_clock::time_point& tp)
|
||||
{
|
||||
using time_point = std::chrono::system_clock::time_point;
|
||||
using duration = time_point::duration;
|
||||
|
||||
const auto count = buffer.read<duration::rep>();
|
||||
tp = time_point{duration{count}};
|
||||
}
|
||||
const auto count = buffer.read<duration::rep>();
|
||||
tp = time_point{duration{count}};
|
||||
}
|
||||
|
||||
inline void serialize(buffer_serializer& buffer, const std::filesystem::path& path)
|
||||
{
|
||||
buffer.write_string<char16_t>(path.u16string());
|
||||
}
|
||||
inline void serialize(buffer_serializer& buffer, const std::filesystem::path& path)
|
||||
{
|
||||
buffer.write_string<char16_t>(path.u16string());
|
||||
}
|
||||
|
||||
inline void deserialize(buffer_deserializer& buffer, std::filesystem::path& path)
|
||||
{
|
||||
path = buffer.read_string<char16_t>();
|
||||
}
|
||||
inline void deserialize(buffer_deserializer& buffer, std::filesystem::path& path)
|
||||
{
|
||||
path = buffer.read_string<char16_t>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,94 +2,94 @@
|
||||
|
||||
#include "emulator.hpp"
|
||||
|
||||
template <typename PointerType, typename Register, Register InstructionPointer, Register
|
||||
StackPointer, typename HookableInstructions>
|
||||
template <typename PointerType, typename Register, Register InstructionPointer, Register StackPointer,
|
||||
typename HookableInstructions>
|
||||
class typed_emulator : public emulator
|
||||
{
|
||||
public:
|
||||
using registers = Register;
|
||||
using pointer_type = PointerType;
|
||||
using hookable_instructions = HookableInstructions;
|
||||
public:
|
||||
using registers = Register;
|
||||
using pointer_type = PointerType;
|
||||
using hookable_instructions = HookableInstructions;
|
||||
|
||||
static constexpr size_t pointer_size = sizeof(pointer_type);
|
||||
static constexpr registers stack_pointer = StackPointer;
|
||||
static constexpr registers instruction_pointer = InstructionPointer;
|
||||
static constexpr size_t pointer_size = sizeof(pointer_type);
|
||||
static constexpr registers stack_pointer = StackPointer;
|
||||
static constexpr registers instruction_pointer = InstructionPointer;
|
||||
|
||||
void start_from_ip(const std::chrono::nanoseconds timeout = {}, const size_t count = 0)
|
||||
{
|
||||
this->start(this->read_instruction_pointer(), 0, timeout, count);
|
||||
}
|
||||
void start_from_ip(const std::chrono::nanoseconds timeout = {}, const size_t count = 0)
|
||||
{
|
||||
this->start(this->read_instruction_pointer(), 0, timeout, count);
|
||||
}
|
||||
|
||||
void write_register(registers reg, const void* value, const size_t size)
|
||||
{
|
||||
this->write_raw_register(static_cast<int>(reg), value, size);
|
||||
}
|
||||
void write_register(registers reg, const void* value, const size_t size)
|
||||
{
|
||||
this->write_raw_register(static_cast<int>(reg), value, size);
|
||||
}
|
||||
|
||||
void read_register(registers reg, void* value, const size_t size)
|
||||
{
|
||||
this->read_raw_register(static_cast<int>(reg), value, size);
|
||||
}
|
||||
void read_register(registers reg, void* value, const size_t size)
|
||||
{
|
||||
this->read_raw_register(static_cast<int>(reg), value, size);
|
||||
}
|
||||
|
||||
template <typename T = pointer_type>
|
||||
T reg(const registers regid)
|
||||
{
|
||||
T value{};
|
||||
this->read_register(regid, &value, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
template <typename T = pointer_type>
|
||||
T reg(const registers regid)
|
||||
{
|
||||
T value{};
|
||||
this->read_register(regid, &value, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T = pointer_type, typename S>
|
||||
void reg(const registers regid, const S& maybe_value)
|
||||
{
|
||||
T value = static_cast<T>(maybe_value);
|
||||
this->write_register(regid, &value, sizeof(value));
|
||||
}
|
||||
template <typename T = pointer_type, typename S>
|
||||
void reg(const registers regid, const S& maybe_value)
|
||||
{
|
||||
T value = static_cast<T>(maybe_value);
|
||||
this->write_register(regid, &value, sizeof(value));
|
||||
}
|
||||
|
||||
pointer_type read_instruction_pointer()
|
||||
{
|
||||
return this->reg(instruction_pointer);
|
||||
}
|
||||
pointer_type read_instruction_pointer()
|
||||
{
|
||||
return this->reg(instruction_pointer);
|
||||
}
|
||||
|
||||
pointer_type read_stack_pointer()
|
||||
{
|
||||
return this->reg(stack_pointer);
|
||||
}
|
||||
pointer_type read_stack_pointer()
|
||||
{
|
||||
return this->reg(stack_pointer);
|
||||
}
|
||||
|
||||
pointer_type read_stack(const size_t index)
|
||||
{
|
||||
pointer_type result{};
|
||||
const auto sp = this->read_stack_pointer();
|
||||
pointer_type read_stack(const size_t index)
|
||||
{
|
||||
pointer_type result{};
|
||||
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)
|
||||
{
|
||||
const auto sp = this->read_stack_pointer() - pointer_size;
|
||||
this->reg(stack_pointer, sp);
|
||||
this->write_memory(sp, &value, sizeof(value));
|
||||
}
|
||||
void push_stack(const pointer_type& value)
|
||||
{
|
||||
const auto sp = this->read_stack_pointer() - pointer_size;
|
||||
this->reg(stack_pointer, sp);
|
||||
this->write_memory(sp, &value, sizeof(value));
|
||||
}
|
||||
|
||||
pointer_type pop_stack()
|
||||
{
|
||||
pointer_type result{};
|
||||
const auto sp = this->read_stack_pointer();
|
||||
this->read_memory(sp, &result, sizeof(result));
|
||||
this->reg(stack_pointer, sp + pointer_size);
|
||||
pointer_type pop_stack()
|
||||
{
|
||||
pointer_type result{};
|
||||
const auto sp = this->read_stack_pointer();
|
||||
this->read_memory(sp, &result, sizeof(result));
|
||||
this->reg(stack_pointer, sp + pointer_size);
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
emulator_hook* hook_instruction(hookable_instructions instruction_type, instruction_hook_callback callback)
|
||||
{
|
||||
return this->hook_instruction(static_cast<int>(instruction_type), std::move(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));
|
||||
}
|
||||
|
||||
private:
|
||||
emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) override = 0;
|
||||
private:
|
||||
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 write_raw_register(int reg, const 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;
|
||||
};
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
|
||||
enum class x64_hookable_instructions
|
||||
{
|
||||
invalid,
|
||||
syscall,
|
||||
cpuid,
|
||||
rdtsc,
|
||||
rdtscp,
|
||||
invalid,
|
||||
syscall,
|
||||
cpuid,
|
||||
rdtsc,
|
||||
rdtscp,
|
||||
};
|
||||
|
||||
using x64_emulator = typed_emulator<uint64_t, x64_register, x64_register::rip,
|
||||
x64_register::rsp, x64_hookable_instructions>;
|
||||
using x64_emulator =
|
||||
typed_emulator<uint64_t, x64_register, x64_register::rip, x64_register::rsp, x64_hookable_instructions>;
|
||||
|
||||
@@ -2,244 +2,244 @@
|
||||
|
||||
enum class x64_register
|
||||
{
|
||||
invalid = 0,
|
||||
ah,
|
||||
al,
|
||||
ax,
|
||||
bh,
|
||||
bl,
|
||||
bp,
|
||||
bpl,
|
||||
bx,
|
||||
ch,
|
||||
cl,
|
||||
cs,
|
||||
cx,
|
||||
dh,
|
||||
di,
|
||||
dil,
|
||||
dl,
|
||||
ds,
|
||||
dx,
|
||||
eax,
|
||||
ebp,
|
||||
ebx,
|
||||
ecx,
|
||||
edi,
|
||||
edx,
|
||||
eflags,
|
||||
eip,
|
||||
es = eip + 2,
|
||||
esi,
|
||||
esp,
|
||||
fpsw,
|
||||
fs,
|
||||
gs,
|
||||
ip,
|
||||
rax,
|
||||
rbp,
|
||||
rbx,
|
||||
rcx,
|
||||
rdi,
|
||||
rdx,
|
||||
rip,
|
||||
rsi = rip + 2,
|
||||
rsp,
|
||||
si,
|
||||
sil,
|
||||
sp,
|
||||
spl,
|
||||
ss,
|
||||
cr0,
|
||||
cr1,
|
||||
cr2,
|
||||
cr3,
|
||||
cr4,
|
||||
cr8 = cr4 + 4,
|
||||
dr0 = cr8 + 8,
|
||||
dr1,
|
||||
dr2,
|
||||
dr3,
|
||||
dr4,
|
||||
dr5,
|
||||
dr6,
|
||||
dr7,
|
||||
fp0 = dr7 + 9,
|
||||
fp1,
|
||||
fp2,
|
||||
fp3,
|
||||
fp4,
|
||||
fp5,
|
||||
fp6,
|
||||
fp7,
|
||||
k0,
|
||||
k1,
|
||||
k2,
|
||||
k3,
|
||||
k4,
|
||||
k5,
|
||||
k6,
|
||||
k7,
|
||||
mm0,
|
||||
mm1,
|
||||
mm2,
|
||||
mm3,
|
||||
mm4,
|
||||
mm5,
|
||||
mm6,
|
||||
mm7,
|
||||
r8,
|
||||
r9,
|
||||
r10,
|
||||
r11,
|
||||
r12,
|
||||
r13,
|
||||
r14,
|
||||
r15,
|
||||
st0,
|
||||
st1,
|
||||
st2,
|
||||
st3,
|
||||
st4,
|
||||
st5,
|
||||
st6,
|
||||
st7,
|
||||
xmm0,
|
||||
xmm1,
|
||||
xmm2,
|
||||
xmm3,
|
||||
xmm4,
|
||||
xmm5,
|
||||
xmm6,
|
||||
xmm7,
|
||||
xmm8,
|
||||
xmm9,
|
||||
xmm10,
|
||||
xmm11,
|
||||
xmm12,
|
||||
xmm13,
|
||||
xmm14,
|
||||
xmm15,
|
||||
xmm16,
|
||||
xmm17,
|
||||
xmm18,
|
||||
xmm19,
|
||||
xmm20,
|
||||
xmm21,
|
||||
xmm22,
|
||||
xmm23,
|
||||
xmm24,
|
||||
xmm25,
|
||||
xmm26,
|
||||
xmm27,
|
||||
xmm28,
|
||||
xmm29,
|
||||
xmm30,
|
||||
xmm31,
|
||||
ymm0,
|
||||
ymm1,
|
||||
ymm2,
|
||||
ymm3,
|
||||
ymm4,
|
||||
ymm5,
|
||||
ymm6,
|
||||
ymm7,
|
||||
ymm8,
|
||||
ymm9,
|
||||
ymm10,
|
||||
ymm11,
|
||||
ymm12,
|
||||
ymm13,
|
||||
ymm14,
|
||||
ymm15,
|
||||
ymm16,
|
||||
ymm17,
|
||||
ymm18,
|
||||
ymm19,
|
||||
ymm20,
|
||||
ymm21,
|
||||
ymm22,
|
||||
ymm23,
|
||||
ymm24,
|
||||
ymm25,
|
||||
ymm26,
|
||||
ymm27,
|
||||
ymm28,
|
||||
ymm29,
|
||||
ymm30,
|
||||
ymm31,
|
||||
zmm0,
|
||||
zmm1,
|
||||
zmm2,
|
||||
zmm3,
|
||||
zmm4,
|
||||
zmm5,
|
||||
zmm6,
|
||||
zmm7,
|
||||
zmm8,
|
||||
zmm9,
|
||||
zmm10,
|
||||
zmm11,
|
||||
zmm12,
|
||||
zmm13,
|
||||
zmm14,
|
||||
zmm15,
|
||||
zmm16,
|
||||
zmm17,
|
||||
zmm18,
|
||||
zmm19,
|
||||
zmm20,
|
||||
zmm21,
|
||||
zmm22,
|
||||
zmm23,
|
||||
zmm24,
|
||||
zmm25,
|
||||
zmm26,
|
||||
zmm27,
|
||||
zmm28,
|
||||
zmm29,
|
||||
zmm30,
|
||||
zmm31,
|
||||
r8b,
|
||||
r9b,
|
||||
r10b,
|
||||
r11b,
|
||||
r12b,
|
||||
r13b,
|
||||
r14b,
|
||||
r15b,
|
||||
r8d,
|
||||
r9d,
|
||||
r10d,
|
||||
r11d,
|
||||
r12d,
|
||||
r13d,
|
||||
r14d,
|
||||
r15d,
|
||||
r8w,
|
||||
r9w,
|
||||
r10w,
|
||||
r11w,
|
||||
r12w,
|
||||
r13w,
|
||||
r14w,
|
||||
r15w,
|
||||
idtr,
|
||||
gdtr,
|
||||
ldtr,
|
||||
tr,
|
||||
fpcw,
|
||||
fptag,
|
||||
msr,
|
||||
mxcsr,
|
||||
fs_base,
|
||||
gs_base,
|
||||
flags,
|
||||
rflags,
|
||||
fip,
|
||||
fcs,
|
||||
fdp,
|
||||
fds,
|
||||
fop,
|
||||
end, // Must be last
|
||||
invalid = 0,
|
||||
ah,
|
||||
al,
|
||||
ax,
|
||||
bh,
|
||||
bl,
|
||||
bp,
|
||||
bpl,
|
||||
bx,
|
||||
ch,
|
||||
cl,
|
||||
cs,
|
||||
cx,
|
||||
dh,
|
||||
di,
|
||||
dil,
|
||||
dl,
|
||||
ds,
|
||||
dx,
|
||||
eax,
|
||||
ebp,
|
||||
ebx,
|
||||
ecx,
|
||||
edi,
|
||||
edx,
|
||||
eflags,
|
||||
eip,
|
||||
es = eip + 2,
|
||||
esi,
|
||||
esp,
|
||||
fpsw,
|
||||
fs,
|
||||
gs,
|
||||
ip,
|
||||
rax,
|
||||
rbp,
|
||||
rbx,
|
||||
rcx,
|
||||
rdi,
|
||||
rdx,
|
||||
rip,
|
||||
rsi = rip + 2,
|
||||
rsp,
|
||||
si,
|
||||
sil,
|
||||
sp,
|
||||
spl,
|
||||
ss,
|
||||
cr0,
|
||||
cr1,
|
||||
cr2,
|
||||
cr3,
|
||||
cr4,
|
||||
cr8 = cr4 + 4,
|
||||
dr0 = cr8 + 8,
|
||||
dr1,
|
||||
dr2,
|
||||
dr3,
|
||||
dr4,
|
||||
dr5,
|
||||
dr6,
|
||||
dr7,
|
||||
fp0 = dr7 + 9,
|
||||
fp1,
|
||||
fp2,
|
||||
fp3,
|
||||
fp4,
|
||||
fp5,
|
||||
fp6,
|
||||
fp7,
|
||||
k0,
|
||||
k1,
|
||||
k2,
|
||||
k3,
|
||||
k4,
|
||||
k5,
|
||||
k6,
|
||||
k7,
|
||||
mm0,
|
||||
mm1,
|
||||
mm2,
|
||||
mm3,
|
||||
mm4,
|
||||
mm5,
|
||||
mm6,
|
||||
mm7,
|
||||
r8,
|
||||
r9,
|
||||
r10,
|
||||
r11,
|
||||
r12,
|
||||
r13,
|
||||
r14,
|
||||
r15,
|
||||
st0,
|
||||
st1,
|
||||
st2,
|
||||
st3,
|
||||
st4,
|
||||
st5,
|
||||
st6,
|
||||
st7,
|
||||
xmm0,
|
||||
xmm1,
|
||||
xmm2,
|
||||
xmm3,
|
||||
xmm4,
|
||||
xmm5,
|
||||
xmm6,
|
||||
xmm7,
|
||||
xmm8,
|
||||
xmm9,
|
||||
xmm10,
|
||||
xmm11,
|
||||
xmm12,
|
||||
xmm13,
|
||||
xmm14,
|
||||
xmm15,
|
||||
xmm16,
|
||||
xmm17,
|
||||
xmm18,
|
||||
xmm19,
|
||||
xmm20,
|
||||
xmm21,
|
||||
xmm22,
|
||||
xmm23,
|
||||
xmm24,
|
||||
xmm25,
|
||||
xmm26,
|
||||
xmm27,
|
||||
xmm28,
|
||||
xmm29,
|
||||
xmm30,
|
||||
xmm31,
|
||||
ymm0,
|
||||
ymm1,
|
||||
ymm2,
|
||||
ymm3,
|
||||
ymm4,
|
||||
ymm5,
|
||||
ymm6,
|
||||
ymm7,
|
||||
ymm8,
|
||||
ymm9,
|
||||
ymm10,
|
||||
ymm11,
|
||||
ymm12,
|
||||
ymm13,
|
||||
ymm14,
|
||||
ymm15,
|
||||
ymm16,
|
||||
ymm17,
|
||||
ymm18,
|
||||
ymm19,
|
||||
ymm20,
|
||||
ymm21,
|
||||
ymm22,
|
||||
ymm23,
|
||||
ymm24,
|
||||
ymm25,
|
||||
ymm26,
|
||||
ymm27,
|
||||
ymm28,
|
||||
ymm29,
|
||||
ymm30,
|
||||
ymm31,
|
||||
zmm0,
|
||||
zmm1,
|
||||
zmm2,
|
||||
zmm3,
|
||||
zmm4,
|
||||
zmm5,
|
||||
zmm6,
|
||||
zmm7,
|
||||
zmm8,
|
||||
zmm9,
|
||||
zmm10,
|
||||
zmm11,
|
||||
zmm12,
|
||||
zmm13,
|
||||
zmm14,
|
||||
zmm15,
|
||||
zmm16,
|
||||
zmm17,
|
||||
zmm18,
|
||||
zmm19,
|
||||
zmm20,
|
||||
zmm21,
|
||||
zmm22,
|
||||
zmm23,
|
||||
zmm24,
|
||||
zmm25,
|
||||
zmm26,
|
||||
zmm27,
|
||||
zmm28,
|
||||
zmm29,
|
||||
zmm30,
|
||||
zmm31,
|
||||
r8b,
|
||||
r9b,
|
||||
r10b,
|
||||
r11b,
|
||||
r12b,
|
||||
r13b,
|
||||
r14b,
|
||||
r15b,
|
||||
r8d,
|
||||
r9d,
|
||||
r10d,
|
||||
r11d,
|
||||
r12d,
|
||||
r13d,
|
||||
r14d,
|
||||
r15d,
|
||||
r8w,
|
||||
r9w,
|
||||
r10w,
|
||||
r11w,
|
||||
r12w,
|
||||
r13w,
|
||||
r14w,
|
||||
r15w,
|
||||
idtr,
|
||||
gdtr,
|
||||
ldtr,
|
||||
tr,
|
||||
fpcw,
|
||||
fptag,
|
||||
msr,
|
||||
mxcsr,
|
||||
fs_base,
|
||||
gs_base,
|
||||
flags,
|
||||
rflags,
|
||||
fip,
|
||||
fcs,
|
||||
fdp,
|
||||
fds,
|
||||
fop,
|
||||
end, // Must be last
|
||||
};
|
||||
|
||||
@@ -9,192 +9,183 @@ bool use_gdb = false;
|
||||
|
||||
namespace
|
||||
{
|
||||
void run_emulation(windows_emulator& win_emu)
|
||||
{
|
||||
try
|
||||
{
|
||||
win_emu.log.disable_output(true);
|
||||
win_emu.start();
|
||||
void run_emulation(windows_emulator& win_emu)
|
||||
{
|
||||
try
|
||||
{
|
||||
win_emu.log.disable_output(true);
|
||||
win_emu.start();
|
||||
|
||||
if (win_emu.process().exception_rip.has_value())
|
||||
{
|
||||
throw std::runtime_error("Exception!");
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
win_emu.log.disable_output(false);
|
||||
win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 "\n",
|
||||
win_emu.emu().read_instruction_pointer());
|
||||
throw;
|
||||
}
|
||||
if (win_emu.process().exception_rip.has_value())
|
||||
{
|
||||
throw std::runtime_error("Exception!");
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
win_emu.log.disable_output(false);
|
||||
win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 "\n",
|
||||
win_emu.emu().read_instruction_pointer());
|
||||
throw;
|
||||
}
|
||||
|
||||
win_emu.log.disable_output(false);
|
||||
}
|
||||
win_emu.log.disable_output(false);
|
||||
}
|
||||
|
||||
void forward_emulator(windows_emulator& win_emu)
|
||||
{
|
||||
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().stop();
|
||||
});
|
||||
void forward_emulator(windows_emulator& win_emu)
|
||||
{
|
||||
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().stop(); });
|
||||
|
||||
run_emulation(win_emu);
|
||||
}
|
||||
run_emulation(win_emu);
|
||||
}
|
||||
|
||||
struct fuzzer_executer : fuzzer::executer
|
||||
{
|
||||
windows_emulator emu{};
|
||||
std::span<const std::byte> emulator_data{};
|
||||
std::unordered_set<uint64_t> visited_blocks{};
|
||||
const std::function<fuzzer::coverage_functor>* handler{nullptr};
|
||||
struct fuzzer_executer : fuzzer::executer
|
||||
{
|
||||
windows_emulator emu{};
|
||||
std::span<const std::byte> emulator_data{};
|
||||
std::unordered_set<uint64_t> visited_blocks{};
|
||||
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)
|
||||
: 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);
|
||||
}
|
||||
});
|
||||
utils::buffer_deserializer deserializer{emulator_data};
|
||||
emu.deserialize(deserializer);
|
||||
emu.save_snapshot();
|
||||
|
||||
utils::buffer_deserializer deserializer{emulator_data};
|
||||
emu.deserialize(deserializer);
|
||||
emu.save_snapshot();
|
||||
const auto ret = emu.emu().read_stack(0);
|
||||
|
||||
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)
|
||||
{
|
||||
emu.emu().stop();
|
||||
});
|
||||
}
|
||||
void restore_emulator()
|
||||
{
|
||||
/*utils::buffer_deserializer deserializer{ emulator_data };
|
||||
emu.deserialize(deserializer);*/
|
||||
emu.restore_snapshot();
|
||||
}
|
||||
|
||||
void restore_emulator()
|
||||
{
|
||||
/*utils::buffer_deserializer deserializer{ emulator_data };
|
||||
emu.deserialize(deserializer);*/
|
||||
emu.restore_snapshot();
|
||||
}
|
||||
fuzzer::execution_result execute(std::span<const uint8_t> data,
|
||||
const std::function<fuzzer::coverage_functor>& coverage_handler) override
|
||||
{
|
||||
// printf("Input size: %zd\n", data.size());
|
||||
this->handler = &coverage_handler;
|
||||
this->visited_blocks.clear();
|
||||
|
||||
fuzzer::execution_result execute(std::span<const uint8_t> data,
|
||||
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();
|
||||
|
||||
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))),
|
||||
memory_permission::read_write);
|
||||
emu.emu().write_memory(memory, data.data(), data.size());
|
||||
emu.emu().reg(x64_register::rcx, memory);
|
||||
emu.emu().reg<uint64_t>(x64_register::rdx, data.size());
|
||||
|
||||
emu.emu().reg(x64_register::rcx, memory);
|
||||
emu.emu().reg<uint64_t>(x64_register::rdx, data.size());
|
||||
try
|
||||
{
|
||||
run_emulation(emu);
|
||||
return fuzzer::execution_result::success;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return fuzzer::execution_result::error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
run_emulation(emu);
|
||||
return fuzzer::execution_result::success;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return fuzzer::execution_result::error;
|
||||
}
|
||||
}
|
||||
};
|
||||
struct my_fuzzing_handler : fuzzer::fuzzing_handler
|
||||
{
|
||||
std::vector<std::byte> emulator_state{};
|
||||
std::atomic_bool stop_fuzzing{false};
|
||||
|
||||
struct my_fuzzing_handler : fuzzer::fuzzing_handler
|
||||
{
|
||||
std::vector<std::byte> emulator_state{};
|
||||
std::atomic_bool stop_fuzzing{false};
|
||||
my_fuzzing_handler(std::vector<std::byte> emulator_state)
|
||||
: emulator_state(std::move(emulator_state))
|
||||
{
|
||||
}
|
||||
|
||||
my_fuzzing_handler(std::vector<std::byte> emulator_state)
|
||||
: emulator_state(std::move(emulator_state))
|
||||
{
|
||||
}
|
||||
std::unique_ptr<fuzzer::executer> make_executer() override
|
||||
{
|
||||
return std::make_unique<fuzzer_executer>(emulator_state);
|
||||
}
|
||||
|
||||
std::unique_ptr<fuzzer::executer> make_executer() override
|
||||
{
|
||||
return std::make_unique<fuzzer_executer>(emulator_state);
|
||||
}
|
||||
bool stop() override
|
||||
{
|
||||
return stop_fuzzing;
|
||||
}
|
||||
};
|
||||
|
||||
bool stop() override
|
||||
{
|
||||
return stop_fuzzing;
|
||||
}
|
||||
};
|
||||
void run_fuzzer(const windows_emulator& base_emulator)
|
||||
{
|
||||
const auto concurrency = std::thread::hardware_concurrency() + 2;
|
||||
|
||||
void run_fuzzer(const windows_emulator& base_emulator)
|
||||
{
|
||||
const auto concurrency = std::thread::hardware_concurrency() + 2;
|
||||
utils::buffer_serializer serializer{};
|
||||
base_emulator.serialize(serializer);
|
||||
|
||||
utils::buffer_serializer serializer{};
|
||||
base_emulator.serialize(serializer);
|
||||
my_fuzzing_handler handler{serializer.move_buffer()};
|
||||
|
||||
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)
|
||||
{
|
||||
emulator_settings settings{
|
||||
.application = application,
|
||||
};
|
||||
windows_emulator win_emu{std::move(settings)};
|
||||
|
||||
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)
|
||||
{
|
||||
if (argc <= 1)
|
||||
{
|
||||
puts("Application not specified!");
|
||||
return 1;
|
||||
}
|
||||
if (argc <= 1)
|
||||
{
|
||||
puts("Application not specified!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
//setvbuf(stdout, nullptr, _IOFBF, 0x10000);
|
||||
if (argc > 2 && argv[1] == "-d"s)
|
||||
{
|
||||
use_gdb = true;
|
||||
}
|
||||
// setvbuf(stdout, nullptr, _IOFBF, 0x10000);
|
||||
if (argc > 2 && argv[1] == "-d"s)
|
||||
{
|
||||
use_gdb = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
do
|
||||
{
|
||||
run(argv[use_gdb ? 2 : 1]);
|
||||
}
|
||||
while (use_gdb);
|
||||
try
|
||||
{
|
||||
do
|
||||
{
|
||||
run(argv[use_gdb ? 2 : 1]);
|
||||
} while (use_gdb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
puts(e.what());
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
puts(e.what());
|
||||
|
||||
#if defined(_WIN32) && 0
|
||||
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
|
||||
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int)
|
||||
{
|
||||
return main(__argc, __argv);
|
||||
return main(__argc, __argv);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5,128 +5,121 @@
|
||||
|
||||
namespace fuzzer
|
||||
{
|
||||
namespace
|
||||
{
|
||||
class fuzzing_context
|
||||
{
|
||||
public:
|
||||
fuzzing_context(input_generator& generator, fuzzing_handler& handler)
|
||||
: generator(generator)
|
||||
, handler(handler)
|
||||
{
|
||||
}
|
||||
namespace
|
||||
{
|
||||
class fuzzing_context
|
||||
{
|
||||
public:
|
||||
fuzzing_context(input_generator& generator, fuzzing_handler& handler)
|
||||
: generator(generator),
|
||||
handler(handler)
|
||||
{
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
this->stop_ = true;
|
||||
}
|
||||
void stop()
|
||||
{
|
||||
this->stop_ = true;
|
||||
}
|
||||
|
||||
bool should_stop()
|
||||
{
|
||||
if (this->stop_)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool should_stop()
|
||||
{
|
||||
if (this->stop_)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!handler.stop())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!handler.stop())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this->stop_ = true;
|
||||
return true;
|
||||
}
|
||||
this->stop_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
input_generator& generator;
|
||||
fuzzing_handler& handler;
|
||||
std::atomic_uint64_t executions{0};
|
||||
input_generator& generator;
|
||||
fuzzing_handler& handler;
|
||||
std::atomic_uint64_t executions{0};
|
||||
|
||||
private:
|
||||
std::atomic_bool stop_{false};
|
||||
};
|
||||
private:
|
||||
std::atomic_bool stop_{false};
|
||||
};
|
||||
|
||||
void perform_fuzzing_iteration(fuzzing_context& context, executer& executer)
|
||||
{
|
||||
++context.executions;
|
||||
context.generator.access_input([&](const std::span<const uint8_t> input)
|
||||
{
|
||||
uint64_t score{0};
|
||||
const auto result = executer.execute(input, [&](uint64_t)
|
||||
{
|
||||
++score;
|
||||
});
|
||||
void perform_fuzzing_iteration(fuzzing_context& context, executer& executer)
|
||||
{
|
||||
++context.executions;
|
||||
context.generator.access_input([&](const std::span<const uint8_t> input) {
|
||||
uint64_t score{0};
|
||||
const auto result = executer.execute(input, [&](uint64_t) { ++score; });
|
||||
|
||||
if (result == execution_result::error)
|
||||
{
|
||||
printf("Found error!\n");
|
||||
context.stop();
|
||||
}
|
||||
if (result == execution_result::error)
|
||||
{
|
||||
printf("Found error!\n");
|
||||
context.stop();
|
||||
}
|
||||
|
||||
return score;
|
||||
});
|
||||
}
|
||||
return score;
|
||||
});
|
||||
}
|
||||
|
||||
void worker(fuzzing_context& context)
|
||||
{
|
||||
const auto executer = context.handler.make_executer();
|
||||
void worker(fuzzing_context& context)
|
||||
{
|
||||
const auto executer = context.handler.make_executer();
|
||||
|
||||
while (!context.should_stop())
|
||||
{
|
||||
perform_fuzzing_iteration(context, *executer);
|
||||
}
|
||||
}
|
||||
while (!context.should_stop())
|
||||
{
|
||||
perform_fuzzing_iteration(context, *executer);
|
||||
}
|
||||
}
|
||||
|
||||
struct worker_pool
|
||||
{
|
||||
fuzzing_context* context_{nullptr};
|
||||
std::vector<std::thread> workers_{};
|
||||
struct worker_pool
|
||||
{
|
||||
fuzzing_context* context_{nullptr};
|
||||
std::vector<std::thread> workers_{};
|
||||
|
||||
worker_pool(fuzzing_context& context, const size_t concurrency)
|
||||
: context_(&context)
|
||||
{
|
||||
this->workers_.reserve(concurrency);
|
||||
worker_pool(fuzzing_context& context, const size_t concurrency)
|
||||
: context_(&context)
|
||||
{
|
||||
this->workers_.reserve(concurrency);
|
||||
|
||||
for (size_t i = 0; i < concurrency; ++i)
|
||||
{
|
||||
this->workers_.emplace_back([&context]
|
||||
{
|
||||
worker(context);
|
||||
});
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < concurrency; ++i)
|
||||
{
|
||||
this->workers_.emplace_back([&context] { worker(context); });
|
||||
}
|
||||
}
|
||||
|
||||
~worker_pool()
|
||||
{
|
||||
if (this->workers_.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
~worker_pool()
|
||||
{
|
||||
if (this->workers_.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this->context_->stop();
|
||||
this->context_->stop();
|
||||
|
||||
for (auto& w : this->workers_)
|
||||
{
|
||||
w.join();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
for (auto& w : this->workers_)
|
||||
{
|
||||
w.join();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void run(fuzzing_handler& handler, const size_t concurrency)
|
||||
{
|
||||
input_generator generator{};
|
||||
fuzzing_context context{generator, handler};
|
||||
worker_pool pool{context, concurrency};
|
||||
void run(fuzzing_handler& handler, const size_t concurrency)
|
||||
{
|
||||
input_generator generator{};
|
||||
fuzzing_context context{generator, handler};
|
||||
worker_pool pool{context, concurrency};
|
||||
|
||||
while (!context.should_stop())
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::seconds{1});
|
||||
while (!context.should_stop())
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::seconds{1});
|
||||
|
||||
const auto executions = context.executions.exchange(0);
|
||||
const auto highest_scorer = context.generator.get_highest_scorer();
|
||||
const auto avg_score = context.generator.get_average_score();
|
||||
printf("Executions/s: %" PRIu64 " - Score: %" PRIx64 " - Avg: %.3f\n", executions, highest_scorer.score,
|
||||
avg_score);
|
||||
}
|
||||
}
|
||||
const auto executions = context.executions.exchange(0);
|
||||
const auto highest_scorer = context.generator.get_highest_scorer();
|
||||
const auto avg_score = context.generator.get_average_score();
|
||||
printf("Executions/s: %" PRIu64 " - Score: %" PRIx64 " - Avg: %.3f\n", executions, highest_scorer.score,
|
||||
avg_score);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,33 +7,33 @@
|
||||
|
||||
namespace fuzzer
|
||||
{
|
||||
using coverage_functor = void(uint64_t address);
|
||||
using coverage_functor = void(uint64_t address);
|
||||
|
||||
enum class execution_result
|
||||
{
|
||||
success,
|
||||
error,
|
||||
};
|
||||
enum class execution_result
|
||||
{
|
||||
success,
|
||||
error,
|
||||
};
|
||||
|
||||
struct executer
|
||||
{
|
||||
virtual ~executer() = default;
|
||||
struct executer
|
||||
{
|
||||
virtual ~executer() = default;
|
||||
|
||||
virtual execution_result execute(std::span<const uint8_t> data,
|
||||
const std::function<coverage_functor>& coverage_handler) = 0;
|
||||
};
|
||||
virtual execution_result execute(std::span<const uint8_t> data,
|
||||
const std::function<coverage_functor>& coverage_handler) = 0;
|
||||
};
|
||||
|
||||
struct fuzzing_handler
|
||||
{
|
||||
virtual ~fuzzing_handler() = default;
|
||||
struct fuzzing_handler
|
||||
{
|
||||
virtual ~fuzzing_handler() = default;
|
||||
|
||||
virtual std::unique_ptr<executer> make_executer() = 0;
|
||||
virtual std::unique_ptr<executer> make_executer() = 0;
|
||||
|
||||
virtual bool stop()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
virtual bool stop()
|
||||
{
|
||||
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
|
||||
{
|
||||
constexpr size_t MAX_TOP_SCORER = 20;
|
||||
namespace
|
||||
{
|
||||
constexpr size_t MAX_TOP_SCORER = 20;
|
||||
|
||||
void mutate_input(random_generator& rng, std::vector<uint8_t>& input)
|
||||
{
|
||||
if (input.empty() || rng.get(3) == 0)
|
||||
{
|
||||
const auto new_bytes = rng.get_geometric<size_t>() + 1;
|
||||
input.resize(input.size() + new_bytes);
|
||||
}
|
||||
else if (rng.get(10) == 0)
|
||||
{
|
||||
const auto remove_bytes = rng.get_geometric<size_t>() % input.size();
|
||||
input.resize(input.size() - remove_bytes);
|
||||
}
|
||||
void mutate_input(random_generator& rng, std::vector<uint8_t>& input)
|
||||
{
|
||||
if (input.empty() || rng.get(3) == 0)
|
||||
{
|
||||
const auto new_bytes = rng.get_geometric<size_t>() + 1;
|
||||
input.resize(input.size() + new_bytes);
|
||||
}
|
||||
else if (rng.get(10) == 0)
|
||||
{
|
||||
const auto remove_bytes = rng.get_geometric<size_t>() % input.size();
|
||||
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)
|
||||
{
|
||||
const auto index = rng.get<size_t>(input.size());
|
||||
input[index] = rng.get<uint8_t>();
|
||||
}
|
||||
}
|
||||
}
|
||||
input_generator::input_generator() = default;
|
||||
|
||||
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()
|
||||
{
|
||||
std::vector<uint8_t> input{};
|
||||
std::unique_lock lock{this->mutex_};
|
||||
if (!this->top_scorer_.empty())
|
||||
{
|
||||
const auto index = this->rng.get<size_t>() % this->top_scorer_.size();
|
||||
input = this->top_scorer_[index].data;
|
||||
}
|
||||
|
||||
if (!this->top_scorer_.empty())
|
||||
{
|
||||
const auto index = this->rng.get<size_t>() % this->top_scorer_.size();
|
||||
input = this->top_scorer_[index].data;
|
||||
}
|
||||
mutate_input(this->rng, input);
|
||||
|
||||
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)
|
||||
{
|
||||
auto next_input = this->generate_next_input();
|
||||
const auto score = handler(next_input);
|
||||
input_entry e{};
|
||||
e.data = std::move(next_input);
|
||||
e.score = score;
|
||||
|
||||
input_entry e{};
|
||||
e.data = std::move(next_input);
|
||||
e.score = score;
|
||||
this->store_input_entry(std::move(e));
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
std::unique_lock lock{this->mutex_};
|
||||
return this->highest_scorer_;
|
||||
}
|
||||
double input_generator::get_average_score()
|
||||
{
|
||||
std::unique_lock lock{this->mutex_};
|
||||
|
||||
double input_generator::get_average_score()
|
||||
{
|
||||
std::unique_lock lock{this->mutex_};
|
||||
double score{0.0};
|
||||
for (const auto& e : this->top_scorer_)
|
||||
{
|
||||
score += static_cast<double>(e.score);
|
||||
}
|
||||
|
||||
double score{0.0};
|
||||
for (const auto& e : this->top_scorer_)
|
||||
{
|
||||
score += static_cast<double>(e.score);
|
||||
}
|
||||
return score / static_cast<double>(this->top_scorer_.size());
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
std::unique_lock lock{this->mutex_};
|
||||
if (entry.score < this->lowest_score && this->rng.get(40) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.score < this->lowest_score && this->rng.get(40) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (entry.score > this->highest_scorer_.score)
|
||||
{
|
||||
this->highest_scorer_ = entry;
|
||||
}
|
||||
|
||||
if (entry.score > this->highest_scorer_.score)
|
||||
{
|
||||
this->highest_scorer_ = entry;
|
||||
}
|
||||
if (this->top_scorer_.size() < MAX_TOP_SCORER)
|
||||
{
|
||||
this->top_scorer_.emplace_back(std::move(entry));
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->top_scorer_.size() < MAX_TOP_SCORER)
|
||||
{
|
||||
this->top_scorer_.emplace_back(std::move(entry));
|
||||
return;
|
||||
}
|
||||
const auto insert_at_random = this->rng.get(10) == 0;
|
||||
const auto index =
|
||||
insert_at_random ? (this->rng.get<size_t>() % this->top_scorer_.size()) : this->lowest_scorer;
|
||||
|
||||
const auto insert_at_random = this->rng.get(10) == 0;
|
||||
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->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;
|
||||
this->lowest_scorer = 0;
|
||||
|
||||
for (size_t i = 1; i < this->top_scorer_.size(); ++i)
|
||||
{
|
||||
if (this->top_scorer_[i].score < this->lowest_score)
|
||||
{
|
||||
this->lowest_score = this->top_scorer_[i].score;
|
||||
this->lowest_scorer = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (size_t i = 1; i < this->top_scorer_.size(); ++i)
|
||||
{
|
||||
if (this->top_scorer_[i].score < this->lowest_score)
|
||||
{
|
||||
this->lowest_score = this->top_scorer_[i].score;
|
||||
this->lowest_scorer = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,37 +8,37 @@
|
||||
|
||||
namespace fuzzer
|
||||
{
|
||||
using input_score = uint64_t;
|
||||
using input_handler = input_score(std::span<const uint8_t>);
|
||||
using input_score = uint64_t;
|
||||
using input_handler = input_score(std::span<const uint8_t>);
|
||||
|
||||
struct input_entry
|
||||
{
|
||||
std::vector<uint8_t> data{};
|
||||
input_score score{};
|
||||
};
|
||||
struct input_entry
|
||||
{
|
||||
std::vector<uint8_t> data{};
|
||||
input_score score{};
|
||||
};
|
||||
|
||||
class input_generator
|
||||
{
|
||||
public:
|
||||
input_generator();
|
||||
class input_generator
|
||||
{
|
||||
public:
|
||||
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();
|
||||
double get_average_score();
|
||||
input_entry get_highest_scorer();
|
||||
double get_average_score();
|
||||
|
||||
private:
|
||||
std::mutex mutex_{};
|
||||
random_generator rng{};
|
||||
private:
|
||||
std::mutex mutex_{};
|
||||
random_generator rng{};
|
||||
|
||||
std::vector<input_entry> top_scorer_{};
|
||||
input_score lowest_score{0};
|
||||
size_t lowest_scorer{0};
|
||||
std::vector<input_entry> top_scorer_{};
|
||||
input_score lowest_score{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
|
||||
{
|
||||
random_generator::random_generator()
|
||||
: rng_(std::random_device()())
|
||||
{
|
||||
}
|
||||
random_generator::random_generator()
|
||||
: rng_(std::random_device()())
|
||||
{
|
||||
}
|
||||
|
||||
std::mt19937::result_type random_generator::generate_number()
|
||||
{
|
||||
return this->distribution_(this->rng_);
|
||||
}
|
||||
std::mt19937::result_type random_generator::generate_number()
|
||||
{
|
||||
return this->distribution_(this->rng_);
|
||||
}
|
||||
|
||||
void random_generator::fill(void* data, const size_t size)
|
||||
{
|
||||
this->fill(std::span(static_cast<uint8_t*>(data), size));
|
||||
}
|
||||
void random_generator::fill(void* data, const size_t size)
|
||||
{
|
||||
this->fill(std::span(static_cast<uint8_t*>(data), size));
|
||||
}
|
||||
|
||||
void random_generator::fill(std::span<uint8_t> data)
|
||||
{
|
||||
size_t i = 0;
|
||||
while (i < data.size())
|
||||
{
|
||||
const auto number = this->generate_number();
|
||||
void random_generator::fill(std::span<uint8_t> data)
|
||||
{
|
||||
size_t i = 0;
|
||||
while (i < data.size())
|
||||
{
|
||||
const auto number = this->generate_number();
|
||||
|
||||
const auto remaining_data = data.size() - i;
|
||||
const auto data_to_fill = std::min(remaining_data, sizeof(number));
|
||||
const auto remaining_data = data.size() - i;
|
||||
const auto data_to_fill = std::min(remaining_data, sizeof(number));
|
||||
|
||||
memcpy(data.data() + i, &number, data_to_fill);
|
||||
i += data_to_fill;
|
||||
}
|
||||
}
|
||||
memcpy(data.data() + i, &number, data_to_fill);
|
||||
i += data_to_fill;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,65 +6,65 @@
|
||||
|
||||
namespace fuzzer
|
||||
{
|
||||
class random_generator
|
||||
{
|
||||
public:
|
||||
random_generator();
|
||||
class random_generator
|
||||
{
|
||||
public:
|
||||
random_generator();
|
||||
|
||||
void fill(std::span<uint8_t> data);
|
||||
void fill(void* data, size_t size);
|
||||
void fill(std::span<uint8_t> data);
|
||||
void fill(void* data, size_t size);
|
||||
|
||||
template <typename T>
|
||||
requires(std::is_trivially_copyable_v<T>)
|
||||
T get()
|
||||
{
|
||||
T value{};
|
||||
this->fill(&value, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
template <typename T>
|
||||
requires(std::is_trivially_copyable_v<T>)
|
||||
T get()
|
||||
{
|
||||
T value{};
|
||||
this->fill(&value, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T get(const T& max)
|
||||
{
|
||||
return this->get<T>() % max;
|
||||
}
|
||||
template <typename T>
|
||||
T get(const T& max)
|
||||
{
|
||||
return this->get<T>() % max;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T get(T min, T max)
|
||||
{
|
||||
if (max < min)
|
||||
{
|
||||
std::swap(max, min);
|
||||
}
|
||||
template <typename T>
|
||||
T get(T min, T max)
|
||||
{
|
||||
if (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>
|
||||
T get_geometric()
|
||||
{
|
||||
T value{0};
|
||||
template <typename T>
|
||||
T get_geometric()
|
||||
{
|
||||
T value{0};
|
||||
|
||||
while (this->get<bool>())
|
||||
{
|
||||
++value;
|
||||
}
|
||||
while (this->get<bool>())
|
||||
{
|
||||
++value;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mt19937 rng_;
|
||||
std::uniform_int_distribution<std::mt19937::result_type> distribution_{};
|
||||
private:
|
||||
std::mt19937 rng_;
|
||||
std::uniform_int_distribution<std::mt19937::result_type> distribution_{};
|
||||
|
||||
std::mt19937::result_type generate_number();
|
||||
};
|
||||
std::mt19937::result_type generate_number();
|
||||
};
|
||||
|
||||
template <>
|
||||
inline bool random_generator::get<bool>()
|
||||
{
|
||||
return (this->generate_number() & 1) != 0;
|
||||
}
|
||||
template <>
|
||||
inline bool random_generator::get<bool>()
|
||||
{
|
||||
return (this->generate_number() & 1) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@ __declspec(dllexport) bool do_the_task = true;
|
||||
|
||||
struct tls_struct
|
||||
{
|
||||
DWORD num = 1337;
|
||||
DWORD num = 1337;
|
||||
|
||||
tls_struct()
|
||||
{
|
||||
num = GetCurrentThreadId();
|
||||
}
|
||||
tls_struct()
|
||||
{
|
||||
num = GetCurrentThreadId();
|
||||
}
|
||||
};
|
||||
|
||||
thread_local tls_struct tls_var{};
|
||||
@@ -32,297 +32,293 @@ thread_local tls_struct tls_var{};
|
||||
// getenv is broken right now :(
|
||||
std::string read_env(const char* env)
|
||||
{
|
||||
char buffer[0x1000] = {};
|
||||
if (!GetEnvironmentVariableA(env, buffer, sizeof(buffer)))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
char buffer[0x1000] = {};
|
||||
if (!GetEnvironmentVariableA(env, buffer, sizeof(buffer)))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return buffer;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
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{};
|
||||
threads.reserve(thread_count);
|
||||
std::vector<std::thread> threads{};
|
||||
threads.reserve(thread_count);
|
||||
|
||||
for (auto i = 0ULL; i < thread_count; ++i)
|
||||
{
|
||||
threads.emplace_back([&counter]
|
||||
{
|
||||
++counter;
|
||||
std::this_thread::yield();
|
||||
++counter;
|
||||
// Host scheduling/cpu performance can have impact on emulator scheduling
|
||||
//std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
++counter;
|
||||
});
|
||||
}
|
||||
for (auto i = 0ULL; i < thread_count; ++i)
|
||||
{
|
||||
threads.emplace_back([&counter] {
|
||||
++counter;
|
||||
std::this_thread::yield();
|
||||
++counter;
|
||||
// Host scheduling/cpu performance can have impact on emulator scheduling
|
||||
// std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
++counter;
|
||||
});
|
||||
}
|
||||
|
||||
for (auto& t : threads)
|
||||
{
|
||||
t.join();
|
||||
}
|
||||
for (auto& t : threads)
|
||||
{
|
||||
t.join();
|
||||
}
|
||||
|
||||
return counter == (thread_count * 3ULL);
|
||||
return counter == (thread_count * 3ULL);
|
||||
}
|
||||
|
||||
bool test_tls()
|
||||
{
|
||||
std::atomic_bool kill{false};
|
||||
std::atomic_uint32_t successes{0};
|
||||
constexpr uint32_t thread_count = 2;
|
||||
std::atomic_bool kill{false};
|
||||
std::atomic_uint32_t successes{0};
|
||||
constexpr uint32_t thread_count = 2;
|
||||
|
||||
std::vector<std::thread> ts{};
|
||||
kill = false;
|
||||
std::vector<std::thread> ts{};
|
||||
kill = false;
|
||||
|
||||
for (size_t i = 0; i < thread_count; ++i)
|
||||
{
|
||||
ts.emplace_back([&]
|
||||
{
|
||||
while (!kill)
|
||||
{
|
||||
std::this_thread::yield();
|
||||
}
|
||||
for (size_t i = 0; i < thread_count; ++i)
|
||||
{
|
||||
ts.emplace_back([&] {
|
||||
while (!kill)
|
||||
{
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
if (tls_var.num == GetCurrentThreadId())
|
||||
{
|
||||
++successes;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (tls_var.num == GetCurrentThreadId())
|
||||
{
|
||||
++successes;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
LoadLibraryA("d3dcompiler_47.dll");
|
||||
LoadLibraryA("dsound.dll");
|
||||
/*LoadLibraryA("d3d9.dll");
|
||||
LoadLibraryA("dxgi.dll");
|
||||
LoadLibraryA("wlanapi.dll");*/
|
||||
LoadLibraryA("d3dcompiler_47.dll");
|
||||
LoadLibraryA("dsound.dll");
|
||||
/*LoadLibraryA("d3d9.dll");
|
||||
LoadLibraryA("dxgi.dll");
|
||||
LoadLibraryA("wlanapi.dll");*/
|
||||
|
||||
kill = true;
|
||||
kill = true;
|
||||
|
||||
for (auto& t : ts)
|
||||
{
|
||||
if (t.joinable())
|
||||
{
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
for (auto& t : ts)
|
||||
{
|
||||
if (t.joinable())
|
||||
{
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
|
||||
return successes == thread_count;
|
||||
return successes == thread_count;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
const auto* filename = "a.txt";
|
||||
const auto* filename = "a.txt";
|
||||
|
||||
FILE* fp{};
|
||||
(void)fopen_s(&fp, filename, "wb");
|
||||
FILE* fp{};
|
||||
(void)fopen_s(&fp, filename, "wb");
|
||||
|
||||
if (!fp)
|
||||
{
|
||||
puts("Bad file");
|
||||
return false;
|
||||
}
|
||||
if (!fp)
|
||||
{
|
||||
puts("Bad file");
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string text = "Blub";
|
||||
const std::string text = "Blub";
|
||||
|
||||
(void)fwrite(text.data(), 1, text.size(), fp);
|
||||
(void)fclose(fp);
|
||||
(void)fwrite(text.data(), 1, text.size(), fp);
|
||||
(void)fclose(fp);
|
||||
|
||||
std::ifstream t(filename);
|
||||
t.seekg(0, std::ios::end);
|
||||
const size_t size = t.tellg();
|
||||
std::string buffer(size, ' ');
|
||||
t.seekg(0);
|
||||
t.read(buffer.data(), static_cast<std::streamsize>(size));
|
||||
std::ifstream t(filename);
|
||||
t.seekg(0, std::ios::end);
|
||||
const size_t size = t.tellg();
|
||||
std::string buffer(size, ' ');
|
||||
t.seekg(0);
|
||||
t.read(buffer.data(), static_cast<std::streamsize>(size));
|
||||
|
||||
return text == buffer;
|
||||
return text == buffer;
|
||||
}
|
||||
|
||||
bool test_dir_io()
|
||||
{
|
||||
size_t count = 0;
|
||||
size_t count = 0;
|
||||
|
||||
for (auto i : std::filesystem::directory_iterator(R"(C:\Windows\System32\)"))
|
||||
{
|
||||
++count;
|
||||
if (count > 30)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (auto i : std::filesystem::directory_iterator(R"(C:\Windows\System32\)"))
|
||||
{
|
||||
++count;
|
||||
if (count > 30)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return count > 30;
|
||||
return count > 30;
|
||||
}
|
||||
|
||||
std::optional<std::string> read_registry_string(const HKEY root, const char* path, const char* value)
|
||||
{
|
||||
HKEY key{};
|
||||
if (RegOpenKeyExA(root, path, 0, KEY_READ, &key) !=
|
||||
ERROR_SUCCESS)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
HKEY key{};
|
||||
if (RegOpenKeyExA(root, path, 0, KEY_READ, &key) != ERROR_SUCCESS)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
char data[MAX_PATH]{};
|
||||
DWORD length = sizeof(data);
|
||||
const auto res = RegQueryValueExA(key, value, nullptr, nullptr, reinterpret_cast<uint8_t*>(data), &length);
|
||||
char data[MAX_PATH]{};
|
||||
DWORD length = sizeof(data);
|
||||
const auto res = RegQueryValueExA(key, value, nullptr, nullptr, reinterpret_cast<uint8_t*>(data), &length);
|
||||
|
||||
if (RegCloseKey(key) != ERROR_SUCCESS)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
if (RegCloseKey(key) != ERROR_SUCCESS)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (res != ERROR_SUCCESS)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
if (res != ERROR_SUCCESS)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
if (length == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return {std::string(data, min(length - 1, sizeof(data)))};
|
||||
return {std::string(data, min(length - 1, sizeof(data)))};
|
||||
}
|
||||
|
||||
bool test_registry()
|
||||
{
|
||||
const auto val = read_registry_string(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows\CurrentVersion)",
|
||||
"ProgramFilesDir");
|
||||
if (!val)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto val =
|
||||
read_registry_string(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows\CurrentVersion)", "ProgramFilesDir");
|
||||
if (!val)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return *val == "C:\\Program Files";
|
||||
return *val == "C:\\Program Files";
|
||||
}
|
||||
|
||||
void throw_exception()
|
||||
{
|
||||
if (do_the_task)
|
||||
{
|
||||
throw std::runtime_error("OK");
|
||||
}
|
||||
if (do_the_task)
|
||||
{
|
||||
throw std::runtime_error("OK");
|
||||
}
|
||||
}
|
||||
|
||||
bool test_exceptions()
|
||||
{
|
||||
try
|
||||
{
|
||||
throw_exception();
|
||||
return false;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
return e.what() == std::string("OK");
|
||||
}
|
||||
try
|
||||
{
|
||||
throw_exception();
|
||||
return false;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
return e.what() == std::string("OK");
|
||||
}
|
||||
}
|
||||
|
||||
void throw_access_violation()
|
||||
{
|
||||
if (do_the_task)
|
||||
{
|
||||
*reinterpret_cast<int*>(1) = 1;
|
||||
}
|
||||
if (do_the_task)
|
||||
{
|
||||
*reinterpret_cast<int*>(1) = 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool test_access_violation_exception()
|
||||
{
|
||||
__try
|
||||
{
|
||||
throw_access_violation();
|
||||
return false;
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
return GetExceptionCode() == STATUS_ACCESS_VIOLATION;
|
||||
}
|
||||
__try
|
||||
{
|
||||
throw_access_violation();
|
||||
return false;
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
return GetExceptionCode() == STATUS_ACCESS_VIOLATION;
|
||||
}
|
||||
}
|
||||
|
||||
bool test_ud2_exception(void* address)
|
||||
{
|
||||
__try
|
||||
{
|
||||
static_cast<void(*)()>(address)();
|
||||
return false;
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
return GetExceptionCode() == STATUS_ILLEGAL_INSTRUCTION;
|
||||
}
|
||||
__try
|
||||
{
|
||||
static_cast<void (*)()>(address)();
|
||||
return false;
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
return GetExceptionCode() == STATUS_ILLEGAL_INSTRUCTION;
|
||||
}
|
||||
}
|
||||
|
||||
bool test_illegal_instruction_exception()
|
||||
{
|
||||
const auto address = VirtualAlloc(nullptr, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
if (!address)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto address = VirtualAlloc(nullptr, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
if (!address)
|
||||
{
|
||||
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()
|
||||
{
|
||||
return test_access_violation_exception()
|
||||
&& test_illegal_instruction_exception();
|
||||
return test_access_violation_exception() && test_illegal_instruction_exception();
|
||||
}
|
||||
|
||||
void print_time()
|
||||
{
|
||||
const auto epoch_time = std::chrono::system_clock::now().time_since_epoch();
|
||||
printf("Time: %lld\n", epoch_time.count());
|
||||
const auto epoch_time = std::chrono::system_clock::now().time_since_epoch();
|
||||
printf("Time: %lld\n", epoch_time.count());
|
||||
}
|
||||
|
||||
#define RUN_TEST(func, name) \
|
||||
{ \
|
||||
printf("Running test '" name "': "); \
|
||||
const auto res = func(); \
|
||||
valid &= res; \
|
||||
puts(res ? "Success" : "Fail"); \
|
||||
}
|
||||
#define RUN_TEST(func, name) \
|
||||
{ \
|
||||
printf("Running test '" name "': "); \
|
||||
const auto res = func(); \
|
||||
valid &= res; \
|
||||
puts(res ? "Success" : "Fail"); \
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
if (argc == 2 && argv[1] == "-time"sv)
|
||||
{
|
||||
print_time();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && argv[1] == "-time"sv)
|
||||
{
|
||||
print_time();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool valid = true;
|
||||
bool valid = true;
|
||||
|
||||
RUN_TEST(test_io, "I/O")
|
||||
RUN_TEST(test_dir_io, "Dir I/O")
|
||||
RUN_TEST(test_registry, "Registry")
|
||||
RUN_TEST(test_threads, "Threads")
|
||||
RUN_TEST(test_env, "Environment")
|
||||
RUN_TEST(test_exceptions, "Exceptions")
|
||||
RUN_TEST(test_native_exceptions, "Native Exceptions")
|
||||
RUN_TEST(test_tls, "TLS")
|
||||
RUN_TEST(test_io, "I/O")
|
||||
RUN_TEST(test_dir_io, "Dir I/O")
|
||||
RUN_TEST(test_registry, "Registry")
|
||||
RUN_TEST(test_threads, "Threads")
|
||||
RUN_TEST(test_env, "Environment")
|
||||
RUN_TEST(test_exceptions, "Exceptions")
|
||||
RUN_TEST(test_native_exceptions, "Native Exceptions")
|
||||
RUN_TEST(test_tls, "TLS")
|
||||
|
||||
return valid ? 0 : 1;
|
||||
return valid ? 0 : 1;
|
||||
}
|
||||
|
||||
@@ -8,36 +8,35 @@
|
||||
template <typename ReturnType, typename... Args>
|
||||
class function_wrapper : public object
|
||||
{
|
||||
public:
|
||||
using user_data_pointer = void*;
|
||||
using c_function_type = ReturnType(Args..., user_data_pointer);
|
||||
using functor_type = std::function<ReturnType(Args...)>;
|
||||
public:
|
||||
using user_data_pointer = void*;
|
||||
using c_function_type = ReturnType(Args..., user_data_pointer);
|
||||
using functor_type = std::function<ReturnType(Args...)>;
|
||||
|
||||
function_wrapper() = default;
|
||||
function_wrapper() = default;
|
||||
|
||||
function_wrapper(functor_type functor)
|
||||
: functor_(std::make_unique<functor_type>(std::move(functor)))
|
||||
{
|
||||
}
|
||||
function_wrapper(functor_type functor)
|
||||
: functor_(std::make_unique<functor_type>(std::move(functor)))
|
||||
{
|
||||
}
|
||||
|
||||
c_function_type* get_c_function() const
|
||||
{
|
||||
return +[](Args... args, user_data_pointer user_data) -> ReturnType
|
||||
{
|
||||
return (*static_cast<functor_type*>(user_data))(std::forward<Args>(args)...);
|
||||
};
|
||||
}
|
||||
c_function_type* get_c_function() const
|
||||
{
|
||||
return +[](Args... args, user_data_pointer user_data) -> ReturnType {
|
||||
return (*static_cast<functor_type*>(user_data))(std::forward<Args>(args)...);
|
||||
};
|
||||
}
|
||||
|
||||
void* get_function() const
|
||||
{
|
||||
return reinterpret_cast<void*>(this->get_c_function());
|
||||
}
|
||||
void* get_function() const
|
||||
{
|
||||
return reinterpret_cast<void*>(this->get_c_function());
|
||||
}
|
||||
|
||||
user_data_pointer get_user_data() const
|
||||
{
|
||||
return this->functor_.get();
|
||||
}
|
||||
user_data_pointer get_user_data() const
|
||||
{
|
||||
return this->functor_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<functor_type> functor_{};
|
||||
private:
|
||||
std::unique_ptr<functor_type> functor_{};
|
||||
};
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
struct object
|
||||
{
|
||||
object() = default;
|
||||
virtual ~object() = default;
|
||||
object() = default;
|
||||
virtual ~object() = default;
|
||||
|
||||
object(object&&) = default;
|
||||
object(const object&) = default;
|
||||
object& operator=(object&&) = default;
|
||||
object& operator=(const object&) = default;
|
||||
object(object&&) = default;
|
||||
object(const object&) = default;
|
||||
object& operator=(object&&) = default;
|
||||
object& operator=(const object&) = default;
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4505)
|
||||
#pragma warning(disable : 4505)
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
@@ -25,27 +25,27 @@
|
||||
|
||||
namespace unicorn
|
||||
{
|
||||
struct unicorn_error : std::runtime_error
|
||||
{
|
||||
unicorn_error(const uc_err error_code)
|
||||
: std::runtime_error(uc_strerror(error_code))
|
||||
, code(error_code)
|
||||
{
|
||||
}
|
||||
struct unicorn_error : std::runtime_error
|
||||
{
|
||||
unicorn_error(const uc_err error_code)
|
||||
: std::runtime_error(uc_strerror(error_code)),
|
||||
code(error_code)
|
||||
{
|
||||
}
|
||||
|
||||
uc_err code{};
|
||||
};
|
||||
uc_err code{};
|
||||
};
|
||||
|
||||
inline void throw_if_unicorn_error(const uc_err error_code)
|
||||
{
|
||||
if (error_code != UC_ERR_OK)
|
||||
{
|
||||
throw unicorn_error(error_code);
|
||||
}
|
||||
}
|
||||
inline void throw_if_unicorn_error(const uc_err error_code)
|
||||
{
|
||||
if (error_code != UC_ERR_OK)
|
||||
{
|
||||
throw unicorn_error(error_code);
|
||||
}
|
||||
}
|
||||
|
||||
inline void uce(const uc_err error_code)
|
||||
{
|
||||
throw_if_unicorn_error(error_code);
|
||||
}
|
||||
inline void uce(const uc_err error_code)
|
||||
{
|
||||
throw_if_unicorn_error(error_code);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,74 +4,73 @@
|
||||
|
||||
namespace unicorn
|
||||
{
|
||||
class unicorn_hook
|
||||
{
|
||||
public:
|
||||
unicorn_hook() = default;
|
||||
class unicorn_hook
|
||||
{
|
||||
public:
|
||||
unicorn_hook() = default;
|
||||
|
||||
unicorn_hook(uc_engine* uc)
|
||||
: unicorn_hook(uc, {})
|
||||
{
|
||||
}
|
||||
unicorn_hook(uc_engine* uc)
|
||||
: unicorn_hook(uc, {})
|
||||
{
|
||||
}
|
||||
|
||||
unicorn_hook(uc_engine* uc, const uc_hook hook)
|
||||
: uc_(uc)
|
||||
, hook_(hook)
|
||||
{
|
||||
}
|
||||
unicorn_hook(uc_engine* uc, const uc_hook hook)
|
||||
: uc_(uc),
|
||||
hook_(hook)
|
||||
{
|
||||
}
|
||||
|
||||
~unicorn_hook()
|
||||
{
|
||||
release();
|
||||
}
|
||||
~unicorn_hook()
|
||||
{
|
||||
release();
|
||||
}
|
||||
|
||||
unicorn_hook(const unicorn_hook&) = delete;
|
||||
unicorn_hook& operator=(const unicorn_hook&) = delete;
|
||||
unicorn_hook(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
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
uc_hook* make_reference()
|
||||
{
|
||||
if (!this->uc_)
|
||||
{
|
||||
throw std::runtime_error("Cannot make reference on default constructed hook");
|
||||
}
|
||||
|
||||
uc_hook* make_reference()
|
||||
{
|
||||
if (!this->uc_)
|
||||
{
|
||||
throw std::runtime_error("Cannot make reference on default constructed hook");
|
||||
}
|
||||
this->release();
|
||||
return &this->hook_;
|
||||
}
|
||||
|
||||
this->release();
|
||||
return &this->hook_;
|
||||
}
|
||||
unicorn_hook& operator=(unicorn_hook&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->release();
|
||||
|
||||
unicorn_hook& operator=(unicorn_hook&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->release();
|
||||
this->uc_ = obj.uc_;
|
||||
this->hook_ = obj.hook_;
|
||||
|
||||
this->uc_ = obj.uc_;
|
||||
this->hook_ = obj.hook_;
|
||||
obj.hook_ = {};
|
||||
obj.uc_ = {};
|
||||
}
|
||||
|
||||
obj.hook_ = {};
|
||||
obj.uc_ = {};
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
void release()
|
||||
{
|
||||
if (this->hook_ && this->uc_)
|
||||
{
|
||||
uc_hook_del(this->uc_, this->hook_);
|
||||
this->hook_ = {};
|
||||
}
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
if (this->hook_ && this->uc_)
|
||||
{
|
||||
uc_hook_del(this->uc_, this->hook_);
|
||||
this->hook_ = {};
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
uc_engine* uc_{};
|
||||
uc_hook hook_{};
|
||||
};
|
||||
private:
|
||||
uc_engine* uc_{};
|
||||
uc_hook hook_{};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,62 +6,61 @@
|
||||
|
||||
namespace unicorn
|
||||
{
|
||||
class unicorn_memory_regions
|
||||
{
|
||||
public:
|
||||
unicorn_memory_regions(uc_engine* uc)
|
||||
{
|
||||
uce(uc_mem_regions(uc, &this->regions_, &this->count_));
|
||||
}
|
||||
class unicorn_memory_regions
|
||||
{
|
||||
public:
|
||||
unicorn_memory_regions(uc_engine* uc)
|
||||
{
|
||||
uce(uc_mem_regions(uc, &this->regions_, &this->count_));
|
||||
}
|
||||
|
||||
~unicorn_memory_regions()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
~unicorn_memory_regions()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
unicorn_memory_regions(const unicorn_memory_regions&) = delete;
|
||||
unicorn_memory_regions& operator=(const unicorn_memory_regions&) = delete;
|
||||
|
||||
unicorn_memory_regions(const unicorn_memory_regions&) = delete;
|
||||
unicorn_memory_regions& operator=(const unicorn_memory_regions&) = delete;
|
||||
unicorn_memory_regions(unicorn_memory_regions&& obj) noexcept
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
|
||||
unicorn_memory_regions(unicorn_memory_regions&& obj) noexcept
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
unicorn_memory_regions& operator=(unicorn_memory_regions&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->release();
|
||||
|
||||
unicorn_memory_regions& operator=(unicorn_memory_regions&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->release();
|
||||
this->count_ = obj.count_;
|
||||
this->regions_ = obj.regions_;
|
||||
|
||||
this->count_ = obj.count_;
|
||||
this->regions_ = obj.regions_;
|
||||
obj.count_ = {};
|
||||
obj.regions_ = nullptr;
|
||||
}
|
||||
|
||||
obj.count_ = {};
|
||||
obj.regions_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
std::span<uc_mem_region> get_span() const
|
||||
{
|
||||
return {this->regions_, this->count_};
|
||||
}
|
||||
|
||||
std::span<uc_mem_region> get_span() const
|
||||
{
|
||||
return {this->regions_, this->count_};
|
||||
}
|
||||
private:
|
||||
uint32_t count_{};
|
||||
uc_mem_region* regions_{};
|
||||
|
||||
private:
|
||||
uint32_t count_{};
|
||||
uc_mem_region* regions_{};
|
||||
void release()
|
||||
{
|
||||
if (this->regions_)
|
||||
{
|
||||
uc_free(regions_);
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
if (this->regions_)
|
||||
{
|
||||
uc_free(regions_);
|
||||
}
|
||||
|
||||
this->count_ = {};
|
||||
this->regions_ = nullptr;
|
||||
}
|
||||
};
|
||||
this->count_ = {};
|
||||
this->regions_ = nullptr;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,6 @@
|
||||
|
||||
namespace unicorn
|
||||
{
|
||||
UNICORN_EMULATOR_DLL_STORAGE
|
||||
std::unique_ptr<x64_emulator> create_x64_emulator();
|
||||
UNICORN_EMULATOR_DLL_STORAGE
|
||||
std::unique_ptr<x64_emulator> create_x64_emulator();
|
||||
}
|
||||
|
||||
@@ -2,46 +2,46 @@
|
||||
|
||||
namespace test
|
||||
{
|
||||
TEST(EmulationTest, BasicEmulationWorks)
|
||||
{
|
||||
auto emu = create_sample_emulator();
|
||||
emu.start();
|
||||
TEST(EmulationTest, BasicEmulationWorks)
|
||||
{
|
||||
auto emu = create_sample_emulator();
|
||||
emu.start();
|
||||
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(emu);
|
||||
}
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(emu);
|
||||
}
|
||||
|
||||
TEST(EmulationTest, CountedEmulationWorks)
|
||||
{
|
||||
constexpr auto count = 200000;
|
||||
TEST(EmulationTest, CountedEmulationWorks)
|
||||
{
|
||||
constexpr auto count = 200000;
|
||||
|
||||
auto emu = create_sample_emulator();
|
||||
emu.start({}, count);
|
||||
auto emu = create_sample_emulator();
|
||||
emu.start({}, count);
|
||||
|
||||
ASSERT_EQ(emu.process().executed_instructions, count);
|
||||
}
|
||||
ASSERT_EQ(emu.process().executed_instructions, count);
|
||||
}
|
||||
|
||||
TEST(EmulationTest, CountedEmulationIsAccurate)
|
||||
{
|
||||
auto emu = create_sample_emulator();
|
||||
emu.start();
|
||||
TEST(EmulationTest, CountedEmulationIsAccurate)
|
||||
{
|
||||
auto emu = create_sample_emulator();
|
||||
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;
|
||||
const auto instructionsToExecute = executedInstructions - offset;
|
||||
constexpr auto offset = 1;
|
||||
const auto instructionsToExecute = executedInstructions - offset;
|
||||
|
||||
new_emu.start({}, instructionsToExecute);
|
||||
new_emu.start({}, instructionsToExecute);
|
||||
|
||||
ASSERT_EQ(new_emu.process().executed_instructions, instructionsToExecute);
|
||||
ASSERT_NOT_TERMINATED(new_emu);
|
||||
ASSERT_EQ(new_emu.process().executed_instructions, instructionsToExecute);
|
||||
ASSERT_NOT_TERMINATED(new_emu);
|
||||
|
||||
new_emu.start({}, offset);
|
||||
new_emu.start({}, offset);
|
||||
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(new_emu);
|
||||
ASSERT_EQ(new_emu.process().executed_instructions, executedInstructions);
|
||||
}
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(new_emu);
|
||||
ASSERT_EQ(new_emu.process().executed_instructions, executedInstructions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,37 +3,36 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <windows_emulator.hpp>
|
||||
|
||||
#define ASSERT_NOT_TERMINATED(win_emu) \
|
||||
do { \
|
||||
ASSERT_FALSE((win_emu).process().exit_status.has_value()); \
|
||||
} while(false)
|
||||
#define ASSERT_NOT_TERMINATED(win_emu) \
|
||||
do \
|
||||
{ \
|
||||
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) \
|
||||
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)
|
||||
#define ASSERT_TERMINATED_SUCCESSFULLY(win_emu) ASSERT_TERMINATED_WITH_STATUS(win_emu, STATUS_SUCCESS)
|
||||
|
||||
namespace test
|
||||
{
|
||||
inline windows_emulator create_sample_emulator(emulator_settings settings)
|
||||
{
|
||||
settings.application = "./test-sample.exe";
|
||||
return windows_emulator{std::move(settings)};
|
||||
}
|
||||
inline windows_emulator create_sample_emulator(emulator_settings settings)
|
||||
{
|
||||
settings.application = "./test-sample.exe";
|
||||
return windows_emulator{std::move(settings)};
|
||||
}
|
||||
|
||||
inline windows_emulator create_sample_emulator()
|
||||
{
|
||||
emulator_settings settings
|
||||
{
|
||||
.disable_logging = true,
|
||||
.use_relative_time = true,
|
||||
};
|
||||
inline windows_emulator create_sample_emulator()
|
||||
{
|
||||
emulator_settings settings{
|
||||
.disable_logging = 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[])
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -2,77 +2,77 @@
|
||||
|
||||
namespace test
|
||||
{
|
||||
TEST(SerializationTest, SerializedDataIsReproducible)
|
||||
{
|
||||
auto emu1 = create_sample_emulator();
|
||||
emu1.start();
|
||||
TEST(SerializationTest, SerializedDataIsReproducible)
|
||||
{
|
||||
auto emu1 = create_sample_emulator();
|
||||
emu1.start();
|
||||
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(emu1);
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(emu1);
|
||||
|
||||
utils::buffer_serializer serializer1{};
|
||||
emu1.serialize(serializer1);
|
||||
utils::buffer_serializer serializer1{};
|
||||
emu1.serialize(serializer1);
|
||||
|
||||
utils::buffer_deserializer deserializer{serializer1.get_buffer()};
|
||||
utils::buffer_deserializer deserializer{serializer1.get_buffer()};
|
||||
|
||||
windows_emulator new_emu{};
|
||||
new_emu.deserialize(deserializer);
|
||||
windows_emulator new_emu{};
|
||||
new_emu.deserialize(deserializer);
|
||||
|
||||
utils::buffer_serializer serializer2{};
|
||||
new_emu.serialize(serializer2);
|
||||
utils::buffer_serializer serializer2{};
|
||||
new_emu.serialize(serializer2);
|
||||
|
||||
auto buffer1 = serializer1.move_buffer();
|
||||
auto buffer2 = serializer2.move_buffer();
|
||||
auto buffer1 = serializer1.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)
|
||||
{
|
||||
auto emu1 = create_sample_emulator();
|
||||
emu1.start();
|
||||
TEST(SerializationTest, EmulationIsReproducible)
|
||||
{
|
||||
auto emu1 = create_sample_emulator();
|
||||
emu1.start();
|
||||
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(emu1);
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(emu1);
|
||||
|
||||
utils::buffer_serializer serializer1{};
|
||||
emu1.serialize(serializer1);
|
||||
utils::buffer_serializer serializer1{};
|
||||
emu1.serialize(serializer1);
|
||||
|
||||
auto emu2 = create_sample_emulator();
|
||||
emu2.start();
|
||||
auto emu2 = create_sample_emulator();
|
||||
emu2.start();
|
||||
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(emu2);
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(emu2);
|
||||
|
||||
utils::buffer_serializer serializer2{};
|
||||
emu2.serialize(serializer2);
|
||||
utils::buffer_serializer serializer2{};
|
||||
emu2.serialize(serializer2);
|
||||
|
||||
ASSERT_EQ(serializer1.get_buffer(), serializer2.get_buffer());
|
||||
}
|
||||
ASSERT_EQ(serializer1.get_buffer(), serializer2.get_buffer());
|
||||
}
|
||||
|
||||
TEST(SerializationTest, DeserializedEmulatorBehavesLikeSource)
|
||||
{
|
||||
auto emu = create_sample_emulator();
|
||||
emu.start({}, 100);
|
||||
TEST(SerializationTest, DeserializedEmulatorBehavesLikeSource)
|
||||
{
|
||||
auto emu = create_sample_emulator();
|
||||
emu.start({}, 100);
|
||||
|
||||
utils::buffer_serializer serializer{};
|
||||
emu.serialize(serializer);
|
||||
utils::buffer_serializer serializer{};
|
||||
emu.serialize(serializer);
|
||||
|
||||
utils::buffer_deserializer deserializer{serializer.get_buffer()};
|
||||
utils::buffer_deserializer deserializer{serializer.get_buffer()};
|
||||
|
||||
windows_emulator new_emu{};
|
||||
new_emu.log.disable_output(true);
|
||||
new_emu.deserialize(deserializer);
|
||||
windows_emulator new_emu{};
|
||||
new_emu.log.disable_output(true);
|
||||
new_emu.deserialize(deserializer);
|
||||
|
||||
new_emu.start();
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(new_emu);
|
||||
new_emu.start();
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(new_emu);
|
||||
|
||||
emu.start();
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(emu);
|
||||
emu.start();
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(emu);
|
||||
|
||||
utils::buffer_serializer serializer1{};
|
||||
utils::buffer_serializer serializer2{};
|
||||
utils::buffer_serializer serializer1{};
|
||||
utils::buffer_serializer serializer2{};
|
||||
|
||||
emu.serialize(serializer1);
|
||||
new_emu.serialize(serializer2);
|
||||
emu.serialize(serializer1);
|
||||
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
|
||||
{
|
||||
TEST(TimeTest, SystemTimeIsAccurate)
|
||||
{
|
||||
std::string output_buffer{};
|
||||
TEST(TimeTest, SystemTimeIsAccurate)
|
||||
{
|
||||
std::string output_buffer{};
|
||||
|
||||
const emulator_settings settings{
|
||||
.arguments = {u"-time"},
|
||||
.stdout_callback = [&output_buffer](const std::string_view data)
|
||||
{
|
||||
output_buffer.append(data);
|
||||
},
|
||||
.disable_logging = true,
|
||||
.use_relative_time = false,
|
||||
};
|
||||
const emulator_settings settings{
|
||||
.arguments = {u"-time"},
|
||||
.stdout_callback = [&output_buffer](const std::string_view data) { output_buffer.append(data); },
|
||||
.disable_logging = true,
|
||||
.use_relative_time = false,
|
||||
};
|
||||
|
||||
auto emu = create_sample_emulator(settings);
|
||||
emu.start();
|
||||
auto emu = create_sample_emulator(settings);
|
||||
emu.start();
|
||||
|
||||
constexpr auto prefix = "Time: "sv;
|
||||
constexpr auto prefix = "Time: "sv;
|
||||
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(emu);
|
||||
ASSERT_TRUE(output_buffer.starts_with(prefix));
|
||||
ASSERT_TERMINATED_SUCCESSFULLY(emu);
|
||||
ASSERT_TRUE(output_buffer.starts_with(prefix));
|
||||
|
||||
output_buffer = output_buffer.substr(prefix.size());
|
||||
while (!output_buffer.empty() && (output_buffer.back() == '\n' || output_buffer.back() == '\r'))
|
||||
{
|
||||
output_buffer.pop_back();
|
||||
}
|
||||
output_buffer = output_buffer.substr(prefix.size());
|
||||
while (!output_buffer.empty() && (output_buffer.back() == '\n' || output_buffer.back() == '\r'))
|
||||
{
|
||||
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 tp(time_duration);
|
||||
const time_point::duration time_duration(time);
|
||||
const time_point tp(time_duration);
|
||||
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
const auto diff = now - tp;
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
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
|
||||
{
|
||||
void restore(x64_emulator& emu, const CONTEXT64& context)
|
||||
{
|
||||
if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS_64)
|
||||
{
|
||||
emu.reg(x64_register::dr0, context.Dr0);
|
||||
emu.reg(x64_register::dr1, context.Dr1);
|
||||
emu.reg(x64_register::dr2, context.Dr2);
|
||||
emu.reg(x64_register::dr3, context.Dr3);
|
||||
emu.reg(x64_register::dr6, context.Dr6);
|
||||
emu.reg(x64_register::dr7, context.Dr7);
|
||||
}
|
||||
void restore(x64_emulator& emu, const CONTEXT64& context)
|
||||
{
|
||||
if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS_64)
|
||||
{
|
||||
emu.reg(x64_register::dr0, context.Dr0);
|
||||
emu.reg(x64_register::dr1, context.Dr1);
|
||||
emu.reg(x64_register::dr2, context.Dr2);
|
||||
emu.reg(x64_register::dr3, context.Dr3);
|
||||
emu.reg(x64_register::dr6, context.Dr6);
|
||||
emu.reg(x64_register::dr7, context.Dr7);
|
||||
}
|
||||
|
||||
if (context.ContextFlags & CONTEXT_CONTROL_64)
|
||||
{
|
||||
emu.reg<uint16_t>(x64_register::ss, context.SegSs);
|
||||
emu.reg<uint16_t>(x64_register::cs, context.SegCs);
|
||||
if (context.ContextFlags & CONTEXT_CONTROL_64)
|
||||
{
|
||||
emu.reg<uint16_t>(x64_register::ss, context.SegSs);
|
||||
emu.reg<uint16_t>(x64_register::cs, context.SegCs);
|
||||
|
||||
emu.reg(x64_register::rip, context.Rip);
|
||||
emu.reg(x64_register::rsp, context.Rsp);
|
||||
emu.reg(x64_register::rip, context.Rip);
|
||||
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)
|
||||
{
|
||||
emu.reg(x64_register::rax, context.Rax);
|
||||
emu.reg(x64_register::rbx, context.Rbx);
|
||||
emu.reg(x64_register::rcx, context.Rcx);
|
||||
emu.reg(x64_register::rdx, context.Rdx);
|
||||
emu.reg(x64_register::rbp, context.Rbp);
|
||||
emu.reg(x64_register::rsi, context.Rsi);
|
||||
emu.reg(x64_register::rdi, context.Rdi);
|
||||
emu.reg(x64_register::r8, context.R8);
|
||||
emu.reg(x64_register::r9, context.R9);
|
||||
emu.reg(x64_register::r10, context.R10);
|
||||
emu.reg(x64_register::r11, context.R11);
|
||||
emu.reg(x64_register::r12, context.R12);
|
||||
emu.reg(x64_register::r13, context.R13);
|
||||
emu.reg(x64_register::r14, context.R14);
|
||||
emu.reg(x64_register::r15, context.R15);
|
||||
}
|
||||
if (context.ContextFlags & CONTEXT_INTEGER_64)
|
||||
{
|
||||
emu.reg(x64_register::rax, context.Rax);
|
||||
emu.reg(x64_register::rbx, context.Rbx);
|
||||
emu.reg(x64_register::rcx, context.Rcx);
|
||||
emu.reg(x64_register::rdx, context.Rdx);
|
||||
emu.reg(x64_register::rbp, context.Rbp);
|
||||
emu.reg(x64_register::rsi, context.Rsi);
|
||||
emu.reg(x64_register::rdi, context.Rdi);
|
||||
emu.reg(x64_register::r8, context.R8);
|
||||
emu.reg(x64_register::r9, context.R9);
|
||||
emu.reg(x64_register::r10, context.R10);
|
||||
emu.reg(x64_register::r11, context.R11);
|
||||
emu.reg(x64_register::r12, context.R12);
|
||||
emu.reg(x64_register::r13, context.R13);
|
||||
emu.reg(x64_register::r14, context.R14);
|
||||
emu.reg(x64_register::r15, context.R15);
|
||||
}
|
||||
|
||||
/*if (context.ContextFlags & CONTEXT_SEGMENTS)
|
||||
{
|
||||
emu.reg<uint16_t>(x64_register::ds, context.SegDs);
|
||||
emu.reg<uint16_t>(x64_register::es, context.SegEs);
|
||||
emu.reg<uint16_t>(x64_register::fs, context.SegFs);
|
||||
emu.reg<uint16_t>(x64_register::gs, context.SegGs);
|
||||
}*/
|
||||
/*if (context.ContextFlags & CONTEXT_SEGMENTS)
|
||||
{
|
||||
emu.reg<uint16_t>(x64_register::ds, context.SegDs);
|
||||
emu.reg<uint16_t>(x64_register::es, context.SegEs);
|
||||
emu.reg<uint16_t>(x64_register::fs, context.SegFs);
|
||||
emu.reg<uint16_t>(x64_register::gs, context.SegGs);
|
||||
}*/
|
||||
|
||||
if (context.ContextFlags & CONTEXT_FLOATING_POINT_64)
|
||||
{
|
||||
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::fptag, context.FltSave.TagWord);
|
||||
if (context.ContextFlags & CONTEXT_FLOATING_POINT_64)
|
||||
{
|
||||
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::fptag, context.FltSave.TagWord);
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::st0) + i);
|
||||
emu.reg<M128A>(reg, context.FltSave.FloatRegisters[i]);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::st0) + i);
|
||||
emu.reg<M128A>(reg, context.FltSave.FloatRegisters[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.ContextFlags & CONTEXT_XSTATE_64)
|
||||
{
|
||||
emu.reg<uint32_t>(x64_register::mxcsr, context.MxCsr);
|
||||
if (context.ContextFlags & CONTEXT_XSTATE_64)
|
||||
{
|
||||
emu.reg<uint32_t>(x64_register::mxcsr, context.MxCsr);
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::xmm0) + i);
|
||||
emu.reg<M128A>(reg, (&context.Xmm0)[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::xmm0) + i);
|
||||
emu.reg<M128A>(reg, (&context.Xmm0)[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void save(x64_emulator& emu, CONTEXT64& context)
|
||||
{
|
||||
if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS_64)
|
||||
{
|
||||
context.Dr0 = emu.reg(x64_register::dr0);
|
||||
context.Dr1 = emu.reg(x64_register::dr1);
|
||||
context.Dr2 = emu.reg(x64_register::dr2);
|
||||
context.Dr3 = emu.reg(x64_register::dr3);
|
||||
context.Dr6 = emu.reg(x64_register::dr6);
|
||||
context.Dr7 = emu.reg(x64_register::dr7);
|
||||
}
|
||||
void save(x64_emulator& emu, CONTEXT64& context)
|
||||
{
|
||||
if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS_64)
|
||||
{
|
||||
context.Dr0 = emu.reg(x64_register::dr0);
|
||||
context.Dr1 = emu.reg(x64_register::dr1);
|
||||
context.Dr2 = emu.reg(x64_register::dr2);
|
||||
context.Dr3 = emu.reg(x64_register::dr3);
|
||||
context.Dr6 = emu.reg(x64_register::dr6);
|
||||
context.Dr7 = emu.reg(x64_register::dr7);
|
||||
}
|
||||
|
||||
if (context.ContextFlags & CONTEXT_CONTROL_64)
|
||||
{
|
||||
context.SegSs = emu.reg<uint16_t>(x64_register::ss);
|
||||
context.SegCs = emu.reg<uint16_t>(x64_register::cs);
|
||||
context.Rip = emu.reg(x64_register::rip);
|
||||
context.Rsp = emu.reg(x64_register::rsp);
|
||||
context.EFlags = emu.reg<uint32_t>(x64_register::eflags);
|
||||
}
|
||||
if (context.ContextFlags & CONTEXT_CONTROL_64)
|
||||
{
|
||||
context.SegSs = emu.reg<uint16_t>(x64_register::ss);
|
||||
context.SegCs = emu.reg<uint16_t>(x64_register::cs);
|
||||
context.Rip = emu.reg(x64_register::rip);
|
||||
context.Rsp = emu.reg(x64_register::rsp);
|
||||
context.EFlags = emu.reg<uint32_t>(x64_register::eflags);
|
||||
}
|
||||
|
||||
if (context.ContextFlags & CONTEXT_INTEGER_64)
|
||||
{
|
||||
context.Rax = emu.reg(x64_register::rax);
|
||||
context.Rbx = emu.reg(x64_register::rbx);
|
||||
context.Rcx = emu.reg(x64_register::rcx);
|
||||
context.Rdx = emu.reg(x64_register::rdx);
|
||||
context.Rbp = emu.reg(x64_register::rbp);
|
||||
context.Rsi = emu.reg(x64_register::rsi);
|
||||
context.Rdi = emu.reg(x64_register::rdi);
|
||||
context.R8 = emu.reg(x64_register::r8);
|
||||
context.R9 = emu.reg(x64_register::r9);
|
||||
context.R10 = emu.reg(x64_register::r10);
|
||||
context.R11 = emu.reg(x64_register::r11);
|
||||
context.R12 = emu.reg(x64_register::r12);
|
||||
context.R13 = emu.reg(x64_register::r13);
|
||||
context.R14 = emu.reg(x64_register::r14);
|
||||
context.R15 = emu.reg(x64_register::r15);
|
||||
}
|
||||
if (context.ContextFlags & CONTEXT_INTEGER_64)
|
||||
{
|
||||
context.Rax = emu.reg(x64_register::rax);
|
||||
context.Rbx = emu.reg(x64_register::rbx);
|
||||
context.Rcx = emu.reg(x64_register::rcx);
|
||||
context.Rdx = emu.reg(x64_register::rdx);
|
||||
context.Rbp = emu.reg(x64_register::rbp);
|
||||
context.Rsi = emu.reg(x64_register::rsi);
|
||||
context.Rdi = emu.reg(x64_register::rdi);
|
||||
context.R8 = emu.reg(x64_register::r8);
|
||||
context.R9 = emu.reg(x64_register::r9);
|
||||
context.R10 = emu.reg(x64_register::r10);
|
||||
context.R11 = emu.reg(x64_register::r11);
|
||||
context.R12 = emu.reg(x64_register::r12);
|
||||
context.R13 = emu.reg(x64_register::r13);
|
||||
context.R14 = emu.reg(x64_register::r14);
|
||||
context.R15 = emu.reg(x64_register::r15);
|
||||
}
|
||||
|
||||
if (context.ContextFlags & CONTEXT_SEGMENTS_64)
|
||||
{
|
||||
context.SegDs = emu.reg<uint16_t>(x64_register::ds);
|
||||
context.SegEs = emu.reg<uint16_t>(x64_register::es);
|
||||
context.SegFs = emu.reg<uint16_t>(x64_register::fs);
|
||||
context.SegGs = emu.reg<uint16_t>(x64_register::gs);
|
||||
}
|
||||
if (context.ContextFlags & CONTEXT_SEGMENTS_64)
|
||||
{
|
||||
context.SegDs = emu.reg<uint16_t>(x64_register::ds);
|
||||
context.SegEs = emu.reg<uint16_t>(x64_register::es);
|
||||
context.SegFs = emu.reg<uint16_t>(x64_register::fs);
|
||||
context.SegGs = emu.reg<uint16_t>(x64_register::gs);
|
||||
}
|
||||
|
||||
if (context.ContextFlags & CONTEXT_FLOATING_POINT_64)
|
||||
{
|
||||
context.FltSave.ControlWord = emu.reg<uint16_t>(x64_register::fpcw);
|
||||
context.FltSave.StatusWord = emu.reg<uint16_t>(x64_register::fpsw);
|
||||
context.FltSave.TagWord = static_cast<BYTE>(emu.reg<uint16_t>(x64_register::fptag));
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::st0) + i);
|
||||
context.FltSave.FloatRegisters[i] = emu.reg<M128A>(reg);
|
||||
}
|
||||
}
|
||||
if (context.ContextFlags & CONTEXT_FLOATING_POINT_64)
|
||||
{
|
||||
context.FltSave.ControlWord = emu.reg<uint16_t>(x64_register::fpcw);
|
||||
context.FltSave.StatusWord = emu.reg<uint16_t>(x64_register::fpsw);
|
||||
context.FltSave.TagWord = static_cast<BYTE>(emu.reg<uint16_t>(x64_register::fptag));
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::st0) + i);
|
||||
context.FltSave.FloatRegisters[i] = emu.reg<M128A>(reg);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.ContextFlags & CONTEXT_XSTATE_64)
|
||||
{
|
||||
context.MxCsr = emu.reg<uint32_t>(x64_register::mxcsr);
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::xmm0) + i);
|
||||
(&context.Xmm0)[i] = emu.reg<M128A>(reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (context.ContextFlags & CONTEXT_XSTATE_64)
|
||||
{
|
||||
context.MxCsr = emu.reg<uint32_t>(x64_register::mxcsr);
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::xmm0) + i);
|
||||
(&context.Xmm0)[i] = emu.reg<M128A>(reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
|
||||
namespace context_frame
|
||||
{
|
||||
void save(x64_emulator& emu, CONTEXT64& context);
|
||||
void restore(x64_emulator& emu, const CONTEXT64& context);
|
||||
void save(x64_emulator& emu, CONTEXT64& context);
|
||||
void restore(x64_emulator& emu, const CONTEXT64& context);
|
||||
}
|
||||
|
||||
@@ -3,137 +3,134 @@
|
||||
|
||||
#include <utils/finally.hpp>
|
||||
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
#include <gdbstub.h>
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
gdb_action_t map_gdb_action(const gdb_action action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case gdb_action::none:
|
||||
return ACT_NONE;
|
||||
case gdb_action::resume:
|
||||
return ACT_RESUME;
|
||||
case gdb_action::shutdown:
|
||||
return ACT_SHUTDOWN;
|
||||
}
|
||||
gdb_action_t map_gdb_action(const gdb_action action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case gdb_action::none:
|
||||
return ACT_NONE;
|
||||
case gdb_action::resume:
|
||||
return ACT_RESUME;
|
||||
case gdb_action::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)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case BP_SOFTWARE:
|
||||
return breakpoint_type::software;
|
||||
case BP_HARDWARE_EXEC:
|
||||
return breakpoint_type::hardware_exec;
|
||||
case BP_HARDWARE_WRITE:
|
||||
return breakpoint_type::hardware_write;
|
||||
case BP_HARDWARE_READ:
|
||||
return breakpoint_type::hardware_read;
|
||||
case BP_HARDWARE_READ_WRITE:
|
||||
return breakpoint_type::hardware_read_write;
|
||||
}
|
||||
breakpoint_type map_breakpoint_type(const bp_type_t type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case BP_SOFTWARE:
|
||||
return breakpoint_type::software;
|
||||
case BP_HARDWARE_EXEC:
|
||||
return breakpoint_type::hardware_exec;
|
||||
case BP_HARDWARE_WRITE:
|
||||
return breakpoint_type::hardware_write;
|
||||
case BP_HARDWARE_READ:
|
||||
return breakpoint_type::hardware_read;
|
||||
case BP_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)
|
||||
{
|
||||
return *static_cast<gdb_stub_handler*>(args);
|
||||
}
|
||||
gdb_stub_handler& get_handler(void* args)
|
||||
{
|
||||
return *static_cast<gdb_stub_handler*>(args);
|
||||
}
|
||||
|
||||
gdb_action_t cont(void* args)
|
||||
{
|
||||
return map_gdb_action(get_handler(args).cont());
|
||||
}
|
||||
gdb_action_t cont(void* args)
|
||||
{
|
||||
return map_gdb_action(get_handler(args).cont());
|
||||
}
|
||||
|
||||
gdb_action_t stepi(void* args)
|
||||
{
|
||||
return map_gdb_action(get_handler(args).stepi());
|
||||
}
|
||||
gdb_action_t stepi(void* args)
|
||||
{
|
||||
return map_gdb_action(get_handler(args).stepi());
|
||||
}
|
||||
|
||||
int read_reg(void* args, const int regno, size_t* value)
|
||||
{
|
||||
return get_handler(args).read_reg(regno, value) ? 0 : 1;
|
||||
}
|
||||
int read_reg(void* args, const int regno, size_t* value)
|
||||
{
|
||||
return get_handler(args).read_reg(regno, value) ? 0 : 1;
|
||||
}
|
||||
|
||||
int write_reg(void* args, const int regno, const size_t value)
|
||||
{
|
||||
return get_handler(args).write_reg(regno, value) ? 0 : 1;
|
||||
}
|
||||
int write_reg(void* args, const int regno, const size_t value)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return get_handler(args).read_mem(addr, len, val) ? 0 : 1;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
void on_interrupt(void* args)
|
||||
{
|
||||
get_handler(args).on_interrupt();
|
||||
}
|
||||
void on_interrupt(void* args)
|
||||
{
|
||||
get_handler(args).on_interrupt();
|
||||
}
|
||||
|
||||
target_ops get_target_ops()
|
||||
{
|
||||
target_ops ops{};
|
||||
target_ops get_target_ops()
|
||||
{
|
||||
target_ops ops{};
|
||||
|
||||
ops.cont = cont;
|
||||
ops.stepi = stepi;
|
||||
ops.read_reg = read_reg;
|
||||
ops.write_reg = write_reg;
|
||||
ops.read_mem = read_mem;
|
||||
ops.write_mem = write_mem;
|
||||
ops.set_bp = set_bp;
|
||||
ops.del_bp = del_bp;
|
||||
ops.on_interrupt = on_interrupt;
|
||||
ops.cont = cont;
|
||||
ops.stepi = stepi;
|
||||
ops.read_reg = read_reg;
|
||||
ops.write_reg = write_reg;
|
||||
ops.read_mem = read_mem;
|
||||
ops.write_mem = write_mem;
|
||||
ops.set_bp = set_bp;
|
||||
ops.del_bp = del_bp;
|
||||
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,
|
||||
std::string bind_address)
|
||||
{
|
||||
const arch_info_t info
|
||||
{
|
||||
target_description.data(),
|
||||
static_cast<int>(register_count),
|
||||
sizeof(uint64_t),
|
||||
};
|
||||
const arch_info_t info{
|
||||
target_description.data(),
|
||||
static_cast<int>(register_count),
|
||||
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()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!gdbstub_init(&stub, &ops, info, bind_address.data()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto _ = utils::finally([&]
|
||||
{
|
||||
gdbstub_close(&stub);
|
||||
});
|
||||
const auto _ = utils::finally([&] { gdbstub_close(&stub); });
|
||||
|
||||
return gdbstub_run(&stub, &handler);
|
||||
return gdbstub_run(&stub, &handler);
|
||||
}
|
||||
|
||||
@@ -2,37 +2,37 @@
|
||||
|
||||
enum class gdb_action : uint8_t
|
||||
{
|
||||
none,
|
||||
resume,
|
||||
shutdown,
|
||||
none,
|
||||
resume,
|
||||
shutdown,
|
||||
};
|
||||
|
||||
enum class breakpoint_type : uint8_t
|
||||
{
|
||||
software,
|
||||
hardware_exec,
|
||||
hardware_write,
|
||||
hardware_read,
|
||||
hardware_read_write,
|
||||
software,
|
||||
hardware_exec,
|
||||
hardware_write,
|
||||
hardware_read,
|
||||
hardware_read_write,
|
||||
};
|
||||
|
||||
struct gdb_stub_handler
|
||||
{
|
||||
virtual ~gdb_stub_handler() = default;
|
||||
virtual ~gdb_stub_handler() = default;
|
||||
|
||||
virtual gdb_action cont() = 0;
|
||||
virtual gdb_action stepi() = 0;
|
||||
virtual gdb_action cont() = 0;
|
||||
virtual gdb_action stepi() = 0;
|
||||
|
||||
virtual bool read_reg(int regno, size_t* value) = 0;
|
||||
virtual bool write_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 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 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 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 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 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,
|
||||
|
||||
@@ -5,41 +5,41 @@
|
||||
|
||||
class win_x64_gdb_stub_handler : public x64_gdb_stub_handler
|
||||
{
|
||||
public:
|
||||
win_x64_gdb_stub_handler(windows_emulator& win_emu)
|
||||
: x64_gdb_stub_handler(win_emu.emu())
|
||||
, win_emu_(&win_emu)
|
||||
{
|
||||
}
|
||||
public:
|
||||
win_x64_gdb_stub_handler(windows_emulator& win_emu)
|
||||
: x64_gdb_stub_handler(win_emu.emu()),
|
||||
win_emu_(&win_emu)
|
||||
{
|
||||
}
|
||||
|
||||
gdb_action cont() override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->win_emu_->start();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
puts(e.what());
|
||||
}
|
||||
gdb_action cont() override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->win_emu_->start();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
puts(e.what());
|
||||
}
|
||||
|
||||
return gdb_action::resume;
|
||||
}
|
||||
return gdb_action::resume;
|
||||
}
|
||||
|
||||
gdb_action stepi() override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->win_emu_->start({}, 1);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
puts(e.what());
|
||||
}
|
||||
gdb_action stepi() override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->win_emu_->start({}, 1);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
puts(e.what());
|
||||
}
|
||||
|
||||
return gdb_action::resume;
|
||||
}
|
||||
return gdb_action::resume;
|
||||
}
|
||||
|
||||
private:
|
||||
windows_emulator* win_emu_{};
|
||||
private:
|
||||
windows_emulator* win_emu_{};
|
||||
};
|
||||
|
||||
@@ -4,212 +4,194 @@
|
||||
#include "scoped_hook.hpp"
|
||||
|
||||
inline std::vector gdb_registers{
|
||||
x64_register::rax,
|
||||
x64_register::rbx,
|
||||
x64_register::rcx,
|
||||
x64_register::rdx,
|
||||
x64_register::rsi,
|
||||
x64_register::rdi,
|
||||
x64_register::rbp,
|
||||
x64_register::rsp,
|
||||
x64_register::r8,
|
||||
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,*/
|
||||
x64_register::rax, x64_register::rbx, x64_register::rcx, x64_register::rdx, x64_register::rsi, x64_register::rdi,
|
||||
x64_register::rbp, x64_register::rsp, x64_register::r8, 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)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case breakpoint_type::software:
|
||||
case breakpoint_type::hardware_exec:
|
||||
return memory_operation::exec;
|
||||
case breakpoint_type::hardware_read:
|
||||
return memory_permission::read;
|
||||
case breakpoint_type::hardware_write:
|
||||
return memory_permission::write;
|
||||
case breakpoint_type::hardware_read_write:
|
||||
return memory_permission::read_write;
|
||||
default:
|
||||
throw std::runtime_error("Bad bp type");
|
||||
}
|
||||
switch (type)
|
||||
{
|
||||
case breakpoint_type::software:
|
||||
case breakpoint_type::hardware_exec:
|
||||
return memory_operation::exec;
|
||||
case breakpoint_type::hardware_read:
|
||||
return memory_permission::read;
|
||||
case breakpoint_type::hardware_write:
|
||||
return memory_permission::write;
|
||||
case breakpoint_type::hardware_read_write:
|
||||
return memory_permission::read_write;
|
||||
default:
|
||||
throw std::runtime_error("Bad bp type");
|
||||
}
|
||||
}
|
||||
|
||||
struct breakpoint_key
|
||||
{
|
||||
size_t addr{};
|
||||
size_t size{};
|
||||
breakpoint_type type{};
|
||||
size_t addr{};
|
||||
size_t size{};
|
||||
breakpoint_type type{};
|
||||
|
||||
bool operator==(const breakpoint_key& other) const
|
||||
{
|
||||
return this->addr == other.addr && this->size == other.size && this->type == other.type;
|
||||
}
|
||||
bool operator==(const breakpoint_key& other) const
|
||||
{
|
||||
return this->addr == other.addr && this->size == other.size && this->type == other.type;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::hash<breakpoint_key>
|
||||
{
|
||||
std::size_t operator()(const breakpoint_key& k) const noexcept
|
||||
{
|
||||
return ((std::hash<size_t>()(k.addr)
|
||||
^ (std::hash<size_t>()(k.size) << 1)) >> 1)
|
||||
^ (std::hash<size_t>()(static_cast<size_t>(k.type)) << 1);
|
||||
}
|
||||
std::size_t operator()(const breakpoint_key& k) const noexcept
|
||||
{
|
||||
return ((std::hash<size_t>()(k.addr) ^ (std::hash<size_t>()(k.size) << 1)) >> 1) ^
|
||||
(std::hash<size_t>()(static_cast<size_t>(k.type)) << 1);
|
||||
}
|
||||
};
|
||||
|
||||
class x64_gdb_stub_handler : public gdb_stub_handler
|
||||
{
|
||||
public:
|
||||
x64_gdb_stub_handler(x64_emulator& emu)
|
||||
: emu_(&emu)
|
||||
{
|
||||
}
|
||||
public:
|
||||
x64_gdb_stub_handler(x64_emulator& emu)
|
||||
: emu_(&emu)
|
||||
{
|
||||
}
|
||||
|
||||
~x64_gdb_stub_handler() override = default;
|
||||
~x64_gdb_stub_handler() override = default;
|
||||
|
||||
gdb_action cont() override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->emu_->start_from_ip();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
puts(e.what());
|
||||
}
|
||||
gdb_action cont() override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->emu_->start_from_ip();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
puts(e.what());
|
||||
}
|
||||
|
||||
return gdb_action::resume;
|
||||
}
|
||||
return gdb_action::resume;
|
||||
}
|
||||
|
||||
gdb_action stepi() override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->emu_->start_from_ip({}, 1);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
puts(e.what());
|
||||
}
|
||||
gdb_action stepi() override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->emu_->start_from_ip({}, 1);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
puts(e.what());
|
||||
}
|
||||
|
||||
return gdb_action::resume;
|
||||
}
|
||||
return gdb_action::resume;
|
||||
}
|
||||
|
||||
bool read_reg(const int regno, size_t* value) override
|
||||
{
|
||||
*value = 0;
|
||||
bool read_reg(const int regno, size_t* value) override
|
||||
{
|
||||
*value = 0;
|
||||
|
||||
try
|
||||
{
|
||||
if (static_cast<size_t>(regno) >= gdb_registers.size())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (static_cast<size_t>(regno) >= gdb_registers.size())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
this->emu_->read_register(gdb_registers[regno], value, sizeof(*value));
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
this->emu_->read_register(gdb_registers[regno], value, sizeof(*value));
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool write_reg(const int regno, const size_t value) override
|
||||
{
|
||||
try
|
||||
{
|
||||
if (static_cast<size_t>(regno) >= gdb_registers.size())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool write_reg(const int regno, const size_t value) override
|
||||
{
|
||||
try
|
||||
{
|
||||
if (static_cast<size_t>(regno) >= gdb_registers.size())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
this->emu_->write_register(gdb_registers[regno], &value, sizeof(value));
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this->emu_->write_register(gdb_registers[regno], &value, sizeof(value));
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool read_mem(const size_t addr, const size_t len, void* val) override
|
||||
{
|
||||
return this->emu_->try_read_memory(addr, val, len);
|
||||
}
|
||||
bool read_mem(const size_t addr, const size_t len, void* val) override
|
||||
{
|
||||
return this->emu_->try_read_memory(addr, val, len);
|
||||
}
|
||||
|
||||
bool write_mem(const size_t addr, const size_t len, void* val) override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->emu_->write_memory(addr, val, len);
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool write_mem(const size_t addr, const size_t len, void* val) override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->emu_->write_memory(addr, val, len);
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool set_bp(const breakpoint_type type, const size_t addr, const size_t size) override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->hooks_[{addr, size, type}] = scoped_hook(*this->emu_, this->emu_->hook_memory_access(
|
||||
addr, size, map_breakpoint_type(type),
|
||||
[this](uint64_t, size_t, uint64_t, memory_operation)
|
||||
{
|
||||
this->on_interrupt();
|
||||
}));
|
||||
bool set_bp(const breakpoint_type type, const size_t addr, const size_t size) override
|
||||
{
|
||||
try
|
||||
{
|
||||
this->hooks_[{addr, size, type}] = scoped_hook(
|
||||
*this->emu_, this->emu_->hook_memory_access(
|
||||
addr, size, map_breakpoint_type(type),
|
||||
[this](uint64_t, size_t, uint64_t, memory_operation) { this->on_interrupt(); }));
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool del_bp(const breakpoint_type type, const size_t addr, const size_t size) override
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto entry = this->hooks_.find({addr, size, type});
|
||||
if (entry == this->hooks_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool del_bp(const breakpoint_type type, const size_t addr, const size_t size) override
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto entry = this->hooks_.find({addr, size, type});
|
||||
if (entry == this->hooks_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this->hooks_.erase(entry);
|
||||
this->hooks_.erase(entry);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void on_interrupt() override
|
||||
{
|
||||
this->emu_->stop();
|
||||
}
|
||||
void on_interrupt() override
|
||||
{
|
||||
this->emu_->stop();
|
||||
}
|
||||
|
||||
private:
|
||||
x64_emulator* emu_{};
|
||||
std::unordered_map<breakpoint_key, scoped_hook> hooks_{};
|
||||
private:
|
||||
x64_emulator* emu_{};
|
||||
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>
|
||||
struct TDI_CONNECTION_INFORMATION
|
||||
{
|
||||
LONG UserDataLength;
|
||||
typename Traits::PVOID UserData;
|
||||
LONG OptionsLength;
|
||||
typename Traits::PVOID Options;
|
||||
LONG RemoteAddressLength;
|
||||
typename Traits::PVOID RemoteAddress;
|
||||
LONG UserDataLength;
|
||||
typename Traits::PVOID UserData;
|
||||
LONG OptionsLength;
|
||||
typename Traits::PVOID Options;
|
||||
LONG RemoteAddressLength;
|
||||
typename Traits::PVOID RemoteAddress;
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
struct TDI_REQUEST
|
||||
{
|
||||
union
|
||||
{
|
||||
typename Traits::HANDLE AddressHandle;
|
||||
EMULATOR_CAST(typename Traits::PVOID, CONNECTION_CONTEXT) ConnectionContext;
|
||||
typename Traits::HANDLE ControlChannel;
|
||||
} Handle;
|
||||
union
|
||||
{
|
||||
typename Traits::HANDLE AddressHandle;
|
||||
EMULATOR_CAST(typename Traits::PVOID, CONNECTION_CONTEXT) ConnectionContext;
|
||||
typename Traits::HANDLE ControlChannel;
|
||||
} Handle;
|
||||
|
||||
typename Traits::PVOID RequestNotifyObject;
|
||||
typename Traits::PVOID RequestContext;
|
||||
TDI_STATUS TdiStatus;
|
||||
typename Traits::PVOID RequestNotifyObject;
|
||||
typename Traits::PVOID RequestContext;
|
||||
TDI_STATUS TdiStatus;
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
struct TDI_REQUEST_SEND_DATAGRAM
|
||||
{
|
||||
TDI_REQUEST<Traits> Request;
|
||||
EMULATOR_CAST(typename Traits::PVOID, PTDI_CONNECTION_INFORMATION) SendDatagramInformation;
|
||||
TDI_REQUEST<Traits> Request;
|
||||
EMULATOR_CAST(typename Traits::PVOID, PTDI_CONNECTION_INFORMATION) SendDatagramInformation;
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
struct AFD_SEND_INFO
|
||||
{
|
||||
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
|
||||
ULONG BufferCount;
|
||||
ULONG AfdFlags;
|
||||
ULONG TdiFlags;
|
||||
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
|
||||
ULONG BufferCount;
|
||||
ULONG AfdFlags;
|
||||
ULONG TdiFlags;
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
struct AFD_SEND_DATAGRAM_INFO
|
||||
{
|
||||
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
|
||||
ULONG BufferCount;
|
||||
ULONG AfdFlags;
|
||||
TDI_REQUEST_SEND_DATAGRAM<Traits> TdiRequest;
|
||||
TDI_CONNECTION_INFORMATION<Traits> TdiConnInfo;
|
||||
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
|
||||
ULONG BufferCount;
|
||||
ULONG AfdFlags;
|
||||
TDI_REQUEST_SEND_DATAGRAM<Traits> TdiRequest;
|
||||
TDI_CONNECTION_INFORMATION<Traits> TdiConnInfo;
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
struct AFD_RECV_INFO
|
||||
{
|
||||
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
|
||||
ULONG BufferCount;
|
||||
ULONG AfdFlags;
|
||||
ULONG TdiFlags;
|
||||
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
|
||||
ULONG BufferCount;
|
||||
ULONG AfdFlags;
|
||||
ULONG TdiFlags;
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
struct AFD_RECV_DATAGRAM_INFO
|
||||
{
|
||||
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
|
||||
ULONG BufferCount;
|
||||
ULONG AfdFlags;
|
||||
ULONG TdiFlags;
|
||||
typename Traits::PVOID Address;
|
||||
EMULATOR_CAST(typename Traits::PVOID, PULONG) AddressLength;
|
||||
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
|
||||
ULONG BufferCount;
|
||||
ULONG AfdFlags;
|
||||
ULONG TdiFlags;
|
||||
typename Traits::PVOID Address;
|
||||
EMULATOR_CAST(typename Traits::PVOID, PULONG) AddressLength;
|
||||
};
|
||||
|
||||
struct AFD_POLL_HANDLE_INFO64
|
||||
{
|
||||
EmulatorTraits<Emu64>::HANDLE Handle;
|
||||
ULONG PollEvents;
|
||||
NTSTATUS Status;
|
||||
EmulatorTraits<Emu64>::HANDLE Handle;
|
||||
ULONG PollEvents;
|
||||
NTSTATUS Status;
|
||||
};
|
||||
|
||||
struct AFD_POLL_INFO64
|
||||
{
|
||||
LARGE_INTEGER Timeout;
|
||||
ULONG NumberOfHandles;
|
||||
BOOLEAN Unique;
|
||||
AFD_POLL_HANDLE_INFO64 Handles[1];
|
||||
LARGE_INTEGER Timeout;
|
||||
ULONG NumberOfHandles;
|
||||
BOOLEAN Unique;
|
||||
AFD_POLL_HANDLE_INFO64 Handles[1];
|
||||
};
|
||||
|
||||
#define AFD_POLL_RECEIVE_BIT 0
|
||||
@@ -117,59 +117,57 @@ struct AFD_POLL_INFO64
|
||||
#define AFD_NUM_POLL_EVENTS 11
|
||||
#define AFD_POLL_ALL ((1 << AFD_NUM_POLL_EVENTS) - 1)
|
||||
|
||||
#define _AFD_REQUEST(ioctl) \
|
||||
((((ULONG)(ioctl)) >> 2) & 0x03FF)
|
||||
#define _AFD_BASE(ioctl) \
|
||||
((((ULONG)(ioctl)) >> 12) & 0xFFFFF)
|
||||
#define _AFD_REQUEST(ioctl) ((((ULONG)(ioctl)) >> 2) & 0x03FF)
|
||||
#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_CONNECT 1
|
||||
#define AFD_START_LISTEN 2
|
||||
#define AFD_WAIT_FOR_LISTEN 3
|
||||
#define AFD_ACCEPT 4
|
||||
#define AFD_RECEIVE 5
|
||||
#define AFD_RECEIVE_DATAGRAM 6
|
||||
#define AFD_SEND 7
|
||||
#define AFD_SEND_DATAGRAM 8
|
||||
#define AFD_POLL 9
|
||||
#define AFD_PARTIAL_DISCONNECT 10
|
||||
#define AFD_BIND 0
|
||||
#define AFD_CONNECT 1
|
||||
#define AFD_START_LISTEN 2
|
||||
#define AFD_WAIT_FOR_LISTEN 3
|
||||
#define AFD_ACCEPT 4
|
||||
#define AFD_RECEIVE 5
|
||||
#define AFD_RECEIVE_DATAGRAM 6
|
||||
#define AFD_SEND 7
|
||||
#define AFD_SEND_DATAGRAM 8
|
||||
#define AFD_POLL 9
|
||||
#define AFD_PARTIAL_DISCONNECT 10
|
||||
|
||||
#define AFD_GET_ADDRESS 11
|
||||
#define AFD_QUERY_RECEIVE_INFO 12
|
||||
#define AFD_QUERY_HANDLES 13
|
||||
#define AFD_SET_INFORMATION 14
|
||||
#define AFD_GET_CONTEXT_LENGTH 15
|
||||
#define AFD_GET_CONTEXT 16
|
||||
#define AFD_SET_CONTEXT 17
|
||||
#define AFD_GET_ADDRESS 11
|
||||
#define AFD_QUERY_RECEIVE_INFO 12
|
||||
#define AFD_QUERY_HANDLES 13
|
||||
#define AFD_SET_INFORMATION 14
|
||||
#define AFD_GET_CONTEXT_LENGTH 15
|
||||
#define AFD_GET_CONTEXT 16
|
||||
#define AFD_SET_CONTEXT 17
|
||||
|
||||
#define AFD_SET_CONNECT_DATA 18
|
||||
#define AFD_SET_CONNECT_OPTIONS 19
|
||||
#define AFD_SET_DISCONNECT_DATA 20
|
||||
#define AFD_SET_DISCONNECT_OPTIONS 21
|
||||
#define AFD_SET_CONNECT_DATA 18
|
||||
#define AFD_SET_CONNECT_OPTIONS 19
|
||||
#define AFD_SET_DISCONNECT_DATA 20
|
||||
#define AFD_SET_DISCONNECT_OPTIONS 21
|
||||
|
||||
#define AFD_GET_CONNECT_DATA 22
|
||||
#define AFD_GET_CONNECT_OPTIONS 23
|
||||
#define AFD_GET_DISCONNECT_DATA 24
|
||||
#define AFD_GET_DISCONNECT_OPTIONS 25
|
||||
#define AFD_GET_CONNECT_DATA 22
|
||||
#define AFD_GET_CONNECT_OPTIONS 23
|
||||
#define AFD_GET_DISCONNECT_DATA 24
|
||||
#define AFD_GET_DISCONNECT_OPTIONS 25
|
||||
|
||||
#define AFD_SIZE_CONNECT_DATA 26
|
||||
#define AFD_SIZE_CONNECT_OPTIONS 27
|
||||
#define AFD_SIZE_DISCONNECT_DATA 28
|
||||
#define AFD_SIZE_DISCONNECT_OPTIONS 29
|
||||
#define AFD_SIZE_CONNECT_DATA 26
|
||||
#define AFD_SIZE_CONNECT_OPTIONS 27
|
||||
#define AFD_SIZE_DISCONNECT_DATA 28
|
||||
#define AFD_SIZE_DISCONNECT_OPTIONS 29
|
||||
|
||||
#define AFD_GET_INFORMATION 30
|
||||
#define AFD_TRANSMIT_FILE 31
|
||||
#define AFD_SUPER_ACCEPT 32
|
||||
#define AFD_GET_INFORMATION 30
|
||||
#define AFD_TRANSMIT_FILE 31
|
||||
#define AFD_SUPER_ACCEPT 32
|
||||
|
||||
#define AFD_EVENT_SELECT 33
|
||||
#define AFD_ENUM_NETWORK_EVENTS 34
|
||||
#define AFD_EVENT_SELECT 33
|
||||
#define AFD_ENUM_NETWORK_EVENTS 34
|
||||
|
||||
#define AFD_DEFER_ACCEPT 35
|
||||
#define AFD_WAIT_FOR_LISTEN_LIFO 36
|
||||
#define AFD_SET_QOS 37
|
||||
#define AFD_GET_QOS 38
|
||||
#define AFD_NO_OPERATION 39
|
||||
#define AFD_VALIDATE_GROUP 40
|
||||
#define AFD_DEFER_ACCEPT 35
|
||||
#define AFD_WAIT_FOR_LISTEN_LIFO 36
|
||||
#define AFD_SET_QOS 37
|
||||
#define AFD_GET_QOS 38
|
||||
#define AFD_NO_OPERATION 39
|
||||
#define AFD_VALIDATE_GROUP 40
|
||||
#define AFD_GET_UNACCEPTED_CONNECT_DATA 41
|
||||
|
||||
@@ -8,31 +8,31 @@ using emulator_pointer = uint64_t;
|
||||
template <typename T>
|
||||
class object_wrapper
|
||||
{
|
||||
T* obj_;
|
||||
T* obj_;
|
||||
|
||||
public:
|
||||
object_wrapper(T& obj)
|
||||
: obj_(&obj)
|
||||
{
|
||||
}
|
||||
public:
|
||||
object_wrapper(T& obj)
|
||||
: obj_(&obj)
|
||||
{
|
||||
}
|
||||
|
||||
T& get() const
|
||||
{
|
||||
return *this->obj_;
|
||||
}
|
||||
T& get() const
|
||||
{
|
||||
return *this->obj_;
|
||||
}
|
||||
|
||||
operator T&() const
|
||||
{
|
||||
return this->get();
|
||||
}
|
||||
operator T&() const
|
||||
{
|
||||
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;
|
||||
@@ -45,256 +45,250 @@ using windows_emulator_wrapper = object_wrapper<windows_emulator>;
|
||||
template <typename T>
|
||||
class emulator_object
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
emulator_object(const x64_emulator_wrapper& wrapper, const uint64_t address = 0)
|
||||
: emulator_object(wrapper.get(), address)
|
||||
{
|
||||
}
|
||||
emulator_object(const x64_emulator_wrapper& wrapper, const uint64_t address = 0)
|
||||
: emulator_object(wrapper.get(), address)
|
||||
{
|
||||
}
|
||||
|
||||
emulator_object(emulator& emu, const uint64_t address = 0)
|
||||
: emu_(&emu)
|
||||
, address_(address)
|
||||
{
|
||||
}
|
||||
emulator_object(emulator& emu, const uint64_t address = 0)
|
||||
: emu_(&emu),
|
||||
address_(address)
|
||||
{
|
||||
}
|
||||
|
||||
emulator_object(emulator& emu, const void* address)
|
||||
: emulator_object(emu, reinterpret_cast<uint64_t>(address))
|
||||
{
|
||||
}
|
||||
emulator_object(emulator& emu, const void* address)
|
||||
: emulator_object(emu, reinterpret_cast<uint64_t>(address))
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t value() const
|
||||
{
|
||||
return this->address_;
|
||||
}
|
||||
uint64_t value() const
|
||||
{
|
||||
return this->address_;
|
||||
}
|
||||
|
||||
constexpr uint64_t size() const
|
||||
{
|
||||
return sizeof(T);
|
||||
}
|
||||
constexpr uint64_t size() const
|
||||
{
|
||||
return sizeof(T);
|
||||
}
|
||||
|
||||
uint64_t end() const
|
||||
{
|
||||
return this->value() + this->size();
|
||||
}
|
||||
uint64_t end() const
|
||||
{
|
||||
return this->value() + this->size();
|
||||
}
|
||||
|
||||
T* ptr() const
|
||||
{
|
||||
return reinterpret_cast<T*>(this->address_);
|
||||
}
|
||||
T* ptr() const
|
||||
{
|
||||
return reinterpret_cast<T*>(this->address_);
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return this->address_ != 0;
|
||||
}
|
||||
operator bool() const
|
||||
{
|
||||
return this->address_ != 0;
|
||||
}
|
||||
|
||||
T read(const size_t index = 0) const
|
||||
{
|
||||
T obj{};
|
||||
this->emu_->read_memory(this->address_ + index * this->size(), &obj, sizeof(obj));
|
||||
return obj;
|
||||
}
|
||||
T read(const size_t index = 0) const
|
||||
{
|
||||
T obj{};
|
||||
this->emu_->read_memory(this->address_ + index * this->size(), &obj, sizeof(obj));
|
||||
return obj;
|
||||
}
|
||||
|
||||
void write(const T& value, const size_t index = 0) const
|
||||
{
|
||||
this->emu_->write_memory(this->address_ + index * this->size(), &value, sizeof(value));
|
||||
}
|
||||
void write(const T& value, const size_t index = 0) const
|
||||
{
|
||||
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
|
||||
{
|
||||
if (this->operator bool())
|
||||
{
|
||||
this->write(value, index);
|
||||
}
|
||||
}
|
||||
void write_if_valid(const T& value, const size_t index = 0) const
|
||||
{
|
||||
if (this->operator bool())
|
||||
{
|
||||
this->write(value, index);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void access(const F& accessor, const size_t index = 0) const
|
||||
{
|
||||
T obj{};
|
||||
this->emu_->read_memory(this->address_ + index * this->size(), &obj, sizeof(obj));
|
||||
template <typename F>
|
||||
void access(const F& accessor, const size_t index = 0) const
|
||||
{
|
||||
T 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
|
||||
{
|
||||
buffer.write(this->address_);
|
||||
}
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(this->address_);
|
||||
}
|
||||
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(this->address_);
|
||||
}
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(this->address_);
|
||||
}
|
||||
|
||||
void set_address(const uint64_t address)
|
||||
{
|
||||
this->address_ = address;
|
||||
}
|
||||
void set_address(const uint64_t address)
|
||||
{
|
||||
this->address_ = address;
|
||||
}
|
||||
|
||||
private:
|
||||
emulator* emu_{};
|
||||
uint64_t address_{};
|
||||
private:
|
||||
emulator* emu_{};
|
||||
uint64_t address_{};
|
||||
};
|
||||
|
||||
|
||||
// TODO: warning emulator_utils is hardcoded for 64bit unicode_string usage
|
||||
class emulator_allocator
|
||||
{
|
||||
public:
|
||||
emulator_allocator(emulator& emu)
|
||||
: emu_(&emu)
|
||||
{
|
||||
}
|
||||
public:
|
||||
emulator_allocator(emulator& emu)
|
||||
: emu_(&emu)
|
||||
{
|
||||
}
|
||||
|
||||
emulator_allocator(emulator& emu, const uint64_t address, const uint64_t size)
|
||||
: emu_(&emu)
|
||||
, address_(address)
|
||||
, size_(size)
|
||||
, active_address_(address)
|
||||
{
|
||||
}
|
||||
emulator_allocator(emulator& emu, const uint64_t address, const uint64_t size)
|
||||
: emu_(&emu),
|
||||
address_(address),
|
||||
size_(size),
|
||||
active_address_(address)
|
||||
{
|
||||
}
|
||||
|
||||
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_end = potential_start + count;
|
||||
const auto total_end = this->address_ + this->size_;
|
||||
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_end = potential_start + count;
|
||||
const auto total_end = this->address_ + this->size_;
|
||||
|
||||
if (potential_end > total_end)
|
||||
{
|
||||
throw std::runtime_error("Out of memory");
|
||||
}
|
||||
if (potential_end > total_end)
|
||||
{
|
||||
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>
|
||||
emulator_object<T> reserve(const size_t count = 1)
|
||||
{
|
||||
const auto potential_start = this->reserve(sizeof(T) * count, alignof(T));
|
||||
return emulator_object<T>(*this->emu_, potential_start);
|
||||
}
|
||||
template <typename T>
|
||||
emulator_object<T> reserve(const size_t count = 1)
|
||||
{
|
||||
const auto potential_start = this->reserve(sizeof(T) * count, alignof(T));
|
||||
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)
|
||||
{
|
||||
UNICODE_STRING<EmulatorTraits<Emu64>> uc_str{};
|
||||
this->make_unicode_string(uc_str, str);
|
||||
return reinterpret_cast<char16_t*>(uc_str.Buffer);
|
||||
}
|
||||
void make_unicode_string(UNICODE_STRING<EmulatorTraits<Emu64>>& result, const std::u16string_view str)
|
||||
{
|
||||
constexpr auto element_size = sizeof(str[0]);
|
||||
constexpr auto required_alignment = alignof(decltype(str[0]));
|
||||
const auto total_length = str.size() * element_size;
|
||||
|
||||
void make_unicode_string(UNICODE_STRING<EmulatorTraits<Emu64>>& result, const std::u16string_view str)
|
||||
{
|
||||
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);
|
||||
|
||||
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{};
|
||||
this->emu_->write_memory(string_buffer + total_length, nullbyte.data(), nullbyte.size());
|
||||
result.Buffer = string_buffer;
|
||||
result.Length = static_cast<USHORT>(total_length);
|
||||
result.MaximumLength = static_cast<USHORT>(total_length + element_size);
|
||||
}
|
||||
|
||||
result.Buffer = string_buffer;
|
||||
result.Length = static_cast<USHORT>(total_length);
|
||||
result.MaximumLength = static_cast<USHORT>(total_length + element_size);
|
||||
}
|
||||
emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> make_unicode_string(const std::u16string_view str)
|
||||
{
|
||||
const auto unicode_string = this->reserve<UNICODE_STRING<EmulatorTraits<Emu64>>>();
|
||||
|
||||
emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> make_unicode_string(const std::u16string_view str)
|
||||
{
|
||||
const auto unicode_string = this->reserve<UNICODE_STRING<EmulatorTraits<Emu64>>>();
|
||||
unicode_string.access(
|
||||
[&](UNICODE_STRING<EmulatorTraits<Emu64>>& unicode_str) { this->make_unicode_string(unicode_str, str); });
|
||||
|
||||
unicode_string.access([&](UNICODE_STRING<EmulatorTraits<Emu64>>& unicode_str)
|
||||
{
|
||||
this->make_unicode_string(unicode_str, str);
|
||||
});
|
||||
return unicode_string;
|
||||
}
|
||||
|
||||
return unicode_string;
|
||||
}
|
||||
uint64_t get_base() const
|
||||
{
|
||||
return this->address_;
|
||||
}
|
||||
|
||||
uint64_t get_base() const
|
||||
{
|
||||
return this->address_;
|
||||
}
|
||||
uint64_t get_size() const
|
||||
{
|
||||
return this->size_;
|
||||
}
|
||||
|
||||
uint64_t get_size() const
|
||||
{
|
||||
return this->size_;
|
||||
}
|
||||
uint64_t get_next_address() const
|
||||
{
|
||||
return this->active_address_;
|
||||
}
|
||||
|
||||
uint64_t get_next_address() const
|
||||
{
|
||||
return this->active_address_;
|
||||
}
|
||||
emulator& get_emulator() const
|
||||
{
|
||||
return *this->emu_;
|
||||
}
|
||||
|
||||
emulator& get_emulator() const
|
||||
{
|
||||
return *this->emu_;
|
||||
}
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(this->address_);
|
||||
buffer.write(this->size_);
|
||||
buffer.write(this->active_address_);
|
||||
}
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(this->address_);
|
||||
buffer.write(this->size_);
|
||||
buffer.write(this->active_address_);
|
||||
}
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(this->address_);
|
||||
buffer.read(this->size_);
|
||||
buffer.read(this->active_address_);
|
||||
}
|
||||
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(this->address_);
|
||||
buffer.read(this->size_);
|
||||
buffer.read(this->active_address_);
|
||||
}
|
||||
void release()
|
||||
{
|
||||
if (this->emu_ && this->address_ && this->size_)
|
||||
{
|
||||
this->emu_->release_memory(this->address_, this->size_);
|
||||
this->address_ = 0;
|
||||
this->size_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
if (this->emu_ && this->address_ && this->size_)
|
||||
{
|
||||
this->emu_->release_memory(this->address_, this->size_);
|
||||
this->address_ = 0;
|
||||
this->size_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
emulator* emu_{};
|
||||
uint64_t address_{};
|
||||
uint64_t size_{};
|
||||
uint64_t active_address_{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)
|
||||
{
|
||||
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>>, Buffer) == 8);
|
||||
static_assert(sizeof(UNICODE_STRING<EmulatorTraits<Emu64>>) == 16);
|
||||
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>>, Buffer) == 8);
|
||||
static_assert(sizeof(UNICODE_STRING<EmulatorTraits<Emu64>>) == 16);
|
||||
|
||||
std::u16string result{};
|
||||
result.resize(ucs.Length / 2);
|
||||
std::u16string result{};
|
||||
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,
|
||||
const emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> uc_string)
|
||||
{
|
||||
const auto ucs = uc_string.read();
|
||||
return read_unicode_string(emu, ucs);
|
||||
const auto ucs = uc_string.read();
|
||||
return read_unicode_string(emu, ucs);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
enum type : uint16_t
|
||||
{
|
||||
reserved = 0,
|
||||
file,
|
||||
device,
|
||||
event,
|
||||
section,
|
||||
symlink,
|
||||
directory,
|
||||
semaphore,
|
||||
port,
|
||||
thread,
|
||||
registry,
|
||||
mutant,
|
||||
token,
|
||||
};
|
||||
enum type : uint16_t
|
||||
{
|
||||
reserved = 0,
|
||||
file,
|
||||
device,
|
||||
event,
|
||||
section,
|
||||
symlink,
|
||||
directory,
|
||||
semaphore,
|
||||
port,
|
||||
thread,
|
||||
registry,
|
||||
mutant,
|
||||
token,
|
||||
};
|
||||
};
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
struct handle_value
|
||||
{
|
||||
uint64_t id : 32;
|
||||
uint64_t type : 16;
|
||||
uint64_t padding : 14;
|
||||
uint64_t is_system : 1;
|
||||
uint64_t is_pseudo : 1;
|
||||
uint64_t id : 32;
|
||||
uint64_t type : 16;
|
||||
uint64_t padding : 14;
|
||||
uint64_t is_system : 1;
|
||||
uint64_t is_pseudo : 1;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
@@ -37,315 +37,315 @@ static_assert(sizeof(handle_value) == 8);
|
||||
// TODO: this is a concrete 64bit handle
|
||||
union handle
|
||||
{
|
||||
handle_value value;
|
||||
uint64_t bits;
|
||||
std::uint64_t h;
|
||||
handle_value value;
|
||||
uint64_t bits;
|
||||
std::uint64_t h;
|
||||
};
|
||||
|
||||
namespace utils
|
||||
{
|
||||
inline void serialize(buffer_serializer& buffer, const handle& h)
|
||||
{
|
||||
buffer.write(h.bits);
|
||||
}
|
||||
inline void serialize(buffer_serializer& buffer, const handle& h)
|
||||
{
|
||||
buffer.write(h.bits);
|
||||
}
|
||||
|
||||
inline void deserialize(buffer_deserializer& buffer, handle& h)
|
||||
{
|
||||
buffer.read(h.bits);
|
||||
}
|
||||
inline void deserialize(buffer_deserializer& buffer, handle& h)
|
||||
{
|
||||
buffer.read(h.bits);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return h1.bits == h2;
|
||||
return h1.bits == h2;
|
||||
}
|
||||
|
||||
inline handle_value get_handle_value(const uint64_t h)
|
||||
{
|
||||
handle hh{};
|
||||
hh.bits = h;
|
||||
return hh.value;
|
||||
handle hh{};
|
||||
hh.bits = h;
|
||||
return hh.value;
|
||||
}
|
||||
|
||||
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.id = id;
|
||||
value.type = type;
|
||||
value.is_system = false;
|
||||
value.is_pseudo = is_pseudo;
|
||||
value.padding = 0;
|
||||
value.id = id;
|
||||
value.type = type;
|
||||
value.is_system = false;
|
||||
value.is_pseudo = is_pseudo;
|
||||
|
||||
return {value};
|
||||
return {value};
|
||||
}
|
||||
|
||||
constexpr handle make_handle(const uint64_t value)
|
||||
{
|
||||
handle h{};
|
||||
h.bits = value;
|
||||
return h;
|
||||
handle h{};
|
||||
h.bits = value;
|
||||
return h;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
template <typename, typename = void>
|
||||
struct has_deleter_function : std::false_type
|
||||
{
|
||||
};
|
||||
template <typename, typename = void>
|
||||
struct has_deleter_function : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename 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>
|
||||
{
|
||||
};
|
||||
template <typename 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>
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
struct generic_handle_store
|
||||
{
|
||||
virtual ~generic_handle_store() = default;
|
||||
virtual bool erase(const handle h) = 0;
|
||||
virtual ~generic_handle_store() = default;
|
||||
virtual bool erase(const handle h) = 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
|
||||
{
|
||||
public:
|
||||
using index_type = uint32_t;
|
||||
using value_map = std::map<index_type, T>;
|
||||
public:
|
||||
using index_type = uint32_t;
|
||||
using value_map = std::map<index_type, T>;
|
||||
|
||||
bool block_mutation(bool blocked)
|
||||
{
|
||||
std::swap(this->block_mutation_, blocked);
|
||||
return blocked;
|
||||
}
|
||||
bool block_mutation(bool blocked)
|
||||
{
|
||||
std::swap(this->block_mutation_, blocked);
|
||||
return blocked;
|
||||
}
|
||||
|
||||
handle store(T value)
|
||||
{
|
||||
if (this->block_mutation_)
|
||||
{
|
||||
throw std::runtime_error("Mutation of handle store is blocked!");
|
||||
}
|
||||
handle store(T value)
|
||||
{
|
||||
if (this->block_mutation_)
|
||||
{
|
||||
throw std::runtime_error("Mutation of handle store is blocked!");
|
||||
}
|
||||
|
||||
auto index = this->find_free_index();
|
||||
this->store_.emplace(index, std::move(value));
|
||||
auto index = this->find_free_index();
|
||||
this->store_.emplace(index, std::move(value));
|
||||
|
||||
return make_handle(index);
|
||||
}
|
||||
return make_handle(index);
|
||||
}
|
||||
|
||||
handle make_handle(const index_type index) const
|
||||
{
|
||||
handle h{};
|
||||
h.bits = 0;
|
||||
h.value.is_pseudo = false;
|
||||
h.value.type = Type;
|
||||
h.value.id = index << IndexShift;
|
||||
handle make_handle(const index_type index) const
|
||||
{
|
||||
handle h{};
|
||||
h.bits = 0;
|
||||
h.value.is_pseudo = false;
|
||||
h.value.type = Type;
|
||||
h.value.id = index << IndexShift;
|
||||
|
||||
return h;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
T* get_by_index(const uint32_t index)
|
||||
{
|
||||
return this->get(this->make_handle(index));
|
||||
}
|
||||
T* get_by_index(const uint32_t index)
|
||||
{
|
||||
return this->get(this->make_handle(index));
|
||||
}
|
||||
|
||||
T* get(const handle_value h)
|
||||
{
|
||||
const auto entry = this->get_iterator(h);
|
||||
if (entry == this->store_.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
T* get(const handle_value h)
|
||||
{
|
||||
const auto entry = this->get_iterator(h);
|
||||
if (entry == this->store_.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &entry->second;
|
||||
}
|
||||
return &entry->second;
|
||||
}
|
||||
|
||||
T* get(const handle h)
|
||||
{
|
||||
return this->get(h.value);
|
||||
}
|
||||
T* get(const handle h)
|
||||
{
|
||||
return this->get(h.value);
|
||||
}
|
||||
|
||||
T* get(const uint64_t h)
|
||||
{
|
||||
handle hh{};
|
||||
hh.bits = h;
|
||||
T* get(const uint64_t h)
|
||||
{
|
||||
handle hh{};
|
||||
hh.bits = h;
|
||||
|
||||
return this->get(hh);
|
||||
}
|
||||
return this->get(hh);
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return this->store_.size();
|
||||
}
|
||||
size_t size() const
|
||||
{
|
||||
return this->store_.size();
|
||||
}
|
||||
|
||||
bool erase(const typename value_map::iterator& entry)
|
||||
{
|
||||
if (this->block_mutation_)
|
||||
{
|
||||
throw std::runtime_error("Mutation of handle store is blocked!");
|
||||
}
|
||||
bool erase(const typename value_map::iterator& entry)
|
||||
{
|
||||
if (this->block_mutation_)
|
||||
{
|
||||
throw std::runtime_error("Mutation of handle store is blocked!");
|
||||
}
|
||||
|
||||
if (entry == this->store_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (entry == this->store_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if constexpr (handle_detail::has_deleter_function<T>())
|
||||
{
|
||||
if (!T::deleter(entry->second))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if constexpr (handle_detail::has_deleter_function<T>())
|
||||
{
|
||||
if (!T::deleter(entry->second))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
this->store_.erase(entry);
|
||||
return true;
|
||||
}
|
||||
this->store_.erase(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool erase(const handle_value h)
|
||||
{
|
||||
const auto entry = this->get_iterator(h);
|
||||
return this->erase(entry);
|
||||
}
|
||||
bool erase(const handle_value h)
|
||||
{
|
||||
const auto entry = this->get_iterator(h);
|
||||
return this->erase(entry);
|
||||
}
|
||||
|
||||
bool erase(const handle h) override
|
||||
{
|
||||
return this->erase(h.value);
|
||||
}
|
||||
bool erase(const handle h) override
|
||||
{
|
||||
return this->erase(h.value);
|
||||
}
|
||||
|
||||
bool erase(const uint64_t h)
|
||||
{
|
||||
handle hh{};
|
||||
hh.bits = h;
|
||||
bool erase(const uint64_t h)
|
||||
{
|
||||
handle hh{};
|
||||
hh.bits = h;
|
||||
|
||||
return this->erase(hh);
|
||||
}
|
||||
return this->erase(hh);
|
||||
}
|
||||
|
||||
bool erase(const T& value)
|
||||
{
|
||||
const auto entry = this->find(value);
|
||||
return this->erase(entry);
|
||||
}
|
||||
bool erase(const T& value)
|
||||
{
|
||||
const auto entry = this->find(value);
|
||||
return this->erase(entry);
|
||||
}
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(this->block_mutation_);
|
||||
buffer.write_map(this->store_);
|
||||
}
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(this->block_mutation_);
|
||||
buffer.write_map(this->store_);
|
||||
}
|
||||
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(this->block_mutation_);
|
||||
buffer.read_map(this->store_);
|
||||
}
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(this->block_mutation_);
|
||||
buffer.read_map(this->store_);
|
||||
}
|
||||
|
||||
typename value_map::iterator find(const T& value)
|
||||
{
|
||||
auto i = this->store_.begin();
|
||||
for (; i != this->store_.end(); ++i)
|
||||
{
|
||||
if (&i->second == &value)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
typename value_map::iterator find(const T& value)
|
||||
{
|
||||
auto i = this->store_.begin();
|
||||
for (; i != this->store_.end(); ++i)
|
||||
{
|
||||
if (&i->second == &value)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
typename value_map::const_iterator find(const T& value) const
|
||||
{
|
||||
auto i = this->store_.begin();
|
||||
for (; i != this->store_.end(); ++i)
|
||||
{
|
||||
if (&i->second == &value)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
typename value_map::const_iterator find(const T& value) const
|
||||
{
|
||||
auto i = this->store_.begin();
|
||||
for (; i != this->store_.end(); ++i)
|
||||
{
|
||||
if (&i->second == &value)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
handle find_handle(const T& value) const
|
||||
{
|
||||
const auto entry = this->find(value);
|
||||
if (entry == this->end())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
handle find_handle(const T& value) const
|
||||
{
|
||||
const auto entry = this->find(value);
|
||||
if (entry == this->end())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return this->make_handle(entry->first);
|
||||
}
|
||||
return this->make_handle(entry->first);
|
||||
}
|
||||
|
||||
handle find_handle(const T* value) const
|
||||
{
|
||||
if (!value)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
handle find_handle(const T* value) const
|
||||
{
|
||||
if (!value)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return this->find_handle(*value);
|
||||
}
|
||||
return this->find_handle(*value);
|
||||
}
|
||||
|
||||
typename value_map::iterator begin()
|
||||
{
|
||||
return this->store_.begin();
|
||||
}
|
||||
typename value_map::iterator begin()
|
||||
{
|
||||
return this->store_.begin();
|
||||
}
|
||||
|
||||
typename value_map::const_iterator begin() const
|
||||
{
|
||||
return this->store_.begin();
|
||||
}
|
||||
typename value_map::const_iterator begin() const
|
||||
{
|
||||
return this->store_.begin();
|
||||
}
|
||||
|
||||
typename value_map::iterator end()
|
||||
{
|
||||
return this->store_.end();
|
||||
}
|
||||
typename value_map::iterator end()
|
||||
{
|
||||
return this->store_.end();
|
||||
}
|
||||
|
||||
typename value_map::const_iterator end() const
|
||||
{
|
||||
return this->store_.end();
|
||||
}
|
||||
typename value_map::const_iterator end() const
|
||||
{
|
||||
return this->store_.end();
|
||||
}
|
||||
|
||||
private:
|
||||
typename value_map::iterator get_iterator(const handle_value h)
|
||||
{
|
||||
if (h.type != Type || h.is_pseudo)
|
||||
{
|
||||
return this->store_.end();
|
||||
}
|
||||
private:
|
||||
typename value_map::iterator get_iterator(const handle_value h)
|
||||
{
|
||||
if (h.type != Type || h.is_pseudo)
|
||||
{
|
||||
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 index = 1;
|
||||
for (; index > 0; ++index)
|
||||
{
|
||||
if (!this->store_.contains(index))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
uint32_t find_free_index()
|
||||
{
|
||||
uint32_t index = 1;
|
||||
for (; index > 0; ++index)
|
||||
{
|
||||
if (!this->store_.contains(index))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
bool block_mutation_{false};
|
||||
value_map store_{};
|
||||
bool block_mutation_{false};
|
||||
value_map store_{};
|
||||
};
|
||||
|
||||
constexpr auto KNOWN_DLLS_DIRECTORY = make_pseudo_handle(0x1, handle_types::directory);
|
||||
|
||||
@@ -3,30 +3,27 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
struct dummy_device : stateless_device
|
||||
{
|
||||
NTSTATUS io_control(windows_emulator&, const io_device_context&) override
|
||||
{
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
};
|
||||
struct dummy_device : stateless_device
|
||||
{
|
||||
NTSTATUS io_control(windows_emulator&, const io_device_context&) override
|
||||
{
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
std::unique_ptr<io_device> create_device(const std::u16string_view device)
|
||||
{
|
||||
if (device == u"CNG"
|
||||
|| device == u"KsecDD"
|
||||
|| device == u"PcwDrv"
|
||||
|| device == u"DeviceApi\\CMApi"
|
||||
|| device == u"ConDrv\\Server")
|
||||
{
|
||||
return std::make_unique<dummy_device>();
|
||||
}
|
||||
if (device == u"CNG" || device == u"KsecDD" || device == u"PcwDrv" || device == u"DeviceApi\\CMApi" ||
|
||||
device == u"ConDrv\\Server")
|
||||
{
|
||||
return std::make_unique<dummy_device>();
|
||||
}
|
||||
|
||||
if (device == u"Afd\\Endpoint")
|
||||
{
|
||||
return create_afd_endpoint();
|
||||
}
|
||||
if (device == u"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
|
||||
{
|
||||
handle event{};
|
||||
emulator_pointer /*PIO_APC_ROUTINE*/ apc_routine{};
|
||||
emulator_pointer apc_context{};
|
||||
emulator_object<IO_STATUS_BLOCK<EmulatorTraits<Emu64>>> io_status_block;
|
||||
ULONG io_control_code{};
|
||||
emulator_pointer input_buffer{};
|
||||
ULONG input_buffer_length{};
|
||||
emulator_pointer output_buffer{};
|
||||
ULONG output_buffer_length{};
|
||||
handle event{};
|
||||
emulator_pointer /*PIO_APC_ROUTINE*/ apc_routine{};
|
||||
emulator_pointer apc_context{};
|
||||
emulator_object<IO_STATUS_BLOCK<EmulatorTraits<Emu64>>> io_status_block;
|
||||
ULONG io_control_code{};
|
||||
emulator_pointer input_buffer{};
|
||||
ULONG input_buffer_length{};
|
||||
emulator_pointer output_buffer{};
|
||||
ULONG output_buffer_length{};
|
||||
|
||||
io_device_context(x64_emulator& emu)
|
||||
: io_status_block(emu)
|
||||
{
|
||||
}
|
||||
io_device_context(x64_emulator& emu)
|
||||
: io_status_block(emu)
|
||||
{
|
||||
}
|
||||
|
||||
io_device_context(utils::buffer_deserializer& buffer)
|
||||
: io_device_context(buffer.read<x64_emulator_wrapper>().get())
|
||||
{
|
||||
}
|
||||
io_device_context(utils::buffer_deserializer& buffer)
|
||||
: io_device_context(buffer.read<x64_emulator_wrapper>().get())
|
||||
{
|
||||
}
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(event);
|
||||
buffer.write(apc_routine);
|
||||
buffer.write(apc_context);
|
||||
buffer.write(io_status_block);
|
||||
buffer.write(io_control_code);
|
||||
buffer.write(input_buffer);
|
||||
buffer.write(input_buffer_length);
|
||||
buffer.write(output_buffer);
|
||||
buffer.write(output_buffer_length);
|
||||
}
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(event);
|
||||
buffer.write(apc_routine);
|
||||
buffer.write(apc_context);
|
||||
buffer.write(io_status_block);
|
||||
buffer.write(io_control_code);
|
||||
buffer.write(input_buffer);
|
||||
buffer.write(input_buffer_length);
|
||||
buffer.write(output_buffer);
|
||||
buffer.write(output_buffer_length);
|
||||
}
|
||||
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(event);
|
||||
buffer.read(apc_routine);
|
||||
buffer.read(apc_context);
|
||||
buffer.read(io_status_block);
|
||||
buffer.read(io_control_code);
|
||||
buffer.read(input_buffer);
|
||||
buffer.read(input_buffer_length);
|
||||
buffer.read(output_buffer);
|
||||
buffer.read(output_buffer_length);
|
||||
}
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(event);
|
||||
buffer.read(apc_routine);
|
||||
buffer.read(apc_context);
|
||||
buffer.read(io_status_block);
|
||||
buffer.read(io_control_code);
|
||||
buffer.read(input_buffer);
|
||||
buffer.read(input_buffer_length);
|
||||
buffer.read(output_buffer);
|
||||
buffer.read(output_buffer_length);
|
||||
}
|
||||
};
|
||||
|
||||
struct io_device_creation_data
|
||||
{
|
||||
uint64_t buffer;
|
||||
uint32_t length;
|
||||
uint64_t buffer;
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
inline void write_io_status(const emulator_object<IO_STATUS_BLOCK<EmulatorTraits<Emu64>>> io_status_block,
|
||||
const NTSTATUS status)
|
||||
{
|
||||
if (io_status_block)
|
||||
{
|
||||
io_status_block.access([&](IO_STATUS_BLOCK<EmulatorTraits<Emu64>>& status_block)
|
||||
{
|
||||
status_block.Status = status;
|
||||
});
|
||||
}
|
||||
if (io_status_block)
|
||||
{
|
||||
io_status_block.access(
|
||||
[&](IO_STATUS_BLOCK<EmulatorTraits<Emu64>>& status_block) { status_block.Status = status; });
|
||||
}
|
||||
}
|
||||
|
||||
struct io_device
|
||||
{
|
||||
io_device() = default;
|
||||
virtual ~io_device() = default;
|
||||
io_device() = default;
|
||||
virtual ~io_device() = default;
|
||||
|
||||
io_device(io_device&&) = default;
|
||||
io_device& operator=(io_device&&) = default;
|
||||
io_device(io_device&&) = default;
|
||||
io_device& operator=(io_device&&) = default;
|
||||
|
||||
io_device(const io_device&) = delete;
|
||||
io_device& operator=(const io_device&) = delete;
|
||||
io_device(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)
|
||||
{
|
||||
(void)win_emu;
|
||||
(void)data;
|
||||
}
|
||||
virtual void create(windows_emulator& win_emu, const io_device_creation_data& data)
|
||||
{
|
||||
(void)win_emu;
|
||||
(void)data;
|
||||
}
|
||||
|
||||
virtual void work(windows_emulator& win_emu)
|
||||
{
|
||||
(void)win_emu;
|
||||
}
|
||||
virtual void work(windows_emulator& win_emu)
|
||||
{
|
||||
(void)win_emu;
|
||||
}
|
||||
|
||||
virtual void serialize(utils::buffer_serializer& buffer) const = 0;
|
||||
virtual void deserialize(utils::buffer_deserializer& buffer) = 0;
|
||||
virtual void serialize(utils::buffer_serializer& buffer) const = 0;
|
||||
virtual void deserialize(utils::buffer_deserializer& buffer) = 0;
|
||||
|
||||
NTSTATUS execute_ioctl(windows_emulator& win_emu, const io_device_context& c)
|
||||
{
|
||||
if (c.io_status_block)
|
||||
{
|
||||
c.io_status_block.write({});
|
||||
}
|
||||
NTSTATUS execute_ioctl(windows_emulator& win_emu, const io_device_context& c)
|
||||
{
|
||||
if (c.io_status_block)
|
||||
{
|
||||
c.io_status_block.write({});
|
||||
}
|
||||
|
||||
const auto result = this->io_control(win_emu, c);
|
||||
write_io_status(c.io_status_block, result);
|
||||
return result;
|
||||
}
|
||||
const auto result = this->io_control(win_emu, c);
|
||||
write_io_status(c.io_status_block, result);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
class io_device_container : public io_device
|
||||
{
|
||||
public:
|
||||
io_device_container() = default;
|
||||
public:
|
||||
io_device_container() = default;
|
||||
|
||||
io_device_container(std::u16string device, windows_emulator& win_emu, const io_device_creation_data& data)
|
||||
: device_name_(std::move(device))
|
||||
{
|
||||
this->setup();
|
||||
this->device_->create(win_emu, data);
|
||||
}
|
||||
io_device_container(std::u16string device, windows_emulator& win_emu, const io_device_creation_data& data)
|
||||
: device_name_(std::move(device))
|
||||
{
|
||||
this->setup();
|
||||
this->device_->create(win_emu, data);
|
||||
}
|
||||
|
||||
NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& context) override
|
||||
{
|
||||
this->assert_validity();
|
||||
return this->device_->io_control(win_emu, context);
|
||||
}
|
||||
NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& context) override
|
||||
{
|
||||
this->assert_validity();
|
||||
return this->device_->io_control(win_emu, context);
|
||||
}
|
||||
|
||||
void work(windows_emulator& win_emu) override
|
||||
{
|
||||
this->assert_validity();
|
||||
return this->device_->work(win_emu);
|
||||
}
|
||||
void work(windows_emulator& win_emu) override
|
||||
{
|
||||
this->assert_validity();
|
||||
return this->device_->work(win_emu);
|
||||
}
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const override
|
||||
{
|
||||
this->assert_validity();
|
||||
void serialize(utils::buffer_serializer& buffer) const override
|
||||
{
|
||||
this->assert_validity();
|
||||
|
||||
buffer.write_string(this->device_name_);
|
||||
this->device_->serialize(buffer);
|
||||
}
|
||||
buffer.write_string(this->device_name_);
|
||||
this->device_->serialize(buffer);
|
||||
}
|
||||
|
||||
void deserialize(utils::buffer_deserializer& buffer) override
|
||||
{
|
||||
buffer.read_string(this->device_name_);
|
||||
this->setup();
|
||||
this->device_->deserialize(buffer);
|
||||
}
|
||||
void deserialize(utils::buffer_deserializer& buffer) override
|
||||
{
|
||||
buffer.read_string(this->device_name_);
|
||||
this->setup();
|
||||
this->device_->deserialize(buffer);
|
||||
}
|
||||
|
||||
template <typename T = io_device>
|
||||
requires(std::is_base_of_v<io_device, T> || std::is_same_v<io_device, T>)
|
||||
T* get_internal_device()
|
||||
{
|
||||
this->assert_validity();
|
||||
auto* value = this->device_.get();
|
||||
return dynamic_cast<T*>(value);
|
||||
}
|
||||
template <typename T = io_device>
|
||||
requires(std::is_base_of_v<io_device, T> || std::is_same_v<io_device, T>)
|
||||
T* get_internal_device()
|
||||
{
|
||||
this->assert_validity();
|
||||
auto* value = this->device_.get();
|
||||
return dynamic_cast<T*>(value);
|
||||
}
|
||||
|
||||
private:
|
||||
std::u16string device_name_{};
|
||||
std::unique_ptr<io_device> device_{};
|
||||
private:
|
||||
std::u16string device_name_{};
|
||||
std::unique_ptr<io_device> device_{};
|
||||
|
||||
void setup()
|
||||
{
|
||||
this->device_ = create_device(this->device_name_);
|
||||
}
|
||||
void setup()
|
||||
{
|
||||
this->device_ = create_device(this->device_name_);
|
||||
}
|
||||
|
||||
void assert_validity() const
|
||||
{
|
||||
if (!this->device_)
|
||||
{
|
||||
throw std::runtime_error("Device not created!");
|
||||
}
|
||||
}
|
||||
void assert_validity() const
|
||||
{
|
||||
if (!this->device_)
|
||||
{
|
||||
throw std::runtime_error("Device not created!");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -10,216 +10,214 @@ constexpr auto KUSD_BUFFER_SIZE = page_align_up(KUSD_SIZE);
|
||||
|
||||
namespace
|
||||
{
|
||||
void setup_kusd(KUSER_SHARED_DATA64& kusd, const bool use_relative_time)
|
||||
{
|
||||
memset(reinterpret_cast<void*>(&kusd), 0, sizeof(kusd));
|
||||
void setup_kusd(KUSER_SHARED_DATA64& kusd, const bool use_relative_time)
|
||||
{
|
||||
memset(reinterpret_cast<void*>(&kusd), 0, sizeof(kusd));
|
||||
|
||||
kusd.TickCountMultiplier = 0x0fa00000;
|
||||
kusd.InterruptTime.LowPart = 0x17bd9547;
|
||||
kusd.InterruptTime.High1Time = 0x0000004b;
|
||||
kusd.InterruptTime.High2Time = 0x0000004b;
|
||||
kusd.SystemTime.LowPart = 0x7af9da99;
|
||||
kusd.SystemTime.High1Time = 0x01db27b9;
|
||||
kusd.SystemTime.High2Time = 0x01db27b9;
|
||||
kusd.TimeZoneBias.LowPart = 0x3c773000;
|
||||
kusd.TimeZoneBias.High1Time = -17;
|
||||
kusd.TimeZoneBias.High2Time = -17;
|
||||
kusd.TimeZoneId = 0x00000002;
|
||||
kusd.LargePageMinimum = 0x00200000;
|
||||
kusd.RNGSeedVersion = 0x0000000000000013;
|
||||
kusd.TimeZoneBiasStamp = 0x00000004;
|
||||
kusd.NtBuildNumber = 0x00006c51;
|
||||
kusd.NtProductType = NtProductWinNt;
|
||||
kusd.ProductTypeIsValid = 0x01;
|
||||
kusd.NativeProcessorArchitecture = 0x0009;
|
||||
kusd.NtMajorVersion = 0x0000000a;
|
||||
kusd.BootId = 0x0000000b;
|
||||
kusd.SystemExpirationDate.QuadPart = 0x01dc26860a9ff300;
|
||||
kusd.SuiteMask = 0x00000110;
|
||||
kusd.MitigationPolicies.MitigationPolicies = 0x0a;
|
||||
kusd.MitigationPolicies.NXSupportPolicy = 0x02;
|
||||
kusd.MitigationPolicies.SEHValidationPolicy = 0x02;
|
||||
kusd.CyclesPerYield = 0x0064;
|
||||
kusd.DismountCount = 0x00000006;
|
||||
kusd.ComPlusPackage = 0x00000001;
|
||||
kusd.LastSystemRITEventTickCount = 0x01ec1fd3;
|
||||
kusd.NumberOfPhysicalPages = 0x00bf0958;
|
||||
kusd.FullNumberOfPhysicalPages = 0x0000000000bf0958;
|
||||
kusd.TickCount.TickCount.LowPart = 0x001f7f05;
|
||||
kusd.TickCount.TickCountQuad = 0x00000000001f7f05;
|
||||
kusd.Cookie = 0x1c3471da;
|
||||
kusd.ConsoleSessionForegroundProcessId = 0x00000000000028f4;
|
||||
kusd.TimeUpdateLock = 0x0000000002b28586;
|
||||
kusd.BaselineSystemTimeQpc = 0x0000004b17cd596c;
|
||||
kusd.BaselineInterruptTimeQpc = 0x0000004b17cd596c;
|
||||
kusd.QpcSystemTimeIncrement = 0x8000000000000000;
|
||||
kusd.QpcInterruptTimeIncrement = 0x8000000000000000;
|
||||
kusd.QpcSystemTimeIncrementShift = 0x01;
|
||||
kusd.QpcInterruptTimeIncrementShift = 0x01;
|
||||
kusd.UnparkedProcessorCount = 0x000c;
|
||||
kusd.TelemetryCoverageRound = 0x00000001;
|
||||
kusd.LangGenerationCount = 0x00000003;
|
||||
kusd.InterruptTimeBias = 0x00000015a5d56406;
|
||||
kusd.QpcBias = 0x000000159530c4af;
|
||||
kusd.ActiveProcessorCount = 0x0000000c;
|
||||
kusd.ActiveGroupCount = 0x01;
|
||||
kusd.QpcData.QpcData = 0x0083;
|
||||
kusd.QpcData.QpcBypassEnabled = 0x83;
|
||||
kusd.TimeZoneBiasEffectiveStart.QuadPart = 0x01db276e654cb2ff;
|
||||
kusd.TimeZoneBiasEffectiveEnd.QuadPart = 0x01db280b8c3b2800;
|
||||
kusd.XState.EnabledFeatures = 0x000000000000001f;
|
||||
kusd.XState.EnabledVolatileFeatures = 0x000000000000000f;
|
||||
kusd.XState.Size = 0x000003c0;
|
||||
kusd.TickCountMultiplier = 0x0fa00000;
|
||||
kusd.InterruptTime.LowPart = 0x17bd9547;
|
||||
kusd.InterruptTime.High1Time = 0x0000004b;
|
||||
kusd.InterruptTime.High2Time = 0x0000004b;
|
||||
kusd.SystemTime.LowPart = 0x7af9da99;
|
||||
kusd.SystemTime.High1Time = 0x01db27b9;
|
||||
kusd.SystemTime.High2Time = 0x01db27b9;
|
||||
kusd.TimeZoneBias.LowPart = 0x3c773000;
|
||||
kusd.TimeZoneBias.High1Time = -17;
|
||||
kusd.TimeZoneBias.High2Time = -17;
|
||||
kusd.TimeZoneId = 0x00000002;
|
||||
kusd.LargePageMinimum = 0x00200000;
|
||||
kusd.RNGSeedVersion = 0x0000000000000013;
|
||||
kusd.TimeZoneBiasStamp = 0x00000004;
|
||||
kusd.NtBuildNumber = 0x00006c51;
|
||||
kusd.NtProductType = NtProductWinNt;
|
||||
kusd.ProductTypeIsValid = 0x01;
|
||||
kusd.NativeProcessorArchitecture = 0x0009;
|
||||
kusd.NtMajorVersion = 0x0000000a;
|
||||
kusd.BootId = 0x0000000b;
|
||||
kusd.SystemExpirationDate.QuadPart = 0x01dc26860a9ff300;
|
||||
kusd.SuiteMask = 0x00000110;
|
||||
kusd.MitigationPolicies.MitigationPolicies = 0x0a;
|
||||
kusd.MitigationPolicies.NXSupportPolicy = 0x02;
|
||||
kusd.MitigationPolicies.SEHValidationPolicy = 0x02;
|
||||
kusd.CyclesPerYield = 0x0064;
|
||||
kusd.DismountCount = 0x00000006;
|
||||
kusd.ComPlusPackage = 0x00000001;
|
||||
kusd.LastSystemRITEventTickCount = 0x01ec1fd3;
|
||||
kusd.NumberOfPhysicalPages = 0x00bf0958;
|
||||
kusd.FullNumberOfPhysicalPages = 0x0000000000bf0958;
|
||||
kusd.TickCount.TickCount.LowPart = 0x001f7f05;
|
||||
kusd.TickCount.TickCountQuad = 0x00000000001f7f05;
|
||||
kusd.Cookie = 0x1c3471da;
|
||||
kusd.ConsoleSessionForegroundProcessId = 0x00000000000028f4;
|
||||
kusd.TimeUpdateLock = 0x0000000002b28586;
|
||||
kusd.BaselineSystemTimeQpc = 0x0000004b17cd596c;
|
||||
kusd.BaselineInterruptTimeQpc = 0x0000004b17cd596c;
|
||||
kusd.QpcSystemTimeIncrement = 0x8000000000000000;
|
||||
kusd.QpcInterruptTimeIncrement = 0x8000000000000000;
|
||||
kusd.QpcSystemTimeIncrementShift = 0x01;
|
||||
kusd.QpcInterruptTimeIncrementShift = 0x01;
|
||||
kusd.UnparkedProcessorCount = 0x000c;
|
||||
kusd.TelemetryCoverageRound = 0x00000001;
|
||||
kusd.LangGenerationCount = 0x00000003;
|
||||
kusd.InterruptTimeBias = 0x00000015a5d56406;
|
||||
kusd.QpcBias = 0x000000159530c4af;
|
||||
kusd.ActiveProcessorCount = 0x0000000c;
|
||||
kusd.ActiveGroupCount = 0x01;
|
||||
kusd.QpcData.QpcData = 0x0083;
|
||||
kusd.QpcData.QpcBypassEnabled = 0x83;
|
||||
kusd.TimeZoneBiasEffectiveStart.QuadPart = 0x01db276e654cb2ff;
|
||||
kusd.TimeZoneBiasEffectiveEnd.QuadPart = 0x01db280b8c3b2800;
|
||||
kusd.XState.EnabledFeatures = 0x000000000000001f;
|
||||
kusd.XState.EnabledVolatileFeatures = 0x000000000000000f;
|
||||
kusd.XState.Size = 0x000003c0;
|
||||
|
||||
if (use_relative_time)
|
||||
{
|
||||
kusd.QpcFrequency = 1000;
|
||||
}
|
||||
else
|
||||
{
|
||||
kusd.QpcFrequency = std::chrono::steady_clock::period::den;
|
||||
}
|
||||
if (use_relative_time)
|
||||
{
|
||||
kusd.QpcFrequency = 1000;
|
||||
}
|
||||
else
|
||||
{
|
||||
kusd.QpcFrequency = std::chrono::steady_clock::period::den;
|
||||
}
|
||||
|
||||
constexpr std::wstring_view root_dir{L"C:\\WINDOWS"};
|
||||
memcpy(&kusd.NtSystemRoot.arr[0], root_dir.data(), root_dir.size() * 2);
|
||||
constexpr std::wstring_view root_dir{L"C:\\WINDOWS"};
|
||||
memcpy(&kusd.NtSystemRoot.arr[0], root_dir.data(), root_dir.size() * 2);
|
||||
|
||||
kusd.ImageNumberLow = IMAGE_FILE_MACHINE_I386;
|
||||
kusd.ImageNumberHigh = IMAGE_FILE_MACHINE_AMD64;
|
||||
}
|
||||
kusd.ImageNumberLow = IMAGE_FILE_MACHINE_I386;
|
||||
kusd.ImageNumberHigh = IMAGE_FILE_MACHINE_AMD64;
|
||||
}
|
||||
}
|
||||
|
||||
namespace utils
|
||||
{
|
||||
inline void serialize(buffer_serializer& buffer, const KUSER_SHARED_DATA64& kusd)
|
||||
{
|
||||
static_assert(KUSD_SIZE == sizeof(kusd));
|
||||
buffer.write(&kusd, KUSD_SIZE);
|
||||
}
|
||||
inline void serialize(buffer_serializer& buffer, const KUSER_SHARED_DATA64& kusd)
|
||||
{
|
||||
static_assert(KUSD_SIZE == sizeof(kusd));
|
||||
buffer.write(&kusd, KUSD_SIZE);
|
||||
}
|
||||
|
||||
inline void deserialize(buffer_deserializer& buffer, KUSER_SHARED_DATA64& kusd)
|
||||
{
|
||||
buffer.read(&kusd, KUSD_SIZE);
|
||||
}
|
||||
inline void deserialize(buffer_deserializer& buffer, KUSER_SHARED_DATA64& kusd)
|
||||
{
|
||||
buffer.read(&kusd, KUSD_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
kusd_mmio::kusd_mmio(x64_emulator& emu, process_context& process)
|
||||
: emu_(&emu)
|
||||
, process_(&process)
|
||||
: emu_(&emu),
|
||||
process_(&process)
|
||||
{
|
||||
}
|
||||
|
||||
kusd_mmio::~kusd_mmio()
|
||||
{
|
||||
this->deregister_mmio();
|
||||
this->deregister_mmio();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
this->use_relative_time_ = use_relative_time;
|
||||
this->use_relative_time_ = use_relative_time;
|
||||
|
||||
setup_kusd(this->kusd_, use_relative_time);
|
||||
this->start_time_ = convert_from_ksystem_time(this->kusd_.SystemTime);
|
||||
setup_kusd(this->kusd_, use_relative_time);
|
||||
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
|
||||
{
|
||||
buffer.write(this->use_relative_time_);
|
||||
buffer.write(this->kusd_);
|
||||
buffer.write(this->start_time_);
|
||||
buffer.write(this->use_relative_time_);
|
||||
buffer.write(this->kusd_);
|
||||
buffer.write(this->start_time_);
|
||||
}
|
||||
|
||||
void kusd_mmio::deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(this->use_relative_time_);
|
||||
buffer.read(this->kusd_);
|
||||
buffer.read(this->start_time_);
|
||||
buffer.read(this->use_relative_time_);
|
||||
buffer.read(this->kusd_);
|
||||
buffer.read(this->start_time_);
|
||||
|
||||
this->deregister_mmio();
|
||||
this->register_mmio();
|
||||
this->deregister_mmio();
|
||||
this->register_mmio();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (addr >= KUSD_SIZE)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto end = addr + size;
|
||||
const auto valid_end = std::min(end, static_cast<uint64_t>(KUSD_SIZE));
|
||||
const auto real_size = valid_end - addr;
|
||||
const auto end = addr + size;
|
||||
const auto valid_end = std::min(end, static_cast<uint64_t>(KUSD_SIZE));
|
||||
const auto real_size = valid_end - addr;
|
||||
|
||||
if (real_size > sizeof(result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (real_size > sizeof(result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto* kusd_buffer = reinterpret_cast<uint8_t*>(&this->kusd_);
|
||||
memcpy(&result, kusd_buffer + addr, real_size);
|
||||
const auto* kusd_buffer = reinterpret_cast<uint8_t*>(&this->kusd_);
|
||||
memcpy(&result, kusd_buffer + addr, real_size);
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t kusd_mmio::address()
|
||||
{
|
||||
return KUSD_ADDRESS;
|
||||
return KUSD_ADDRESS;
|
||||
}
|
||||
|
||||
void kusd_mmio::update()
|
||||
{
|
||||
auto time = this->start_time_;
|
||||
auto time = this->start_time_;
|
||||
|
||||
if (this->use_relative_time_)
|
||||
{
|
||||
const auto passed_time = this->process_->executed_instructions;
|
||||
const auto clock_frequency = static_cast<uint64_t>(this->kusd_.QpcFrequency);
|
||||
if (this->use_relative_time_)
|
||||
{
|
||||
const auto passed_time = this->process_->executed_instructions;
|
||||
const auto clock_frequency = static_cast<uint64_t>(this->kusd_.QpcFrequency);
|
||||
|
||||
using duration = std::chrono::system_clock::duration;
|
||||
time += duration(passed_time * duration::period::den / clock_frequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
time = std::chrono::system_clock::now();
|
||||
}
|
||||
using duration = std::chrono::system_clock::duration;
|
||||
time += duration(passed_time * duration::period::den / clock_frequency);
|
||||
}
|
||||
else
|
||||
{
|
||||
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()
|
||||
{
|
||||
if (this->registered_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (this->registered_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this->registered_ = true;
|
||||
this->registered_ = true;
|
||||
|
||||
this->emu_->allocate_mmio(KUSD_ADDRESS, KUSD_BUFFER_SIZE,
|
||||
[this](const uint64_t addr, const size_t size)
|
||||
{
|
||||
return this->read(addr, size);
|
||||
}, [](const uint64_t, const size_t, const uint64_t)
|
||||
{
|
||||
// Writing not supported!
|
||||
});
|
||||
this->emu_->allocate_mmio(
|
||||
KUSD_ADDRESS, KUSD_BUFFER_SIZE,
|
||||
[this](const uint64_t addr, const size_t size) { return this->read(addr, size); },
|
||||
[](const uint64_t, const size_t, const uint64_t) {
|
||||
// Writing not supported!
|
||||
});
|
||||
}
|
||||
|
||||
void kusd_mmio::deregister_mmio()
|
||||
{
|
||||
if (this->registered_)
|
||||
{
|
||||
this->registered_ = false;
|
||||
this->emu_->release_memory(KUSD_ADDRESS, KUSD_BUFFER_SIZE);
|
||||
}
|
||||
if (this->registered_)
|
||||
{
|
||||
this->registered_ = false;
|
||||
this->emu_->release_memory(KUSD_ADDRESS, KUSD_BUFFER_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,48 +10,48 @@ class windows_emulator;
|
||||
|
||||
class kusd_mmio
|
||||
{
|
||||
public:
|
||||
kusd_mmio(x64_emulator& emu, process_context& process);
|
||||
~kusd_mmio();
|
||||
public:
|
||||
kusd_mmio(x64_emulator& emu, process_context& process);
|
||||
~kusd_mmio();
|
||||
|
||||
kusd_mmio(utils::buffer_deserializer& buffer);
|
||||
kusd_mmio(utils::buffer_deserializer& buffer);
|
||||
|
||||
kusd_mmio(kusd_mmio&&) = delete;
|
||||
kusd_mmio(const kusd_mmio&) = delete;
|
||||
kusd_mmio& operator=(kusd_mmio&& obj) = delete;
|
||||
kusd_mmio& operator=(const kusd_mmio&) = delete;
|
||||
kusd_mmio(kusd_mmio&&) = delete;
|
||||
kusd_mmio(const kusd_mmio&) = delete;
|
||||
kusd_mmio& operator=(kusd_mmio&& obj) = delete;
|
||||
kusd_mmio& operator=(const kusd_mmio&) = delete;
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const;
|
||||
void deserialize(utils::buffer_deserializer& buffer);
|
||||
void serialize(utils::buffer_serializer& buffer) const;
|
||||
void deserialize(utils::buffer_deserializer& buffer);
|
||||
|
||||
KUSER_SHARED_DATA64& get()
|
||||
{
|
||||
return this->kusd_;
|
||||
}
|
||||
KUSER_SHARED_DATA64& get()
|
||||
{
|
||||
return this->kusd_;
|
||||
}
|
||||
|
||||
const KUSER_SHARED_DATA64& get() const
|
||||
{
|
||||
return this->kusd_;
|
||||
}
|
||||
const KUSER_SHARED_DATA64& get() const
|
||||
{
|
||||
return this->kusd_;
|
||||
}
|
||||
|
||||
static uint64_t address();
|
||||
static uint64_t address();
|
||||
|
||||
void setup(bool use_relative_time);
|
||||
void setup(bool use_relative_time);
|
||||
|
||||
private:
|
||||
x64_emulator* emu_{};
|
||||
process_context* process_{};
|
||||
private:
|
||||
x64_emulator* emu_{};
|
||||
process_context* process_{};
|
||||
|
||||
bool registered_{};
|
||||
bool use_relative_time_{};
|
||||
bool registered_{};
|
||||
bool use_relative_time_{};
|
||||
|
||||
KUSER_SHARED_DATA64 kusd_{};
|
||||
std::chrono::system_clock::time_point start_time_{};
|
||||
KUSER_SHARED_DATA64 kusd_{};
|
||||
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 deregister_mmio();
|
||||
void register_mmio();
|
||||
void deregister_mmio();
|
||||
};
|
||||
|
||||
@@ -7,130 +7,141 @@ namespace
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#define COLOR(win, posix) win
|
||||
using color_type = WORD;
|
||||
using color_type = WORD;
|
||||
#else
|
||||
#define COLOR(win, posix) posix
|
||||
using color_type = const char*;
|
||||
using color_type = const char*;
|
||||
#endif
|
||||
|
||||
color_type get_reset_color()
|
||||
{
|
||||
return COLOR(7, "\033[0m");
|
||||
}
|
||||
color_type get_reset_color()
|
||||
{
|
||||
return COLOR(7, "\033[0m");
|
||||
}
|
||||
|
||||
color_type get_color_type(const color c)
|
||||
{
|
||||
using enum color;
|
||||
color_type get_color_type(const color c)
|
||||
{
|
||||
using enum color;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case black: return COLOR(0x8, "\033[0;90m");
|
||||
case red: return COLOR(0xC, "\033[0;91m");
|
||||
case green: return COLOR(0xA, "\033[0;92m");
|
||||
case yellow: return COLOR(0xE, "\033[0;93m");
|
||||
case blue: return COLOR(0x9, "\033[0;94m");
|
||||
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();
|
||||
}
|
||||
}
|
||||
switch (c)
|
||||
{
|
||||
case black:
|
||||
return COLOR(0x8, "\033[0;90m");
|
||||
case red:
|
||||
return COLOR(0xC, "\033[0;91m");
|
||||
case green:
|
||||
return COLOR(0xA, "\033[0;92m");
|
||||
case yellow:
|
||||
return COLOR(0xE, "\033[0;93m");
|
||||
case blue:
|
||||
return COLOR(0x9, "\033[0;94m");
|
||||
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
|
||||
HANDLE get_console_handle()
|
||||
{
|
||||
return GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
}
|
||||
HANDLE get_console_handle()
|
||||
{
|
||||
return GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
}
|
||||
#endif
|
||||
|
||||
void set_color(const color_type color)
|
||||
{
|
||||
void set_color(const color_type color)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(get_console_handle(), color);
|
||||
SetConsoleTextAttribute(get_console_handle(), color);
|
||||
#else
|
||||
printf("%s", color);
|
||||
printf("%s", color);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void reset_color()
|
||||
{
|
||||
(void)fflush(stdout);
|
||||
set_color(get_reset_color());
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
void reset_color()
|
||||
{
|
||||
(void)fflush(stdout);
|
||||
set_color(get_reset_color());
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
|
||||
std::string_view format(va_list* ap, const char* message)
|
||||
{
|
||||
thread_local char buffer[0x1000];
|
||||
std::string_view format(va_list* ap, const char* message)
|
||||
{
|
||||
thread_local char buffer[0x1000];
|
||||
|
||||
#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
|
||||
const int count = vsnprintf(buffer, sizeof(buffer), message, *ap);
|
||||
const int count = vsnprintf(buffer, sizeof(buffer), message, *ap);
|
||||
#endif
|
||||
|
||||
if (count < 0) return {};
|
||||
return {buffer, static_cast<size_t>(count)};
|
||||
}
|
||||
if (count < 0)
|
||||
return {};
|
||||
return {buffer, static_cast<size_t>(count)};
|
||||
}
|
||||
|
||||
#define format_to_string(msg, str)\
|
||||
va_list ap;\
|
||||
va_start(ap, msg);\
|
||||
const auto str = format(&ap, msg);\
|
||||
va_end(ap);
|
||||
#define format_to_string(msg, str) \
|
||||
va_list ap; \
|
||||
va_start(ap, msg); \
|
||||
const auto str = format(&ap, msg); \
|
||||
va_end(ap);
|
||||
|
||||
void print_colored(const std::string_view& line, const color_type base_color)
|
||||
{
|
||||
const auto _ = utils::finally(&reset_color);
|
||||
set_color(base_color);
|
||||
(void)fwrite(line.data(), 1, line.size(), stdout);
|
||||
}
|
||||
void print_colored(const std::string_view& line, const color_type base_color)
|
||||
{
|
||||
const auto _ = utils::finally(&reset_color);
|
||||
set_color(base_color);
|
||||
(void)fwrite(line.data(), 1, line.size(), stdout);
|
||||
}
|
||||
}
|
||||
|
||||
void logger::print(const color c, const std::string_view message) const
|
||||
{
|
||||
if (this->disable_output_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (this->disable_output_)
|
||||
{
|
||||
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
|
||||
{
|
||||
format_to_string(message, data);
|
||||
this->print(c, data);
|
||||
format_to_string(message, data);
|
||||
this->print(c, data);
|
||||
}
|
||||
|
||||
void logger::info(const char* message, ...) const
|
||||
{
|
||||
format_to_string(message, data);
|
||||
this->print(color::cyan, data);
|
||||
format_to_string(message, data);
|
||||
this->print(color::cyan, data);
|
||||
}
|
||||
|
||||
void logger::warn(const char* message, ...) const
|
||||
{
|
||||
format_to_string(message, data);
|
||||
this->print(color::yellow, data);
|
||||
format_to_string(message, data);
|
||||
this->print(color::yellow, data);
|
||||
}
|
||||
|
||||
void logger::error(const char* message, ...) const
|
||||
{
|
||||
format_to_string(message, data);
|
||||
this->print(color::red, data);
|
||||
format_to_string(message, data);
|
||||
this->print(color::red, data);
|
||||
}
|
||||
|
||||
void logger::success(const char* message, ...) const
|
||||
{
|
||||
format_to_string(message, data);
|
||||
this->print(color::green, data);
|
||||
format_to_string(message, data);
|
||||
this->print(color::green, data);
|
||||
}
|
||||
|
||||
void logger::log(const char* message, ...) const
|
||||
{
|
||||
format_to_string(message, data);
|
||||
this->print(color::gray, data);
|
||||
format_to_string(message, data);
|
||||
this->print(color::gray, data);
|
||||
}
|
||||
|
||||
@@ -3,44 +3,44 @@
|
||||
#ifdef OS_WINDOWS
|
||||
#define FORMAT_ATTRIBUTE(fmt_pos, var_pos)
|
||||
#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
|
||||
|
||||
enum class color
|
||||
{
|
||||
black,
|
||||
red,
|
||||
green,
|
||||
yellow,
|
||||
blue,
|
||||
cyan,
|
||||
pink,
|
||||
white,
|
||||
gray,
|
||||
dark_gray,
|
||||
black,
|
||||
red,
|
||||
green,
|
||||
yellow,
|
||||
blue,
|
||||
cyan,
|
||||
pink,
|
||||
white,
|
||||
gray,
|
||||
dark_gray,
|
||||
};
|
||||
|
||||
class logger
|
||||
{
|
||||
public:
|
||||
void print(color c, std::string_view message) const;
|
||||
void print(color c, const char* message, ...) const FORMAT_ATTRIBUTE(3, 4);
|
||||
void info(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 success(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
|
||||
void log(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
|
||||
public:
|
||||
void print(color c, std::string_view message) const;
|
||||
void print(color c, const char* message, ...) const FORMAT_ATTRIBUTE(3, 4);
|
||||
void info(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 success(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
|
||||
void log(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
|
||||
|
||||
void disable_output(const bool value)
|
||||
{
|
||||
this->disable_output_ = value;
|
||||
}
|
||||
void disable_output(const bool value)
|
||||
{
|
||||
this->disable_output_ = value;
|
||||
}
|
||||
|
||||
bool is_output_disabled() const
|
||||
{
|
||||
return this->disable_output_;
|
||||
}
|
||||
bool is_output_disabled() const
|
||||
{
|
||||
return this->disable_output_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool disable_output_{false};
|
||||
private:
|
||||
bool disable_output_{false};
|
||||
};
|
||||
|
||||
@@ -5,69 +5,69 @@
|
||||
|
||||
inline std::string get_permission_string(const memory_permission permission)
|
||||
{
|
||||
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_write = (permission & memory_permission::write) != 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_write = (permission & memory_permission::write) != memory_permission::none;
|
||||
|
||||
std::string res = {};
|
||||
res.reserve(3);
|
||||
std::string res = {};
|
||||
res.reserve(3);
|
||||
|
||||
res.push_back(has_read ? 'r' : '-');
|
||||
res.push_back(has_write ? 'w' : '-');
|
||||
res.push_back(has_exec ? 'x' : '-');
|
||||
res.push_back(has_read ? 'r' : '-');
|
||||
res.push_back(has_write ? 'w' : '-');
|
||||
res.push_back(has_exec ? 'x' : '-');
|
||||
|
||||
return res;
|
||||
return res;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
case PAGE_NOACCESS:
|
||||
return memory_permission::none;
|
||||
case PAGE_READONLY:
|
||||
return memory_permission::read;
|
||||
case PAGE_READWRITE:
|
||||
case PAGE_WRITECOPY:
|
||||
return memory_permission::read | memory_permission::write;
|
||||
case PAGE_EXECUTE:
|
||||
case PAGE_EXECUTE_READ:
|
||||
return memory_permission::read | memory_permission::exec;
|
||||
case PAGE_EXECUTE_READWRITE:
|
||||
return memory_permission::all;
|
||||
case PAGE_EXECUTE_WRITECOPY:
|
||||
default:
|
||||
throw std::runtime_error("Failed to map protection");
|
||||
}
|
||||
switch (nt_protection)
|
||||
{
|
||||
case PAGE_NOACCESS:
|
||||
return memory_permission::none;
|
||||
case PAGE_READONLY:
|
||||
return memory_permission::read;
|
||||
case PAGE_READWRITE:
|
||||
case PAGE_WRITECOPY:
|
||||
return memory_permission::read | memory_permission::write;
|
||||
case PAGE_EXECUTE:
|
||||
case PAGE_EXECUTE_READ:
|
||||
return memory_permission::read | memory_permission::exec;
|
||||
case PAGE_EXECUTE_READWRITE:
|
||||
return memory_permission::all;
|
||||
case PAGE_EXECUTE_WRITECOPY:
|
||||
default:
|
||||
throw std::runtime_error("Failed to map protection");
|
||||
}
|
||||
}
|
||||
|
||||
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_read = (permission & memory_permission::read) != memory_permission::none;
|
||||
const bool has_write = (permission & memory_permission::write) != 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_write = (permission & memory_permission::write) != memory_permission::none;
|
||||
|
||||
if (!has_read)
|
||||
{
|
||||
return PAGE_NOACCESS;
|
||||
}
|
||||
if (!has_read)
|
||||
{
|
||||
return PAGE_NOACCESS;
|
||||
}
|
||||
|
||||
if (has_exec && has_write)
|
||||
{
|
||||
return PAGE_EXECUTE_READWRITE;
|
||||
}
|
||||
if (has_exec && has_write)
|
||||
{
|
||||
return PAGE_EXECUTE_READWRITE;
|
||||
}
|
||||
|
||||
if (has_exec)
|
||||
{
|
||||
return PAGE_EXECUTE_READ;
|
||||
}
|
||||
if (has_exec)
|
||||
{
|
||||
return PAGE_EXECUTE_READ;
|
||||
}
|
||||
|
||||
if (has_write)
|
||||
{
|
||||
return PAGE_READWRITE;
|
||||
}
|
||||
if (has_write)
|
||||
{
|
||||
return PAGE_READWRITE;
|
||||
}
|
||||
|
||||
return PAGE_READONLY;
|
||||
return PAGE_READONLY;
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
|
||||
struct exported_symbol
|
||||
{
|
||||
std::string name{};
|
||||
uint64_t ordinal{};
|
||||
uint64_t rva{};
|
||||
uint64_t address{};
|
||||
std::string name{};
|
||||
uint64_t ordinal{};
|
||||
uint64_t rva{};
|
||||
uint64_t address{};
|
||||
};
|
||||
|
||||
using exported_symbols = std::vector<exported_symbol>;
|
||||
@@ -14,39 +14,39 @@ using address_name_mapping = std::map<uint64_t, std::string>;
|
||||
|
||||
struct mapped_section
|
||||
{
|
||||
std::string name{};
|
||||
basic_memory_region region{};
|
||||
std::string name{};
|
||||
basic_memory_region region{};
|
||||
};
|
||||
|
||||
struct mapped_module
|
||||
{
|
||||
std::string name{};
|
||||
std::filesystem::path path{};
|
||||
std::string name{};
|
||||
std::filesystem::path path{};
|
||||
|
||||
uint64_t image_base{};
|
||||
uint64_t size_of_image{};
|
||||
uint64_t entry_point{};
|
||||
uint64_t image_base{};
|
||||
uint64_t size_of_image{};
|
||||
uint64_t entry_point{};
|
||||
|
||||
exported_symbols exports{};
|
||||
address_name_mapping address_names{};
|
||||
exported_symbols exports{};
|
||||
address_name_mapping address_names{};
|
||||
|
||||
std::vector<mapped_section> sections{};
|
||||
std::vector<mapped_section> sections{};
|
||||
|
||||
bool is_within(const uint64_t address) const
|
||||
{
|
||||
return address >= this->image_base && address < (this->image_base + this->size_of_image);
|
||||
}
|
||||
bool is_within(const uint64_t address) const
|
||||
{
|
||||
return address >= this->image_base && address < (this->image_base + this->size_of_image);
|
||||
}
|
||||
|
||||
uint64_t find_export(const std::string_view export_name) const
|
||||
{
|
||||
for (auto& symbol : this->exports)
|
||||
{
|
||||
if (symbol.name == export_name)
|
||||
{
|
||||
return symbol.address;
|
||||
}
|
||||
}
|
||||
uint64_t find_export(const std::string_view export_name) const
|
||||
{
|
||||
for (auto& symbol : this->exports)
|
||||
{
|
||||
if (symbol.name == export_name)
|
||||
{
|
||||
return symbol.address;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,124 +5,124 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
std::filesystem::path canonicalize_module_path(const std::filesystem::path& file)
|
||||
{
|
||||
constexpr std::u16string_view nt_prefix = u"\\??\\";
|
||||
const auto wide_file = file.u16string();
|
||||
std::filesystem::path canonicalize_module_path(const std::filesystem::path& file)
|
||||
{
|
||||
constexpr std::u16string_view nt_prefix = u"\\??\\";
|
||||
const auto wide_file = file.u16string();
|
||||
|
||||
if (!wide_file.starts_with(nt_prefix))
|
||||
{
|
||||
return canonical(absolute(file));
|
||||
}
|
||||
if (!wide_file.starts_with(nt_prefix))
|
||||
{
|
||||
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
|
||||
{
|
||||
static void serialize(buffer_serializer& buffer, const exported_symbol& sym)
|
||||
{
|
||||
buffer.write(sym.name);
|
||||
buffer.write(sym.ordinal);
|
||||
buffer.write(sym.rva);
|
||||
buffer.write(sym.address);
|
||||
}
|
||||
static void serialize(buffer_serializer& buffer, const exported_symbol& sym)
|
||||
{
|
||||
buffer.write(sym.name);
|
||||
buffer.write(sym.ordinal);
|
||||
buffer.write(sym.rva);
|
||||
buffer.write(sym.address);
|
||||
}
|
||||
|
||||
static void deserialize(buffer_deserializer& buffer, exported_symbol& sym)
|
||||
{
|
||||
buffer.read(sym.name);
|
||||
buffer.read(sym.ordinal);
|
||||
buffer.read(sym.rva);
|
||||
buffer.read(sym.address);
|
||||
}
|
||||
static void deserialize(buffer_deserializer& buffer, exported_symbol& sym)
|
||||
{
|
||||
buffer.read(sym.name);
|
||||
buffer.read(sym.ordinal);
|
||||
buffer.read(sym.rva);
|
||||
buffer.read(sym.address);
|
||||
}
|
||||
|
||||
static void serialize(buffer_serializer& buffer, const mapped_module& mod)
|
||||
{
|
||||
buffer.write_string(mod.name);
|
||||
buffer.write(mod.path.u16string());
|
||||
static void serialize(buffer_serializer& buffer, const mapped_module& mod)
|
||||
{
|
||||
buffer.write_string(mod.name);
|
||||
buffer.write(mod.path.u16string());
|
||||
|
||||
buffer.write(mod.image_base);
|
||||
buffer.write(mod.size_of_image);
|
||||
buffer.write(mod.entry_point);
|
||||
buffer.write(mod.image_base);
|
||||
buffer.write(mod.size_of_image);
|
||||
buffer.write(mod.entry_point);
|
||||
|
||||
buffer.write_vector(mod.exports);
|
||||
buffer.write_map(mod.address_names);
|
||||
}
|
||||
buffer.write_vector(mod.exports);
|
||||
buffer.write_map(mod.address_names);
|
||||
}
|
||||
|
||||
static void deserialize(buffer_deserializer& buffer, mapped_module& mod)
|
||||
{
|
||||
mod.name = buffer.read_string();
|
||||
mod.path = buffer.read_string<std::u16string::value_type>();
|
||||
static void deserialize(buffer_deserializer& buffer, mapped_module& mod)
|
||||
{
|
||||
mod.name = buffer.read_string();
|
||||
mod.path = buffer.read_string<std::u16string::value_type>();
|
||||
|
||||
buffer.read(mod.image_base);
|
||||
buffer.read(mod.size_of_image);
|
||||
buffer.read(mod.entry_point);
|
||||
buffer.read(mod.image_base);
|
||||
buffer.read(mod.size_of_image);
|
||||
buffer.read(mod.entry_point);
|
||||
|
||||
buffer.read_vector(mod.exports);
|
||||
buffer.read_map(mod.address_names);
|
||||
}
|
||||
buffer.read_vector(mod.exports);
|
||||
buffer.read_map(mod.address_names);
|
||||
}
|
||||
}
|
||||
|
||||
module_manager::module_manager(emulator& emu)
|
||||
: emu_(&emu)
|
||||
: emu_(&emu)
|
||||
{
|
||||
}
|
||||
|
||||
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_)
|
||||
{
|
||||
if (mod.second.path == canonical_file)
|
||||
{
|
||||
return &mod.second;
|
||||
}
|
||||
}
|
||||
for (auto& mod : this->modules_)
|
||||
{
|
||||
if (mod.second.path == canonical_file)
|
||||
{
|
||||
return &mod.second;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto mod = map_module_from_file(*this->emu_, std::move(canonical_file));
|
||||
try
|
||||
{
|
||||
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 entry = this->modules_.try_emplace(image_base, std::move(mod));
|
||||
return &entry.first->second;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
logger.error("Failed to map %s: %s\n", file.generic_string().c_str(), e.what());
|
||||
return nullptr;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
logger.error("Failed to map %s: Unknown error\n", file.generic_string().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
const auto image_base = mod.image_base;
|
||||
const auto entry = this->modules_.try_emplace(image_base, std::move(mod));
|
||||
return &entry.first->second;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
logger.error("Failed to map %s: %s\n", file.generic_string().c_str(), e.what());
|
||||
return nullptr;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
logger.error("Failed to map %s: Unknown error\n", file.generic_string().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
buffer.read_map(this->modules_);
|
||||
buffer.read_map(this->modules_);
|
||||
}
|
||||
|
||||
bool module_manager::unmap(const uint64_t address)
|
||||
{
|
||||
const auto mod = this->modules_.find(address);
|
||||
if (mod == this->modules_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto mod = this->modules_.find(address);
|
||||
if (mod == this->modules_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
unmap_module(*this->emu_, mod->second);
|
||||
this->modules_.erase(mod);
|
||||
unmap_module(*this->emu_, mod->second);
|
||||
this->modules_.erase(mod);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -6,58 +6,58 @@ class logger;
|
||||
|
||||
class module_manager
|
||||
{
|
||||
public:
|
||||
module_manager(emulator& emu);
|
||||
public:
|
||||
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)
|
||||
{
|
||||
const auto entry = this->get_module(address);
|
||||
if (entry != this->modules_.end())
|
||||
{
|
||||
return &entry->second;
|
||||
}
|
||||
mapped_module* find_by_address(const uint64_t address)
|
||||
{
|
||||
const auto entry = this->get_module(address);
|
||||
if (entry != this->modules_.end())
|
||||
{
|
||||
return &entry->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* find_name(const uint64_t address)
|
||||
{
|
||||
const auto* mod = this->find_by_address(address);
|
||||
if (!mod)
|
||||
{
|
||||
return "<N/A>";
|
||||
}
|
||||
const char* find_name(const uint64_t address)
|
||||
{
|
||||
const auto* mod = this->find_by_address(address);
|
||||
if (!mod)
|
||||
{
|
||||
return "<N/A>";
|
||||
}
|
||||
|
||||
return mod->name.c_str();
|
||||
}
|
||||
return mod->name.c_str();
|
||||
}
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const;
|
||||
void deserialize(utils::buffer_deserializer& buffer);
|
||||
void serialize(utils::buffer_serializer& buffer) const;
|
||||
void deserialize(utils::buffer_deserializer& buffer);
|
||||
|
||||
bool unmap(const uint64_t address);
|
||||
bool unmap(const uint64_t address);
|
||||
|
||||
private:
|
||||
emulator* emu_{};
|
||||
private:
|
||||
emulator* emu_{};
|
||||
|
||||
using module_map = std::map<uint64_t, mapped_module>;
|
||||
module_map modules_{};
|
||||
using module_map = std::map<uint64_t, mapped_module>;
|
||||
module_map modules_{};
|
||||
|
||||
module_map::iterator get_module(const uint64_t address)
|
||||
{
|
||||
if (this->modules_.empty())
|
||||
{
|
||||
return this->modules_.end();
|
||||
}
|
||||
module_map::iterator get_module(const uint64_t address)
|
||||
{
|
||||
if (this->modules_.empty())
|
||||
{
|
||||
return this->modules_.end();
|
||||
}
|
||||
|
||||
auto upper_bound = this->modules_.upper_bound(address);
|
||||
if (upper_bound == this->modules_.begin())
|
||||
{
|
||||
return this->modules_.end();
|
||||
}
|
||||
auto upper_bound = this->modules_.upper_bound(address);
|
||||
if (upper_bound == this->modules_.begin())
|
||||
{
|
||||
return this->modules_.end();
|
||||
}
|
||||
|
||||
std::advance(upper_bound, -1);
|
||||
return upper_bound;
|
||||
}
|
||||
std::advance(upper_bound, -1);
|
||||
return upper_bound;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,265 +7,259 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
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);
|
||||
size_t optional_header_offset = reinterpret_cast<uintptr_t>(&(nt_headers.OptionalHeader)) - reinterpret_cast<
|
||||
uintptr_t>(&nt_headers);
|
||||
size_t optional_header_size = nt_headers.FileHeader.SizeOfOptionalHeader;
|
||||
const uint8_t* first_section_addr = nt_headers_addr + optional_header_offset + optional_header_size;
|
||||
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);
|
||||
size_t optional_header_offset =
|
||||
reinterpret_cast<uintptr_t>(&(nt_headers.OptionalHeader)) - reinterpret_cast<uintptr_t>(&nt_headers);
|
||||
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 auto first_section_absolute = reinterpret_cast<uint64_t>(first_section_addr);
|
||||
const auto absolute_base = reinterpret_cast<uint64_t>(&nt_headers);
|
||||
return nt_headers_offset + (first_section_absolute - absolute_base);
|
||||
}
|
||||
const auto first_section_absolute = reinterpret_cast<uint64_t>(first_section_addr);
|
||||
const auto absolute_base = reinterpret_cast<uint64_t>(&nt_headers);
|
||||
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> memory{};
|
||||
memory.resize(binary.size_of_image);
|
||||
emu.read_memory(binary.image_base, memory.data(), memory.size());
|
||||
std::vector<uint8_t> read_mapped_memory(const emulator& emu, const mapped_module& binary)
|
||||
{
|
||||
std::vector<uint8_t> memory{};
|
||||
memory.resize(binary.size_of_image);
|
||||
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,
|
||||
const PEOptionalHeader_t<std::uint64_t>& optional_header)
|
||||
{
|
||||
auto& export_directory_entry = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
if (export_directory_entry.VirtualAddress == 0 || export_directory_entry.Size == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
void collect_exports(mapped_module& binary, const utils::safe_buffer_accessor<const uint8_t> buffer,
|
||||
const PEOptionalHeader_t<std::uint64_t>& optional_header)
|
||||
{
|
||||
auto& export_directory_entry = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
if (export_directory_entry.VirtualAddress == 0 || export_directory_entry.Size == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto export_directory = buffer.as<IMAGE_EXPORT_DIRECTORY>(export_directory_entry.
|
||||
VirtualAddress).get();
|
||||
const auto export_directory = buffer.as<IMAGE_EXPORT_DIRECTORY>(export_directory_entry.VirtualAddress).get();
|
||||
|
||||
const auto names_count = export_directory.NumberOfNames;
|
||||
//const auto function_count = export_directory.NumberOfFunctions;
|
||||
const auto names_count = export_directory.NumberOfNames;
|
||||
// const auto function_count = export_directory.NumberOfFunctions;
|
||||
|
||||
const auto names = buffer.as<DWORD>(export_directory.AddressOfNames);
|
||||
const auto ordinals = buffer.as<WORD>(export_directory.AddressOfNameOrdinals);
|
||||
const auto functions = buffer.as<DWORD>(export_directory.AddressOfFunctions);
|
||||
const auto names = buffer.as<DWORD>(export_directory.AddressOfNames);
|
||||
const auto ordinals = buffer.as<WORD>(export_directory.AddressOfNameOrdinals);
|
||||
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++)
|
||||
{
|
||||
const auto ordinal = ordinals.get(i);
|
||||
for (DWORD i = 0; i < names_count; i++)
|
||||
{
|
||||
const auto ordinal = ordinals.get(i);
|
||||
|
||||
exported_symbol symbol{};
|
||||
symbol.ordinal = export_directory.Base + ordinal;
|
||||
symbol.rva = functions.get(ordinal);
|
||||
symbol.address = binary.image_base + symbol.rva;
|
||||
symbol.name = buffer.as_string(names.get(i));
|
||||
exported_symbol symbol{};
|
||||
symbol.ordinal = export_directory.Base + ordinal;
|
||||
symbol.rva = functions.get(ordinal);
|
||||
symbol.address = binary.image_base + symbol.rva;
|
||||
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)
|
||||
{
|
||||
binary.address_names.try_emplace(symbol.address, symbol.name);
|
||||
}
|
||||
}
|
||||
for (const auto& symbol : binary.exports)
|
||||
{
|
||||
binary.address_names.try_emplace(symbol.address, symbol.name);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires(std::is_integral_v<T>)
|
||||
void apply_relocation(const utils::safe_buffer_accessor<uint8_t> buffer, const uint64_t offset,
|
||||
const uint64_t delta)
|
||||
{
|
||||
const auto obj = buffer.as<T>(offset);
|
||||
const auto value = obj.get();
|
||||
const auto new_value = value + static_cast<T>(delta);
|
||||
obj.set(new_value);
|
||||
}
|
||||
template <typename T>
|
||||
requires(std::is_integral_v<T>)
|
||||
void apply_relocation(const utils::safe_buffer_accessor<uint8_t> buffer, const uint64_t offset,
|
||||
const uint64_t delta)
|
||||
{
|
||||
const auto obj = buffer.as<T>(offset);
|
||||
const auto value = obj.get();
|
||||
const auto new_value = value + static_cast<T>(delta);
|
||||
obj.set(new_value);
|
||||
}
|
||||
|
||||
void apply_relocations(const mapped_module& binary, const utils::safe_buffer_accessor<uint8_t> buffer,
|
||||
const PEOptionalHeader_t<std::uint64_t>& optional_header)
|
||||
{
|
||||
const auto delta = binary.image_base - optional_header.ImageBase;
|
||||
if (delta == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
void apply_relocations(const mapped_module& binary, const utils::safe_buffer_accessor<uint8_t> buffer,
|
||||
const PEOptionalHeader_t<std::uint64_t>& optional_header)
|
||||
{
|
||||
const auto delta = binary.image_base - optional_header.ImageBase;
|
||||
if (delta == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto directory = &optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
||||
if (directory->Size == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const auto directory = &optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
||||
if (directory->Size == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto relocation_offset = directory->VirtualAddress;
|
||||
const auto relocation_end = relocation_offset + directory->Size;
|
||||
auto relocation_offset = directory->VirtualAddress;
|
||||
const auto relocation_end = relocation_offset + directory->Size;
|
||||
|
||||
while (relocation_offset < relocation_end)
|
||||
{
|
||||
const auto relocation = buffer.as<IMAGE_BASE_RELOCATION>(relocation_offset).get();
|
||||
while (relocation_offset < relocation_end)
|
||||
{
|
||||
const auto relocation = buffer.as<IMAGE_BASE_RELOCATION>(relocation_offset).get();
|
||||
|
||||
if (relocation.VirtualAddress <= 0 || relocation.SizeOfBlock <= sizeof(IMAGE_BASE_RELOCATION))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (relocation.VirtualAddress <= 0 || relocation.SizeOfBlock <= sizeof(IMAGE_BASE_RELOCATION))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const auto data_size = relocation.SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION);
|
||||
const auto entry_count = data_size / sizeof(uint16_t);
|
||||
const auto data_size = relocation.SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION);
|
||||
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)
|
||||
{
|
||||
const auto entry = entries.get(i);
|
||||
for (size_t i = 0; i < entry_count; ++i)
|
||||
{
|
||||
const auto entry = entries.get(i);
|
||||
|
||||
const int type = entry >> 12;
|
||||
const auto offset = static_cast<uint16_t>(entry & 0xfff);
|
||||
const auto total_offset = relocation.VirtualAddress + offset;
|
||||
const int type = entry >> 12;
|
||||
const auto offset = static_cast<uint16_t>(entry & 0xfff);
|
||||
const auto total_offset = relocation.VirtualAddress + offset;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case IMAGE_REL_BASED_ABSOLUTE:
|
||||
break;
|
||||
switch (type)
|
||||
{
|
||||
case IMAGE_REL_BASED_ABSOLUTE:
|
||||
break;
|
||||
|
||||
case IMAGE_REL_BASED_HIGHLOW:
|
||||
apply_relocation<DWORD>(buffer, total_offset, delta);
|
||||
break;
|
||||
case IMAGE_REL_BASED_HIGHLOW:
|
||||
apply_relocation<DWORD>(buffer, total_offset, delta);
|
||||
break;
|
||||
|
||||
case IMAGE_REL_BASED_DIR64:
|
||||
apply_relocation<ULONGLONG>(buffer, total_offset, delta);
|
||||
break;
|
||||
case IMAGE_REL_BASED_DIR64:
|
||||
apply_relocation<ULONGLONG>(buffer, total_offset, delta);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("Unknown relocation type: " + std::to_string(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error("Unknown relocation type: " + std::to_string(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void map_sections(emulator& emu, mapped_module& binary,
|
||||
const utils::safe_buffer_accessor<const uint8_t> buffer,
|
||||
const PENTHeaders_t<std::uint64_t>& nt_headers, const uint64_t nt_headers_offset)
|
||||
{
|
||||
const auto first_section_offset = get_first_section_offset(nt_headers, nt_headers_offset);
|
||||
const auto sections = buffer.as<IMAGE_SECTION_HEADER>(first_section_offset);
|
||||
void map_sections(emulator& emu, mapped_module& binary, const utils::safe_buffer_accessor<const uint8_t> buffer,
|
||||
const PENTHeaders_t<std::uint64_t>& nt_headers, const uint64_t nt_headers_offset)
|
||||
{
|
||||
const auto first_section_offset = get_first_section_offset(nt_headers, nt_headers_offset);
|
||||
const auto sections = buffer.as<IMAGE_SECTION_HEADER>(first_section_offset);
|
||||
|
||||
for (size_t i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i)
|
||||
{
|
||||
const auto section = sections.get(i);
|
||||
const auto target_ptr = binary.image_base + section.VirtualAddress;
|
||||
for (size_t i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i)
|
||||
{
|
||||
const auto section = sections.get(i);
|
||||
const auto target_ptr = binary.image_base + section.VirtualAddress;
|
||||
|
||||
if (section.SizeOfRawData > 0)
|
||||
{
|
||||
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);
|
||||
emu.write_memory(target_ptr, source_ptr, size_of_data);
|
||||
}
|
||||
if (section.SizeOfRawData > 0)
|
||||
{
|
||||
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);
|
||||
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)
|
||||
{
|
||||
permissions |= memory_permission::exec;
|
||||
}
|
||||
if (section.Characteristics & IMAGE_SCN_MEM_EXECUTE)
|
||||
{
|
||||
permissions |= memory_permission::exec;
|
||||
}
|
||||
|
||||
if (section.Characteristics & IMAGE_SCN_MEM_READ)
|
||||
{
|
||||
permissions |= memory_permission::read;
|
||||
}
|
||||
if (section.Characteristics & IMAGE_SCN_MEM_READ)
|
||||
{
|
||||
permissions |= memory_permission::read;
|
||||
}
|
||||
|
||||
if (section.Characteristics & IMAGE_SCN_MEM_WRITE)
|
||||
{
|
||||
permissions |= memory_permission::write;
|
||||
}
|
||||
if (section.Characteristics & IMAGE_SCN_MEM_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{};
|
||||
section_info.region.start = target_ptr;
|
||||
section_info.region.length = size_of_section;
|
||||
section_info.region.permissions = permissions;
|
||||
mapped_section section_info{};
|
||||
section_info.region.start = target_ptr;
|
||||
section_info.region.length = size_of_section;
|
||||
section_info.region.permissions = permissions;
|
||||
|
||||
for (size_t j = 0; j < sizeof(section.Name) && section.Name[j]; ++j)
|
||||
{
|
||||
section_info.name.push_back(static_cast<char>(section.Name[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]));
|
||||
}
|
||||
|
||||
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,
|
||||
std::filesystem::path file)
|
||||
mapped_module map_module_from_data(emulator& emu, const std::span<const uint8_t> data, std::filesystem::path file)
|
||||
{
|
||||
mapped_module binary{};
|
||||
binary.path = std::move(file);
|
||||
binary.name = binary.path.filename().string();
|
||||
mapped_module binary{};
|
||||
binary.path = std::move(file);
|
||||
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 nt_headers_offset = dos_header.e_lfanew;
|
||||
const auto dos_header = buffer.as<PEDosHeader_t>(0).get();
|
||||
const auto nt_headers_offset = dos_header.e_lfanew;
|
||||
|
||||
const auto nt_headers = buffer.as<PENTHeaders_t<std::uint64_t>>(nt_headers_offset).get();
|
||||
auto& optional_header = nt_headers.OptionalHeader;
|
||||
const auto nt_headers = buffer.as<PENTHeaders_t<std::uint64_t>>(nt_headers_offset).get();
|
||||
auto& optional_header = nt_headers.OptionalHeader;
|
||||
|
||||
if (nt_headers.FileHeader.Machine != PEMachineType::AMD64)
|
||||
{
|
||||
throw std::runtime_error("Unsupported architecture!");
|
||||
}
|
||||
if (nt_headers.FileHeader.Machine != PEMachineType::AMD64)
|
||||
{
|
||||
throw std::runtime_error("Unsupported architecture!");
|
||||
}
|
||||
|
||||
binary.image_base = optional_header.ImageBase;
|
||||
binary.size_of_image = page_align_up(optional_header.SizeOfImage); // TODO: Sanitize
|
||||
binary.image_base = optional_header.ImageBase;
|
||||
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))
|
||||
{
|
||||
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 has_dynamic_base =
|
||||
optional_header.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
|
||||
const auto is_relocatable = is_dll || has_dynamic_base;
|
||||
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);
|
||||
const auto is_dll = nt_headers.FileHeader.Characteristics & IMAGE_FILE_DLL;
|
||||
const auto has_dynamic_base = optional_header.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
|
||||
const auto is_relocatable = is_dll || has_dynamic_base;
|
||||
|
||||
if (!is_relocatable || !emu.allocate_memory(binary.image_base, binary.size_of_image,
|
||||
memory_permission::read))
|
||||
{
|
||||
throw std::runtime_error("Memory range not allocatable");
|
||||
}
|
||||
}
|
||||
if (!is_relocatable || !emu.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::read))
|
||||
{
|
||||
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);
|
||||
emu.write_memory(binary.image_base, header_buffer,
|
||||
optional_header.SizeOfHeaders);
|
||||
const auto* header_buffer = buffer.get_pointer_for_range(0, optional_header.SizeOfHeaders);
|
||||
emu.write_memory(binary.image_base, header_buffer, 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);
|
||||
utils::safe_buffer_accessor<uint8_t> mapped_buffer{mapped_memory};
|
||||
auto mapped_memory = read_mapped_memory(emu, binary);
|
||||
utils::safe_buffer_accessor<uint8_t> mapped_buffer{mapped_memory};
|
||||
|
||||
apply_relocations(binary, mapped_buffer, optional_header);
|
||||
collect_exports(binary, mapped_buffer, optional_header);
|
||||
apply_relocations(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)
|
||||
{
|
||||
const auto data = utils::io::read_file(file);
|
||||
if (data.empty())
|
||||
{
|
||||
throw std::runtime_error("Bad file data");
|
||||
}
|
||||
const auto data = utils::io::read_file(file);
|
||||
if (data.empty())
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 "mapped_module.hpp"
|
||||
|
||||
mapped_module map_module_from_data(emulator& emu, std::span<const uint8_t> data,
|
||||
std::filesystem::path file);
|
||||
mapped_module map_module_from_data(emulator& emu, std::span<const uint8_t> data, 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);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,216 +5,216 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr uint64_t MAIN_ROOT_OFFSET = 0x1000;
|
||||
constexpr uint64_t MAIN_KEY_BLOCK_OFFSET = MAIN_ROOT_OFFSET + 0x20;
|
||||
constexpr uint64_t MAIN_ROOT_OFFSET = 0x1000;
|
||||
constexpr uint64_t MAIN_KEY_BLOCK_OFFSET = MAIN_ROOT_OFFSET + 0x20;
|
||||
|
||||
struct offset_entry_t
|
||||
{
|
||||
int32_t offset;
|
||||
int32_t hash;
|
||||
};
|
||||
struct offset_entry_t
|
||||
{
|
||||
int32_t offset;
|
||||
int32_t hash;
|
||||
};
|
||||
|
||||
struct offsets_t
|
||||
{
|
||||
int32_t block_size;
|
||||
char block_type[2];
|
||||
int16_t count;
|
||||
offset_entry_t entries[1];
|
||||
};
|
||||
struct offsets_t
|
||||
{
|
||||
int32_t block_size;
|
||||
char block_type[2];
|
||||
int16_t count;
|
||||
offset_entry_t entries[1];
|
||||
};
|
||||
|
||||
struct key_block_t
|
||||
{
|
||||
int32_t block_size;
|
||||
char block_type[2];
|
||||
uint8_t dummya[18];
|
||||
int32_t subkey_count;
|
||||
uint8_t dummyb[4];
|
||||
int32_t subkeys;
|
||||
uint8_t dummyc[4];
|
||||
int32_t value_count;
|
||||
int32_t offsets;
|
||||
uint8_t dummyd[28];
|
||||
int16_t len;
|
||||
int16_t du;
|
||||
char name[255];
|
||||
};
|
||||
struct key_block_t
|
||||
{
|
||||
int32_t block_size;
|
||||
char block_type[2];
|
||||
uint8_t dummya[18];
|
||||
int32_t subkey_count;
|
||||
uint8_t dummyb[4];
|
||||
int32_t subkeys;
|
||||
uint8_t dummyc[4];
|
||||
int32_t value_count;
|
||||
int32_t offsets;
|
||||
uint8_t dummyd[28];
|
||||
int16_t len;
|
||||
int16_t du;
|
||||
char name[255];
|
||||
};
|
||||
|
||||
struct value_block_t
|
||||
{
|
||||
int32_t block_size;
|
||||
char block_type[2];
|
||||
int16_t name_len;
|
||||
int32_t size;
|
||||
int32_t offset;
|
||||
int32_t value_type;
|
||||
int16_t flags;
|
||||
int16_t dummy;
|
||||
char name[255];
|
||||
};
|
||||
struct value_block_t
|
||||
{
|
||||
int32_t block_size;
|
||||
char block_type[2];
|
||||
int16_t name_len;
|
||||
int32_t size;
|
||||
int32_t offset;
|
||||
int32_t value_type;
|
||||
int16_t flags;
|
||||
int16_t dummy;
|
||||
char name[255];
|
||||
};
|
||||
|
||||
bool read_file_data_safe(std::ifstream& file, const uint64_t offset, void* buffer, const size_t size)
|
||||
{
|
||||
if (file.bad())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool read_file_data_safe(std::ifstream& file, const uint64_t offset, void* buffer, const size_t size)
|
||||
{
|
||||
if (file.bad())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
file.clear();
|
||||
file.clear();
|
||||
|
||||
if (!file.good())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!file.good())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
file.seekg(static_cast<std::streamoff>(offset));
|
||||
file.seekg(static_cast<std::streamoff>(offset));
|
||||
|
||||
if (!file.good())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!file.good())
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (!read_file_data_safe(file, offset, buffer, size))
|
||||
{
|
||||
throw std::runtime_error("Failed to read file data");
|
||||
}
|
||||
}
|
||||
void read_file_data(std::ifstream& file, const uint64_t offset, void* buffer, const size_t size)
|
||||
{
|
||||
if (!read_file_data_safe(file, offset, buffer, size))
|
||||
{
|
||||
throw std::runtime_error("Failed to read file data");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::byte> read_file_data(std::ifstream& file, const uint64_t offset, const size_t size)
|
||||
{
|
||||
std::vector<std::byte> result{};
|
||||
result.resize(size);
|
||||
std::vector<std::byte> read_file_data(std::ifstream& file, const uint64_t offset, const size_t size)
|
||||
{
|
||||
std::vector<std::byte> result{};
|
||||
result.resize(size);
|
||||
|
||||
read_file_data(file, offset, result.data(), size);
|
||||
return result;
|
||||
}
|
||||
read_file_data(file, offset, result.data(), size);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string read_file_data_string(std::ifstream& file, const uint64_t offset, const size_t size)
|
||||
{
|
||||
std::string result{};
|
||||
result.resize(size);
|
||||
std::string read_file_data_string(std::ifstream& file, const uint64_t offset, const size_t size)
|
||||
{
|
||||
std::string result{};
|
||||
result.resize(size);
|
||||
|
||||
read_file_data(file, offset, result.data(), size);
|
||||
return result;
|
||||
}
|
||||
read_file_data(file, offset, result.data(), size);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename 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 obj{};
|
||||
read_file_data(file, offset + (array_index * sizeof(T)), &obj, sizeof(T));
|
||||
return obj;
|
||||
}
|
||||
template <typename 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 obj{};
|
||||
read_file_data(file, offset + (array_index * sizeof(T)), &obj, sizeof(T));
|
||||
return obj;
|
||||
}
|
||||
|
||||
hive_key parse_root_block(std::ifstream& file, const std::filesystem::path& file_path)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (read_file_data_string(file, 0, 4) != "regf")
|
||||
{
|
||||
throw std::runtime_error("Invalid signature");
|
||||
}
|
||||
hive_key parse_root_block(std::ifstream& file, const std::filesystem::path& file_path)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (read_file_data_string(file, 0, 4) != "regf")
|
||||
{
|
||||
throw std::runtime_error("Invalid signature");
|
||||
}
|
||||
|
||||
const auto key_block = read_file_object<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};
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw std::runtime_error("Bad hive file '" + file_path.string() + "': " + e.what());
|
||||
}
|
||||
}
|
||||
return {key_block.subkeys, key_block.value_count, key_block.offsets};
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw std::runtime_error("Bad hive file '" + file_path.string() + "': " + e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hive_value* hive_key::get_value(std::ifstream& file, const std::string_view name)
|
||||
{
|
||||
this->parse(file);
|
||||
this->parse(file);
|
||||
|
||||
const auto entry = this->values_.find(name);
|
||||
if (entry == this->values_.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
const auto entry = this->values_.find(name);
|
||||
if (entry == this->values_.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto& value = entry->second;
|
||||
auto& value = entry->second;
|
||||
|
||||
if (!value.parsed)
|
||||
{
|
||||
value.data = read_file_data(file, MAIN_ROOT_OFFSET + value.data_offset, value.data_length);
|
||||
value.parsed = true;
|
||||
}
|
||||
if (!value.parsed)
|
||||
{
|
||||
value.data = read_file_data(file, MAIN_ROOT_OFFSET + value.data_offset, value.data_length);
|
||||
value.parsed = true;
|
||||
}
|
||||
|
||||
return &value;
|
||||
return &value;
|
||||
}
|
||||
|
||||
void hive_key::parse(std::ifstream& file)
|
||||
{
|
||||
if (this->parsed_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (this->parsed_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this->parsed_ = true;
|
||||
this->parsed_ = true;
|
||||
|
||||
// Values
|
||||
// Values
|
||||
|
||||
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 value = read_file_object<value_block_t>(file, MAIN_ROOT_OFFSET + offset);
|
||||
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 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_value.parsed = false;
|
||||
raw_value.type = value.value_type;
|
||||
raw_value.name = value_name;
|
||||
raw_value.data_length = value.size & 0xffff;
|
||||
raw_value.data_offset = value.offset + 4;
|
||||
raw_hive_value raw_value{};
|
||||
raw_value.parsed = false;
|
||||
raw_value.type = value.value_type;
|
||||
raw_value.name = value_name;
|
||||
raw_value.data_length = value.size & 0xffff;
|
||||
raw_value.data_offset = value.offset + 4;
|
||||
|
||||
if (value.size & 1 << 31)
|
||||
{
|
||||
raw_value.data_offset = offset + static_cast<int>(offsetof(value_block_t, offset));
|
||||
}
|
||||
if (value.size & 1 << 31)
|
||||
{
|
||||
raw_value.data_offset = offset + static_cast<int>(offsetof(value_block_t, offset));
|
||||
}
|
||||
|
||||
utils::string::to_lower_inplace(value_name);
|
||||
this->values_[std::move(value_name)] = std::move(raw_value);
|
||||
}
|
||||
utils::string::to_lower_inplace(value_name);
|
||||
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')
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (item.block_type[1] != 'f' && item.block_type[1] != 'h')
|
||||
{
|
||||
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)
|
||||
{
|
||||
const auto offset_entry = read_file_object<offset_entry_t>(file, MAIN_ROOT_OFFSET + entry_offsets, 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 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_block_offset = MAIN_ROOT_OFFSET + offset_entry.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))));
|
||||
utils::string::to_lower_inplace(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);
|
||||
|
||||
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)
|
||||
: file_(file_path, std::ios::binary)
|
||||
, root_key_(parse_root_block(file_, file_path))
|
||||
: file_(file_path, std::ios::binary),
|
||||
root_key_(parse_root_block(file_, file_path))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -8,95 +8,95 @@
|
||||
|
||||
struct hive_value
|
||||
{
|
||||
uint32_t type{};
|
||||
std::string name{};
|
||||
std::vector<std::byte> data{};
|
||||
uint32_t type{};
|
||||
std::string name{};
|
||||
std::vector<std::byte> data{};
|
||||
};
|
||||
|
||||
class hive_key
|
||||
{
|
||||
public:
|
||||
hive_key(const int subkey_block_offset, const int value_count, const int value_offsets)
|
||||
: subkey_block_offset_(subkey_block_offset)
|
||||
, value_count_(value_count)
|
||||
, value_offsets_(value_offsets)
|
||||
{
|
||||
}
|
||||
public:
|
||||
hive_key(const int subkey_block_offset, const int value_count, const int value_offsets)
|
||||
: subkey_block_offset_(subkey_block_offset),
|
||||
value_count_(value_count),
|
||||
value_offsets_(value_offsets)
|
||||
{
|
||||
}
|
||||
|
||||
utils::unordered_string_map<hive_key>& get_sub_keys(std::ifstream& file)
|
||||
{
|
||||
this->parse(file);
|
||||
return this->sub_keys_;
|
||||
}
|
||||
utils::unordered_string_map<hive_key>& get_sub_keys(std::ifstream& file)
|
||||
{
|
||||
this->parse(file);
|
||||
return this->sub_keys_;
|
||||
}
|
||||
|
||||
hive_key* get_sub_key(std::ifstream& file, const std::string_view name)
|
||||
{
|
||||
auto& sub_keys = this->get_sub_keys(file);
|
||||
const auto entry = sub_keys.find(name);
|
||||
hive_key* get_sub_key(std::ifstream& file, const std::string_view name)
|
||||
{
|
||||
auto& sub_keys = this->get_sub_keys(file);
|
||||
const auto entry = sub_keys.find(name);
|
||||
|
||||
if (entry == sub_keys.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (entry == sub_keys.end())
|
||||
{
|
||||
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:
|
||||
struct raw_hive_value : hive_value
|
||||
{
|
||||
bool parsed{false};
|
||||
int data_offset{};
|
||||
size_t data_length{};
|
||||
};
|
||||
private:
|
||||
struct raw_hive_value : hive_value
|
||||
{
|
||||
bool parsed{false};
|
||||
int data_offset{};
|
||||
size_t data_length{};
|
||||
};
|
||||
|
||||
bool parsed_{false};
|
||||
utils::unordered_string_map<hive_key> sub_keys_{};
|
||||
utils::unordered_string_map<raw_hive_value> values_{};
|
||||
bool parsed_{false};
|
||||
utils::unordered_string_map<hive_key> sub_keys_{};
|
||||
utils::unordered_string_map<raw_hive_value> values_{};
|
||||
|
||||
const int subkey_block_offset_{};
|
||||
const int value_count_{};
|
||||
const int value_offsets_{};
|
||||
const int subkey_block_offset_{};
|
||||
const int value_count_{};
|
||||
const int value_offsets_{};
|
||||
|
||||
void parse(std::ifstream& file);
|
||||
void parse(std::ifstream& file);
|
||||
};
|
||||
|
||||
class hive_parser
|
||||
{
|
||||
public:
|
||||
explicit hive_parser(const std::filesystem::path& file_path);
|
||||
public:
|
||||
explicit hive_parser(const std::filesystem::path& file_path);
|
||||
|
||||
[[nodiscard]] hive_key* get_sub_key(const std::filesystem::path& key)
|
||||
{
|
||||
hive_key* current_key = &this->root_key_;
|
||||
[[nodiscard]] hive_key* get_sub_key(const std::filesystem::path& key)
|
||||
{
|
||||
hive_key* current_key = &this->root_key_;
|
||||
|
||||
for (const auto& key_part : key)
|
||||
{
|
||||
if (!current_key)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
for (const auto& key_part : key)
|
||||
{
|
||||
if (!current_key)
|
||||
{
|
||||
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)
|
||||
{
|
||||
auto* sub_key = this->get_sub_key(key);
|
||||
if (!sub_key)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
[[nodiscard]] const hive_value* get_value(const std::filesystem::path& key, const std::string_view name)
|
||||
{
|
||||
auto* sub_key = this->get_sub_key(key);
|
||||
if (!sub_key)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return sub_key->get_value(this->file_, name);
|
||||
}
|
||||
return sub_key->get_value(this->file_, name);
|
||||
}
|
||||
|
||||
private:
|
||||
std::ifstream file_{};
|
||||
hive_key root_key_;
|
||||
private:
|
||||
std::ifstream file_{};
|
||||
hive_key root_key_;
|
||||
};
|
||||
|
||||
@@ -7,33 +7,33 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
std::filesystem::path canonicalize_path(const std::filesystem::path& key)
|
||||
{
|
||||
auto path = key.lexically_normal().wstring();
|
||||
return utils::string::to_lower_consume(path);
|
||||
}
|
||||
std::filesystem::path canonicalize_path(const std::filesystem::path& key)
|
||||
{
|
||||
auto path = key.lexically_normal().wstring();
|
||||
return utils::string::to_lower_consume(path);
|
||||
}
|
||||
|
||||
bool is_subpath(const std::filesystem::path& root, const std::filesystem::path& p)
|
||||
{
|
||||
auto root_it = root.begin();
|
||||
auto p_it = p.begin();
|
||||
bool is_subpath(const std::filesystem::path& root, const std::filesystem::path& p)
|
||||
{
|
||||
auto root_it = root.begin();
|
||||
auto p_it = p.begin();
|
||||
|
||||
for (; root_it != root.end(); ++root_it, ++p_it)
|
||||
{
|
||||
if (p_it == p.end() || *root_it != *p_it)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (; root_it != root.end(); ++root_it, ++p_it)
|
||||
{
|
||||
if (p_it == p.end() || *root_it != *p_it)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void register_hive(registry_manager::hive_map& hives,
|
||||
const std::filesystem::path& key, const std::filesystem::path& file)
|
||||
{
|
||||
hives[canonicalize_path(key)] = std::make_unique<hive_parser>(file);
|
||||
}
|
||||
void register_hive(registry_manager::hive_map& hives, const std::filesystem::path& key,
|
||||
const std::filesystem::path& file)
|
||||
{
|
||||
hives[canonicalize_path(key)] = std::make_unique<hive_parser>(file);
|
||||
}
|
||||
}
|
||||
|
||||
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(const std::filesystem::path& hive_path)
|
||||
: hive_path_(absolute(hive_path))
|
||||
: hive_path_(absolute(hive_path))
|
||||
{
|
||||
this->setup();
|
||||
this->setup();
|
||||
}
|
||||
|
||||
void registry_manager::setup()
|
||||
{
|
||||
this->path_mapping_.clear();
|
||||
this->hives_.clear();
|
||||
this->path_mapping_.clear();
|
||||
this->hives_.clear();
|
||||
|
||||
const std::filesystem::path root = R"(\registry)";
|
||||
const std::filesystem::path machine = root / "machine";
|
||||
const std::filesystem::path root = R"(\registry)";
|
||||
const std::filesystem::path machine = root / "machine";
|
||||
|
||||
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 / "sam", this->hive_path_ / "SAM");
|
||||
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 / "hardware", this->hive_path_ / "HARDWARE");
|
||||
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 / "sam", this->hive_path_ / "SAM");
|
||||
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 / "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
|
||||
{
|
||||
buffer.write(this->hive_path_);
|
||||
buffer.write(this->hive_path_);
|
||||
}
|
||||
|
||||
void registry_manager::deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(this->hive_path_);
|
||||
this->setup();
|
||||
buffer.read(this->hive_path_);
|
||||
this->setup();
|
||||
}
|
||||
|
||||
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_)
|
||||
{
|
||||
if (is_subpath(mapping.first, canonical_path))
|
||||
{
|
||||
return mapping.second / canonical_path.lexically_relative(mapping.first);
|
||||
}
|
||||
}
|
||||
for (const auto& mapping : this->path_mapping_)
|
||||
{
|
||||
if (is_subpath(mapping.first, canonical_path))
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
const auto normal_key = this->normalize_path(key);
|
||||
const auto normal_key = this->normalize_path(key);
|
||||
|
||||
if (is_subpath(normal_key, "\\registry\\machine"))
|
||||
{
|
||||
registry_key reg_key{};
|
||||
reg_key.hive = normal_key;
|
||||
return {std::move(reg_key)};
|
||||
}
|
||||
if (is_subpath(normal_key, "\\registry\\machine"))
|
||||
{
|
||||
registry_key reg_key{};
|
||||
reg_key.hive = normal_key;
|
||||
return {std::move(reg_key)};
|
||||
}
|
||||
|
||||
const auto iterator = this->find_hive(normal_key);
|
||||
if (iterator == this->hives_.end())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
const auto iterator = this->find_hive(normal_key);
|
||||
if (iterator == this->hives_.end())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
registry_key reg_key{};
|
||||
reg_key.hive = iterator->first;
|
||||
reg_key.path = normal_key.lexically_relative(reg_key.hive);
|
||||
registry_key reg_key{};
|
||||
reg_key.hive = iterator->first;
|
||||
reg_key.path = normal_key.lexically_relative(reg_key.hive);
|
||||
|
||||
if (reg_key.path.empty())
|
||||
{
|
||||
return {std::move(reg_key)};
|
||||
}
|
||||
if (reg_key.path.empty())
|
||||
{
|
||||
return {std::move(reg_key)};
|
||||
}
|
||||
|
||||
const auto entry = iterator->second->get_sub_key(reg_key.path);
|
||||
if (!entry)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto entry = iterator->second->get_sub_key(reg_key.path);
|
||||
if (!entry)
|
||||
{
|
||||
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)
|
||||
{
|
||||
utils::string::to_lower_inplace(name);
|
||||
utils::string::to_lower_inplace(name);
|
||||
|
||||
const auto iterator = this->hives_.find(key.hive);
|
||||
if (iterator == this->hives_.end())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto iterator = this->hives_.find(key.hive);
|
||||
if (iterator == this->hives_.end())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto* entry = iterator->second->get_value(key.path, name);
|
||||
if (!entry)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
auto* entry = iterator->second->get_value(key.path, name);
|
||||
if (!entry)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
registry_value v{};
|
||||
v.type = entry->type;
|
||||
v.name = entry->name;
|
||||
v.data = entry->data;
|
||||
registry_value v{};
|
||||
v.type = entry->type;
|
||||
v.name = entry->name;
|
||||
v.data = entry->data;
|
||||
|
||||
return v;
|
||||
return v;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (is_subpath(i->first, key))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
for (auto i = this->hives_.begin(); i != this->hives_.end(); ++i)
|
||||
{
|
||||
if (is_subpath(i->first, key))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return this->hives_.end();
|
||||
return this->hives_.end();
|
||||
}
|
||||
|
||||
@@ -6,61 +6,60 @@
|
||||
|
||||
struct registry_key
|
||||
{
|
||||
std::filesystem::path hive{};
|
||||
std::filesystem::path path{};
|
||||
std::filesystem::path hive{};
|
||||
std::filesystem::path path{};
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(this->hive);
|
||||
buffer.write(this->path);
|
||||
}
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(this->hive);
|
||||
buffer.write(this->path);
|
||||
}
|
||||
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(this->hive);
|
||||
buffer.read(this->path);
|
||||
}
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(this->hive);
|
||||
buffer.read(this->path);
|
||||
}
|
||||
};
|
||||
|
||||
struct registry_value
|
||||
{
|
||||
uint32_t type;
|
||||
std::string_view name;
|
||||
std::span<const std::byte> data;
|
||||
uint32_t type;
|
||||
std::string_view name;
|
||||
std::span<const std::byte> data;
|
||||
};
|
||||
|
||||
class registry_manager
|
||||
{
|
||||
public:
|
||||
using hive_ptr = std::unique_ptr<hive_parser>;
|
||||
using hive_map = std::unordered_map<std::filesystem::path, hive_ptr>;
|
||||
public:
|
||||
using hive_ptr = std::unique_ptr<hive_parser>;
|
||||
using hive_map = std::unordered_map<std::filesystem::path, hive_ptr>;
|
||||
|
||||
registry_manager();
|
||||
registry_manager(const std::filesystem::path& hive_path);
|
||||
~registry_manager();
|
||||
registry_manager();
|
||||
registry_manager(const std::filesystem::path& hive_path);
|
||||
~registry_manager();
|
||||
|
||||
registry_manager(registry_manager&&) noexcept;
|
||||
registry_manager& operator=(registry_manager&&) noexcept;
|
||||
registry_manager(registry_manager&&) noexcept;
|
||||
registry_manager& operator=(registry_manager&&) noexcept;
|
||||
|
||||
registry_manager(const registry_manager&) = delete;
|
||||
registry_manager& operator=(const registry_manager&) = delete;
|
||||
registry_manager(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;
|
||||
void deserialize(utils::buffer_deserializer& buffer);
|
||||
std::optional<registry_key> get_key(const std::filesystem::path& key);
|
||||
std::optional<registry_value> get_value(const registry_key& key, std::string name);
|
||||
|
||||
std::optional<registry_key> get_key(const std::filesystem::path& key);
|
||||
std::optional<registry_value> get_value(const registry_key& key, std::string name);
|
||||
private:
|
||||
std::filesystem::path hive_path_{};
|
||||
hive_map hives_{};
|
||||
std::unordered_map<std::filesystem::path, std::filesystem::path> path_mapping_{};
|
||||
|
||||
private:
|
||||
std::filesystem::path hive_path_{};
|
||||
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;
|
||||
void add_path_mapping(const std::filesystem::path& key, const std::filesystem::path& value);
|
||||
|
||||
std::filesystem::path normalize_path(const std::filesystem::path& path) const;
|
||||
void add_path_mapping(const std::filesystem::path& key, const std::filesystem::path& value);
|
||||
hive_map::iterator find_hive(const std::filesystem::path& key);
|
||||
|
||||
hive_map::iterator find_hive(const std::filesystem::path& key);
|
||||
|
||||
void setup();
|
||||
void setup();
|
||||
};
|
||||
|
||||
@@ -2,32 +2,32 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4005)
|
||||
#pragma warning(disable: 4127)
|
||||
#pragma warning(disable: 4201)
|
||||
#pragma warning(disable: 4244)
|
||||
#pragma warning(disable: 4245)
|
||||
#pragma warning(disable: 4324)
|
||||
#pragma warning(disable: 4458)
|
||||
#pragma warning(disable: 4471)
|
||||
#pragma warning(disable: 4505)
|
||||
#pragma warning(disable: 4702)
|
||||
#pragma warning(disable: 4996)
|
||||
#pragma warning(disable: 5054)
|
||||
#pragma warning(disable: 6011)
|
||||
#pragma warning(disable: 6297)
|
||||
#pragma warning(disable: 6385)
|
||||
#pragma warning(disable: 6386)
|
||||
#pragma warning(disable: 6387)
|
||||
#pragma warning(disable: 26110)
|
||||
#pragma warning(disable: 26451)
|
||||
#pragma warning(disable: 26444)
|
||||
#pragma warning(disable: 26451)
|
||||
#pragma warning(disable: 26489)
|
||||
#pragma warning(disable: 26495)
|
||||
#pragma warning(disable: 26498)
|
||||
#pragma warning(disable: 26812)
|
||||
#pragma warning(disable: 28020)
|
||||
#pragma warning(disable : 4005)
|
||||
#pragma warning(disable : 4127)
|
||||
#pragma warning(disable : 4201)
|
||||
#pragma warning(disable : 4244)
|
||||
#pragma warning(disable : 4245)
|
||||
#pragma warning(disable : 4324)
|
||||
#pragma warning(disable : 4458)
|
||||
#pragma warning(disable : 4471)
|
||||
#pragma warning(disable : 4505)
|
||||
#pragma warning(disable : 4702)
|
||||
#pragma warning(disable : 4996)
|
||||
#pragma warning(disable : 5054)
|
||||
#pragma warning(disable : 6011)
|
||||
#pragma warning(disable : 6297)
|
||||
#pragma warning(disable : 6385)
|
||||
#pragma warning(disable : 6386)
|
||||
#pragma warning(disable : 6387)
|
||||
#pragma warning(disable : 26110)
|
||||
#pragma warning(disable : 26451)
|
||||
#pragma warning(disable : 26444)
|
||||
#pragma warning(disable : 26451)
|
||||
#pragma warning(disable : 26489)
|
||||
#pragma warning(disable : 26495)
|
||||
#pragma warning(disable : 26498)
|
||||
#pragma warning(disable : 26812)
|
||||
#pragma warning(disable : 28020)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
||||
@@ -3,145 +3,138 @@
|
||||
|
||||
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)
|
||||
{
|
||||
buffer.read(obj.name);
|
||||
obj.handler = nullptr;
|
||||
buffer.read(obj.name);
|
||||
obj.handler = nullptr;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
buffer.read_map(this->handlers_);
|
||||
this->add_handlers();
|
||||
buffer.read_map(this->handlers_);
|
||||
this->add_handlers();
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
this->handlers_ = {};
|
||||
this->handlers_ = {};
|
||||
|
||||
const auto ntdll_syscalls = find_syscalls(ntdll_exports, ntdll_data);
|
||||
const auto win32u_syscalls = find_syscalls(win32u_exports, win32u_data);
|
||||
const auto ntdll_syscalls = find_syscalls(ntdll_exports, ntdll_data);
|
||||
const auto win32u_syscalls = find_syscalls(win32u_exports, win32u_data);
|
||||
|
||||
map_syscalls(this->handlers_, ntdll_syscalls);
|
||||
map_syscalls(this->handlers_, win32u_syscalls);
|
||||
map_syscalls(this->handlers_, ntdll_syscalls);
|
||||
map_syscalls(this->handlers_, win32u_syscalls);
|
||||
|
||||
this->add_handlers();
|
||||
this->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_)
|
||||
{
|
||||
const auto handler = handler_mapping.find(entry.second.name);
|
||||
if (handler == handler_mapping.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (auto& entry : this->handlers_)
|
||||
{
|
||||
const auto handler = handler_mapping.find(entry.second.name);
|
||||
if (handler == handler_mapping.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.second.handler = handler->second;
|
||||
entry.second.handler = handler->second;
|
||||
|
||||
#ifndef NDEBUG
|
||||
handler_mapping.erase(handler);
|
||||
handler_mapping.erase(handler);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void syscall_dispatcher::dispatch(windows_emulator& win_emu)
|
||||
{
|
||||
auto& emu = win_emu.emu();
|
||||
auto& context = win_emu.process();
|
||||
auto& emu = win_emu.emu();
|
||||
auto& context = win_emu.process();
|
||||
|
||||
const auto address = emu.read_instruction_pointer();
|
||||
const auto syscall_id = emu.reg<uint32_t>(x64_register::eax);
|
||||
const auto address = emu.read_instruction_pointer();
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
if (!entry->second.handler)
|
||||
{
|
||||
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);
|
||||
c.emu.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entry->second.handler)
|
||||
{
|
||||
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);
|
||||
c.emu.stop();
|
||||
return;
|
||||
}
|
||||
const auto* mod = context.mod_manager.find_by_address(address);
|
||||
if (mod != context.ntdll && mod != context.win32u)
|
||||
{
|
||||
win_emu.log.print(color::blue, "Executing inline syscall: %s (0x%X) at 0x%" PRIx64 " (%s)\n",
|
||||
entry->second.name.c_str(), syscall_id, address, mod ? mod->name.c_str() : "<N/A>");
|
||||
}
|
||||
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);
|
||||
if (mod != context.ntdll && mod != context.win32u)
|
||||
{
|
||||
win_emu.log.print(color::blue, "Executing inline syscall: %s (0x%X) at 0x%" PRIx64 " (%s)\n",
|
||||
entry->second.name.c_str(),
|
||||
syscall_id,
|
||||
address, mod ? mod->name.c_str() : "<N/A>");
|
||||
}
|
||||
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);
|
||||
win_emu.log.print(color::dark_gray,
|
||||
"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);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto* previous_mod = context.mod_manager.find_by_address(context.previous_ip);
|
||||
win_emu.log.print(color::blue,
|
||||
"Crafted out-of-line syscall: %s (0x%X) at 0x%" PRIx64 " (%s) via 0x%" PRIx64
|
||||
" (%s)\n",
|
||||
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>");
|
||||
}
|
||||
}
|
||||
|
||||
win_emu.log.print(color::dark_gray,
|
||||
"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);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto* previous_mod = context.mod_manager.find_by_address(context.previous_ip);
|
||||
win_emu.log.print(color::blue,
|
||||
"Crafted out-of-line syscall: %s (0x%X) at 0x%" PRIx64 " (%s) via 0x%" PRIx64
|
||||
" (%s)\n",
|
||||
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>");
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
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,
|
||||
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"
|
||||
|
||||
struct syscall_context;
|
||||
using syscall_handler = void(*)(const syscall_context& c);
|
||||
using syscall_handler = void (*)(const syscall_context& c);
|
||||
|
||||
struct syscall_handler_entry
|
||||
{
|
||||
syscall_handler handler{};
|
||||
std::string name{};
|
||||
syscall_handler handler{};
|
||||
std::string name{};
|
||||
};
|
||||
|
||||
class windows_emulator;
|
||||
|
||||
class syscall_dispatcher
|
||||
{
|
||||
public:
|
||||
syscall_dispatcher() = default;
|
||||
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);
|
||||
public:
|
||||
syscall_dispatcher() = default;
|
||||
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);
|
||||
|
||||
void dispatch(windows_emulator& win_emu);
|
||||
void dispatch(windows_emulator& win_emu);
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const;
|
||||
void deserialize(utils::buffer_deserializer& buffer);
|
||||
void serialize(utils::buffer_serializer& buffer) const;
|
||||
void deserialize(utils::buffer_deserializer& buffer);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
std::string get_syscall_name(const uint64_t id)
|
||||
{
|
||||
return this->handlers_.at(id).name;
|
||||
}
|
||||
std::string get_syscall_name(const uint64_t id)
|
||||
{
|
||||
return this->handlers_.at(id).name;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<uint64_t, syscall_handler_entry> handlers_{};
|
||||
private:
|
||||
std::map<uint64_t, syscall_handler_entry> handlers_{};
|
||||
|
||||
static void add_handlers(std::map<std::string, syscall_handler>& handler_mapping);
|
||||
void add_handlers();
|
||||
static void add_handlers(std::map<std::string, syscall_handler>& handler_mapping);
|
||||
void add_handlers();
|
||||
};
|
||||
|
||||
@@ -5,202 +5,193 @@
|
||||
|
||||
struct syscall_context
|
||||
{
|
||||
windows_emulator& win_emu;
|
||||
x64_emulator& emu;
|
||||
process_context& proc;
|
||||
mutable bool write_status{true};
|
||||
mutable bool retrigger_syscall{false};
|
||||
windows_emulator& win_emu;
|
||||
x64_emulator& emu;
|
||||
process_context& proc;
|
||||
mutable bool write_status{true};
|
||||
mutable bool retrigger_syscall{false};
|
||||
};
|
||||
|
||||
inline uint64_t get_syscall_argument(x64_emulator& emu, const size_t index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return emu.reg(x64_register::r10);
|
||||
case 1:
|
||||
return emu.reg(x64_register::rdx);
|
||||
case 2:
|
||||
return emu.reg(x64_register::r8);
|
||||
case 3:
|
||||
return emu.reg(x64_register::r9);
|
||||
default:
|
||||
return emu.read_stack(index + 1);
|
||||
}
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return emu.reg(x64_register::r10);
|
||||
case 1:
|
||||
return emu.reg(x64_register::rdx);
|
||||
case 2:
|
||||
return emu.reg(x64_register::r8);
|
||||
case 3:
|
||||
return emu.reg(x64_register::r9);
|
||||
default:
|
||||
return emu.read_stack(index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool is_uppercase(const char character)
|
||||
{
|
||||
return toupper(character) == character;
|
||||
return toupper(character) == character;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (!is_syscall(symbol.name))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!is_syscall(symbol.name))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
constexpr auto instruction_size = 5;
|
||||
constexpr auto instruction_offset = 3;
|
||||
constexpr auto instruction_operand_offset = 1;
|
||||
constexpr auto instruction_opcode = static_cast<std::byte>(0xB8);
|
||||
constexpr auto instruction_size = 5;
|
||||
constexpr auto instruction_offset = 3;
|
||||
constexpr auto instruction_operand_offset = 1;
|
||||
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)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
if (data.size() < (instruction_rva + instruction_size) || data[instruction_rva] != instruction_opcode)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
uint32_t syscall_id{0};
|
||||
static_assert(sizeof(syscall_id) <= (instruction_size - instruction_operand_offset));
|
||||
memcpy(&syscall_id, data.data() + instruction_rva + instruction_operand_offset, sizeof(syscall_id));
|
||||
uint32_t syscall_id{0};
|
||||
static_assert(sizeof(syscall_id) <= (instruction_size - instruction_operand_offset));
|
||||
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)
|
||||
{
|
||||
std::map<uint64_t, std::string> syscalls{};
|
||||
std::map<uint64_t, std::string> syscalls{};
|
||||
|
||||
for (const auto& symbol : exports)
|
||||
{
|
||||
const auto id = extract_syscall_id(symbol, data);
|
||||
if (id)
|
||||
{
|
||||
auto& entry = syscalls[*id];
|
||||
for (const auto& symbol : exports)
|
||||
{
|
||||
const auto id = extract_syscall_id(symbol, data);
|
||||
if (id)
|
||||
{
|
||||
auto& entry = syscalls[*id];
|
||||
|
||||
if (!entry.empty())
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Syscall with id " + std::to_string(*id) + ", which is mapping to " + symbol.name +
|
||||
", was already mapped to " + entry);
|
||||
}
|
||||
if (!entry.empty())
|
||||
{
|
||||
throw std::runtime_error("Syscall with id " + std::to_string(*id) + ", which is mapping to " +
|
||||
symbol.name + ", 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,
|
||||
std::map<uint64_t, std::string> syscalls)
|
||||
inline void map_syscalls(std::map<uint64_t, syscall_handler_entry>& handlers, std::map<uint64_t, std::string> syscalls)
|
||||
{
|
||||
for (auto& [id, name] : syscalls)
|
||||
{
|
||||
auto& entry = handlers[id];
|
||||
for (auto& [id, name] : syscalls)
|
||||
{
|
||||
auto& entry = handlers[id];
|
||||
|
||||
if (!entry.name.empty())
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Syscall with id " + std::to_string(id) + ", which is mapping to " + name +
|
||||
", was previously mapped to " + entry.name);
|
||||
}
|
||||
if (!entry.name.empty())
|
||||
{
|
||||
throw std::runtime_error("Syscall with id " + std::to_string(id) + ", which is mapping to " + name +
|
||||
", was previously mapped to " + entry.name);
|
||||
}
|
||||
|
||||
entry.name = std::move(name);
|
||||
entry.handler = nullptr;
|
||||
}
|
||||
entry.name = std::move(name);
|
||||
entry.handler = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
const auto arg = get_syscall_argument(emu, index);
|
||||
return static_cast<T>(arg);
|
||||
const auto arg = get_syscall_argument(emu, index);
|
||||
return static_cast<T>(arg);
|
||||
}
|
||||
|
||||
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 h{};
|
||||
h.bits = resolve_argument<uint64_t>(emu, index);
|
||||
return h;
|
||||
handle h{};
|
||||
h.bits = resolve_argument<uint64_t>(emu, index);
|
||||
return h;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
const auto arg = get_syscall_argument(emu, index);
|
||||
return T(emu, arg);
|
||||
const auto arg = get_syscall_argument(emu, index);
|
||||
return T(emu, arg);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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)
|
||||
{
|
||||
if (c.write_status && !c.retrigger_syscall)
|
||||
{
|
||||
c.emu.reg<uint64_t>(x64_register::rax, static_cast<uint64_t>(status));
|
||||
}
|
||||
if (c.write_status && !c.retrigger_syscall)
|
||||
{
|
||||
c.emu.reg<uint64_t>(x64_register::rax, static_cast<uint64_t>(status));
|
||||
}
|
||||
|
||||
const auto new_ip = c.emu.read_instruction_pointer();
|
||||
if (initial_ip != new_ip || c.retrigger_syscall)
|
||||
{
|
||||
c.emu.reg(x64_register::rip, new_ip - 2);
|
||||
}
|
||||
const auto new_ip = c.emu.read_instruction_pointer();
|
||||
if (initial_ip != new_ip || c.retrigger_syscall)
|
||||
{
|
||||
c.emu.reg(x64_register::rip, new_ip - 2);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
write_status(c, ret, ip);
|
||||
const auto ret = handler();
|
||||
write_status(c, ret, ip);
|
||||
}
|
||||
|
||||
template <typename... 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;
|
||||
std::tuple<const syscall_context&, Args...> func_args
|
||||
{
|
||||
c,
|
||||
resolve_indexed_argument<std::remove_cv_t<std::remove_reference_t<Args>>>(c.emu, index)...
|
||||
};
|
||||
size_t index = 0;
|
||||
std::tuple<const syscall_context&, Args...> func_args{
|
||||
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));
|
||||
write_status(c, ret, ip);
|
||||
const auto ret = std::apply(handler, std::move(func_args));
|
||||
write_status(c, ret, ip);
|
||||
}
|
||||
|
||||
template <auto Handler>
|
||||
syscall_handler make_syscall_handler()
|
||||
{
|
||||
return +[](const syscall_context& c)
|
||||
{
|
||||
forward_syscall(c, Handler);
|
||||
};
|
||||
return +[](const syscall_context& c) { forward_syscall(c, Handler); };
|
||||
}
|
||||
|
||||
template <typename T, typename Traits>
|
||||
void write_attribute(emulator& emu, const PS_ATTRIBUTE<Traits>& attribute, const T& value)
|
||||
{
|
||||
if (attribute.ReturnLength)
|
||||
{
|
||||
emulator_object<typename Traits::SIZE_T>{emu, attribute.ReturnLength}.write(sizeof(T));
|
||||
}
|
||||
if (attribute.ReturnLength)
|
||||
{
|
||||
emulator_object<typename Traits::SIZE_T>{emu, attribute.ReturnLength}.write(sizeof(T));
|
||||
}
|
||||
|
||||
if (attribute.Size >= sizeof(T))
|
||||
{
|
||||
emulator_object<T>{emu, attribute.Value}.write(value);
|
||||
}
|
||||
if (attribute.Size >= sizeof(T))
|
||||
{
|
||||
emulator_object<T>{emu, attribute.Value}.write(value);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (delay_interval.QuadPart <= 0)
|
||||
{
|
||||
const auto relative_time = -delay_interval.QuadPart;
|
||||
const auto relative_ticks_in_ms = relative_time / 10;
|
||||
const auto relative_fraction_ns = (relative_time % 10) * 100;
|
||||
const auto relative_duration = std::chrono::microseconds(relative_ticks_in_ms) +
|
||||
std::chrono::nanoseconds(relative_fraction_ns);
|
||||
if (delay_interval.QuadPart <= 0)
|
||||
{
|
||||
const auto relative_time = -delay_interval.QuadPart;
|
||||
const auto relative_ticks_in_ms = relative_time / 10;
|
||||
const auto relative_fraction_ns = (relative_time % 10) * 100;
|
||||
const auto relative_duration =
|
||||
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_fraction_ns = (delay_interval.QuadPart % HUNDRED_NANOSECONDS_IN_ONE_SECOND) * 100;
|
||||
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_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 =
|
||||
std::chrono::system_clock::from_time_t(delay_seconds_since_1970) +
|
||||
std::chrono::nanoseconds(delay_fraction_ns);
|
||||
const auto target_time =
|
||||
std::chrono::system_clock::from_time_t(delay_seconds_since_1970) + 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<
|
||||
std::chrono::microseconds>(target_time - now_system);
|
||||
const auto duration_until_target = std::chrono::duration_cast<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)
|
||||
{
|
||||
const auto duration = tp.time_since_epoch();
|
||||
const auto ns_duration = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
|
||||
const auto duration = tp.time_since_epoch();
|
||||
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{};
|
||||
time.LowPart = static_cast<uint32_t>(total_ticks);
|
||||
time.High1Time = static_cast<int32_t>(total_ticks >> 32);
|
||||
time.High2Time = time.High1Time;
|
||||
KSYSTEM_TIME time{};
|
||||
time.LowPart = static_cast<uint32_t>(total_ticks);
|
||||
time.High1Time = static_cast<int32_t>(total_ticks >> 32);
|
||||
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)
|
||||
{
|
||||
const auto time = convert_to_ksystem_time(tp);
|
||||
memcpy(const_cast<KSYSTEM_TIME*>(dest), &time, sizeof(*dest));
|
||||
const auto time = convert_to_ksystem_time(tp);
|
||||
memcpy(const_cast<KSYSTEM_TIME*>(dest), &time, sizeof(*dest));
|
||||
}
|
||||
|
||||
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;
|
||||
totalTicks -= WINDOWS_EPOCH_DIFFERENCE;
|
||||
auto totalTicks = (static_cast<int64_t>(time.High1Time) << 32) | time.LowPart;
|
||||
totalTicks -= WINDOWS_EPOCH_DIFFERENCE;
|
||||
|
||||
const auto duration = std::chrono::system_clock::duration(totalTicks * 100);
|
||||
return std::chrono::system_clock::time_point(duration);
|
||||
const auto duration = std::chrono::system_clock::duration(totalTicks * 100);
|
||||
return std::chrono::system_clock::time_point(duration);
|
||||
}
|
||||
|
||||
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
|
||||
@@ -278,7 +267,7 @@ using __time64_t = int64_t;
|
||||
|
||||
inline LARGE_INTEGER convert_unix_to_windows_time(const __time64_t unix_time)
|
||||
{
|
||||
LARGE_INTEGER windows_time{};
|
||||
windows_time.QuadPart = (unix_time + EPOCH_DIFFERENCE_1601_TO_1970_SECONDS) * HUNDRED_NANOSECONDS_IN_ONE_SECOND;
|
||||
return windows_time;
|
||||
LARGE_INTEGER windows_time{};
|
||||
windows_time.QuadPart = (unix_time + EPOCH_DIFFERENCE_1601_TO_1970_SECONDS) * HUNDRED_NANOSECONDS_IN_ONE_SECOND;
|
||||
return windows_time;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user