Format all the code

This commit is contained in:
momo5502
2025-01-06 17:13:33 +01:00
parent 64c2a79f0f
commit bff8420ffd
100 changed files with 16439 additions and 14509 deletions

View File

@@ -7,254 +7,247 @@
namespace namespace
{ {
struct analysis_options struct analysis_options
{ {
bool use_gdb{false}; bool use_gdb{false};
bool concise_logging{false}; bool concise_logging{false};
}; };
void watch_system_objects(windows_emulator& win_emu, const bool cache_logging) void watch_system_objects(windows_emulator& win_emu, const bool cache_logging)
{ {
(void)win_emu; (void)win_emu;
(void)cache_logging; (void)cache_logging;
#ifdef OS_WINDOWS #ifdef OS_WINDOWS
watch_object(win_emu, *win_emu.current_thread().teb, cache_logging); watch_object(win_emu, *win_emu.current_thread().teb, cache_logging);
watch_object(win_emu, win_emu.process().peb, cache_logging); watch_object(win_emu, win_emu.process().peb, cache_logging);
watch_object(win_emu, emulator_object<KUSER_SHARED_DATA64>{win_emu.emu(), kusd_mmio::address()}, cache_logging); watch_object(win_emu, emulator_object<KUSER_SHARED_DATA64>{win_emu.emu(), kusd_mmio::address()}, cache_logging);
auto* params_hook = watch_object(win_emu, win_emu.process().process_params, cache_logging); auto* params_hook = watch_object(win_emu, win_emu.process().process_params, cache_logging);
win_emu.emu().hook_memory_write(win_emu.process().peb.value() + offsetof(PEB64, ProcessParameters), 0x8, win_emu.emu().hook_memory_write(
[&, cache_logging](const uint64_t address, size_t, const uint64_t value) win_emu.process().peb.value() + offsetof(PEB64, ProcessParameters), 0x8,
{ [&, cache_logging](const uint64_t address, size_t, const uint64_t value) {
const auto target_address = win_emu.process().peb.value() + offsetof( const auto target_address = win_emu.process().peb.value() + offsetof(PEB64, ProcessParameters);
PEB64, ProcessParameters);
if (address == target_address) if (address == target_address)
{ {
const emulator_object<RTL_USER_PROCESS_PARAMETERS64> obj{ const emulator_object<RTL_USER_PROCESS_PARAMETERS64> obj{win_emu.emu(), value};
win_emu.emu(), value
};
win_emu.emu().delete_hook(params_hook); win_emu.emu().delete_hook(params_hook);
params_hook = watch_object(win_emu, obj, cache_logging); params_hook = watch_object(win_emu, obj, cache_logging);
} }
}); });
#endif #endif
} }
void run_emulation(windows_emulator& win_emu, const analysis_options& options) void run_emulation(windows_emulator& win_emu, const analysis_options& options)
{ {
try try
{ {
if (options.use_gdb) if (options.use_gdb)
{ {
const auto* address = "127.0.0.1:28960"; const auto* address = "127.0.0.1:28960";
win_emu.log.print(color::pink, "Waiting for GDB connection on %s...\n", address); win_emu.log.print(color::pink, "Waiting for GDB connection on %s...\n", address);
win_x64_gdb_stub_handler handler{win_emu}; win_x64_gdb_stub_handler handler{win_emu};
run_gdb_stub(handler, "i386:x86-64", gdb_registers.size(), address); run_gdb_stub(handler, "i386:x86-64", gdb_registers.size(), address);
} }
else else
{ {
win_emu.start(); win_emu.start();
} }
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 " - %s\n", win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 " - %s\n",
win_emu.emu().read_instruction_pointer(), e.what()); win_emu.emu().read_instruction_pointer(), e.what());
throw; throw;
} }
catch (...) catch (...)
{ {
win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 "\n", win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 "\n",
win_emu.emu().read_instruction_pointer()); win_emu.emu().read_instruction_pointer());
throw; throw;
} }
const auto exit_status = win_emu.process().exit_status; const auto exit_status = win_emu.process().exit_status;
if (exit_status.has_value()) if (exit_status.has_value())
{ {
win_emu.log.print(color::red, "Emulation terminated with status: %X\n", *exit_status); win_emu.log.print(color::red, "Emulation terminated with status: %X\n", *exit_status);
} }
else else
{ {
win_emu.log.print(color::red, "Emulation terminated without status!\n"); win_emu.log.print(color::red, "Emulation terminated without status!\n");
} }
} }
std::vector<std::u16string> parse_arguments(const std::span<const std::string_view> args) std::vector<std::u16string> parse_arguments(const std::span<const std::string_view> args)
{ {
std::vector<std::u16string> wide_args{}; std::vector<std::u16string> wide_args{};
wide_args.reserve(args.size() - 1); wide_args.reserve(args.size() - 1);
for (size_t i = 1; i < args.size(); ++i) for (size_t i = 1; i < args.size(); ++i)
{ {
const auto& arg = args[i]; const auto& arg = args[i];
wide_args.emplace_back(arg.begin(), arg.end()); wide_args.emplace_back(arg.begin(), arg.end());
} }
return wide_args; return wide_args;
} }
void run(const analysis_options& options, const std::span<const std::string_view> args) void run(const analysis_options& options, const std::span<const std::string_view> args)
{ {
if (args.empty()) if (args.empty())
{ {
return; return;
} }
emulator_settings settings{ emulator_settings settings{
.application = args[0], .application = args[0],
.arguments = parse_arguments(args), .arguments = parse_arguments(args),
.silent_until_main = options.concise_logging, .silent_until_main = options.concise_logging,
}; };
windows_emulator win_emu{std::move(settings)}; windows_emulator win_emu{std::move(settings)};
(void)&watch_system_objects; (void)&watch_system_objects;
watch_system_objects(win_emu, options.concise_logging); watch_system_objects(win_emu, options.concise_logging);
win_emu.buffer_stdout = true; win_emu.buffer_stdout = true;
//win_emu.verbose_calls = true; // win_emu.verbose_calls = true;
const auto& exe = *win_emu.process().executable; const auto& exe = *win_emu.process().executable;
const auto concise_logging = options.concise_logging; const auto concise_logging = options.concise_logging;
for (const auto& section : exe.sections) for (const auto& section : exe.sections)
{ {
if ((section.region.permissions & memory_permission::exec) != memory_permission::exec) if ((section.region.permissions & memory_permission::exec) != memory_permission::exec)
{ {
continue; continue;
} }
auto read_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t) auto read_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t) {
{ const auto rip = win_emu.emu().read_instruction_pointer();
const auto rip = win_emu.emu().read_instruction_pointer(); if (win_emu.process().mod_manager.find_by_address(rip) != win_emu.process().executable)
if (win_emu.process().mod_manager.find_by_address(rip) != win_emu.process().executable) {
{ return;
return; }
}
if (concise_logging) if (concise_logging)
{ {
static uint64_t count{0}; static uint64_t count{0};
++count; ++count;
if (count > 100 && count % 10000 != 0) return; if (count > 100 && count % 10000 != 0)
} return;
}
win_emu.log.print( win_emu.log.print(color::green,
color::green, "Reading from executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n",
"Reading from executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n", section.name.c_str(), address, rip);
section.name.c_str(), address, rip); };
};
const auto write_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t) const auto write_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t) {
{ const auto rip = win_emu.emu().read_instruction_pointer();
const auto rip = win_emu.emu().read_instruction_pointer(); if (win_emu.process().mod_manager.find_by_address(rip) != win_emu.process().executable)
if (win_emu.process().mod_manager.find_by_address(rip) != win_emu.process().executable) {
{ return;
return; }
}
if (concise_logging) if (concise_logging)
{ {
static uint64_t count{0}; static uint64_t count{0};
++count; ++count;
if (count > 100 && count % 10000 != 0) return; if (count > 100 && count % 10000 != 0)
} return;
}
win_emu.log.print( win_emu.log.print(color::blue, "Writing to executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n",
color::blue, section.name.c_str(), address, rip);
"Writing to executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n", };
section.name.c_str(), address, rip);
};
win_emu.emu().hook_memory_read(section.region.start, section.region.length, std::move(read_handler)); win_emu.emu().hook_memory_read(section.region.start, section.region.length, std::move(read_handler));
win_emu.emu().hook_memory_write(section.region.start, section.region.length, std::move(write_handler)); win_emu.emu().hook_memory_write(section.region.start, section.region.length, std::move(write_handler));
} }
run_emulation(win_emu, options); run_emulation(win_emu, options);
} }
std::vector<std::string_view> bundle_arguments(const int argc, char** argv) std::vector<std::string_view> bundle_arguments(const int argc, char** argv)
{ {
std::vector<std::string_view> args{}; std::vector<std::string_view> args{};
for (int i = 1; i < argc; ++i) for (int i = 1; i < argc; ++i)
{ {
args.push_back(argv[i]); args.push_back(argv[i]);
} }
return args; return args;
} }
analysis_options parse_options(std::vector<std::string_view>& args) analysis_options parse_options(std::vector<std::string_view>& args)
{ {
analysis_options options{}; analysis_options options{};
while (!args.empty()) while (!args.empty())
{ {
auto arg_it = args.begin(); auto arg_it = args.begin();
const auto& arg = *arg_it; const auto& arg = *arg_it;
if (arg == "-d") if (arg == "-d")
{ {
options.use_gdb = true; options.use_gdb = true;
} }
else if (arg == "-c") else if (arg == "-c")
{ {
options.concise_logging = true; options.concise_logging = true;
} }
else else
{ {
break; break;
} }
args.erase(arg_it); args.erase(arg_it);
} }
return options; return options;
} }
} }
int main(const int argc, char** argv) int main(const int argc, char** argv)
{ {
try try
{ {
auto args = bundle_arguments(argc, argv); auto args = bundle_arguments(argc, argv);
const auto options = parse_options(args); const auto options = parse_options(args);
if (args.empty()) if (args.empty())
{ {
throw std::runtime_error("Application not specified!"); throw std::runtime_error("Application not specified!");
} }
do do
{ {
run(options, args); run(options, args);
} } while (options.use_gdb);
while (options.use_gdb);
return 0; return 0;
} }
catch (std::exception& e) catch (std::exception& e)
{ {
puts(e.what()); puts(e.what());
#if defined(_WIN32) && 0 #if defined(_WIN32) && 0
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR); MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
#endif #endif
} }
return 1; return 1;
} }
#ifdef _WIN32 #ifdef _WIN32
int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int) int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int)
{ {
return main(__argc, __argv); return main(__argc, __argv);
} }
#endif #endif

View File

@@ -5,36 +5,32 @@
template <typename T> template <typename T>
emulator_hook* watch_object(windows_emulator& emu, emulator_object<T> object, const bool cache_logging = false) emulator_hook* watch_object(windows_emulator& emu, emulator_object<T> object, const bool cache_logging = false)
{ {
const reflect_type_info<T> info{}; const reflect_type_info<T> info{};
return emu.emu().hook_memory_read(object.value(), object.size(), return emu.emu().hook_memory_read(
[i = std::move(info), object, &emu, cache_logging]( object.value(), object.size(),
const uint64_t address, size_t, uint64_t) [i = std::move(info), object, &emu, cache_logging](const uint64_t address, size_t, uint64_t) {
{ const auto rip = emu.emu().read_instruction_pointer();
const auto rip = emu.emu().read_instruction_pointer(); const auto* mod = emu.process().mod_manager.find_by_address(rip);
const auto* mod = emu.process().mod_manager.find_by_address(rip); const auto is_main_access = mod == emu.process().executable;
const auto is_main_access = mod == emu.process().executable;
if (!emu.verbose_calls && !is_main_access) if (!emu.verbose_calls && !is_main_access)
{ {
return; return;
} }
if (cache_logging) if (cache_logging)
{ {
static std::unordered_set<uint64_t> logged_addresses{}; static std::unordered_set<uint64_t> logged_addresses{};
if (is_main_access && !logged_addresses.insert(address).second) if (is_main_access && !logged_addresses.insert(address).second)
{ {
return; return;
} }
} }
const auto offset = address - object.value(); const auto offset = address - object.value();
emu.log.print(is_main_access ? color::green : color::dark_gray, emu.log.print(is_main_access ? color::green : color::dark_gray,
"Object access: %s - 0x%llX (%s) at 0x%llX (%s)\n", "Object access: %s - 0x%llX (%s) at 0x%llX (%s)\n", i.get_type_name().c_str(), offset,
i.get_type_name().c_str(), i.get_member_name(offset).c_str(), rip, mod ? mod->name.c_str() : "<N/A>");
offset, });
i.get_member_name(offset).c_str(), rip,
mod ? mod->name.c_str() : "<N/A>");
});
} }

File diff suppressed because it is too large Load Diff

View File

@@ -15,51 +15,50 @@
template <typename T> template <typename T>
class reflect_type_info class reflect_type_info
{ {
public: public:
reflect_type_info() reflect_type_info()
{ {
this->type_name_ = reflect::type_name<T>(); this->type_name_ = reflect::type_name<T>();
reflect::for_each<T>([this](auto I) reflect::for_each<T>([this](auto I) {
{ const auto member_name = reflect::member_name<I, T>();
const auto member_name = reflect::member_name<I, T>(); const auto member_offset = reflect::offset_of<I, T>();
const auto member_offset = reflect::offset_of<I, T>();
this->members_[member_offset] = member_name; this->members_[member_offset] = member_name;
}); });
} }
std::string get_member_name(const size_t offset) const std::string get_member_name(const size_t offset) const
{ {
size_t last_offset{}; size_t last_offset{};
std::string_view last_member{}; std::string_view last_member{};
for (const auto& member : this->members_) for (const auto& member : this->members_)
{ {
if (offset == member.first) if (offset == member.first)
{ {
return member.second; return member.second;
} }
if (offset < member.first) if (offset < member.first)
{ {
const auto diff = offset - last_offset; const auto diff = offset - last_offset;
return std::string(last_member) + "+" + std::to_string(diff); return std::string(last_member) + "+" + std::to_string(diff);
} }
last_offset = member.first; last_offset = member.first;
last_member = member.second; last_member = member.second;
} }
return "<N/A>"; return "<N/A>";
} }
const std::string& get_type_name() const const std::string& get_type_name() const
{ {
return this->type_name_; return this->type_name_;
} }
private: private:
std::string type_name_{}; std::string type_name_{};
std::map<size_t, std::string> members_{}; std::map<size_t, std::string> members_{};
}; };

View File

@@ -5,61 +5,59 @@
#define THE_SIZE 30 #define THE_SIZE 30
extern "C" NO_INLINE EXPORT_SYMBOL extern "C" NO_INLINE EXPORT_SYMBOL void vulnerable(const uint8_t* data, const size_t size)
void vulnerable(const uint8_t* data, const size_t size)
{ {
if (size < 10) if (size < 10)
{ {
return; return;
} }
if (data[9] != 'A') if (data[9] != 'A')
{ {
return; return;
} }
if (data[8] != 'B') if (data[8] != 'B')
{ {
return; return;
} }
if (data[7] != 'C') if (data[7] != 'C')
{ {
return; return;
} }
if (data[2] != 'V') if (data[2] != 'V')
{ {
return; return;
} }
if (data[4] != 'H') if (data[4] != 'H')
{ {
return; return;
} }
if (size < 100) if (size < 100)
{ {
return; return;
} }
*(int*)1 = 1; *(int*)1 = 1;
} }
uint8_t buffer[THE_SIZE] = {}; uint8_t buffer[THE_SIZE] = {};
int main(int argc, const char* argv[]) int main(int argc, const char* argv[])
{ {
const void* input = buffer; const void* input = buffer;
auto size = sizeof(buffer); auto size = sizeof(buffer);
if (argc > 1) if (argc > 1)
{ {
input = argv[1]; input = argv[1];
size = strlen(argv[1]); size = strlen(argv[1]);
} }
vulnerable((uint8_t*)input, size); vulnerable((uint8_t*)input, size);
return 0; return 0;
} }

View File

@@ -8,376 +8,365 @@ using namespace std::literals;
namespace network namespace network
{ {
void initialize_wsa() void initialize_wsa()
{ {
#ifdef _WIN32 #ifdef _WIN32
static struct wsa_initializer static struct wsa_initializer
{ {
public: wsa_initializer()
wsa_initializer() {
{ WSADATA wsa_data;
WSADATA wsa_data; if (WSAStartup(MAKEWORD(2, 2), &wsa_data))
if (WSAStartup(MAKEWORD(2, 2), &wsa_data)) {
{ throw std::runtime_error("Unable to initialize WSA");
throw std::runtime_error("Unable to initialize WSA"); }
} }
}
~wsa_initializer() ~wsa_initializer()
{ {
WSACleanup(); WSACleanup();
} }
} _; } _;
#endif #endif
} }
address::address() address::address()
{ {
initialize_wsa(); initialize_wsa();
ZeroMemory(&this->storage_, this->get_max_size()); ZeroMemory(&this->storage_, this->get_max_size());
this->address_.sa_family = AF_UNSPEC; this->address_.sa_family = AF_UNSPEC;
} }
address::address(const std::string& addr, const std::optional<int>& family) address::address(const std::string& addr, const std::optional<int>& family)
: address() : address()
{ {
this->parse(addr, family); this->parse(addr, family);
} }
address::address(const sockaddr_in6& addr) address::address(const sockaddr_in6& addr)
: address() : address()
{ {
this->address6_ = addr; this->address6_ = addr;
} }
address::address(const sockaddr_in& addr) address::address(const sockaddr_in& addr)
: address() : address()
{ {
this->address4_ = addr; this->address4_ = addr;
} }
address::address(const sockaddr* addr, const socklen_t length) address::address(const sockaddr* addr, const socklen_t length)
: address() : address()
{ {
this->set_address(addr, length); this->set_address(addr, length);
} }
void address::set_ipv4(const uint32_t ip) void address::set_ipv4(const uint32_t ip)
{ {
in_addr addr{}; in_addr addr{};
addr.s_addr = ip; addr.s_addr = ip;
this->set_ipv4(addr); this->set_ipv4(addr);
} }
bool address::operator==(const address& obj) const bool address::operator==(const address& obj) const
{ {
if (this->address_.sa_family != obj.address_.sa_family) if (this->address_.sa_family != obj.address_.sa_family)
{ {
return false; return false;
} }
if (this->get_port() != obj.get_port()) if (this->get_port() != obj.get_port())
{ {
return false; return false;
} }
if (this->address_.sa_family == AF_INET) if (this->address_.sa_family == AF_INET)
{ {
return this->address4_.sin_addr.s_addr == obj.address4_.sin_addr.s_addr; return this->address4_.sin_addr.s_addr == obj.address4_.sin_addr.s_addr;
} }
else if (this->address_.sa_family == AF_INET6) else if (this->address_.sa_family == AF_INET6)
{ {
return !memcmp(this->address6_.sin6_addr.s6_addr, obj.address6_.sin6_addr.s6_addr, return !memcmp(this->address6_.sin6_addr.s6_addr, obj.address6_.sin6_addr.s6_addr,
sizeof(obj.address6_.sin6_addr.s6_addr)); sizeof(obj.address6_.sin6_addr.s6_addr));
} }
return false; return false;
} }
void address::set_ipv4(const in_addr& addr) void address::set_ipv4(const in_addr& addr)
{ {
ZeroMemory(&this->address4_, sizeof(this->address4_)); ZeroMemory(&this->address4_, sizeof(this->address4_));
this->address4_.sin_family = AF_INET; this->address4_.sin_family = AF_INET;
this->address4_.sin_addr = addr; this->address4_.sin_addr = addr;
} }
void address::set_ipv6(const in6_addr& addr) void address::set_ipv6(const in6_addr& addr)
{ {
ZeroMemory(&this->address6_, sizeof(this->address6_)); ZeroMemory(&this->address6_, sizeof(this->address6_));
this->address6_.sin6_family = AF_INET6; this->address6_.sin6_family = AF_INET6;
this->address6_.sin6_addr = addr; this->address6_.sin6_addr = addr;
} }
void address::set_address(const sockaddr* addr, const socklen_t length) void address::set_address(const sockaddr* addr, const socklen_t length)
{ {
if (static_cast<size_t>(length) >= sizeof(sockaddr_in) && addr->sa_family == AF_INET) if (static_cast<size_t>(length) >= sizeof(sockaddr_in) && addr->sa_family == AF_INET)
{ {
this->address4_ = *reinterpret_cast<const sockaddr_in*>(addr); this->address4_ = *reinterpret_cast<const sockaddr_in*>(addr);
} }
else if (static_cast<size_t>(length) == sizeof(sockaddr_in6) && addr->sa_family == AF_INET6) else if (static_cast<size_t>(length) == sizeof(sockaddr_in6) && addr->sa_family == AF_INET6)
{ {
this->address6_ = *reinterpret_cast<const sockaddr_in6*>(addr); this->address6_ = *reinterpret_cast<const sockaddr_in6*>(addr);
} }
else else
{ {
throw std::runtime_error("Invalid network address"); throw std::runtime_error("Invalid network address");
} }
} }
void address::set_port(const unsigned short port) void address::set_port(const unsigned short port)
{ {
switch (this->address_.sa_family) switch (this->address_.sa_family)
{ {
case AF_INET: case AF_INET:
this->address4_.sin_port = htons(port); this->address4_.sin_port = htons(port);
break; break;
case AF_INET6: case AF_INET6:
this->address6_.sin6_port = htons(port); this->address6_.sin6_port = htons(port);
break; break;
default: default:
throw std::runtime_error("Invalid address family"); throw std::runtime_error("Invalid address family");
} }
} }
unsigned short address::get_port() const unsigned short address::get_port() const
{ {
switch (this->address_.sa_family) switch (this->address_.sa_family)
{ {
case AF_INET: case AF_INET:
return ntohs(this->address4_.sin_port); return ntohs(this->address4_.sin_port);
case AF_INET6: case AF_INET6:
return ntohs(this->address6_.sin6_port); return ntohs(this->address6_.sin6_port);
default: default:
return 0; return 0;
} }
} }
std::string address::to_string() const std::string address::to_string() const
{ {
char buffer[1000] = {0}; char buffer[1000] = {0};
std::string addr; std::string addr;
switch (this->address_.sa_family) switch (this->address_.sa_family)
{ {
case AF_INET: case AF_INET:
inet_ntop(this->address_.sa_family, &this->address4_.sin_addr, buffer, sizeof(buffer)); inet_ntop(this->address_.sa_family, &this->address4_.sin_addr, buffer, sizeof(buffer));
addr = std::string(buffer); addr = std::string(buffer);
break; break;
case AF_INET6: case AF_INET6:
inet_ntop(this->address_.sa_family, &this->address6_.sin6_addr, buffer, sizeof(buffer)); inet_ntop(this->address_.sa_family, &this->address6_.sin6_addr, buffer, sizeof(buffer));
addr = "[" + std::string(buffer) + "]"; addr = "[" + std::string(buffer) + "]";
break; break;
default: default:
buffer[0] = '?'; buffer[0] = '?';
buffer[1] = 0; buffer[1] = 0;
addr = std::string(buffer); addr = std::string(buffer);
break; break;
} }
return addr + ":"s + std::to_string(this->get_port()); return addr + ":"s + std::to_string(this->get_port());
} }
bool address::is_local() const bool address::is_local() const
{ {
if (this->address_.sa_family != AF_INET) if (this->address_.sa_family != AF_INET)
{ {
return false; return false;
} }
// According to: https://en.wikipedia.org/wiki/Private_network // According to: https://en.wikipedia.org/wiki/Private_network
uint8_t bytes[4]; uint8_t bytes[4];
*reinterpret_cast<uint32_t*>(&bytes) = this->address4_.sin_addr.s_addr; *reinterpret_cast<uint32_t*>(&bytes) = this->address4_.sin_addr.s_addr;
// 10.X.X.X // 10.X.X.X
if (bytes[0] == 10) if (bytes[0] == 10)
{ {
return true; return true;
} }
// 192.168.X.X // 192.168.X.X
if (bytes[0] == 192 if (bytes[0] == 192 && bytes[1] == 168)
&& bytes[1] == 168) {
{ return true;
return true; }
}
// 172.16.X.X - 172.31.X.X // 172.16.X.X - 172.31.X.X
if (bytes[0] == 172 if (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] < 32)
&& bytes[1] >= 16 {
&& bytes[1] < 32) return true;
{ }
return true;
}
// 127.0.0.1 // 127.0.0.1
if (this->address4_.sin_addr.s_addr == 0x0100007F) if (this->address4_.sin_addr.s_addr == 0x0100007F)
{ {
return true; return true;
} }
return false; return false;
} }
sockaddr& address::get_addr() sockaddr& address::get_addr()
{ {
return this->address_; return this->address_;
} }
const sockaddr& address::get_addr() const const sockaddr& address::get_addr() const
{ {
return this->address_; return this->address_;
} }
sockaddr_in& address::get_in_addr() sockaddr_in& address::get_in_addr()
{ {
return this->address4_; return this->address4_;
} }
sockaddr_in6& address::get_in6_addr() sockaddr_in6& address::get_in6_addr()
{ {
return this->address6_; return this->address6_;
} }
const sockaddr_in& address::get_in_addr() const const sockaddr_in& address::get_in_addr() const
{ {
return this->address4_; return this->address4_;
} }
const sockaddr_in6& address::get_in6_addr() const const sockaddr_in6& address::get_in6_addr() const
{ {
return this->address6_; return this->address6_;
} }
socklen_t address::get_size() const socklen_t address::get_size() const
{ {
switch (this->address_.sa_family) switch (this->address_.sa_family)
{ {
case AF_INET: case AF_INET:
return static_cast<socklen_t>(sizeof(this->address4_)); return static_cast<socklen_t>(sizeof(this->address4_));
case AF_INET6: case AF_INET6:
return static_cast<socklen_t>(sizeof(this->address6_)); return static_cast<socklen_t>(sizeof(this->address6_));
default: default:
return static_cast<socklen_t>(sizeof(this->address_)); return static_cast<socklen_t>(sizeof(this->address_));
} }
} }
socklen_t address::get_max_size() const socklen_t address::get_max_size() const
{ {
constexpr auto s = sizeof(this->address_); constexpr auto s = sizeof(this->address_);
constexpr auto s4 = sizeof(this->address4_); constexpr auto s4 = sizeof(this->address4_);
constexpr auto s6 = sizeof(this->address6_); constexpr auto s6 = sizeof(this->address6_);
constexpr auto sstore = sizeof(this->storage_); constexpr auto sstore = sizeof(this->storage_);
constexpr auto max_size = std::max(sstore, std::max(s, std::max(s4, s6))); constexpr auto max_size = std::max(sstore, std::max(s, std::max(s4, s6)));
static_assert(max_size == sstore); static_assert(max_size == sstore);
return static_cast<socklen_t>(max_size); return static_cast<socklen_t>(max_size);
} }
bool address::is_ipv4() const bool address::is_ipv4() const
{ {
return this->address_.sa_family == AF_INET; return this->address_.sa_family == AF_INET;
} }
bool address::is_ipv6() const bool address::is_ipv6() const
{ {
return this->address_.sa_family == AF_INET6; return this->address_.sa_family == AF_INET6;
} }
bool address::is_supported() const bool address::is_supported() const
{ {
return is_ipv4() || is_ipv6(); return is_ipv4() || is_ipv6();
} }
void address::parse(std::string addr, const std::optional<int>& family) void address::parse(std::string addr, const std::optional<int>& family)
{ {
std::optional<uint16_t> port_value{}; std::optional<uint16_t> port_value{};
const auto pos = addr.find_last_of(':'); const auto pos = addr.find_last_of(':');
if (pos != std::string::npos) if (pos != std::string::npos)
{ {
auto port = addr.substr(pos + 1); auto port = addr.substr(pos + 1);
port_value = uint16_t(atoi(port.data())); port_value = uint16_t(atoi(port.data()));
addr = addr.substr(0, pos); addr = addr.substr(0, pos);
} }
this->resolve(addr, family); this->resolve(addr, family);
if (port_value) if (port_value)
{ {
this->set_port(*port_value); this->set_port(*port_value);
} }
} }
void address::resolve(const std::string& hostname, const std::optional<int>& family) void address::resolve(const std::string& hostname, const std::optional<int>& family)
{ {
const auto port = this->get_port(); const auto port = this->get_port();
auto port_reset_action = utils::finally([this, port]() auto port_reset_action = utils::finally([this, port]() { this->set_port(port); });
{
this->set_port(port);
});
const auto result = resolve_multiple(hostname); const auto result = resolve_multiple(hostname);
for (const auto& addr : result) for (const auto& addr : result)
{ {
if (addr.is_supported() && (!family || addr.get_addr().sa_family == *family)) if (addr.is_supported() && (!family || addr.get_addr().sa_family == *family))
{ {
this->set_address(&addr.get_addr(), addr.get_size()); this->set_address(&addr.get_addr(), addr.get_size());
return; return;
} }
} }
port_reset_action.cancel(); port_reset_action.cancel();
throw std::runtime_error{"Unable to resolve hostname: " + hostname}; throw std::runtime_error{"Unable to resolve hostname: " + hostname};
} }
std::vector<address> address::resolve_multiple(const std::string& hostname) std::vector<address> address::resolve_multiple(const std::string& hostname)
{ {
std::vector<address> results{}; std::vector<address> results{};
addrinfo* result = nullptr; addrinfo* result = nullptr;
if (!getaddrinfo(hostname.data(), nullptr, nullptr, &result)) if (!getaddrinfo(hostname.data(), nullptr, nullptr, &result))
{ {
const auto _2 = utils::finally([&result] const auto _2 = utils::finally([&result] { freeaddrinfo(result); });
{
freeaddrinfo(result);
});
for (auto* i = result; i; i = i->ai_next) for (auto* i = result; i; i = i->ai_next)
{ {
if (i->ai_family == AF_INET || i->ai_family == AF_INET6) if (i->ai_family == AF_INET || i->ai_family == AF_INET6)
{ {
address a{}; address a{};
a.set_address(i->ai_addr, static_cast<socklen_t>(i->ai_addrlen)); a.set_address(i->ai_addr, static_cast<socklen_t>(i->ai_addrlen));
results.emplace_back(std::move(a)); results.emplace_back(std::move(a));
} }
} }
} }
return results; return results;
} }
} }
std::size_t std::hash<network::address>::operator()(const network::address& a) const noexcept std::size_t std::hash<network::address>::operator()(const network::address& a) const noexcept
{ {
const uint32_t family = a.get_addr().sa_family; const uint32_t family = a.get_addr().sa_family;
const uint32_t port = a.get_port(); const uint32_t port = a.get_port();
std::size_t hash = std::hash<uint32_t>{}(family); std::size_t hash = std::hash<uint32_t>{}(family);
hash ^= std::hash<uint32_t>{}(port); hash ^= std::hash<uint32_t>{}(port);
switch (a.get_addr().sa_family) switch (a.get_addr().sa_family)
{ {
case AF_INET: case AF_INET:
hash ^= std::hash<decltype(a.get_in_addr().sin_addr.s_addr)>{}(a.get_in_addr().sin_addr.s_addr); hash ^= std::hash<decltype(a.get_in_addr().sin_addr.s_addr)>{}(a.get_in_addr().sin_addr.s_addr);
break; break;
case AF_INET6: case AF_INET6:
hash ^= std::hash<std::string_view>{}(std::string_view{ hash ^= std::hash<std::string_view>{}(
reinterpret_cast<const char*>(a.get_in6_addr().sin6_addr.s6_addr), std::string_view{reinterpret_cast<const char*>(a.get_in6_addr().sin6_addr.s6_addr),
sizeof(a.get_in6_addr().sin6_addr.s6_addr) sizeof(a.get_in6_addr().sin6_addr.s6_addr)});
}); break;
break; }
}
return hash; return hash;
} }

View File

@@ -36,71 +36,71 @@ using socklen_t = int;
namespace network namespace network
{ {
void initialize_wsa(); void initialize_wsa();
class address class address
{ {
public: public:
address(); address();
address(const std::string& addr, const std::optional<int>& family = {}); address(const std::string& addr, const std::optional<int>& family = {});
address(const sockaddr_in& addr); address(const sockaddr_in& addr);
address(const sockaddr_in6& addr); address(const sockaddr_in6& addr);
address(const sockaddr* addr, socklen_t length); address(const sockaddr* addr, socklen_t length);
void set_ipv4(uint32_t ip); void set_ipv4(uint32_t ip);
void set_ipv4(const in_addr& addr); void set_ipv4(const in_addr& addr);
void set_ipv6(const in6_addr& addr); void set_ipv6(const in6_addr& addr);
void set_address(const sockaddr* addr, socklen_t length); void set_address(const sockaddr* addr, socklen_t length);
void set_port(unsigned short port); void set_port(unsigned short port);
[[nodiscard]] unsigned short get_port() const; [[nodiscard]] unsigned short get_port() const;
sockaddr& get_addr(); sockaddr& get_addr();
sockaddr_in& get_in_addr(); sockaddr_in& get_in_addr();
sockaddr_in6& get_in6_addr(); sockaddr_in6& get_in6_addr();
const sockaddr& get_addr() const; const sockaddr& get_addr() const;
const sockaddr_in& get_in_addr() const; const sockaddr_in& get_in_addr() const;
const sockaddr_in6& get_in6_addr() const; const sockaddr_in6& get_in6_addr() const;
socklen_t get_size() const; socklen_t get_size() const;
socklen_t get_max_size() const; socklen_t get_max_size() const;
bool is_ipv4() const; bool is_ipv4() const;
bool is_ipv6() const; bool is_ipv6() const;
bool is_supported() const; bool is_supported() const;
[[nodiscard]] bool is_local() const; [[nodiscard]] bool is_local() const;
[[nodiscard]] std::string to_string() const; [[nodiscard]] std::string to_string() const;
bool operator==(const address& obj) const; bool operator==(const address& obj) const;
bool operator!=(const address& obj) const bool operator!=(const address& obj) const
{ {
return !(*this == obj); return !(*this == obj);
} }
static std::vector<address> resolve_multiple(const std::string& hostname); static std::vector<address> resolve_multiple(const std::string& hostname);
private: private:
union union
{ {
sockaddr address_; sockaddr address_;
sockaddr_in address4_; sockaddr_in address4_;
sockaddr_in6 address6_; sockaddr_in6 address6_;
sockaddr_storage storage_; sockaddr_storage storage_;
}; };
void parse(std::string addr, const std::optional<int>& family = {}); void parse(std::string addr, const std::optional<int>& family = {});
void resolve(const std::string& hostname, const std::optional<int>& family = {}); void resolve(const std::string& hostname, const std::optional<int>& family = {});
}; };
} }
namespace std namespace std
{ {
template <> template <>
struct hash<network::address> struct hash<network::address>
{ {
std::size_t operator()(const network::address& a) const noexcept; std::size_t operator()(const network::address& a) const noexcept;
}; };
} }

View File

@@ -6,226 +6,225 @@ using namespace std::literals;
namespace network namespace network
{ {
socket::socket(const int af) socket::socket(const int af)
: address_family_(af) : address_family_(af)
{ {
initialize_wsa(); initialize_wsa();
this->socket_ = ::socket(af, SOCK_DGRAM, IPPROTO_UDP); this->socket_ = ::socket(af, SOCK_DGRAM, IPPROTO_UDP);
if (af == AF_INET6) if (af == AF_INET6)
{ {
int i = 1; int i = 1;
setsockopt(this->socket_, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&i), setsockopt(this->socket_, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&i),
static_cast<int>(sizeof(i))); static_cast<int>(sizeof(i)));
} }
} }
socket::~socket() socket::~socket()
{ {
this->release(); this->release();
} }
socket::socket(socket&& obj) noexcept socket::socket(socket&& obj) noexcept
{ {
this->operator=(std::move(obj)); this->operator=(std::move(obj));
} }
socket& socket::operator=(socket&& obj) noexcept socket& socket::operator=(socket&& obj) noexcept
{ {
if (this != &obj) if (this != &obj)
{ {
this->release(); this->release();
this->socket_ = obj.socket_; this->socket_ = obj.socket_;
this->port_ = obj.port_; this->port_ = obj.port_;
this->address_family_ = obj.address_family_; this->address_family_ = obj.address_family_;
obj.socket_ = INVALID_SOCKET; obj.socket_ = INVALID_SOCKET;
obj.address_family_ = AF_UNSPEC; obj.address_family_ = AF_UNSPEC;
} }
return *this; return *this;
} }
void socket::release() void socket::release()
{ {
if (this->socket_ != INVALID_SOCKET) if (this->socket_ != INVALID_SOCKET)
{ {
closesocket(this->socket_); closesocket(this->socket_);
this->socket_ = INVALID_SOCKET; this->socket_ = INVALID_SOCKET;
} }
} }
bool socket::bind_port(const address& target) bool socket::bind_port(const address& target)
{ {
const auto result = bind(this->socket_, &target.get_addr(), target.get_size()) == 0; const auto result = bind(this->socket_, &target.get_addr(), target.get_size()) == 0;
if (result) if (result)
{ {
this->port_ = target.get_port(); this->port_ = target.get_port();
} }
return result; return result;
} }
bool socket::send(const address& target, const void* data, const size_t size) const bool socket::send(const address& target, const void* data, const size_t size) const
{ {
const auto res = sendto(this->socket_, static_cast<const char*>(data), static_cast<send_size>(size), 0, const auto res = sendto(this->socket_, static_cast<const char*>(data), static_cast<send_size>(size), 0,
&target.get_addr(), &target.get_addr(), target.get_size());
target.get_size()); return static_cast<size_t>(res) == size;
return static_cast<size_t>(res) == size; }
}
bool socket::send(const address& target, const std::string& data) const bool socket::send(const address& target, const std::string& data) const
{ {
return this->send(target, data.data(), data.size()); return this->send(target, data.data(), data.size());
} }
bool socket::receive(address& source, std::string& data) const bool socket::receive(address& source, std::string& data) const
{ {
char buffer[0x2000]; char buffer[0x2000];
auto len = source.get_max_size(); auto len = source.get_max_size();
const auto result = recvfrom(this->socket_, buffer, static_cast<int>(sizeof(buffer)), 0, &source.get_addr(), const auto result =
&len); recvfrom(this->socket_, buffer, static_cast<int>(sizeof(buffer)), 0, &source.get_addr(), &len);
if (result == SOCKET_ERROR) if (result == SOCKET_ERROR)
{ {
return false; return false;
} }
data.assign(buffer, buffer + result); data.assign(buffer, buffer + result);
return true; return true;
} }
bool socket::set_blocking(const bool blocking) bool socket::set_blocking(const bool blocking)
{ {
return socket::set_blocking(this->socket_, blocking); return socket::set_blocking(this->socket_, blocking);
} }
bool socket::set_blocking(SOCKET s, const bool blocking) bool socket::set_blocking(SOCKET s, const bool blocking)
{ {
#ifdef _WIN32 #ifdef _WIN32
unsigned long mode = blocking ? 0 : 1; unsigned long mode = blocking ? 0 : 1;
return ioctlsocket(s, FIONBIO, &mode) == 0; return ioctlsocket(s, FIONBIO, &mode) == 0;
#else #else
int flags = fcntl(s, F_GETFL, 0); int flags = fcntl(s, F_GETFL, 0);
if (flags == -1) return false; if (flags == -1)
flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); return false;
return fcntl(s, F_SETFL, flags) == 0; flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
return fcntl(s, F_SETFL, flags) == 0;
#endif #endif
} }
bool socket::sleep(const std::chrono::milliseconds timeout) const bool socket::sleep(const std::chrono::milliseconds timeout) const
{ {
/*fd_set fdr; /*fd_set fdr;
FD_ZERO(&fdr); FD_ZERO(&fdr);
FD_SET(this->socket_, &fdr); FD_SET(this->socket_, &fdr);
const auto msec = timeout.count(); const auto msec = timeout.count();
timeval tv{}; timeval tv{};
tv.tv_sec = static_cast<long>(msec / 1000ll); tv.tv_sec = static_cast<long>(msec / 1000ll);
tv.tv_usec = static_cast<long>((msec % 1000) * 1000); tv.tv_usec = static_cast<long>((msec % 1000) * 1000);
const auto retval = select(static_cast<int>(this->socket_) + 1, &fdr, nullptr, nullptr, &tv); const auto retval = select(static_cast<int>(this->socket_) + 1, &fdr, nullptr, nullptr, &tv);
if (retval == SOCKET_ERROR) if (retval == SOCKET_ERROR)
{ {
std::this_thread::sleep_for(1ms); std::this_thread::sleep_for(1ms);
return socket_is_ready; return socket_is_ready;
} }
if (retval > 0) if (retval > 0)
{ {
return socket_is_ready; return socket_is_ready;
} }
return !socket_is_ready;*/ return !socket_is_ready;*/
std::vector<const socket*> sockets{}; std::vector<const socket*> sockets{};
sockets.push_back(this); sockets.push_back(this);
return sleep_sockets(sockets, timeout); return sleep_sockets(sockets, timeout);
} }
bool socket::sleep_until(const std::chrono::high_resolution_clock::time_point time_point) const bool socket::sleep_until(const std::chrono::high_resolution_clock::time_point time_point) const
{ {
const auto duration = time_point - std::chrono::high_resolution_clock::now(); const auto duration = time_point - std::chrono::high_resolution_clock::now();
return this->sleep(std::chrono::duration_cast<std::chrono::milliseconds>(duration)); return this->sleep(std::chrono::duration_cast<std::chrono::milliseconds>(duration));
} }
SOCKET socket::get_socket() const SOCKET socket::get_socket() const
{ {
return this->socket_; return this->socket_;
} }
uint16_t socket::get_port() const uint16_t socket::get_port() const
{ {
return this->port_; return this->port_;
} }
int socket::get_address_family() const int socket::get_address_family() const
{ {
return this->address_family_; return this->address_family_;
} }
bool socket::sleep_sockets(const std::span<const socket*>& sockets, const std::chrono::milliseconds timeout) bool socket::sleep_sockets(const std::span<const socket*>& sockets, const std::chrono::milliseconds timeout)
{ {
std::vector<pollfd> pfds{}; std::vector<pollfd> pfds{};
pfds.resize(sockets.size()); pfds.resize(sockets.size());
for (size_t i = 0; i < sockets.size(); ++i) for (size_t i = 0; i < sockets.size(); ++i)
{ {
auto& pfd = pfds.at(i); auto& pfd = pfds.at(i);
const auto& socket = sockets[i]; const auto& socket = sockets[i];
pfd.fd = socket->get_socket(); pfd.fd = socket->get_socket();
pfd.events = POLLIN; pfd.events = POLLIN;
pfd.revents = 0; pfd.revents = 0;
} }
const auto retval = poll(pfds.data(), static_cast<uint32_t>(pfds.size()), const auto retval = poll(pfds.data(), static_cast<uint32_t>(pfds.size()), static_cast<int>(timeout.count()));
static_cast<int>(timeout.count()));
if (retval == SOCKET_ERROR) if (retval == SOCKET_ERROR)
{ {
std::this_thread::sleep_for(1ms); std::this_thread::sleep_for(1ms);
return socket_is_ready; return socket_is_ready;
} }
if (retval > 0) if (retval > 0)
{ {
return socket_is_ready; return socket_is_ready;
} }
return !socket_is_ready; return !socket_is_ready;
} }
bool socket::is_socket_ready(const SOCKET s, const bool in_poll) bool socket::is_socket_ready(const SOCKET s, const bool in_poll)
{ {
pollfd pfd{}; pollfd pfd{};
pfd.fd = s; pfd.fd = s;
pfd.events = in_poll ? POLLIN : POLLOUT; pfd.events = in_poll ? POLLIN : POLLOUT;
pfd.revents = 0; pfd.revents = 0;
const auto retval = poll(&pfd, 1, 0); const auto retval = poll(&pfd, 1, 0);
if (retval == SOCKET_ERROR) if (retval == SOCKET_ERROR)
{ {
std::this_thread::sleep_for(1ms); std::this_thread::sleep_for(1ms);
return socket_is_ready; return socket_is_ready;
} }
if (retval > 0) if (retval > 0)
{ {
return socket_is_ready; return socket_is_ready;
} }
return !socket_is_ready; return !socket_is_ready;
} }
bool socket::sleep_sockets_until(const std::span<const socket*>& sockets, bool socket::sleep_sockets_until(const std::span<const socket*>& sockets,
const std::chrono::high_resolution_clock::time_point time_point) const std::chrono::high_resolution_clock::time_point time_point)
{ {
const auto duration = time_point - std::chrono::high_resolution_clock::now(); const auto duration = time_point - std::chrono::high_resolution_clock::now();
return sleep_sockets(sockets, std::chrono::duration_cast<std::chrono::milliseconds>(duration)); return sleep_sockets(sockets, std::chrono::duration_cast<std::chrono::milliseconds>(duration));
} }
} }

View File

@@ -8,63 +8,63 @@
#ifdef _WIN32 #ifdef _WIN32
using send_size = int; using send_size = int;
#define GET_SOCKET_ERROR() (WSAGetLastError()) #define GET_SOCKET_ERROR() (WSAGetLastError())
#define poll WSAPoll #define poll WSAPoll
#define SOCK_WOULDBLOCK WSAEWOULDBLOCK #define SOCK_WOULDBLOCK WSAEWOULDBLOCK
#else #else
using SOCKET = int; using SOCKET = int;
using send_size = size_t; using send_size = size_t;
#define INVALID_SOCKET (SOCKET)(~0) #define INVALID_SOCKET (SOCKET)(~0)
#define SOCKET_ERROR (-1) #define SOCKET_ERROR (-1)
#define GET_SOCKET_ERROR() (errno) #define GET_SOCKET_ERROR() (errno)
#define closesocket close #define closesocket close
#define SOCK_WOULDBLOCK EWOULDBLOCK #define SOCK_WOULDBLOCK EWOULDBLOCK
#endif #endif
namespace network namespace network
{ {
class socket class socket
{ {
public: public:
socket() = default; socket() = default;
socket(int af); socket(int af);
~socket(); ~socket();
socket(const socket& obj) = delete; socket(const socket& obj) = delete;
socket& operator=(const socket& obj) = delete; socket& operator=(const socket& obj) = delete;
socket(socket&& obj) noexcept; socket(socket&& obj) noexcept;
socket& operator=(socket&& obj) noexcept; socket& operator=(socket&& obj) noexcept;
bool bind_port(const address& target); bool bind_port(const address& target);
[[maybe_unused]] bool send(const address& target, const void* data, size_t size) const; [[maybe_unused]] bool send(const address& target, const void* data, size_t size) const;
[[maybe_unused]] bool send(const address& target, const std::string& data) const; [[maybe_unused]] bool send(const address& target, const std::string& data) const;
bool receive(address& source, std::string& data) const; bool receive(address& source, std::string& data) const;
bool set_blocking(bool blocking); bool set_blocking(bool blocking);
static bool set_blocking(SOCKET s, bool blocking); static bool set_blocking(SOCKET s, bool blocking);
static constexpr bool socket_is_ready = true; static constexpr bool socket_is_ready = true;
bool sleep(std::chrono::milliseconds timeout) const; bool sleep(std::chrono::milliseconds timeout) const;
bool sleep_until(std::chrono::high_resolution_clock::time_point time_point) const; bool sleep_until(std::chrono::high_resolution_clock::time_point time_point) const;
SOCKET get_socket() const; SOCKET get_socket() const;
uint16_t get_port() const; uint16_t get_port() const;
int get_address_family() const; int get_address_family() const;
static bool sleep_sockets(const std::span<const socket*>& sockets, std::chrono::milliseconds timeout); static bool sleep_sockets(const std::span<const socket*>& sockets, std::chrono::milliseconds timeout);
static bool sleep_sockets_until(const std::span<const socket*>& sockets, static bool sleep_sockets_until(const std::span<const socket*>& sockets,
std::chrono::high_resolution_clock::time_point time_point); std::chrono::high_resolution_clock::time_point time_point);
static bool is_socket_ready(SOCKET s, bool in_poll); static bool is_socket_ready(SOCKET s, bool in_poll);
private: private:
int address_family_{AF_UNSPEC}; int address_family_{AF_UNSPEC};
uint16_t port_ = 0; uint16_t port_ = 0;
SOCKET socket_ = INVALID_SOCKET; SOCKET socket_ = INVALID_SOCKET;
void release(); void release();
}; };
} }

View File

@@ -3,7 +3,7 @@
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
#define OS_WINDOWS #define OS_WINDOWS
#elif defined(__APPLE__) || defined(__MACH__) #elif defined(__APPLE__) || defined(__MACH__)
#define OS_MAC #define OS_MAC
#elif defined(__linux__) #elif defined(__linux__)
#define OS_LINUX #define OS_LINUX
#else #else
@@ -11,9 +11,9 @@
#endif #endif
#ifdef OS_WINDOWS #ifdef OS_WINDOWS
#define EXPORT_SYMBOL __declspec(dllexport) #define EXPORT_SYMBOL __declspec(dllexport)
#define IMPORT_SYMBOL __declspec(dllimport) #define IMPORT_SYMBOL __declspec(dllimport)
#define NO_INLINE __declspec(noinline) #define NO_INLINE __declspec(noinline)
#define DECLSPEC_ALIGN(n) __declspec(align(n)) #define DECLSPEC_ALIGN(n) __declspec(align(n))
@@ -24,21 +24,21 @@
#define EXPORT_SYMBOL __attribute__((visibility("default"))) #define EXPORT_SYMBOL __attribute__((visibility("default")))
#define IMPORT_SYMBOL #define IMPORT_SYMBOL
#define NO_INLINE __attribute__((noinline)) #define NO_INLINE __attribute__((noinline))
#define DECLSPEC_ALIGN(n) alignas(n) #define DECLSPEC_ALIGN(n) alignas(n)
#define fopen_s fopen #define fopen_s fopen
#define RESTRICTED_POINTER __restrict #define RESTRICTED_POINTER __restrict
#ifdef OS_MAC #ifdef OS_MAC
#define _fseeki64 fseeko #define _fseeki64 fseeko
#define _ftelli64 ftello #define _ftelli64 ftello
#define _stat64 stat #define _stat64 stat
#else #else
#define _fseeki64 fseeko64 #define _fseeki64 fseeko64
#define _ftelli64 ftello64 #define _ftelli64 ftello64
#define _stat64 stat64 #define _stat64 stat64
#endif #endif
#endif #endif

View File

@@ -1,246 +1,250 @@
#pragma once #pragma once
#define ACCESS_MASK DWORD #define ACCESS_MASK DWORD
#define DEVICE_TYPE DWORD #define DEVICE_TYPE DWORD
#define FILE_DEVICE_DISK 0x00000007 #define FILE_DEVICE_DISK 0x00000007
#define FILE_DEVICE_CONSOLE 0x00000050 #define FILE_DEVICE_CONSOLE 0x00000050
#define FILE_SUPERSEDE 0x00000000 #define FILE_SUPERSEDE 0x00000000
#define FILE_OPEN 0x00000001 #define FILE_OPEN 0x00000001
#define FILE_CREATE 0x00000002 #define FILE_CREATE 0x00000002
#define FILE_OPEN_IF 0x00000003 #define FILE_OPEN_IF 0x00000003
#define FILE_OVERWRITE 0x00000004 #define FILE_OVERWRITE 0x00000004
#define FILE_OVERWRITE_IF 0x00000005 #define FILE_OVERWRITE_IF 0x00000005
#define FILE_MAXIMUM_DISPOSITION 0x00000005 #define FILE_MAXIMUM_DISPOSITION 0x00000005
#ifndef OS_WINDOWS #ifndef OS_WINDOWS
#define GENERIC_READ 0x80000000 #define GENERIC_READ 0x80000000
#define GENERIC_WRITE 0x40000000 #define GENERIC_WRITE 0x40000000
#define GENERIC_EXECUTE 0x20000000 #define GENERIC_EXECUTE 0x20000000
#define GENERIC_ALL 0x10000000 #define GENERIC_ALL 0x10000000
#undef DELETE #undef DELETE
#define DELETE 0x00010000 #define DELETE 0x00010000
#define READ_CONTROL 0x00020000 #define READ_CONTROL 0x00020000
#define WRITE_DAC 0x00040000 #define WRITE_DAC 0x00040000
#define WRITE_OWNER 0x00080000 #define WRITE_OWNER 0x00080000
#define SYNCHRONIZE 0x00100000 #define SYNCHRONIZE 0x00100000
#define STANDARD_RIGHTS_REQUIRED 0x000f0000 #define STANDARD_RIGHTS_REQUIRED 0x000f0000
#define FILE_READ_DATA 0x0001 /* file & pipe */ #define FILE_READ_DATA 0x0001 /* file & pipe */
#define FILE_LIST_DIRECTORY 0x0001 /* directory */ #define FILE_LIST_DIRECTORY 0x0001 /* directory */
#define FILE_WRITE_DATA 0x0002 /* file & pipe */ #define FILE_WRITE_DATA 0x0002 /* file & pipe */
#define FILE_ADD_FILE 0x0002 /* directory */ #define FILE_ADD_FILE 0x0002 /* directory */
#define FILE_APPEND_DATA 0x0004 /* file */ #define FILE_APPEND_DATA 0x0004 /* file */
#define FILE_ADD_SUBDIRECTORY 0x0004 /* directory */ #define FILE_ADD_SUBDIRECTORY 0x0004 /* directory */
#define FILE_CREATE_PIPE_INSTANCE 0x0004 /* named pipe */ #define FILE_CREATE_PIPE_INSTANCE 0x0004 /* named pipe */
#define FILE_READ_EA 0x0008 /* file & directory */ #define FILE_READ_EA 0x0008 /* file & directory */
#define FILE_READ_PROPERTIES FILE_READ_EA #define FILE_READ_PROPERTIES FILE_READ_EA
#define FILE_WRITE_EA 0x0010 /* file & directory */ #define FILE_WRITE_EA 0x0010 /* file & directory */
#define FILE_WRITE_PROPERTIES FILE_WRITE_EA #define FILE_WRITE_PROPERTIES FILE_WRITE_EA
#define FILE_EXECUTE 0x0020 /* file */ #define FILE_EXECUTE 0x0020 /* file */
#define FILE_TRAVERSE 0x0020 /* directory */ #define FILE_TRAVERSE 0x0020 /* directory */
#define FILE_DELETE_CHILD 0x0040 /* directory */ #define FILE_DELETE_CHILD 0x0040 /* directory */
#define FILE_READ_ATTRIBUTES 0x0080 /* all */ #define FILE_READ_ATTRIBUTES 0x0080 /* all */
#define FILE_WRITE_ATTRIBUTES 0x0100 /* all */ #define FILE_WRITE_ATTRIBUTES 0x0100 /* all */
#define FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x1ff) #define FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1ff)
#endif #endif
#define FILE_DIRECTORY_FILE 0x00000001 #define FILE_DIRECTORY_FILE 0x00000001
#define FILE_WRITE_THROUGH 0x00000002 #define FILE_WRITE_THROUGH 0x00000002
#define FILE_SEQUENTIAL_ONLY 0x00000004 #define FILE_SEQUENTIAL_ONLY 0x00000004
#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008 #define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008
#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010 #define FILE_SYNCHRONOUS_IO_ALERT 0x00000010
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 #define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
#define FILE_NON_DIRECTORY_FILE 0x00000040 #define FILE_NON_DIRECTORY_FILE 0x00000040
#define FILE_CREATE_TREE_CONNECTION 0x00000080 #define FILE_CREATE_TREE_CONNECTION 0x00000080
#define FILE_ATTRIBUTE_NORMAL 0x00000080 #define FILE_ATTRIBUTE_NORMAL 0x00000080
#define PS_ATTRIBUTE_NUMBER_MASK 0x0000ffff #define PS_ATTRIBUTE_NUMBER_MASK 0x0000ffff
#define PS_ATTRIBUTE_THREAD 0x00010000 // may be used with thread creation #define PS_ATTRIBUTE_THREAD 0x00010000 // may be used with thread creation
#define PS_ATTRIBUTE_INPUT 0x00020000 // input only #define PS_ATTRIBUTE_INPUT 0x00020000 // input only
#define PS_ATTRIBUTE_ADDITIVE 0x00040000 // "accumulated" e.g. bitmasks, counters, etc. #define PS_ATTRIBUTE_ADDITIVE 0x00040000 // "accumulated" e.g. bitmasks, counters, etc.
#define SL_RESTART_SCAN 0x01 #define SL_RESTART_SCAN 0x01
#define SL_RETURN_SINGLE_ENTRY 0x02 #define SL_RETURN_SINGLE_ENTRY 0x02
#define SL_NO_CURSOR_UPDATE 0x10 #define SL_NO_CURSOR_UPDATE 0x10
#define SEC_IMAGE 0x01000000 #define SEC_IMAGE 0x01000000
typedef enum _FSINFOCLASS typedef enum _FSINFOCLASS
{ {
FileFsVolumeInformation = 1, // q: FILE_FS_VOLUME_INFORMATION FileFsVolumeInformation = 1, // q: FILE_FS_VOLUME_INFORMATION
FileFsLabelInformation, // s: FILE_FS_LABEL_INFORMATION (requires FILE_WRITE_DATA to volume) FileFsLabelInformation, // s: FILE_FS_LABEL_INFORMATION (requires FILE_WRITE_DATA to volume)
FileFsSizeInformation, // q: FILE_FS_SIZE_INFORMATION FileFsSizeInformation, // q: FILE_FS_SIZE_INFORMATION
FileFsDeviceInformation, // q: FILE_FS_DEVICE_INFORMATION FileFsDeviceInformation, // q: FILE_FS_DEVICE_INFORMATION
FileFsAttributeInformation, // q: FILE_FS_ATTRIBUTE_INFORMATION FileFsAttributeInformation, // q: FILE_FS_ATTRIBUTE_INFORMATION
FileFsControlInformation, FileFsControlInformation,
// q, s: FILE_FS_CONTROL_INFORMATION (q: requires FILE_READ_DATA; s: requires FILE_WRITE_DATA to volume) // q, s: FILE_FS_CONTROL_INFORMATION (q: requires FILE_READ_DATA; s: requires FILE_WRITE_DATA to volume)
FileFsFullSizeInformation, // q: FILE_FS_FULL_SIZE_INFORMATION FileFsFullSizeInformation, // q: FILE_FS_FULL_SIZE_INFORMATION
FileFsObjectIdInformation, // q; s: FILE_FS_OBJECTID_INFORMATION (s: requires FILE_WRITE_DATA to volume) FileFsObjectIdInformation, // q; s: FILE_FS_OBJECTID_INFORMATION (s: requires FILE_WRITE_DATA to volume)
FileFsDriverPathInformation, // q: FILE_FS_DRIVER_PATH_INFORMATION FileFsDriverPathInformation, // q: FILE_FS_DRIVER_PATH_INFORMATION
FileFsVolumeFlagsInformation, FileFsVolumeFlagsInformation,
// q; s: FILE_FS_VOLUME_FLAGS_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES to volume) // 10 // q; s: FILE_FS_VOLUME_FLAGS_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES to
FileFsSectorSizeInformation, // q: FILE_FS_SECTOR_SIZE_INFORMATION // since WIN8 // volume) // 10
FileFsDataCopyInformation, // q: FILE_FS_DATA_COPY_INFORMATION FileFsSectorSizeInformation, // q: FILE_FS_SECTOR_SIZE_INFORMATION // since WIN8
FileFsMetadataSizeInformation, // q: FILE_FS_METADATA_SIZE_INFORMATION // since THRESHOLD FileFsDataCopyInformation, // q: FILE_FS_DATA_COPY_INFORMATION
FileFsFullSizeInformationEx, // q: FILE_FS_FULL_SIZE_INFORMATION_EX // since REDSTONE5 FileFsMetadataSizeInformation, // q: FILE_FS_METADATA_SIZE_INFORMATION // since THRESHOLD
FileFsGuidInformation, // q: FILE_FS_GUID_INFORMATION // since 23H2 FileFsFullSizeInformationEx, // q: FILE_FS_FULL_SIZE_INFORMATION_EX // since REDSTONE5
FileFsMaximumInformation FileFsGuidInformation, // q: FILE_FS_GUID_INFORMATION // since 23H2
FileFsMaximumInformation
} FSINFOCLASS, *PFSINFOCLASS; } FSINFOCLASS, *PFSINFOCLASS;
typedef enum _FSINFOCLASS FS_INFORMATION_CLASS; typedef enum _FSINFOCLASS FS_INFORMATION_CLASS;
typedef enum _FILE_INFORMATION_CLASS typedef enum _FILE_INFORMATION_CLASS
{ {
FileDirectoryInformation = 1, FileDirectoryInformation = 1,
// q: FILE_DIRECTORY_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // q: FILE_DIRECTORY_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
FileFullDirectoryInformation, FileFullDirectoryInformation,
// q: FILE_FULL_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // q: FILE_FULL_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
FileBothDirectoryInformation, FileBothDirectoryInformation,
// q: FILE_BOTH_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // q: FILE_BOTH_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
FileBasicInformation, FileBasicInformation,
// q; s: FILE_BASIC_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES) // q; s: FILE_BASIC_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
FileStandardInformation, // q: FILE_STANDARD_INFORMATION, FILE_STANDARD_INFORMATION_EX FileStandardInformation, // q: FILE_STANDARD_INFORMATION, FILE_STANDARD_INFORMATION_EX
FileInternalInformation, // q: FILE_INTERNAL_INFORMATION FileInternalInformation, // q: FILE_INTERNAL_INFORMATION
FileEaInformation, // q: FILE_EA_INFORMATION FileEaInformation, // q: FILE_EA_INFORMATION
FileAccessInformation, // q: FILE_ACCESS_INFORMATION FileAccessInformation, // q: FILE_ACCESS_INFORMATION
FileNameInformation, // q: FILE_NAME_INFORMATION FileNameInformation, // q: FILE_NAME_INFORMATION
FileRenameInformation, // s: FILE_RENAME_INFORMATION (requires DELETE) // 10 FileRenameInformation, // s: FILE_RENAME_INFORMATION (requires DELETE) // 10
FileLinkInformation, // s: FILE_LINK_INFORMATION FileLinkInformation, // s: FILE_LINK_INFORMATION
FileNamesInformation, // q: FILE_NAMES_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) FileNamesInformation, // q: FILE_NAMES_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
FileDispositionInformation, // s: FILE_DISPOSITION_INFORMATION (requires DELETE) FileDispositionInformation, // s: FILE_DISPOSITION_INFORMATION (requires DELETE)
FilePositionInformation, // q; s: FILE_POSITION_INFORMATION FilePositionInformation, // q; s: FILE_POSITION_INFORMATION
FileFullEaInformation, // FILE_FULL_EA_INFORMATION FileFullEaInformation, // FILE_FULL_EA_INFORMATION
FileModeInformation, // q; s: FILE_MODE_INFORMATION FileModeInformation, // q; s: FILE_MODE_INFORMATION
FileAlignmentInformation, // q: FILE_ALIGNMENT_INFORMATION FileAlignmentInformation, // q: FILE_ALIGNMENT_INFORMATION
FileAllInformation, // q: FILE_ALL_INFORMATION (requires FILE_READ_ATTRIBUTES) FileAllInformation, // q: FILE_ALL_INFORMATION (requires FILE_READ_ATTRIBUTES)
FileAllocationInformation, // s: FILE_ALLOCATION_INFORMATION (requires FILE_WRITE_DATA) FileAllocationInformation, // s: FILE_ALLOCATION_INFORMATION (requires FILE_WRITE_DATA)
FileEndOfFileInformation, // s: FILE_END_OF_FILE_INFORMATION (requires FILE_WRITE_DATA) // 20 FileEndOfFileInformation, // s: FILE_END_OF_FILE_INFORMATION (requires FILE_WRITE_DATA) // 20
FileAlternateNameInformation, // q: FILE_NAME_INFORMATION FileAlternateNameInformation, // q: FILE_NAME_INFORMATION
FileStreamInformation, // q: FILE_STREAM_INFORMATION FileStreamInformation, // q: FILE_STREAM_INFORMATION
FilePipeInformation, FilePipeInformation,
// q; s: FILE_PIPE_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES) // q; s: FILE_PIPE_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
FilePipeLocalInformation, // q: FILE_PIPE_LOCAL_INFORMATION (requires FILE_READ_ATTRIBUTES) FilePipeLocalInformation, // q: FILE_PIPE_LOCAL_INFORMATION (requires FILE_READ_ATTRIBUTES)
FilePipeRemoteInformation, FilePipeRemoteInformation,
// q; s: FILE_PIPE_REMOTE_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES) // q; s: FILE_PIPE_REMOTE_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
FileMailslotQueryInformation, // q: FILE_MAILSLOT_QUERY_INFORMATION FileMailslotQueryInformation, // q: FILE_MAILSLOT_QUERY_INFORMATION
FileMailslotSetInformation, // s: FILE_MAILSLOT_SET_INFORMATION FileMailslotSetInformation, // s: FILE_MAILSLOT_SET_INFORMATION
FileCompressionInformation, // q: FILE_COMPRESSION_INFORMATION FileCompressionInformation, // q: FILE_COMPRESSION_INFORMATION
FileObjectIdInformation, // q: FILE_OBJECTID_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) FileObjectIdInformation, // q: FILE_OBJECTID_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
FileCompletionInformation, // s: FILE_COMPLETION_INFORMATION // 30 FileCompletionInformation, // s: FILE_COMPLETION_INFORMATION // 30
FileMoveClusterInformation, // s: FILE_MOVE_CLUSTER_INFORMATION (requires FILE_WRITE_DATA) FileMoveClusterInformation, // s: FILE_MOVE_CLUSTER_INFORMATION (requires FILE_WRITE_DATA)
FileQuotaInformation, // q: FILE_QUOTA_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) FileQuotaInformation, // q: FILE_QUOTA_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
FileReparsePointInformation, FileReparsePointInformation,
// q: FILE_REPARSE_POINT_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // q: FILE_REPARSE_POINT_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
FileNetworkOpenInformation, // q: FILE_NETWORK_OPEN_INFORMATION (requires FILE_READ_ATTRIBUTES) FileNetworkOpenInformation, // q: FILE_NETWORK_OPEN_INFORMATION (requires FILE_READ_ATTRIBUTES)
FileAttributeTagInformation, // q: FILE_ATTRIBUTE_TAG_INFORMATION (requires FILE_READ_ATTRIBUTES) FileAttributeTagInformation, // q: FILE_ATTRIBUTE_TAG_INFORMATION (requires FILE_READ_ATTRIBUTES)
FileTrackingInformation, // s: FILE_TRACKING_INFORMATION (requires FILE_WRITE_DATA) FileTrackingInformation, // s: FILE_TRACKING_INFORMATION (requires FILE_WRITE_DATA)
FileIdBothDirectoryInformation, FileIdBothDirectoryInformation,
// q: FILE_ID_BOTH_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // q: FILE_ID_BOTH_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
FileIdFullDirectoryInformation, FileIdFullDirectoryInformation,
// q: FILE_ID_FULL_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // q: FILE_ID_FULL_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex])
FileValidDataLengthInformation, FileValidDataLengthInformation,
// s: FILE_VALID_DATA_LENGTH_INFORMATION (requires FILE_WRITE_DATA and/or SeManageVolumePrivilege) // s: FILE_VALID_DATA_LENGTH_INFORMATION (requires FILE_WRITE_DATA and/or SeManageVolumePrivilege)
FileShortNameInformation, // s: FILE_NAME_INFORMATION (requires DELETE) // 40 FileShortNameInformation, // s: FILE_NAME_INFORMATION (requires DELETE) // 40
FileIoCompletionNotificationInformation, FileIoCompletionNotificationInformation,
// q; s: FILE_IO_COMPLETION_NOTIFICATION_INFORMATION (q: requires FILE_READ_ATTRIBUTES) // since VISTA // q; s: FILE_IO_COMPLETION_NOTIFICATION_INFORMATION (q: requires FILE_READ_ATTRIBUTES) // since VISTA
FileIoStatusBlockRangeInformation, // s: FILE_IOSTATUSBLOCK_RANGE_INFORMATION (requires SeLockMemoryPrivilege) FileIoStatusBlockRangeInformation, // s: FILE_IOSTATUSBLOCK_RANGE_INFORMATION (requires SeLockMemoryPrivilege)
FileIoPriorityHintInformation, FileIoPriorityHintInformation,
// q; s: FILE_IO_PRIORITY_HINT_INFORMATION, FILE_IO_PRIORITY_HINT_INFORMATION_EX (q: requires FILE_READ_DATA) // q; s: FILE_IO_PRIORITY_HINT_INFORMATION, FILE_IO_PRIORITY_HINT_INFORMATION_EX (q: requires FILE_READ_DATA)
FileSfioReserveInformation, // q; s: FILE_SFIO_RESERVE_INFORMATION (q: requires FILE_READ_DATA) FileSfioReserveInformation, // q; s: FILE_SFIO_RESERVE_INFORMATION (q: requires FILE_READ_DATA)
FileSfioVolumeInformation, // q: FILE_SFIO_VOLUME_INFORMATION (requires FILE_READ_ATTRIBUTES) FileSfioVolumeInformation, // q: FILE_SFIO_VOLUME_INFORMATION (requires FILE_READ_ATTRIBUTES)
FileHardLinkInformation, // q: FILE_LINKS_INFORMATION FileHardLinkInformation, // q: FILE_LINKS_INFORMATION
FileProcessIdsUsingFileInformation, // q: FILE_PROCESS_IDS_USING_FILE_INFORMATION (requires FILE_READ_ATTRIBUTES) FileProcessIdsUsingFileInformation, // q: FILE_PROCESS_IDS_USING_FILE_INFORMATION (requires FILE_READ_ATTRIBUTES)
FileNormalizedNameInformation, // q: FILE_NAME_INFORMATION FileNormalizedNameInformation, // q: FILE_NAME_INFORMATION
FileNetworkPhysicalNameInformation, // q: FILE_NETWORK_PHYSICAL_NAME_INFORMATION FileNetworkPhysicalNameInformation, // q: FILE_NETWORK_PHYSICAL_NAME_INFORMATION
FileIdGlobalTxDirectoryInformation, FileIdGlobalTxDirectoryInformation,
// q: FILE_ID_GLOBAL_TX_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // since WIN7 // 50 // q: FILE_ID_GLOBAL_TX_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // since WIN7 //
FileIsRemoteDeviceInformation, // q: FILE_IS_REMOTE_DEVICE_INFORMATION (requires FILE_READ_ATTRIBUTES) // 50
FileUnusedInformation, FileIsRemoteDeviceInformation, // q: FILE_IS_REMOTE_DEVICE_INFORMATION (requires FILE_READ_ATTRIBUTES)
FileNumaNodeInformation, // q: FILE_NUMA_NODE_INFORMATION FileUnusedInformation,
FileStandardLinkInformation, // q: FILE_STANDARD_LINK_INFORMATION FileNumaNodeInformation, // q: FILE_NUMA_NODE_INFORMATION
FileRemoteProtocolInformation, // q: FILE_REMOTE_PROTOCOL_INFORMATION FileStandardLinkInformation, // q: FILE_STANDARD_LINK_INFORMATION
FileRenameInformationBypassAccessCheck, // (kernel-mode only); s: FILE_RENAME_INFORMATION // since WIN8 FileRemoteProtocolInformation, // q: FILE_REMOTE_PROTOCOL_INFORMATION
FileLinkInformationBypassAccessCheck, // (kernel-mode only); s: FILE_LINK_INFORMATION FileRenameInformationBypassAccessCheck, // (kernel-mode only); s: FILE_RENAME_INFORMATION // since WIN8
FileVolumeNameInformation, // q: FILE_VOLUME_NAME_INFORMATION FileLinkInformationBypassAccessCheck, // (kernel-mode only); s: FILE_LINK_INFORMATION
FileIdInformation, // q: FILE_ID_INFORMATION FileVolumeNameInformation, // q: FILE_VOLUME_NAME_INFORMATION
FileIdExtdDirectoryInformation, FileIdInformation, // q: FILE_ID_INFORMATION
// q: FILE_ID_EXTD_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // 60 FileIdExtdDirectoryInformation,
FileReplaceCompletionInformation, // s: FILE_COMPLETION_INFORMATION // since WINBLUE // q: FILE_ID_EXTD_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // 60
FileHardLinkFullIdInformation, // q: FILE_LINK_ENTRY_FULL_ID_INFORMATION // FILE_LINKS_FULL_ID_INFORMATION FileReplaceCompletionInformation, // s: FILE_COMPLETION_INFORMATION // since WINBLUE
FileIdExtdBothDirectoryInformation, FileHardLinkFullIdInformation, // q: FILE_LINK_ENTRY_FULL_ID_INFORMATION // FILE_LINKS_FULL_ID_INFORMATION
// q: FILE_ID_EXTD_BOTH_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // since THRESHOLD FileIdExtdBothDirectoryInformation,
FileDispositionInformationEx, // s: FILE_DISPOSITION_INFO_EX (requires DELETE) // since REDSTONE // q: FILE_ID_EXTD_BOTH_DIR_INFORMATION (requires FILE_LIST_DIRECTORY) (NtQueryDirectoryFile[Ex]) // since THRESHOLD
FileRenameInformationEx, // s: FILE_RENAME_INFORMATION_EX FileDispositionInformationEx, // s: FILE_DISPOSITION_INFO_EX (requires DELETE) // since REDSTONE
FileRenameInformationExBypassAccessCheck, // (kernel-mode only); s: FILE_RENAME_INFORMATION_EX FileRenameInformationEx, // s: FILE_RENAME_INFORMATION_EX
FileDesiredStorageClassInformation, FileRenameInformationExBypassAccessCheck, // (kernel-mode only); s: FILE_RENAME_INFORMATION_EX
// q; s: FILE_DESIRED_STORAGE_CLASS_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES) // since REDSTONE2 FileDesiredStorageClassInformation,
FileStatInformation, // q: FILE_STAT_INFORMATION (requires FILE_READ_ATTRIBUTES) // q; s: FILE_DESIRED_STORAGE_CLASS_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires
FileMemoryPartitionInformation, // s: FILE_MEMORY_PARTITION_INFORMATION // since REDSTONE3 // FILE_WRITE_ATTRIBUTES) // since REDSTONE2
FileStatLxInformation, FileStatInformation, // q: FILE_STAT_INFORMATION (requires FILE_READ_ATTRIBUTES)
// q: FILE_STAT_LX_INFORMATION (requires FILE_READ_ATTRIBUTES and FILE_READ_EA) // since REDSTONE4 // 70 FileMemoryPartitionInformation, // s: FILE_MEMORY_PARTITION_INFORMATION // since REDSTONE3
FileCaseSensitiveInformation, FileStatLxInformation,
// q; s: FILE_CASE_SENSITIVE_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES) // q: FILE_STAT_LX_INFORMATION (requires FILE_READ_ATTRIBUTES and FILE_READ_EA) // since REDSTONE4 // 70
FileLinkInformationEx, // s: FILE_LINK_INFORMATION_EX // since REDSTONE5 FileCaseSensitiveInformation,
FileLinkInformationExBypassAccessCheck, // (kernel-mode only); s: FILE_LINK_INFORMATION_EX // q; s: FILE_CASE_SENSITIVE_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
FileStorageReserveIdInformation, FileLinkInformationEx, // s: FILE_LINK_INFORMATION_EX // since REDSTONE5
// q; s: FILE_STORAGE_RESERVE_ID_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES) FileLinkInformationExBypassAccessCheck, // (kernel-mode only); s: FILE_LINK_INFORMATION_EX
FileCaseSensitiveInformationForceAccessCheck, // q; s: FILE_CASE_SENSITIVE_INFORMATION FileStorageReserveIdInformation,
FileKnownFolderInformation, // q; s: FILE_STORAGE_RESERVE_ID_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES)
// q; s: FILE_KNOWN_FOLDER_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES) // since WIN11 FileCaseSensitiveInformationForceAccessCheck, // q; s: FILE_CASE_SENSITIVE_INFORMATION
FileStatBasicInformation, // since 23H2 FileKnownFolderInformation,
FileId64ExtdDirectoryInformation, // FILE_ID_64_EXTD_DIR_INFORMATION // q; s: FILE_KNOWN_FOLDER_INFORMATION (q: requires FILE_READ_ATTRIBUTES; s: requires FILE_WRITE_ATTRIBUTES) //
FileId64ExtdBothDirectoryInformation, // FILE_ID_64_EXTD_BOTH_DIR_INFORMATION // since WIN11
FileIdAllExtdDirectoryInformation, // FILE_ID_ALL_EXTD_DIR_INFORMATION FileStatBasicInformation, // since 23H2
FileIdAllExtdBothDirectoryInformation, // FILE_ID_ALL_EXTD_BOTH_DIR_INFORMATION FileId64ExtdDirectoryInformation, // FILE_ID_64_EXTD_DIR_INFORMATION
FileStreamReservationInformation, // FILE_STREAM_RESERVATION_INFORMATION // since 24H2 FileId64ExtdBothDirectoryInformation, // FILE_ID_64_EXTD_BOTH_DIR_INFORMATION
FileMupProviderInfo, // MUP_PROVIDER_INFORMATION FileIdAllExtdDirectoryInformation, // FILE_ID_ALL_EXTD_DIR_INFORMATION
FileMaximumInformation FileIdAllExtdBothDirectoryInformation, // FILE_ID_ALL_EXTD_BOTH_DIR_INFORMATION
FileStreamReservationInformation, // FILE_STREAM_RESERVATION_INFORMATION // since 24H2
FileMupProviderInfo, // MUP_PROVIDER_INFORMATION
FileMaximumInformation
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
typedef enum _OBJECT_INFORMATION_CLASS typedef enum _OBJECT_INFORMATION_CLASS
{ {
ObjectBasicInformation, // q: OBJECT_BASIC_INFORMATION ObjectBasicInformation, // q: OBJECT_BASIC_INFORMATION
ObjectNameInformation, // q: OBJECT_NAME_INFORMATION ObjectNameInformation, // q: OBJECT_NAME_INFORMATION
ObjectTypeInformation, // q: OBJECT_TYPE_INFORMATION ObjectTypeInformation, // q: OBJECT_TYPE_INFORMATION
ObjectTypesInformation, // q: OBJECT_TYPES_INFORMATION ObjectTypesInformation, // q: OBJECT_TYPES_INFORMATION
ObjectHandleFlagInformation, // qs: OBJECT_HANDLE_FLAG_INFORMATION ObjectHandleFlagInformation, // qs: OBJECT_HANDLE_FLAG_INFORMATION
ObjectSessionInformation, // s: void // change object session // (requires SeTcbPrivilege) ObjectSessionInformation, // s: void // change object session // (requires SeTcbPrivilege)
ObjectSessionObjectInformation, // s: void // change object session // (requires SeTcbPrivilege) ObjectSessionObjectInformation, // s: void // change object session // (requires SeTcbPrivilege)
MaxObjectInfoClass MaxObjectInfoClass
} OBJECT_INFORMATION_CLASS; } OBJECT_INFORMATION_CLASS;
typedef enum _HARDERROR_RESPONSE_OPTION typedef enum _HARDERROR_RESPONSE_OPTION
{ {
OptionAbortRetryIgnore, OptionAbortRetryIgnore,
OptionOk, OptionOk,
OptionOkCancel, OptionOkCancel,
OptionRetryCancel, OptionRetryCancel,
OptionYesNo, OptionYesNo,
OptionYesNoCancel, OptionYesNoCancel,
OptionShutdownSystem, OptionShutdownSystem,
OptionOkNoWait, OptionOkNoWait,
OptionCancelTryContinue OptionCancelTryContinue
} HARDERROR_RESPONSE_OPTION; } HARDERROR_RESPONSE_OPTION;
typedef enum _HARDERROR_RESPONSE typedef enum _HARDERROR_RESPONSE
{ {
ResponseReturnToCaller, ResponseReturnToCaller,
ResponseNotHandled, ResponseNotHandled,
ResponseAbort, ResponseAbort,
ResponseCancel, ResponseCancel,
ResponseIgnore, ResponseIgnore,
ResponseNo, ResponseNo,
ResponseOk, ResponseOk,
ResponseRetry, ResponseRetry,
ResponseYes, ResponseYes,
ResponseTryAgain, ResponseTryAgain,
ResponseContinue ResponseContinue
} HARDERROR_RESPONSE; } HARDERROR_RESPONSE;
typedef USHORT RTL_ATOM; typedef USHORT RTL_ATOM;
@@ -248,113 +252,112 @@ typedef USHORT RTL_ATOM;
template <typename Traits> template <typename Traits>
struct IO_STATUS_BLOCK struct IO_STATUS_BLOCK
{ {
union union
{ {
NTSTATUS Status; NTSTATUS Status;
typename Traits::PVOID Pointer; typename Traits::PVOID Pointer;
}; };
typename Traits::ULONG_PTR Information; typename Traits::ULONG_PTR Information;
}; };
template <typename Traits> template <typename Traits>
struct OBJECT_ATTRIBUTES struct OBJECT_ATTRIBUTES
{ {
ULONG Length; ULONG Length;
typename Traits::HANDLE RootDirectory; typename Traits::HANDLE RootDirectory;
EMULATOR_CAST(typename Traits::PVOID, UNICODE_STRING*) ObjectName; EMULATOR_CAST(typename Traits::PVOID, UNICODE_STRING*) ObjectName;
ULONG Attributes; ULONG Attributes;
typename Traits::PVOID SecurityDescriptor; // PSECURITY_DESCRIPTOR; typename Traits::PVOID SecurityDescriptor; // PSECURITY_DESCRIPTOR;
typename Traits::PVOID SecurityQualityOfService; // PSECURITY_QUALITY_OF_SERVICE typename Traits::PVOID SecurityQualityOfService; // PSECURITY_QUALITY_OF_SERVICE
}; };
typedef struct _FILE_FS_DEVICE_INFORMATION typedef struct _FILE_FS_DEVICE_INFORMATION
{ {
DEVICE_TYPE DeviceType; DEVICE_TYPE DeviceType;
ULONG Characteristics; ULONG Characteristics;
} FILE_FS_DEVICE_INFORMATION, *PFILE_FS_DEVICE_INFORMATION; } FILE_FS_DEVICE_INFORMATION, *PFILE_FS_DEVICE_INFORMATION;
typedef struct _FILE_POSITION_INFORMATION typedef struct _FILE_POSITION_INFORMATION
{ {
LARGE_INTEGER CurrentByteOffset; LARGE_INTEGER CurrentByteOffset;
} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION; } FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION;
typedef struct _FILE_STANDARD_INFORMATION typedef struct _FILE_STANDARD_INFORMATION
{ {
LARGE_INTEGER AllocationSize; LARGE_INTEGER AllocationSize;
LARGE_INTEGER EndOfFile; LARGE_INTEGER EndOfFile;
ULONG NumberOfLinks; ULONG NumberOfLinks;
BOOLEAN DeletePending; BOOLEAN DeletePending;
BOOLEAN Directory; BOOLEAN Directory;
} FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION; } FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION;
typedef struct _FILE_NAME_INFORMATION typedef struct _FILE_NAME_INFORMATION
{ {
ULONG FileNameLength; ULONG FileNameLength;
char16_t FileName[1]; char16_t FileName[1];
} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION; } FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION;
typedef struct _FILE_BASIC_INFORMATION typedef struct _FILE_BASIC_INFORMATION
{ {
LARGE_INTEGER CreationTime; // Specifies the time that the file was created. LARGE_INTEGER CreationTime; // Specifies the time that the file was created.
LARGE_INTEGER LastAccessTime; // Specifies the time that the file was last accessed. LARGE_INTEGER LastAccessTime; // Specifies the time that the file was last accessed.
LARGE_INTEGER LastWriteTime; // Specifies the time that the file was last written to. LARGE_INTEGER LastWriteTime; // Specifies the time that the file was last written to.
LARGE_INTEGER ChangeTime; // Specifies the last time the file was changed. LARGE_INTEGER ChangeTime; // Specifies the last time the file was changed.
ULONG FileAttributes; // Specifies one or more FILE_ATTRIBUTE_XXX flags. ULONG FileAttributes; // Specifies one or more FILE_ATTRIBUTE_XXX flags.
} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION; } FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
typedef struct _FILE_DIRECTORY_INFORMATION typedef struct _FILE_DIRECTORY_INFORMATION
{ {
ULONG NextEntryOffset; ULONG NextEntryOffset;
ULONG FileIndex; ULONG FileIndex;
LARGE_INTEGER CreationTime; LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime; LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime; LARGE_INTEGER ChangeTime;
LARGE_INTEGER EndOfFile; LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize; LARGE_INTEGER AllocationSize;
ULONG FileAttributes; ULONG FileAttributes;
ULONG FileNameLength; ULONG FileNameLength;
char16_t FileName[1]; char16_t FileName[1];
} FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION; } FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION;
typedef struct _FILE_FULL_DIR_INFORMATION typedef struct _FILE_FULL_DIR_INFORMATION
{ {
ULONG NextEntryOffset; ULONG NextEntryOffset;
ULONG FileIndex; ULONG FileIndex;
LARGE_INTEGER CreationTime; LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime; LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime; LARGE_INTEGER ChangeTime;
LARGE_INTEGER EndOfFile; LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize; LARGE_INTEGER AllocationSize;
ULONG FileAttributes; ULONG FileAttributes;
ULONG FileNameLength; ULONG FileNameLength;
ULONG EaSize; ULONG EaSize;
char16_t FileName[1]; char16_t FileName[1];
} FILE_FULL_DIR_INFORMATION, *PFILE_FULL_DIR_INFORMATION; } FILE_FULL_DIR_INFORMATION, *PFILE_FULL_DIR_INFORMATION;
typedef struct _FILE_BOTH_DIR_INFORMATION typedef struct _FILE_BOTH_DIR_INFORMATION
{ {
ULONG NextEntryOffset; ULONG NextEntryOffset;
ULONG FileIndex; ULONG FileIndex;
LARGE_INTEGER CreationTime; LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime; LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime; LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime; LARGE_INTEGER ChangeTime;
LARGE_INTEGER EndOfFile; LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize; LARGE_INTEGER AllocationSize;
ULONG FileAttributes; ULONG FileAttributes;
ULONG FileNameLength; ULONG FileNameLength;
ULONG EaSize; ULONG EaSize;
char ShortNameLength; char ShortNameLength;
char16_t ShortName[12]; char16_t ShortName[12];
char16_t FileName[1]; char16_t FileName[1];
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION; } FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;
#ifndef OS_WINDOWS #ifndef OS_WINDOWS
typedef BOOLEAN SECURITY_CONTEXT_TRACKING_MODE, typedef BOOLEAN SECURITY_CONTEXT_TRACKING_MODE, *PSECURITY_CONTEXT_TRACKING_MODE;
* PSECURITY_CONTEXT_TRACKING_MODE;
typedef struct _SECURITY_QUALITY_OF_SERVICE typedef struct _SECURITY_QUALITY_OF_SERVICE
{ {
DWORD Length; DWORD Length;
@@ -367,17 +370,17 @@ typedef struct _SECURITY_QUALITY_OF_SERVICE
typedef struct _PORT_VIEW64 typedef struct _PORT_VIEW64
{ {
ULONG Length; ULONG Length;
EMULATOR_CAST(std::uint64_t, HANDLE) SectionHandle; EMULATOR_CAST(std::uint64_t, HANDLE) SectionHandle;
ULONG SectionOffset; ULONG SectionOffset;
EMULATOR_CAST(std::int64_t, SIZE_T) ViewSize; EMULATOR_CAST(std::int64_t, SIZE_T) ViewSize;
EmulatorTraits<Emu64>::PVOID ViewBase; EmulatorTraits<Emu64>::PVOID ViewBase;
EmulatorTraits<Emu64>::PVOID ViewRemoteBase; EmulatorTraits<Emu64>::PVOID ViewRemoteBase;
} PORT_VIEW64, *PPORT_VIEW64; } PORT_VIEW64, *PPORT_VIEW64;
typedef struct _REMOTE_PORT_VIEW64 typedef struct _REMOTE_PORT_VIEW64
{ {
ULONG Length; ULONG Length;
EMULATOR_CAST(std::int64_t, SIZE_T) ViewSize; EMULATOR_CAST(std::int64_t, SIZE_T) ViewSize;
EmulatorTraits<Emu64>::PVOID ViewBase; EmulatorTraits<Emu64>::PVOID ViewBase;
} REMOTE_PORT_VIEW64, *PREMOTE_PORT_VIEW64; } REMOTE_PORT_VIEW64, *PREMOTE_PORT_VIEW64;

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +1,21 @@
#pragma once #pragma once
#define PAGE_EXECUTE 0x10 #define PAGE_EXECUTE 0x10
#define PAGE_EXECUTE_READ 0x20 #define PAGE_EXECUTE_READ 0x20
#define PAGE_EXECUTE_READWRITE 0x40 #define PAGE_EXECUTE_READWRITE 0x40
#define PAGE_EXECUTE_WRITECOPY 0x80 #define PAGE_EXECUTE_WRITECOPY 0x80
#define PAGE_NOACCESS 0x01 #define PAGE_NOACCESS 0x01
#define PAGE_READONLY 0x02 #define PAGE_READONLY 0x02
#define PAGE_READWRITE 0x04 #define PAGE_READWRITE 0x04
#define PAGE_WRITECOPY 0x08 #define PAGE_WRITECOPY 0x08
#define PAGE_TARGETS_INVALID 0x40000000 #define PAGE_TARGETS_INVALID 0x40000000
#define PAGE_TARGETS_NO_UPDATE 0x40000000 #define PAGE_TARGETS_NO_UPDATE 0x40000000
#define PAGE_GUARD 0x100 #define PAGE_GUARD 0x100
#define PAGE_NOCACHE 0x200 #define PAGE_NOCACHE 0x200
#define PAGE_WRITECOMBINE 0x400 #define PAGE_WRITECOMBINE 0x400
#define MEM_COMMIT 0x00001000 #define MEM_COMMIT 0x00001000
#define MEM_RESERVE 0x00002000 #define MEM_RESERVE 0x00002000
@@ -38,94 +38,92 @@
typedef enum _MEMORY_INFORMATION_CLASS typedef enum _MEMORY_INFORMATION_CLASS
{ {
MemoryBasicInformation, // q: MEMORY_BASIC_INFORMATION MemoryBasicInformation, // q: MEMORY_BASIC_INFORMATION
MemoryWorkingSetInformation, // q: MEMORY_WORKING_SET_INFORMATION MemoryWorkingSetInformation, // q: MEMORY_WORKING_SET_INFORMATION
MemoryMappedFilenameInformation, // q: UNICODE_STRING MemoryMappedFilenameInformation, // q: UNICODE_STRING
MemoryRegionInformation, // q: MEMORY_REGION_INFORMATION MemoryRegionInformation, // q: MEMORY_REGION_INFORMATION
MemoryWorkingSetExInformation, // q: MEMORY_WORKING_SET_EX_INFORMATION // since VISTA MemoryWorkingSetExInformation, // q: MEMORY_WORKING_SET_EX_INFORMATION // since VISTA
MemorySharedCommitInformation, // q: MEMORY_SHARED_COMMIT_INFORMATION // since WIN8 MemorySharedCommitInformation, // q: MEMORY_SHARED_COMMIT_INFORMATION // since WIN8
MemoryImageInformation, // q: MEMORY_IMAGE_INFORMATION MemoryImageInformation, // q: MEMORY_IMAGE_INFORMATION
MemoryRegionInformationEx, // MEMORY_REGION_INFORMATION MemoryRegionInformationEx, // MEMORY_REGION_INFORMATION
MemoryPrivilegedBasicInformation, // MEMORY_BASIC_INFORMATION MemoryPrivilegedBasicInformation, // MEMORY_BASIC_INFORMATION
MemoryEnclaveImageInformation, // MEMORY_ENCLAVE_IMAGE_INFORMATION // since REDSTONE3 MemoryEnclaveImageInformation, // MEMORY_ENCLAVE_IMAGE_INFORMATION // since REDSTONE3
MemoryBasicInformationCapped, // 10 MemoryBasicInformationCapped, // 10
MemoryPhysicalContiguityInformation, // MEMORY_PHYSICAL_CONTIGUITY_INFORMATION // since 20H1 MemoryPhysicalContiguityInformation, // MEMORY_PHYSICAL_CONTIGUITY_INFORMATION // since 20H1
MemoryBadInformation, // since WIN11 MemoryBadInformation, // since WIN11
MemoryBadInformationAllProcesses, // since 22H1 MemoryBadInformationAllProcesses, // since 22H1
MemoryImageExtensionInformation, // since 24H2 MemoryImageExtensionInformation, // since 24H2
MaxMemoryInfoClass MaxMemoryInfoClass
} MEMORY_INFORMATION_CLASS; } MEMORY_INFORMATION_CLASS;
typedef enum _SECTION_INHERIT typedef enum _SECTION_INHERIT
{ {
ViewShare = 1, ViewShare = 1,
ViewUnmap = 2 ViewUnmap = 2
} SECTION_INHERIT; } SECTION_INHERIT;
typedef struct DECLSPEC_ALIGN(16) _EMU_MEMORY_BASIC_INFORMATION64 typedef struct DECLSPEC_ALIGN(16) _EMU_MEMORY_BASIC_INFORMATION64
{ {
void* BaseAddress; void* BaseAddress;
void* AllocationBase; void* AllocationBase;
DWORD AllocationProtect; DWORD AllocationProtect;
WORD PartitionId; WORD PartitionId;
std::int64_t RegionSize; std::int64_t RegionSize;
DWORD State; DWORD State;
DWORD Protect; DWORD Protect;
DWORD Type; DWORD Type;
} EMU_MEMORY_BASIC_INFORMATION64, *PEMU_MEMORY_BASIC_INFORMATION64; } EMU_MEMORY_BASIC_INFORMATION64, *PEMU_MEMORY_BASIC_INFORMATION64;
typedef struct _MEMORY_IMAGE_INFORMATION64 typedef struct _MEMORY_IMAGE_INFORMATION64
{ {
void* ImageBase; void* ImageBase;
std::int64_t SizeOfImage; std::int64_t SizeOfImage;
union union
{ {
ULONG ImageFlags; ULONG ImageFlags;
struct struct
{ {
ULONG ImagePartialMap : 1; ULONG ImagePartialMap : 1;
ULONG ImageNotExecutable : 1; ULONG ImageNotExecutable : 1;
ULONG ImageSigningLevel : 4; // REDSTONE3 ULONG ImageSigningLevel : 4; // REDSTONE3
ULONG ImageExtensionPresent : 1; // since 24H2 ULONG ImageExtensionPresent : 1; // since 24H2
ULONG Reserved : 25; ULONG Reserved : 25;
}; };
}; };
} MEMORY_IMAGE_INFORMATION64, *PMEMORY_IMAGE_INFORMATION64; } MEMORY_IMAGE_INFORMATION64, *PMEMORY_IMAGE_INFORMATION64;
typedef struct _MEMORY_REGION_INFORMATION typedef struct _MEMORY_REGION_INFORMATION
{ {
void* AllocationBase; void* AllocationBase;
ULONG AllocationProtect; ULONG AllocationProtect;
union union
{ {
ULONG RegionType; ULONG RegionType;
struct struct
{ {
ULONG Private : 1; ULONG Private : 1;
ULONG MappedDataFile : 1; ULONG MappedDataFile : 1;
ULONG MappedImage : 1; ULONG MappedImage : 1;
ULONG MappedPageFile : 1; ULONG MappedPageFile : 1;
ULONG MappedPhysical : 1; ULONG MappedPhysical : 1;
ULONG DirectMapped : 1; ULONG DirectMapped : 1;
ULONG SoftwareEnclave : 1; // REDSTONE3 ULONG SoftwareEnclave : 1; // REDSTONE3
ULONG PageSize64K : 1; ULONG PageSize64K : 1;
ULONG PlaceholderReservation : 1; // REDSTONE4 ULONG PlaceholderReservation : 1; // REDSTONE4
ULONG MappedAwe : 1; // 21H1 ULONG MappedAwe : 1; // 21H1
ULONG MappedWriteWatch : 1; ULONG MappedWriteWatch : 1;
ULONG PageSizeLarge : 1; ULONG PageSizeLarge : 1;
ULONG PageSizeHuge : 1; ULONG PageSizeHuge : 1;
ULONG Reserved : 19; ULONG Reserved : 19;
}; };
}; };
std::int64_t RegionSize; std::int64_t RegionSize;
std::int64_t CommitSize; std::int64_t CommitSize;
DWORD64 PartitionId; // 19H1 DWORD64 PartitionId; // 19H1
DWORD64 NodePreference; // 20H1 DWORD64 NodePreference; // 20H1
} MEMORY_REGION_INFORMATION64, *PMEMORY_REGION_INFORMATION64; } MEMORY_REGION_INFORMATION64, *PMEMORY_REGION_INFORMATION64;

View File

@@ -3,6 +3,6 @@
template <typename Traits> template <typename Traits>
struct EMU_WSABUF struct EMU_WSABUF
{ {
ULONG len; ULONG len;
EMULATOR_CAST(typename Traits::PVOID, CHAR*) buf; EMULATOR_CAST(typename Traits::PVOID, CHAR*) buf;
}; };

View File

@@ -2,8 +2,8 @@
#ifdef _WIN32 #ifdef _WIN32
#pragma warning(push) #pragma warning(push)
#pragma warning(disable: 4201) // nameless struct/union #pragma warning(disable : 4201) // nameless struct/union
#pragma warning(disable: 4702) // unreachable code #pragma warning(disable : 4702) // unreachable code
#else #else
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic" #pragma GCC diagnostic ignored "-Wpedantic"
@@ -27,18 +27,12 @@
#ifdef OS_WINDOWS #ifdef OS_WINDOWS
#pragma comment(lib, "ntdll") #pragma comment(lib, "ntdll")
extern "C" { extern "C"
NTSYSCALLAPI {
NTSTATUS NTSYSCALLAPI NTSTATUS NTAPI NtQuerySystemInformationEx(
NTAPI _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _In_reads_bytes_(InputBufferLength) PVOID InputBuffer,
NtQuerySystemInformationEx( _In_ ULONG InputBufferLength, _Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation,
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength);
_In_reads_bytes_(InputBufferLength) PVOID InputBuffer,
_In_ ULONG InputBufferLength,
_Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
);
} }
#pragma warning(pop) #pragma warning(pop)
#else #else

View File

@@ -20,31 +20,30 @@ using LONGLONG = std::int64_t;
typedef union _ULARGE_INTEGER typedef union _ULARGE_INTEGER
{ {
struct struct
{ {
DWORD LowPart; DWORD LowPart;
DWORD HighPart; DWORD HighPart;
}; };
ULONGLONG QuadPart; ULONGLONG QuadPart;
} ULARGE_INTEGER; } ULARGE_INTEGER;
typedef union _LARGE_INTEGER typedef union _LARGE_INTEGER
{ {
struct struct
{ {
DWORD LowPart; DWORD LowPart;
LONG HighPart; LONG HighPart;
}; };
LONGLONG QuadPart; LONGLONG QuadPart;
} LARGE_INTEGER; } LARGE_INTEGER;
using BYTE = std::uint8_t; using BYTE = std::uint8_t;
#define CHAR BYTE #define CHAR BYTE
#endif #endif
using WORD = std::uint16_t; using WORD = std::uint16_t;
#define UCHAR unsigned char #define UCHAR unsigned char
@@ -53,7 +52,6 @@ using WORD = std::uint16_t;
using CSHORT = short; using CSHORT = short;
using USHORT = WORD; using USHORT = WORD;
#define DUMMYSTRUCTNAME #define DUMMYSTRUCTNAME
#ifndef TRUE #ifndef TRUE

File diff suppressed because it is too large Load Diff

View File

@@ -2,63 +2,63 @@
typedef enum _KEY_INFORMATION_CLASS typedef enum _KEY_INFORMATION_CLASS
{ {
KeyBasicInformation, // KEY_BASIC_INFORMATION KeyBasicInformation, // KEY_BASIC_INFORMATION
KeyNodeInformation, // KEY_NODE_INFORMATION KeyNodeInformation, // KEY_NODE_INFORMATION
KeyFullInformation, // KEY_FULL_INFORMATION KeyFullInformation, // KEY_FULL_INFORMATION
KeyNameInformation, // KEY_NAME_INFORMATION KeyNameInformation, // KEY_NAME_INFORMATION
KeyCachedInformation, // KEY_CACHED_INFORMATION KeyCachedInformation, // KEY_CACHED_INFORMATION
KeyFlagsInformation, // KEY_FLAGS_INFORMATION KeyFlagsInformation, // KEY_FLAGS_INFORMATION
KeyVirtualizationInformation, // KEY_VIRTUALIZATION_INFORMATION KeyVirtualizationInformation, // KEY_VIRTUALIZATION_INFORMATION
KeyHandleTagsInformation, // KEY_HANDLE_TAGS_INFORMATION KeyHandleTagsInformation, // KEY_HANDLE_TAGS_INFORMATION
KeyTrustInformation, // KEY_TRUST_INFORMATION KeyTrustInformation, // KEY_TRUST_INFORMATION
KeyLayerInformation, // KEY_LAYER_INFORMATION KeyLayerInformation, // KEY_LAYER_INFORMATION
MaxKeyInfoClass MaxKeyInfoClass
} KEY_INFORMATION_CLASS; } KEY_INFORMATION_CLASS;
typedef enum _KEY_VALUE_INFORMATION_CLASS typedef enum _KEY_VALUE_INFORMATION_CLASS
{ {
KeyValueBasicInformation, // KEY_VALUE_BASIC_INFORMATION KeyValueBasicInformation, // KEY_VALUE_BASIC_INFORMATION
KeyValueFullInformation, // KEY_VALUE_FULL_INFORMATION KeyValueFullInformation, // KEY_VALUE_FULL_INFORMATION
KeyValuePartialInformation, // KEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInformation, // KEY_VALUE_PARTIAL_INFORMATION
KeyValueFullInformationAlign64, KeyValueFullInformationAlign64,
KeyValuePartialInformationAlign64, // KEY_VALUE_PARTIAL_INFORMATION_ALIGN64 KeyValuePartialInformationAlign64, // KEY_VALUE_PARTIAL_INFORMATION_ALIGN64
KeyValueLayerInformation, // KEY_VALUE_LAYER_INFORMATION KeyValueLayerInformation, // KEY_VALUE_LAYER_INFORMATION
MaxKeyValueInfoClass MaxKeyValueInfoClass
} KEY_VALUE_INFORMATION_CLASS; } KEY_VALUE_INFORMATION_CLASS;
struct KEY_NAME_INFORMATION struct KEY_NAME_INFORMATION
{ {
std::uint32_t NameLength; std::uint32_t NameLength;
char16_t Name[1]; char16_t Name[1];
}; };
struct KEY_HANDLE_TAGS_INFORMATION struct KEY_HANDLE_TAGS_INFORMATION
{ {
ULONG HandleTags; ULONG HandleTags;
}; };
struct KEY_VALUE_BASIC_INFORMATION struct KEY_VALUE_BASIC_INFORMATION
{ {
ULONG TitleIndex; ULONG TitleIndex;
ULONG Type; ULONG Type;
ULONG NameLength; ULONG NameLength;
char16_t Name[1]; char16_t Name[1];
}; };
struct KEY_VALUE_PARTIAL_INFORMATION struct KEY_VALUE_PARTIAL_INFORMATION
{ {
ULONG TitleIndex; ULONG TitleIndex;
ULONG Type; ULONG Type;
ULONG DataLength; ULONG DataLength;
UCHAR Data[1]; UCHAR Data[1];
}; };
struct KEY_VALUE_FULL_INFORMATION struct KEY_VALUE_FULL_INFORMATION
{ {
ULONG TitleIndex; ULONG TitleIndex;
ULONG Type; ULONG Type;
ULONG DataOffset; ULONG DataOffset;
ULONG DataLength; ULONG DataLength;
ULONG NameLength; ULONG NameLength;
char16_t Name[1]; char16_t Name[1];
}; };

View File

@@ -5,42 +5,41 @@
using NTSTATUS = std::uint32_t; using NTSTATUS = std::uint32_t;
#ifndef OS_WINDOWS #ifndef OS_WINDOWS
#define STATUS_WAIT_0 ((NTSTATUS)0x00000000L) #define STATUS_WAIT_0 ((NTSTATUS)0x00000000L)
#define STATUS_TIMEOUT ((NTSTATUS)0x00000102L) #define STATUS_TIMEOUT ((NTSTATUS)0x00000102L)
#define STATUS_ACCESS_VIOLATION ((NTSTATUS)0xC0000005L) #define STATUS_ACCESS_VIOLATION ((NTSTATUS)0xC0000005L)
#define STATUS_INVALID_HANDLE ((NTSTATUS)0xC0000008L) #define STATUS_INVALID_HANDLE ((NTSTATUS)0xC0000008L)
#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000DL) #define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000DL)
#define STATUS_ILLEGAL_INSTRUCTION ((NTSTATUS)0xC000001DL)
#define STATUS_PENDING ((DWORD)0x00000103L) #define STATUS_PENDING ((NTSTATUS)0x00000103L)
#endif #endif
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#define STATUS_WAIT_1 ((NTSTATUS)0x00000001L) #define STATUS_WAIT_1 ((NTSTATUS)0x00000001L)
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0x00000001L) #define STATUS_UNSUCCESSFUL ((NTSTATUS)0x00000001L)
#define STATUS_ALERTED ((NTSTATUS)0x00000101L) #define STATUS_ALERTED ((NTSTATUS)0x00000101L)
#define STATUS_OBJECT_NAME_EXISTS ((NTSTATUS)0x40000000L) #define STATUS_OBJECT_NAME_EXISTS ((NTSTATUS)0x40000000L)
#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L) #define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L)
#define STATUS_ILLEGAL_INSTRUCTION ((DWORD )0xC000001DL) #define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L) #define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L) #define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L) #define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL)
#define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL) #define STATUS_FILE_INVALID ((NTSTATUS)0xC0000098L)
#define STATUS_FILE_INVALID ((NTSTATUS)0xC0000098L) #define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS)0xC00000A0L)
#define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS)0xC00000A0L) #define STATUS_FILE_IS_A_DIRECTORY ((NTSTATUS)0xC00000BAL)
#define STATUS_FILE_IS_A_DIRECTORY ((NTSTATUS)0xC00000BAL) #define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL)
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL) #define STATUS_INVALID_ADDRESS ((NTSTATUS)0xC0000141L)
#define STATUS_INVALID_ADDRESS ((NTSTATUS)0xC0000141L) #define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L)
#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L) #define STATUS_CONNECTION_REFUSED ((NTSTATUS)0xC0000236L)
#define STATUS_CONNECTION_REFUSED ((NTSTATUS)0xC0000236L) #define STATUS_ADDRESS_ALREADY_ASSOCIATED ((NTSTATUS)0xC0000328L)
#define STATUS_ADDRESS_ALREADY_ASSOCIATED ((NTSTATUS)0xC0000328L)
#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L) #define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)
#define FILE_DEVICE_NETWORK 0x00000012
#define FILE_DEVICE_NETWORK 0x00000012 #define FSCTL_AFD_BASE FILE_DEVICE_NETWORK
#define FSCTL_AFD_BASE FILE_DEVICE_NETWORK

View File

@@ -2,15 +2,15 @@
typedef enum _EVENT_TYPE typedef enum _EVENT_TYPE
{ {
NotificationEvent, NotificationEvent,
SynchronizationEvent SynchronizationEvent
} EVENT_TYPE; } EVENT_TYPE;
typedef enum _WAIT_TYPE typedef enum _WAIT_TYPE
{ {
WaitAll, WaitAll,
WaitAny, WaitAny,
WaitNotification, WaitNotification,
WaitDequeue, WaitDequeue,
WaitDpc, WaitDpc,
} WAIT_TYPE; } WAIT_TYPE;

View File

@@ -2,70 +2,69 @@
typedef enum _THREADINFOCLASS typedef enum _THREADINFOCLASS
{ {
ThreadBasicInformation, // q: THREAD_BASIC_INFORMATION ThreadBasicInformation, // q: THREAD_BASIC_INFORMATION
ThreadTimes, // q: KERNEL_USER_TIMES ThreadTimes, // q: KERNEL_USER_TIMES
ThreadPriority, // s: KPRIORITY (requires SeIncreaseBasePriorityPrivilege) ThreadPriority, // s: KPRIORITY (requires SeIncreaseBasePriorityPrivilege)
ThreadBasePriority, // s: KPRIORITY ThreadBasePriority, // s: KPRIORITY
ThreadAffinityMask, // s: KAFFINITY ThreadAffinityMask, // s: KAFFINITY
ThreadImpersonationToken, // s: HANDLE ThreadImpersonationToken, // s: HANDLE
ThreadDescriptorTableEntry, // q: DESCRIPTOR_TABLE_ENTRY (or WOW64_DESCRIPTOR_TABLE_ENTRY) ThreadDescriptorTableEntry, // q: DESCRIPTOR_TABLE_ENTRY (or WOW64_DESCRIPTOR_TABLE_ENTRY)
ThreadEnableAlignmentFaultFixup, // s: BOOLEAN ThreadEnableAlignmentFaultFixup, // s: BOOLEAN
ThreadEventPair, ThreadEventPair,
ThreadQuerySetWin32StartAddress, // q: ULONG_PTR ThreadQuerySetWin32StartAddress, // q: ULONG_PTR
ThreadZeroTlsCell, // s: ULONG // TlsIndex // 10 ThreadZeroTlsCell, // s: ULONG // TlsIndex // 10
ThreadPerformanceCount, // q: LARGE_INTEGER ThreadPerformanceCount, // q: LARGE_INTEGER
ThreadAmILastThread, // q: ULONG ThreadAmILastThread, // q: ULONG
ThreadIdealProcessor, // s: ULONG ThreadIdealProcessor, // s: ULONG
ThreadPriorityBoost, // qs: ULONG ThreadPriorityBoost, // qs: ULONG
ThreadSetTlsArrayAddress, // s: ULONG_PTR // Obsolete ThreadSetTlsArrayAddress, // s: ULONG_PTR // Obsolete
ThreadIsIoPending, // q: ULONG ThreadIsIoPending, // q: ULONG
ThreadHideFromDebugger, // q: BOOLEAN; s: void ThreadHideFromDebugger, // q: BOOLEAN; s: void
ThreadBreakOnTermination, // qs: ULONG ThreadBreakOnTermination, // qs: ULONG
ThreadSwitchLegacyState, // s: void // NtCurrentThread // NPX/FPU ThreadSwitchLegacyState, // s: void // NtCurrentThread // NPX/FPU
ThreadIsTerminated, // q: ULONG // 20 ThreadIsTerminated, // q: ULONG // 20
ThreadLastSystemCall, // q: THREAD_LAST_SYSCALL_INFORMATION ThreadLastSystemCall, // q: THREAD_LAST_SYSCALL_INFORMATION
ThreadIoPriority, // qs: IO_PRIORITY_HINT (requires SeIncreaseBasePriorityPrivilege) ThreadIoPriority, // qs: IO_PRIORITY_HINT (requires SeIncreaseBasePriorityPrivilege)
ThreadCycleTime, // q: THREAD_CYCLE_TIME_INFORMATION ThreadCycleTime, // q: THREAD_CYCLE_TIME_INFORMATION
ThreadPagePriority, // qs: PAGE_PRIORITY_INFORMATION ThreadPagePriority, // qs: PAGE_PRIORITY_INFORMATION
ThreadActualBasePriority, // s: LONG (requires SeIncreaseBasePriorityPrivilege) ThreadActualBasePriority, // s: LONG (requires SeIncreaseBasePriorityPrivilege)
ThreadTebInformation, // q: THREAD_TEB_INFORMATION (requires THREAD_GET_CONTEXT + THREAD_SET_CONTEXT) ThreadTebInformation, // q: THREAD_TEB_INFORMATION (requires THREAD_GET_CONTEXT + THREAD_SET_CONTEXT)
ThreadCSwitchMon, // Obsolete ThreadCSwitchMon, // Obsolete
ThreadCSwitchPmu, ThreadCSwitchPmu,
ThreadWow64Context, // qs: WOW64_CONTEXT, ARM_NT_CONTEXT since 20H1 ThreadWow64Context, // qs: WOW64_CONTEXT, ARM_NT_CONTEXT since 20H1
ThreadGroupInformation, // qs: GROUP_AFFINITY // 30 ThreadGroupInformation, // qs: GROUP_AFFINITY // 30
ThreadUmsInformation, // q: THREAD_UMS_INFORMATION // Obsolete ThreadUmsInformation, // q: THREAD_UMS_INFORMATION // Obsolete
ThreadCounterProfiling, // q: BOOLEAN; s: THREAD_PROFILING_INFORMATION? ThreadCounterProfiling, // q: BOOLEAN; s: THREAD_PROFILING_INFORMATION?
ThreadIdealProcessorEx, // qs: PROCESSOR_NUMBER; s: previous PROCESSOR_NUMBER on return ThreadIdealProcessorEx, // qs: PROCESSOR_NUMBER; s: previous PROCESSOR_NUMBER on return
ThreadCpuAccountingInformation, // q: BOOLEAN; s: HANDLE (NtOpenSession) // NtCurrentThread // since WIN8 ThreadCpuAccountingInformation, // q: BOOLEAN; s: HANDLE (NtOpenSession) // NtCurrentThread // since WIN8
ThreadSuspendCount, // q: ULONG // since WINBLUE ThreadSuspendCount, // q: ULONG // since WINBLUE
ThreadHeterogeneousCpuPolicy, // q: KHETERO_CPU_POLICY // since THRESHOLD ThreadHeterogeneousCpuPolicy, // q: KHETERO_CPU_POLICY // since THRESHOLD
ThreadContainerId, // q: GUID ThreadContainerId, // q: GUID
ThreadNameInformation, // qs: THREAD_NAME_INFORMATION ThreadNameInformation, // qs: THREAD_NAME_INFORMATION
ThreadSelectedCpuSets, ThreadSelectedCpuSets,
ThreadSystemThreadInformation, // q: SYSTEM_THREAD_INFORMATION // 40 ThreadSystemThreadInformation, // q: SYSTEM_THREAD_INFORMATION // 40
ThreadActualGroupAffinity, // q: GROUP_AFFINITY // since THRESHOLD2 ThreadActualGroupAffinity, // q: GROUP_AFFINITY // since THRESHOLD2
ThreadDynamicCodePolicyInfo, // q: ULONG; s: ULONG (NtCurrentThread) ThreadDynamicCodePolicyInfo, // q: ULONG; s: ULONG (NtCurrentThread)
ThreadExplicitCaseSensitivity, // qs: ULONG; s: 0 disables, otherwise enables ThreadExplicitCaseSensitivity, // qs: ULONG; s: 0 disables, otherwise enables
ThreadWorkOnBehalfTicket, // RTL_WORK_ON_BEHALF_TICKET_EX ThreadWorkOnBehalfTicket, // RTL_WORK_ON_BEHALF_TICKET_EX
ThreadSubsystemInformation, // q: SUBSYSTEM_INFORMATION_TYPE // since REDSTONE2 ThreadSubsystemInformation, // q: SUBSYSTEM_INFORMATION_TYPE // since REDSTONE2
ThreadDbgkWerReportActive, // s: ULONG; s: 0 disables, otherwise enables ThreadDbgkWerReportActive, // s: ULONG; s: 0 disables, otherwise enables
ThreadAttachContainer, // s: HANDLE (job object) // NtCurrentThread ThreadAttachContainer, // s: HANDLE (job object) // NtCurrentThread
ThreadManageWritesToExecutableMemory, // MANAGE_WRITES_TO_EXECUTABLE_MEMORY // since REDSTONE3 ThreadManageWritesToExecutableMemory, // MANAGE_WRITES_TO_EXECUTABLE_MEMORY // since REDSTONE3
ThreadPowerThrottlingState, // POWER_THROTTLING_THREAD_STATE // since REDSTONE3 (set), WIN11 22H2 (query) ThreadPowerThrottlingState, // POWER_THROTTLING_THREAD_STATE // since REDSTONE3 (set), WIN11 22H2 (query)
ThreadWorkloadClass, // THREAD_WORKLOAD_CLASS // since REDSTONE5 // 50 ThreadWorkloadClass, // THREAD_WORKLOAD_CLASS // since REDSTONE5 // 50
ThreadCreateStateChange, // since WIN11 ThreadCreateStateChange, // since WIN11
ThreadApplyStateChange, ThreadApplyStateChange,
ThreadStrongerBadHandleChecks, // since 22H1 ThreadStrongerBadHandleChecks, // since 22H1
ThreadEffectiveIoPriority, // q: IO_PRIORITY_HINT ThreadEffectiveIoPriority, // q: IO_PRIORITY_HINT
ThreadEffectivePagePriority, // q: ULONG ThreadEffectivePagePriority, // q: ULONG
ThreadUpdateLockOwnership, // since 24H2 ThreadUpdateLockOwnership, // since 24H2
ThreadSchedulerSharedDataSlot, // SCHEDULER_SHARED_DATA_SLOT_INFORMATION ThreadSchedulerSharedDataSlot, // SCHEDULER_SHARED_DATA_SLOT_INFORMATION
ThreadTebInformationAtomic, // THREAD_TEB_INFORMATION ThreadTebInformationAtomic, // THREAD_TEB_INFORMATION
ThreadIndexInformation, // THREAD_INDEX_INFORMATION ThreadIndexInformation, // THREAD_INDEX_INFORMATION
MaxThreadInfoClass MaxThreadInfoClass
} THREADINFOCLASS; } THREADINFOCLASS;
template <typename Traits> template <typename Traits>
struct THREAD_NAME_INFORMATION struct THREAD_NAME_INFORMATION
{ {
@@ -80,4 +79,4 @@ typedef struct _THREAD_BASIC_INFORMATION64
EMULATOR_CAST(std::uint64_t, KAFFINITY) AffinityMask; EMULATOR_CAST(std::uint64_t, KAFFINITY) AffinityMask;
EMULATOR_CAST(std::uint32_t, KPRIORITY) Priority; EMULATOR_CAST(std::uint32_t, KPRIORITY) Priority;
EMULATOR_CAST(std::uint32_t, KPRIORITY) BasePriority; EMULATOR_CAST(std::uint32_t, KPRIORITY) BasePriority;
} THREAD_BASIC_INFORMATION64, *PTHREAD_BASIC_INFORMATION64; } THREAD_BASIC_INFORMATION64, *PTHREAD_BASIC_INFORMATION64;

View File

@@ -19,19 +19,19 @@ struct EmulatorTraits;
template <> template <>
struct EmulatorTraits<Emu32> struct EmulatorTraits<Emu32>
{ {
using PVOID = std::uint32_t; using PVOID = std::uint32_t;
using ULONG_PTR = std::uint32_t; using ULONG_PTR = std::uint32_t;
using SIZE_T = std::uint32_t; using SIZE_T = std::uint32_t;
using UNICODE = char16_t; using UNICODE = char16_t;
using HANDLE = std::uint32_t; using HANDLE = std::uint32_t;
}; };
template <> template <>
struct EmulatorTraits<Emu64> struct EmulatorTraits<Emu64>
{ {
using PVOID = std::uint64_t; using PVOID = std::uint64_t;
using ULONG_PTR = std::uint64_t; using ULONG_PTR = std::uint64_t;
using SIZE_T = std::uint64_t; using SIZE_T = std::uint64_t;
using UNICODE = char16_t; using UNICODE = char16_t;
using HANDLE = std::uint64_t; using HANDLE = std::uint64_t;
}; };

View File

@@ -5,76 +5,76 @@
template <typename Traits> template <typename Traits>
struct UNICODE_STRING struct UNICODE_STRING
{ {
USHORT Length; USHORT Length;
USHORT MaximumLength; USHORT MaximumLength;
EMULATOR_CAST(typename Traits::PVOID, char16_t*) Buffer; EMULATOR_CAST(typename Traits::PVOID, char16_t*) Buffer;
}; };
inline std::string u16_to_u8(const std::u16string_view u16_view) inline std::string u16_to_u8(const std::u16string_view u16_view)
{ {
std::string utf8_str; std::string utf8_str;
utf8_str.reserve(u16_view.size() * 2); utf8_str.reserve(u16_view.size() * 2);
for (const char16_t ch : u16_view) for (const char16_t ch : u16_view)
{ {
if (ch <= 0x7F) if (ch <= 0x7F)
{ {
utf8_str.push_back(static_cast<char>(ch)); utf8_str.push_back(static_cast<char>(ch));
} }
else if (ch <= 0x7FF) else if (ch <= 0x7FF)
{ {
utf8_str.push_back(static_cast<char>(0xC0 | (ch >> 6))); utf8_str.push_back(static_cast<char>(0xC0 | (ch >> 6)));
utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F))); utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
} }
else else
{ {
utf8_str.push_back(static_cast<char>(0xE0 | (ch >> 12))); utf8_str.push_back(static_cast<char>(0xE0 | (ch >> 12)));
utf8_str.push_back(static_cast<char>(0x80 | ((ch >> 6) & 0x3F))); utf8_str.push_back(static_cast<char>(0x80 | ((ch >> 6) & 0x3F)));
utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F))); utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
} }
} }
return utf8_str; return utf8_str;
} }
inline std::string w_to_u8(const std::wstring_view w_view) inline std::string w_to_u8(const std::wstring_view w_view)
{ {
std::string utf8_str; std::string utf8_str;
utf8_str.reserve(w_view.size() * 2); utf8_str.reserve(w_view.size() * 2);
for (const wchar_t w_ch : w_view) for (const wchar_t w_ch : w_view)
{ {
const auto ch = static_cast<char16_t>(w_ch); const auto ch = static_cast<char16_t>(w_ch);
if (ch <= 0x7F) if (ch <= 0x7F)
{ {
utf8_str.push_back(static_cast<char>(ch)); utf8_str.push_back(static_cast<char>(ch));
} }
else if (ch <= 0x7FF) else if (ch <= 0x7FF)
{ {
utf8_str.push_back(static_cast<char>(0xC0 | (ch >> 6))); utf8_str.push_back(static_cast<char>(0xC0 | (ch >> 6)));
utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F))); utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
} }
else else
{ {
utf8_str.push_back(static_cast<char>(0xE0 | (ch >> 12))); utf8_str.push_back(static_cast<char>(0xE0 | (ch >> 12)));
utf8_str.push_back(static_cast<char>(0x80 | ((ch >> 6) & 0x3F))); utf8_str.push_back(static_cast<char>(0x80 | ((ch >> 6) & 0x3F)));
utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F))); utf8_str.push_back(static_cast<char>(0x80 | (ch & 0x3F)));
} }
} }
return utf8_str; return utf8_str;
} }
#ifndef OS_WINDOWS #ifndef OS_WINDOWS
inline int open_unicode(FILE** handle, const std::u16string& fileName, const std::u16string& mode) inline int open_unicode(FILE** handle, const std::u16string& fileName, const std::u16string& mode)
{ {
*handle = fopen(u16_to_u8(fileName).c_str(), u16_to_u8(mode).c_str()); *handle = fopen(u16_to_u8(fileName).c_str(), u16_to_u8(mode).c_str());
return errno; return errno;
} }
#else #else
inline std::wstring u16_to_w(const std::u16string& u16str) inline std::wstring u16_to_w(const std::u16string& u16str)
{ {
return std::wstring(reinterpret_cast<const wchar_t*>(u16str.data()), u16str.size()); return std::wstring(reinterpret_cast<const wchar_t*>(u16str.data()), u16str.size());
} }
inline auto open_unicode(FILE** handle, const std::u16string& fileName, const std::u16string& mode) inline auto open_unicode(FILE** handle, const std::u16string& fileName, const std::u16string& mode)
{ {
return _wfopen_s(handle, u16_to_w(fileName).c_str(), u16_to_w(mode).c_str()); return _wfopen_s(handle, u16_to_w(fileName).c_str(), u16_to_w(mode).c_str());
} }
#endif #endif

View File

@@ -2,96 +2,95 @@
#include <cstdint> #include <cstdint>
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory #define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory #define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory #define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory #define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
// IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage) // IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory #define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table #define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 // Section contains extended relocations. #define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 // Section contains extended relocations.
#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 // Section can be discarded. #define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 // Section can be discarded.
#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 // Section is not cachable. #define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 // Section is not cachable.
#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 // Section is not pageable. #define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 // Section is not pageable.
#define IMAGE_SCN_MEM_SHARED 0x10000000 // Section is shareable. #define IMAGE_SCN_MEM_SHARED 0x10000000 // Section is shareable.
#define IMAGE_SCN_MEM_EXECUTE 0x20000000 // Section is executable. #define IMAGE_SCN_MEM_EXECUTE 0x20000000 // Section is executable.
#define IMAGE_SCN_MEM_READ 0x40000000 // Section is readable. #define IMAGE_SCN_MEM_READ 0x40000000 // Section is readable.
#define IMAGE_SCN_MEM_WRITE 0x80000000 // Section is writeable. #define IMAGE_SCN_MEM_WRITE 0x80000000 // Section is writeable.
#define IMAGE_SCN_CNT_CODE 0x00000020 // Section contains code. #define IMAGE_SCN_CNT_CODE 0x00000020 // Section contains code.
#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 // Section contains initialized data. #define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 // Section contains initialized data.
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 // Section contains uninitialized data. #define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 // Section contains uninitialized data.
#define IMAGE_REL_BASED_ABSOLUTE 0 #define IMAGE_REL_BASED_ABSOLUTE 0
#define IMAGE_REL_BASED_HIGH 1 #define IMAGE_REL_BASED_HIGH 1
#define IMAGE_REL_BASED_LOW 2 #define IMAGE_REL_BASED_LOW 2
#define IMAGE_REL_BASED_HIGHLOW 3 #define IMAGE_REL_BASED_HIGHLOW 3
#define IMAGE_REL_BASED_HIGHADJ 4 #define IMAGE_REL_BASED_HIGHADJ 4
#define IMAGE_REL_BASED_MIPS_JMPADDR 5 #define IMAGE_REL_BASED_MIPS_JMPADDR 5
#define IMAGE_REL_BASED_ARM_MOV32A 5 #define IMAGE_REL_BASED_ARM_MOV32A 5
#define IMAGE_REL_BASED_ARM_MOV32 5 #define IMAGE_REL_BASED_ARM_MOV32 5
#define IMAGE_REL_BASED_SECTION 6 #define IMAGE_REL_BASED_SECTION 6
#define IMAGE_REL_BASED_REL 7 #define IMAGE_REL_BASED_REL 7
#define IMAGE_REL_BASED_ARM_MOV32T 7 #define IMAGE_REL_BASED_ARM_MOV32T 7
#define IMAGE_REL_BASED_THUMB_MOV32 7 #define IMAGE_REL_BASED_THUMB_MOV32 7
#define IMAGE_REL_BASED_MIPS_JMPADDR16 9 #define IMAGE_REL_BASED_MIPS_JMPADDR16 9
#define IMAGE_REL_BASED_IA64_IMM64 9 #define IMAGE_REL_BASED_IA64_IMM64 9
#define IMAGE_REL_BASED_DIR64 10 #define IMAGE_REL_BASED_DIR64 10
#define IMAGE_REL_BASED_HIGH3ADJ 11 #define IMAGE_REL_BASED_HIGH3ADJ 11
#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040 #define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040
#define IMAGE_FILE_DLL 0x2000 #define IMAGE_FILE_DLL 0x2000
#define IMAGE_FILE_MACHINE_I386 0x014c #define IMAGE_FILE_MACHINE_I386 0x014c
#define IMAGE_FILE_MACHINE_AMD64 0x8664 #define IMAGE_FILE_MACHINE_AMD64 0x8664
#define PROCESSOR_ARCHITECTURE_AMD64 9 #define PROCESSOR_ARCHITECTURE_AMD64 9
enum class PEMachineType : std::uint16_t enum class PEMachineType : std::uint16_t
{ {
UNKNOWN = 0, UNKNOWN = 0,
I386 = 0x014c, // Intel 386. I386 = 0x014c, // Intel 386.
R3000 = 0x0162, // MIPS little-endian, 0x160 big-endian R3000 = 0x0162, // MIPS little-endian, 0x160 big-endian
R4000 = 0x0166, // MIPS little-endian R4000 = 0x0166, // MIPS little-endian
R10000 = 0x0168, // MIPS little-endian R10000 = 0x0168, // MIPS little-endian
WCEMIPSV2 = 0x0169, // MIPS little-endian WCE v2 WCEMIPSV2 = 0x0169, // MIPS little-endian WCE v2
ALPHA = 0x0184, // Alpha_AXP ALPHA = 0x0184, // Alpha_AXP
SH3 = 0x01a2, // SH3 little-endian SH3 = 0x01a2, // SH3 little-endian
SH3DSP = 0x01a3, SH3DSP = 0x01a3,
SH3E = 0x01a4, // SH3E little-endian SH3E = 0x01a4, // SH3E little-endian
SH4 = 0x01a6, // SH4 little-endian SH4 = 0x01a6, // SH4 little-endian
SH5 = 0x01a8, // SH5 SH5 = 0x01a8, // SH5
ARM = 0x01c0, // ARM Little-Endian ARM = 0x01c0, // ARM Little-Endian
THUMB = 0x01c2, // ARM Thumb/Thumb-2 Little-Endian THUMB = 0x01c2, // ARM Thumb/Thumb-2 Little-Endian
ARMNT = 0x01c4, // ARM Thumb-2 Little-Endian ARMNT = 0x01c4, // ARM Thumb-2 Little-Endian
AM33 = 0x01d3, AM33 = 0x01d3,
POWERPC = 0x01F0, // IBM PowerPC Little-Endian POWERPC = 0x01F0, // IBM PowerPC Little-Endian
POWERPCFP = 0x01f1, POWERPCFP = 0x01f1,
IA64 = 0x0200, // Intel 64 IA64 = 0x0200, // Intel 64
MIPS16 = 0x0266, // MIPS MIPS16 = 0x0266, // MIPS
ALPHA64 = 0x0284, // ALPHA64 ALPHA64 = 0x0284, // ALPHA64
MIPSFPU = 0x0366, // MIPS MIPSFPU = 0x0366, // MIPS
MIPSFPU16 = 0x0466, // MIPS MIPSFPU16 = 0x0466, // MIPS
AXP64 = ALPHA64, AXP64 = ALPHA64,
TRICORE = 0x0520, // Infineon TRICORE = 0x0520, // Infineon
CEF = 0x0CEF, CEF = 0x0CEF,
EBC = 0x0EBC, // EFI Byte Code EBC = 0x0EBC, // EFI Byte Code
AMD64 = 0x8664, // AMD64 (K8) AMD64 = 0x8664, // AMD64 (K8)
M32R = 0x9041, // M32R little-endian M32R = 0x9041, // M32R little-endian
CEE = 0xC0EE, CEE = 0xC0EE,
}; };
#pragma pack(push, 4) #pragma pack(push, 4)
template <typename T> template <typename T>
@@ -102,65 +101,64 @@ struct PEOptionalHeaderBasePart2_t
template <> template <>
struct PEOptionalHeaderBasePart2_t<std::uint32_t> struct PEOptionalHeaderBasePart2_t<std::uint32_t>
{ {
std::uint32_t BaseOfData; std::uint32_t BaseOfData;
std::uint32_t ImageBase; std::uint32_t ImageBase;
}; };
template <> template <>
struct PEOptionalHeaderBasePart2_t<std::uint64_t> struct PEOptionalHeaderBasePart2_t<std::uint64_t>
{ {
std::uint64_t ImageBase; std::uint64_t ImageBase;
}; };
template <typename T> template <typename T>
struct PEOptionalHeaderBasePart1_t struct PEOptionalHeaderBasePart1_t
{ {
enum enum
{ {
k_NumberOfDataDirectors = 16 k_NumberOfDataDirectors = 16
}; };
uint16_t Magic; uint16_t Magic;
uint8_t MajorLinkerVersion; uint8_t MajorLinkerVersion;
uint8_t MinorLinkerVersion; uint8_t MinorLinkerVersion;
uint32_t SizeOfCode; uint32_t SizeOfCode;
uint32_t SizeOfInitializedData; uint32_t SizeOfInitializedData;
uint32_t SizeOfUninitializedData; uint32_t SizeOfUninitializedData;
uint32_t AddressOfEntryPoint; uint32_t AddressOfEntryPoint;
uint32_t BaseOfCode; uint32_t BaseOfCode;
}; };
struct PEDirectory_t2 struct PEDirectory_t2
{ {
std::uint32_t VirtualAddress; std::uint32_t VirtualAddress;
std::uint32_t Size; std::uint32_t Size;
}; };
template <typename T> template <typename T>
struct PEOptionalHeaderBasePart3_t : PEOptionalHeaderBasePart1_t<T>, PEOptionalHeaderBasePart2_t<T> struct PEOptionalHeaderBasePart3_t : PEOptionalHeaderBasePart1_t<T>, PEOptionalHeaderBasePart2_t<T>
{ {
uint32_t SectionAlignment; uint32_t SectionAlignment;
uint32_t FileAlignment; uint32_t FileAlignment;
uint16_t MajorOperatingSystemVersion; uint16_t MajorOperatingSystemVersion;
uint16_t MinorOperatingSystemVersion; uint16_t MinorOperatingSystemVersion;
uint16_t MajorImageVersion; uint16_t MajorImageVersion;
uint16_t MinorImageVersion; uint16_t MinorImageVersion;
uint16_t MajorSubsystemVersion; uint16_t MajorSubsystemVersion;
uint16_t MinorSubsystemVersion; uint16_t MinorSubsystemVersion;
uint32_t Win32VersionValue; uint32_t Win32VersionValue;
uint32_t SizeOfImage; uint32_t SizeOfImage;
uint32_t SizeOfHeaders; uint32_t SizeOfHeaders;
uint32_t CheckSum; uint32_t CheckSum;
uint16_t Subsystem; uint16_t Subsystem;
uint16_t DllCharacteristics; uint16_t DllCharacteristics;
T SizeOfStackReserve; T SizeOfStackReserve;
T SizeOfStackCommit; T SizeOfStackCommit;
T SizeOfHeapReserve; T SizeOfHeapReserve;
T SizeOfHeapCommit; T SizeOfHeapCommit;
uint32_t LoaderFlags; uint32_t LoaderFlags;
uint32_t NumberOfRvaAndSizes; uint32_t NumberOfRvaAndSizes;
PEDirectory_t2 DataDirectory[PEOptionalHeaderBasePart1_t<T>::k_NumberOfDataDirectors]; PEDirectory_t2 DataDirectory[PEOptionalHeaderBasePart1_t<T>::k_NumberOfDataDirectors];
}; };
template <typename T> template <typename T>
@@ -171,113 +169,116 @@ struct PEOptionalHeader_t
template <> template <>
struct PEOptionalHeader_t<std::uint32_t> : PEOptionalHeaderBasePart3_t<std::uint32_t> struct PEOptionalHeader_t<std::uint32_t> : PEOptionalHeaderBasePart3_t<std::uint32_t>
{ {
enum enum
{ {
k_Magic = 0x10b, // IMAGE_NT_OPTIONAL_HDR32_MAGIC k_Magic = 0x10b, // IMAGE_NT_OPTIONAL_HDR32_MAGIC
}; };
}; };
template <> template <>
struct PEOptionalHeader_t<std::uint64_t> : PEOptionalHeaderBasePart3_t<std::uint64_t> struct PEOptionalHeader_t<std::uint64_t> : PEOptionalHeaderBasePart3_t<std::uint64_t>
{ {
enum enum
{ {
k_Magic = 0x20b, // IMAGE_NT_OPTIONAL_HDR64_MAGIC k_Magic = 0x20b, // IMAGE_NT_OPTIONAL_HDR64_MAGIC
}; };
}; };
struct PEFileHeader_t struct PEFileHeader_t
{ {
PEMachineType Machine; PEMachineType Machine;
std::uint16_t NumberOfSections; std::uint16_t NumberOfSections;
std::uint32_t TimeDateStamp; std::uint32_t TimeDateStamp;
std::uint32_t PointerToSymbolTable; std::uint32_t PointerToSymbolTable;
std::uint32_t NumberOfSymbols; std::uint32_t NumberOfSymbols;
std::uint16_t SizeOfOptionalHeader; std::uint16_t SizeOfOptionalHeader;
std::uint16_t Characteristics; std::uint16_t Characteristics;
}; };
template <typename T> template <typename T>
struct PENTHeaders_t struct PENTHeaders_t
{ {
enum enum
{ {
k_Signature = 0x00004550, // IMAGE_NT_SIGNATURE k_Signature = 0x00004550, // IMAGE_NT_SIGNATURE
}; };
uint32_t Signature; uint32_t Signature;
PEFileHeader_t FileHeader; PEFileHeader_t FileHeader;
PEOptionalHeader_t<T> OptionalHeader; PEOptionalHeader_t<T> OptionalHeader;
}; };
struct PEDosHeader_t struct PEDosHeader_t
{ {
enum enum
{ {
k_Magic = 0x5A4D k_Magic = 0x5A4D
}; };
std::uint16_t e_magic; // Magic number ( k_Magic ) std::uint16_t e_magic; // Magic number ( k_Magic )
std::uint16_t e_cblp; // Bytes on last page of file std::uint16_t e_cblp; // Bytes on last page of file
std::uint16_t e_cp; // Pages in file std::uint16_t e_cp; // Pages in file
std::uint16_t e_crlc; // Relocations std::uint16_t e_crlc; // Relocations
std::uint16_t e_cparhdr; // Size of header in paragraphs std::uint16_t e_cparhdr; // Size of header in paragraphs
std::uint16_t e_minalloc; // Minimum extra paragraphs needed std::uint16_t e_minalloc; // Minimum extra paragraphs needed
std::uint16_t e_maxalloc; // Maximum extra paragraphs needed std::uint16_t e_maxalloc; // Maximum extra paragraphs needed
std::uint16_t e_ss; // Initial (relative) SS value std::uint16_t e_ss; // Initial (relative) SS value
std::uint16_t e_sp; // Initial SP value std::uint16_t e_sp; // Initial SP value
std::uint16_t e_csum; // Checksum std::uint16_t e_csum; // Checksum
std::uint16_t e_ip; // Initial IP value std::uint16_t e_ip; // Initial IP value
std::uint16_t e_cs; // Initial (relative) CS value std::uint16_t e_cs; // Initial (relative) CS value
std::uint16_t e_lfarlc; // File address of relocation table std::uint16_t e_lfarlc; // File address of relocation table
std::uint16_t e_ovno; // Overlay number std::uint16_t e_ovno; // Overlay number
std::uint16_t e_res[4]; // Reserved words std::uint16_t e_res[4]; // Reserved words
std::uint16_t e_oemid; // OEM identifier (for e_oeminfo) std::uint16_t e_oemid; // OEM identifier (for e_oeminfo)
std::uint16_t e_oeminfo; // OEM information; e_oemid specific std::uint16_t e_oeminfo; // OEM information; e_oemid specific
std::uint16_t e_res2[10]; // Reserved words std::uint16_t e_res2[10]; // Reserved words
std::uint32_t e_lfanew; // File address of new exe header std::uint32_t e_lfanew; // File address of new exe header
}; };
#pragma pack(pop) #pragma pack(pop)
#define IMAGE_SIZEOF_SHORT_NAME 8 #define IMAGE_SIZEOF_SHORT_NAME 8
#ifndef OS_WINDOWS #ifndef OS_WINDOWS
typedef struct _IMAGE_SECTION_HEADER typedef struct _IMAGE_SECTION_HEADER
{ {
std::uint8_t Name[IMAGE_SIZEOF_SHORT_NAME]; std::uint8_t Name[IMAGE_SIZEOF_SHORT_NAME];
union { union
std:: uint32_t PhysicalAddress; {
std::uint32_t VirtualSize; std::uint32_t PhysicalAddress;
std::uint32_t VirtualSize;
} Misc; } Misc;
std::uint32_t VirtualAddress; std::uint32_t VirtualAddress;
std::uint32_t SizeOfRawData; std::uint32_t SizeOfRawData;
std::uint32_t PointerToRawData; std::uint32_t PointerToRawData;
std::uint32_t PointerToRelocations; std::uint32_t PointerToRelocations;
std::uint32_t PointerToLinenumbers; std::uint32_t PointerToLinenumbers;
std::uint16_t NumberOfRelocations; std::uint16_t NumberOfRelocations;
std::uint16_t NumberOfLinenumbers; std::uint16_t NumberOfLinenumbers;
std::uint32_t Characteristics; std::uint32_t Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
typedef struct _IMAGE_EXPORT_DIRECTORY { typedef struct _IMAGE_EXPORT_DIRECTORY
DWORD Characteristics; {
DWORD TimeDateStamp; DWORD Characteristics;
WORD MajorVersion; DWORD TimeDateStamp;
WORD MinorVersion; WORD MajorVersion;
DWORD Name; WORD MinorVersion;
DWORD Base; DWORD Name;
DWORD NumberOfFunctions; DWORD Base;
DWORD NumberOfNames; DWORD NumberOfFunctions;
DWORD AddressOfFunctions; DWORD NumberOfNames;
DWORD AddressOfNames; DWORD AddressOfFunctions;
DWORD AddressOfNameOrdinals; DWORD AddressOfNames;
DWORD AddressOfNameOrdinals;
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY; } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
typedef struct _IMAGE_BASE_RELOCATION { typedef struct _IMAGE_BASE_RELOCATION
DWORD VirtualAddress; {
DWORD SizeOfBlock; DWORD VirtualAddress;
WORD TypeOffset[1]; DWORD SizeOfBlock;
WORD TypeOffset[1];
} IMAGE_BASE_RELOCATION, *PIMAGE_BASE_RELOCATION; } IMAGE_BASE_RELOCATION, *PIMAGE_BASE_RELOCATION;
#endif #endif
@@ -285,56 +286,56 @@ typedef struct _IMAGE_BASE_RELOCATION {
template <typename Traits> template <typename Traits>
struct SECTION_IMAGE_INFORMATION struct SECTION_IMAGE_INFORMATION
{ {
typename Traits::PVOID TransferAddress; typename Traits::PVOID TransferAddress;
ULONG ZeroBits; ULONG ZeroBits;
typename Traits::SIZE_T MaximumStackSize; typename Traits::SIZE_T MaximumStackSize;
typename Traits::SIZE_T CommittedStackSize; typename Traits::SIZE_T CommittedStackSize;
ULONG SubSystemType; ULONG SubSystemType;
union union
{ {
struct struct
{ {
USHORT SubSystemMinorVersion; USHORT SubSystemMinorVersion;
USHORT SubSystemMajorVersion; USHORT SubSystemMajorVersion;
}; };
ULONG SubSystemVersion; ULONG SubSystemVersion;
}; };
union union
{ {
struct struct
{ {
USHORT MajorOperatingSystemVersion; USHORT MajorOperatingSystemVersion;
USHORT MinorOperatingSystemVersion; USHORT MinorOperatingSystemVersion;
}; };
ULONG OperatingSystemVersion; ULONG OperatingSystemVersion;
}; };
USHORT ImageCharacteristics; USHORT ImageCharacteristics;
USHORT DllCharacteristics; USHORT DllCharacteristics;
PEMachineType Machine; PEMachineType Machine;
BOOLEAN ImageContainsCode; BOOLEAN ImageContainsCode;
union union
{ {
UCHAR ImageFlags; UCHAR ImageFlags;
struct struct
{ {
UCHAR ComPlusNativeReady : 1; UCHAR ComPlusNativeReady : 1;
UCHAR ComPlusILOnly : 1; UCHAR ComPlusILOnly : 1;
UCHAR ImageDynamicallyRelocated : 1; UCHAR ImageDynamicallyRelocated : 1;
UCHAR ImageMappedFlat : 1; UCHAR ImageMappedFlat : 1;
UCHAR BaseBelow4gb : 1; UCHAR BaseBelow4gb : 1;
UCHAR ComPlusPrefer32bit : 1; UCHAR ComPlusPrefer32bit : 1;
UCHAR Reserved : 2; UCHAR Reserved : 2;
}; };
}; };
ULONG LoaderFlags; ULONG LoaderFlags;
ULONG ImageFileSize; ULONG ImageFileSize;
ULONG CheckSum; ULONG CheckSum;
}; };

View File

@@ -5,114 +5,114 @@
namespace utils namespace utils
{ {
template <typename T, typename S = const uint8_t> template <typename T, typename S = const uint8_t>
requires(std::is_trivially_copyable_v<T> && std::is_same_v<uint8_t, std::remove_cv_t<S>>) requires(std::is_trivially_copyable_v<T> && std::is_same_v<uint8_t, std::remove_cv_t<S>>)
class safe_object_accessor class safe_object_accessor
{ {
public: public:
safe_object_accessor(const std::span<S> buffer, const size_t offset) safe_object_accessor(const std::span<S> buffer, const size_t offset)
: buffer_(buffer) : buffer_(buffer),
, offset_(offset) offset_(offset)
{ {
} }
/***************************************************************************** /*****************************************************************************
* Object is copied to make sure platform-dependent alignment requirements * Object is copied to make sure platform-dependent alignment requirements
* are respected * are respected
****************************************************************************/ ****************************************************************************/
T get(const size_t element_index = 0) const T get(const size_t element_index = 0) const
{ {
T value{}; T value{};
memcpy(&value, get_valid_pointer(element_index), size); memcpy(&value, get_valid_pointer(element_index), size);
return value; return value;
} }
void set(const T value, const size_t element_index = 0) const void set(const T value, const size_t element_index = 0) const
{ {
memcpy(get_valid_pointer(element_index), &value, size); memcpy(get_valid_pointer(element_index), &value, size);
} }
private: private:
static constexpr auto size = sizeof(T); static constexpr auto size = sizeof(T);
std::span<S> buffer_{}; std::span<S> buffer_{};
size_t offset_{}; size_t offset_{};
S* get_valid_pointer(const size_t element_index) const S* get_valid_pointer(const size_t element_index) const
{ {
const auto start_offset = offset_ + (size * element_index); const auto start_offset = offset_ + (size * element_index);
const auto end_offset = start_offset + size; const auto end_offset = start_offset + size;
if (end_offset > buffer_.size()) if (end_offset > buffer_.size())
{ {
throw std::runtime_error("Buffer accessor overflow"); throw std::runtime_error("Buffer accessor overflow");
} }
return buffer_.data() + start_offset; return buffer_.data() + start_offset;
} }
}; };
template <typename T> template <typename T>
requires(std::is_same_v<uint8_t, std::remove_cv_t<T>>) requires(std::is_same_v<uint8_t, std::remove_cv_t<T>>)
class safe_buffer_accessor class safe_buffer_accessor
{ {
public: public:
safe_buffer_accessor(const std::span<T> buffer) safe_buffer_accessor(const std::span<T> buffer)
: buffer_(buffer) : buffer_(buffer)
{ {
} }
template <typename S> template <typename S>
safe_buffer_accessor(const safe_buffer_accessor<S>& obj) safe_buffer_accessor(const safe_buffer_accessor<S>& obj)
: buffer_(obj.get_buffer()) : buffer_(obj.get_buffer())
{ {
} }
template <typename S> template <typename S>
safe_object_accessor<S, T> as(const size_t offset) const safe_object_accessor<S, T> as(const size_t offset) const
{ {
return {this->buffer_, offset}; return {this->buffer_, offset};
} }
T* get_pointer_for_range(const size_t offset, const size_t size) const T* get_pointer_for_range(const size_t offset, const size_t size) const
{ {
this->validate(offset, size); this->validate(offset, size);
return this->buffer_.data() + offset; return this->buffer_.data() + offset;
} }
void validate(const size_t offset, const size_t size) const void validate(const size_t offset, const size_t size) const
{ {
const auto end = offset + size; const auto end = offset + size;
if (end > buffer_.size()) if (end > buffer_.size())
{ {
throw std::runtime_error("Buffer accessor overflow"); throw std::runtime_error("Buffer accessor overflow");
} }
} }
template <typename S = char> template <typename S = char>
std::basic_string<S> as_string(const size_t offset) const std::basic_string<S> as_string(const size_t offset) const
{ {
safe_object_accessor<S> string_accessor{this->buffer_, offset}; safe_object_accessor<S> string_accessor{this->buffer_, offset};
std::basic_string<S> result{}; std::basic_string<S> result{};
while (true) while (true)
{ {
auto value = string_accessor.get(result.size()); auto value = string_accessor.get(result.size());
if (!value) if (!value)
{ {
return result; return result;
} }
result.push_back(std::move(value)); result.push_back(std::move(value));
} }
} }
std::span<T> get_buffer() const std::span<T> get_buffer() const
{ {
return this->buffer_; return this->buffer_;
} }
private: private:
const std::span<T> buffer_{}; const std::span<T> buffer_{};
}; };
} }

View File

@@ -4,54 +4,60 @@
namespace utils::concurrency namespace utils::concurrency
{ {
template <typename T, typename MutexType = std::mutex> template <typename T, typename MutexType = std::mutex>
class container class container
{ {
public: public:
template <typename R = void, typename F> template <typename R = void, typename F>
R access(F&& accessor) const R access(F&& accessor) const
{ {
std::lock_guard<MutexType> _{mutex_}; std::lock_guard<MutexType> _{mutex_};
return accessor(object_); return accessor(object_);
} }
template <typename R = void, typename F> template <typename R = void, typename F>
R access(F&& accessor) R access(F&& accessor)
{ {
std::lock_guard<MutexType> _{mutex_}; std::lock_guard<MutexType> _{mutex_};
return accessor(object_); return accessor(object_);
} }
template <typename R = void, typename F> template <typename R = void, typename F>
R access_with_lock(F&& accessor) const R access_with_lock(F&& accessor) const
{ {
std::unique_lock<MutexType> lock{mutex_}; std::unique_lock<MutexType> lock{mutex_};
return accessor(object_, lock); return accessor(object_, lock);
} }
template <typename R = void, typename F> template <typename R = void, typename F>
R access_with_lock(F&& accessor) R access_with_lock(F&& accessor)
{ {
std::unique_lock<MutexType> lock{mutex_}; std::unique_lock<MutexType> lock{mutex_};
return accessor(object_, lock); return accessor(object_, lock);
} }
T& get_raw() { return object_; } T& get_raw()
const T& get_raw() const { return object_; } {
return object_;
}
const T& get_raw() const
{
return object_;
}
T copy() const T copy() const
{ {
std::unique_lock<MutexType> lock{mutex_}; std::unique_lock<MutexType> lock{mutex_};
return object_; return object_;
} }
std::unique_lock<MutexType> acquire_lock() std::unique_lock<MutexType> acquire_lock()
{ {
return std::unique_lock<MutexType>{mutex_}; return std::unique_lock<MutexType>{mutex_};
} }
private: private:
mutable MutexType mutex_{}; mutable MutexType mutex_{};
T object_{}; T object_{};
}; };
} }

View File

@@ -7,19 +7,19 @@
namespace utils namespace utils
{ {
struct string_hash struct string_hash
{ {
using is_transparent = void; using is_transparent = void;
size_t operator()(const std::string_view str) const size_t operator()(const std::string_view str) const
{ {
constexpr std::hash<std::string_view> hasher{}; constexpr std::hash<std::string_view> hasher{};
return hasher(str); return hasher(str);
} }
}; };
template <typename T> template <typename T>
using unordered_string_map = std::unordered_map<std::string, T, string_hash, std::equal_to<>>; using unordered_string_map = std::unordered_map<std::string, T, string_hash, std::equal_to<>>;
using unordered_string_set = std::unordered_set<std::string, string_hash, std::equal_to<>>; using unordered_string_set = std::unordered_set<std::string, string_hash, std::equal_to<>>;
} }

View File

@@ -5,91 +5,91 @@
namespace utils namespace utils
{ {
class file_handle class file_handle
{ {
public: public:
file_handle() = default; file_handle() = default;
file_handle(FILE* file) file_handle(FILE* file)
: file_(file) : file_(file)
{ {
} }
~file_handle() ~file_handle()
{ {
this->release(); this->release();
} }
file_handle(const file_handle&) = delete; file_handle(const file_handle&) = delete;
file_handle& operator=(const file_handle&) = delete; file_handle& operator=(const file_handle&) = delete;
file_handle(file_handle&& obj) noexcept file_handle(file_handle&& obj) noexcept
: file_handle() : file_handle()
{ {
this->operator=(std::move(obj)); this->operator=(std::move(obj));
} }
file_handle& operator=(file_handle&& obj) noexcept file_handle& operator=(file_handle&& obj) noexcept
{ {
if (this != &obj) if (this != &obj)
{ {
this->release(); this->release();
this->file_ = obj.file_; this->file_ = obj.file_;
obj.file_ = {}; obj.file_ = {};
} }
return *this; return *this;
} }
file_handle& operator=(FILE* file) noexcept file_handle& operator=(FILE* file) noexcept
{ {
this->release(); this->release();
this->file_ = file; this->file_ = file;
return *this; return *this;
} }
[[nodiscard]] operator bool() const [[nodiscard]] operator bool() const
{ {
return this->file_; return this->file_;
} }
[[nodiscard]] operator FILE*() const [[nodiscard]] operator FILE*() const
{ {
return this->file_; return this->file_;
} }
[[nodiscard]] int64_t size() const [[nodiscard]] int64_t size() const
{ {
const auto current_position = this->tell(); const auto current_position = this->tell();
this->seek_to(0, SEEK_END); this->seek_to(0, SEEK_END);
const auto size = this->tell(); const auto size = this->tell();
this->seek_to(current_position); this->seek_to(current_position);
return size; return size;
} }
bool seek_to(const int64_t position, const int origin = SEEK_SET) const bool seek_to(const int64_t position, const int origin = SEEK_SET) const
{ {
return _fseeki64(this->file_, position, origin) == 0; return _fseeki64(this->file_, position, origin) == 0;
} }
[[nodiscard]] int64_t tell() const [[nodiscard]] int64_t tell() const
{ {
return _ftelli64(this->file_); return _ftelli64(this->file_);
} }
private: private:
FILE* file_{}; FILE* file_{};
void release() void release()
{ {
if (this->file_) if (this->file_)
{ {
(void)fclose(this->file_); (void)fclose(this->file_);
this->file_ = {}; this->file_ = {};
} }
} }
}; };
} }

View File

@@ -4,52 +4,53 @@
namespace utils namespace utils
{ {
/* /*
* Copied from here: https://github.com/microsoft/GSL/blob/e0880931ae5885eb988d1a8a57acf8bc2b8dacda/include/gsl/util#L57 * Copied from here:
*/ * https://github.com/microsoft/GSL/blob/e0880931ae5885eb988d1a8a57acf8bc2b8dacda/include/gsl/util#L57
*/
template <class F> template <class F>
class final_action class final_action
{ {
public: public:
static_assert(!std::is_reference<F>::value && !std::is_const<F>::value && static_assert(!std::is_reference<F>::value && !std::is_const<F>::value && !std::is_volatile<F>::value,
!std::is_volatile<F>::value, "Final_action should store its callable by value");
"Final_action should store its callable by value");
explicit final_action(F f) noexcept : f_(std::move(f)) explicit final_action(F f) noexcept
{ : f_(std::move(f))
} {
}
final_action(final_action&& other) noexcept final_action(final_action&& other) noexcept
: f_(std::move(other.f_)), invoke_(std::exchange(other.invoke_, false)) : f_(std::move(other.f_)),
{ invoke_(std::exchange(other.invoke_, false))
} {
}
final_action(const final_action&) = delete; final_action(const final_action&) = delete;
final_action& operator=(const final_action&) = delete; final_action& operator=(const final_action&) = delete;
final_action& operator=(final_action&&) = delete; final_action& operator=(final_action&&) = delete;
~final_action() noexcept ~final_action() noexcept
{ {
if (invoke_) f_(); if (invoke_)
} f_();
}
// Added by momo5502 // Added by momo5502
void cancel() void cancel()
{ {
invoke_ = false; invoke_ = false;
} }
private: private:
F f_; F f_;
bool invoke_{true}; bool invoke_{true};
}; };
template <class F> template <class F>
final_action<typename std::remove_cv<typename std::remove_reference<F>::type>::type> final_action<typename std::remove_cv<typename std::remove_reference<F>::type>::type> finally(F&& f) noexcept
finally(F&& f) noexcept {
{ return final_action<typename std::remove_cv<typename std::remove_reference<F>::type>::type>(std::forward<F>(f));
return final_action<typename std::remove_cv<typename std::remove_reference<F>::type>::type>( }
std::forward<F>(f));
}
} }

View File

@@ -4,121 +4,123 @@
namespace utils::io namespace utils::io
{ {
bool remove_file(const std::filesystem::path& file) bool remove_file(const std::filesystem::path& file)
{ {
std::error_code ec{}; std::error_code ec{};
return std::filesystem::remove(file, ec) && !ec; return std::filesystem::remove(file, ec) && !ec;
} }
bool move_file(const std::filesystem::path& src, const std::filesystem::path& target) bool move_file(const std::filesystem::path& src, const std::filesystem::path& target)
{ {
copy_folder(src, target); copy_folder(src, target);
return remove_file(src); return remove_file(src);
} }
bool file_exists(const std::filesystem::path& file) bool file_exists(const std::filesystem::path& file)
{ {
return std::ifstream(file).good(); return std::ifstream(file).good();
} }
bool write_file(const std::filesystem::path& file, const std::vector<uint8_t>& data, const bool append) bool write_file(const std::filesystem::path& file, const std::vector<uint8_t>& data, const bool append)
{ {
if (file.has_parent_path()) if (file.has_parent_path())
{ {
io::create_directory(file.parent_path()); io::create_directory(file.parent_path());
} }
std::ofstream stream( std::ofstream stream(file, std::ios::binary | std::ofstream::out |
file, std::ios::binary | std::ofstream::out | (append ? std::ofstream::app : std::ofstream::out)); (append ? std::ofstream::app : std::ofstream::out));
if (stream.is_open()) if (stream.is_open())
{ {
stream.write(reinterpret_cast<const char*>(data.data()), static_cast<std::streamsize>(data.size())); stream.write(reinterpret_cast<const char*>(data.data()), static_cast<std::streamsize>(data.size()));
stream.close(); stream.close();
return true; return true;
} }
return false; return false;
} }
std::vector<uint8_t> read_file(const std::filesystem::path& file) std::vector<uint8_t> read_file(const std::filesystem::path& file)
{ {
std::vector<uint8_t> data; std::vector<uint8_t> data;
read_file(file, &data); read_file(file, &data);
return data; return data;
} }
bool read_file(const std::filesystem::path& file, std::vector<uint8_t>* data) bool read_file(const std::filesystem::path& file, std::vector<uint8_t>* data)
{ {
if (!data) return false; if (!data)
data->clear(); return false;
data->clear();
std::ifstream stream(file, std::ios::binary); std::ifstream stream(file, std::ios::binary);
if (!stream) return false; if (!stream)
return false;
*data = std::vector<uint8_t>{(std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>()}; *data = std::vector<uint8_t>{(std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>()};
return true; return true;
} }
std::size_t file_size(const std::filesystem::path& file) std::size_t file_size(const std::filesystem::path& file)
{ {
std::ifstream stream(file, std::ios::binary); std::ifstream stream(file, std::ios::binary);
if (stream) if (stream)
{ {
stream.seekg(0, std::ios::end); stream.seekg(0, std::ios::end);
return static_cast<std::size_t>(stream.tellg()); return static_cast<std::size_t>(stream.tellg());
} }
return 0; return 0;
} }
bool create_directory(const std::filesystem::path& directory) bool create_directory(const std::filesystem::path& directory)
{ {
std::error_code ec{}; std::error_code ec{};
return std::filesystem::create_directories(directory, ec) && !ec; return std::filesystem::create_directories(directory, ec) && !ec;
} }
bool directory_exists(const std::filesystem::path& directory) bool directory_exists(const std::filesystem::path& directory)
{ {
std::error_code ec{}; std::error_code ec{};
return std::filesystem::is_directory(directory, ec) && !ec; return std::filesystem::is_directory(directory, ec) && !ec;
} }
bool directory_is_empty(const std::filesystem::path& directory) bool directory_is_empty(const std::filesystem::path& directory)
{ {
std::error_code ec{}; std::error_code ec{};
return std::filesystem::is_empty(directory, ec) && !ec; return std::filesystem::is_empty(directory, ec) && !ec;
} }
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target) void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target)
{ {
std::error_code ec{}; std::error_code ec{};
std::filesystem::copy(src, target, std::filesystem::copy(
std::filesystem::copy_options::overwrite_existing | src, target, std::filesystem::copy_options::overwrite_existing | std::filesystem::copy_options::recursive,
std::filesystem::copy_options::recursive, ec); ec);
} }
std::vector<std::filesystem::path> list_files(const std::filesystem::path& directory, const bool recursive) std::vector<std::filesystem::path> list_files(const std::filesystem::path& directory, const bool recursive)
{ {
std::error_code code{}; std::error_code code{};
std::vector<std::filesystem::path> files; std::vector<std::filesystem::path> files;
if (recursive) if (recursive)
{ {
for (auto& file : std::filesystem::recursive_directory_iterator(directory, code)) for (auto& file : std::filesystem::recursive_directory_iterator(directory, code))
{ {
files.push_back(file.path()); files.push_back(file.path());
} }
} }
else else
{ {
for (auto& file : std::filesystem::directory_iterator(directory, code)) for (auto& file : std::filesystem::directory_iterator(directory, code))
{ {
files.push_back(file.path()); files.push_back(file.path());
} }
} }
return files; return files;
} }
} }

View File

@@ -6,17 +6,17 @@
namespace utils::io namespace utils::io
{ {
bool remove_file(const std::filesystem::path& file); bool remove_file(const std::filesystem::path& file);
bool move_file(const std::filesystem::path& src, const std::filesystem::path& target); bool move_file(const std::filesystem::path& src, const std::filesystem::path& target);
bool file_exists(const std::filesystem::path& file); bool file_exists(const std::filesystem::path& file);
bool write_file(const std::filesystem::path& file, const std::vector<uint8_t>& data, bool append = false); bool write_file(const std::filesystem::path& file, const std::vector<uint8_t>& data, bool append = false);
bool read_file(const std::filesystem::path& file, std::vector<uint8_t>* data); bool read_file(const std::filesystem::path& file, std::vector<uint8_t>* data);
std::vector<uint8_t> read_file(const std::filesystem::path& file); std::vector<uint8_t> read_file(const std::filesystem::path& file);
size_t file_size(const std::filesystem::path& file); size_t file_size(const std::filesystem::path& file);
bool create_directory(const std::filesystem::path& directory); bool create_directory(const std::filesystem::path& directory);
bool directory_exists(const std::filesystem::path& directory); bool directory_exists(const std::filesystem::path& directory);
bool directory_is_empty(const std::filesystem::path& directory); bool directory_is_empty(const std::filesystem::path& directory);
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target); void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target);
std::vector<std::filesystem::path> list_files(const std::filesystem::path& directory, bool recursive = false); std::vector<std::filesystem::path> list_files(const std::filesystem::path& directory, bool recursive = false);
} }

View File

@@ -10,80 +10,80 @@
namespace utils::nt namespace utils::nt
{ {
using HandleFunction = HANDLE(); using HandleFunction = HANDLE();
inline HANDLE null_handle() inline HANDLE null_handle()
{ {
return nullptr; return nullptr;
} }
inline HANDLE invalid_handle() inline HANDLE invalid_handle()
{ {
return INVALID_HANDLE_VALUE; return INVALID_HANDLE_VALUE;
} }
template <HandleFunction InvalidHandleFunction = null_handle> template <HandleFunction InvalidHandleFunction = null_handle>
class handle class handle
{ {
public: public:
handle() = default; handle() = default;
handle(const HANDLE h) handle(const HANDLE h)
: handle_(h) : handle_(h)
{ {
} }
~handle() ~handle()
{ {
if (*this) if (*this)
{ {
CloseHandle(this->handle_); CloseHandle(this->handle_);
this->handle_ = InvalidHandleFunction(); this->handle_ = InvalidHandleFunction();
} }
} }
handle(const handle&) = delete; handle(const handle&) = delete;
handle& operator=(const handle&) = delete; handle& operator=(const handle&) = delete;
handle(handle&& obj) noexcept handle(handle&& obj) noexcept
: handle() : handle()
{ {
this->operator=(std::move(obj)); this->operator=(std::move(obj));
} }
handle& operator=(handle&& obj) noexcept handle& operator=(handle&& obj) noexcept
{ {
if (this != &obj) if (this != &obj)
{ {
this->~handle(); this->~handle();
this->handle_ = obj.handle_; this->handle_ = obj.handle_;
obj.handle_ = InvalidHandleFunction(); obj.handle_ = InvalidHandleFunction();
} }
return *this; return *this;
} }
handle& operator=(HANDLE h) noexcept handle& operator=(HANDLE h) noexcept
{ {
this->~handle(); this->~handle();
this->handle_ = h; this->handle_ = h;
return *this; return *this;
} }
[[nodiscard]] operator bool() const [[nodiscard]] operator bool() const
{ {
return this->handle_ != InvalidHandleFunction(); return this->handle_ != InvalidHandleFunction();
} }
[[nodiscard]] operator HANDLE() const [[nodiscard]] operator HANDLE() const
{ {
return this->handle_; return this->handle_;
} }
private: private:
HANDLE handle_{InvalidHandleFunction()}; HANDLE handle_{InvalidHandleFunction()};
}; };
} }
#endif #endif

View File

@@ -6,45 +6,42 @@
namespace utils::string namespace utils::string
{ {
inline char char_to_lower(const char val) inline char char_to_lower(const char val)
{ {
return static_cast<char>(std::tolower(static_cast<unsigned char>(val))); return static_cast<char>(std::tolower(static_cast<unsigned char>(val)));
} }
inline char16_t char_to_lower(const char16_t val) inline char16_t char_to_lower(const char16_t val)
{ {
if (val >= u'A' && val <= u'Z') if (val >= u'A' && val <= u'Z')
{ {
return val + 32; return val + 32;
} }
return val; return val;
} }
inline wchar_t char_to_lower(const wchar_t val) inline wchar_t char_to_lower(const wchar_t val)
{ {
return std::towlower(val); return std::towlower(val);
} }
template <class Elem, class Traits, class Alloc> template <class Elem, class Traits, class Alloc>
void to_lower_inplace(std::basic_string<Elem, Traits, Alloc>& str) void to_lower_inplace(std::basic_string<Elem, Traits, Alloc>& str)
{ {
std::ranges::transform(str, str.begin(), [](const Elem e) std::ranges::transform(str, str.begin(), [](const Elem e) { return char_to_lower(e); });
{ }
return char_to_lower(e);
});
}
template <class Elem, class Traits, class Alloc> template <class Elem, class Traits, class Alloc>
std::basic_string<Elem, Traits, Alloc> to_lower(std::basic_string<Elem, Traits, Alloc> str) std::basic_string<Elem, Traits, Alloc> to_lower(std::basic_string<Elem, Traits, Alloc> str)
{ {
to_lower_inplace(str); to_lower_inplace(str);
return str; return str;
} }
template <class Elem, class Traits, class Alloc> template <class Elem, class Traits, class Alloc>
std::basic_string<Elem, Traits, Alloc> to_lower_consume(std::basic_string<Elem, Traits, Alloc>& str) std::basic_string<Elem, Traits, Alloc> to_lower_consume(std::basic_string<Elem, Traits, Alloc>& str)
{ {
return to_lower(std::move(str)); return to_lower(std::move(str));
} }
} }

View File

@@ -4,23 +4,23 @@
namespace utils namespace utils
{ {
template <typename Clock = std::chrono::high_resolution_clock> template <typename Clock = std::chrono::high_resolution_clock>
class timer class timer
{ {
public: public:
void update() void update()
{ {
this->point_ = Clock::now(); this->point_ = Clock::now();
} }
bool has_elapsed(typename Clock::duration duration) const bool has_elapsed(typename Clock::duration duration) const
{ {
const auto now = Clock::now(); const auto now = Clock::now();
const auto diff = now - this->point_; const auto diff = now - this->point_;
return diff > duration; return diff > duration;
} }
private: private:
typename Clock::time_point point_{Clock::now()}; typename Clock::time_point point_{Clock::now()};
}; };
} }

View File

@@ -4,52 +4,52 @@
template <typename T> template <typename T>
T* offset_pointer(void* data, const size_t offset) T* offset_pointer(void* data, const size_t offset)
{ {
return reinterpret_cast<T*>(static_cast<uint8_t*>(data) + offset); return reinterpret_cast<T*>(static_cast<uint8_t*>(data) + offset);
} }
template <typename T> template <typename T>
const T* offset_pointer(const void* data, const size_t offset) const T* offset_pointer(const void* data, const size_t offset)
{ {
return reinterpret_cast<const T*>(static_cast<const uint8_t*>(data) + offset); return reinterpret_cast<const T*>(static_cast<const uint8_t*>(data) + offset);
} }
constexpr bool is_within_start_and_end(const uint64_t value, const uint64_t start, const uint64_t end) constexpr bool is_within_start_and_end(const uint64_t value, const uint64_t start, const uint64_t end)
{ {
return value >= start && value < end; return value >= start && value < end;
} }
constexpr bool is_within_start_and_length(const uint64_t value, const uint64_t start, const uint64_t length) constexpr bool is_within_start_and_length(const uint64_t value, const uint64_t start, const uint64_t length)
{ {
return is_within_start_and_end(value, start, start + length); return is_within_start_and_end(value, start, start + length);
} }
constexpr bool regions_intersect(const uint64_t start1, const uint64_t end1, const uint64_t start2, const uint64_t end2) constexpr bool regions_intersect(const uint64_t start1, const uint64_t end1, const uint64_t start2, const uint64_t end2)
{ {
return start1 < end2 && start2 < end1; return start1 < end2 && start2 < end1;
} }
constexpr bool regions_with_length_intersect(const uint64_t start1, const uint64_t length1, const uint64_t start2, constexpr bool regions_with_length_intersect(const uint64_t start1, const uint64_t length1, const uint64_t start2,
const uint64_t length2) const uint64_t length2)
{ {
return regions_intersect(start1, start1 + length1, start2, start2 + length2); return regions_intersect(start1, start1 + length1, start2, start2 + length2);
} }
constexpr uint64_t align_down(const uint64_t value, const uint64_t alignment) constexpr uint64_t align_down(const uint64_t value, const uint64_t alignment)
{ {
return value & ~(alignment - 1); return value & ~(alignment - 1);
} }
constexpr uint64_t align_up(const uint64_t value, const uint64_t alignment) constexpr uint64_t align_up(const uint64_t value, const uint64_t alignment)
{ {
return align_down(value + (alignment - 1), alignment); return align_down(value + (alignment - 1), alignment);
} }
constexpr uint64_t page_align_down(const uint64_t value, const uint64_t page_size = 0x1000) constexpr uint64_t page_align_down(const uint64_t value, const uint64_t page_size = 0x1000)
{ {
return align_down(value, page_size); return align_down(value, page_size);
} }
constexpr uint64_t page_align_up(const uint64_t value, const uint64_t page_size = 0x1000) constexpr uint64_t page_align_up(const uint64_t value, const uint64_t page_size = 0x1000)
{ {
return align_up(value, page_size); return align_up(value, page_size);
} }

View File

@@ -11,156 +11,152 @@ using memory_operation = memory_permission;
enum class instruction_hook_continuation : bool enum class instruction_hook_continuation : bool
{ {
run_instruction = false, run_instruction = false,
skip_instruction = true, skip_instruction = true,
}; };
enum class memory_violation_continuation : bool enum class memory_violation_continuation : bool
{ {
stop = false, stop = false,
resume = true, resume = true,
}; };
enum class memory_violation_type : uint8_t enum class memory_violation_type : uint8_t
{ {
unmapped, unmapped,
protection, protection,
}; };
struct basic_block struct basic_block
{ {
uint64_t address; uint64_t address;
size_t instruction_count; size_t instruction_count;
size_t size; size_t size;
}; };
using edge_generation_hook_callback = std::function<void(const basic_block& current_block, using edge_generation_hook_callback =
const basic_block& previous_block)>; std::function<void(const basic_block& current_block, const basic_block& previous_block)>;
using basic_block_hook_callback = std::function<void(const basic_block& block)>; using basic_block_hook_callback = std::function<void(const basic_block& block)>;
using instruction_hook_callback = std::function<instruction_hook_continuation()>; using instruction_hook_callback = std::function<instruction_hook_continuation()>;
using interrupt_hook_callback = std::function<void(int interrupt)>; using interrupt_hook_callback = std::function<void(int interrupt)>;
using simple_memory_hook_callback = std::function<void(uint64_t address, size_t size, uint64_t value)>; using simple_memory_hook_callback = std::function<void(uint64_t address, size_t size, uint64_t value)>;
using complex_memory_hook_callback = std::function<void(uint64_t address, size_t size, uint64_t value, using complex_memory_hook_callback =
memory_operation operation)>; std::function<void(uint64_t address, size_t size, uint64_t value, memory_operation operation)>;
using memory_violation_hook_callback = std::function<memory_violation_continuation( using memory_violation_hook_callback = std::function<memory_violation_continuation(
uint64_t address, size_t size, memory_operation operation, uint64_t address, size_t size, memory_operation operation, memory_violation_type type)>;
memory_violation_type type)>;
class emulator : public memory_manager class emulator : public memory_manager
{ {
public: public:
emulator() = default; emulator() = default;
emulator(const emulator&) = delete; emulator(const emulator&) = delete;
emulator& operator=(const emulator&) = delete; emulator& operator=(const emulator&) = delete;
emulator(emulator&&) = delete; emulator(emulator&&) = delete;
emulator& operator=(emulator&&) = delete; emulator& operator=(emulator&&) = delete;
virtual void start(uint64_t start, uint64_t end = 0, std::chrono::nanoseconds timeout = {}, size_t count = 0) = 0; virtual void start(uint64_t start, uint64_t end = 0, std::chrono::nanoseconds timeout = {}, size_t count = 0) = 0;
virtual void stop() = 0; virtual void stop() = 0;
virtual void read_raw_register(int reg, void* value, size_t size) = 0; virtual void read_raw_register(int reg, void* value, size_t size) = 0;
virtual void write_raw_register(int reg, const void* value, size_t size) = 0; virtual void write_raw_register(int reg, const void* value, size_t size) = 0;
virtual std::vector<std::byte> save_registers() = 0; virtual std::vector<std::byte> save_registers() = 0;
virtual void restore_registers(const std::vector<std::byte>& register_data) = 0; virtual void restore_registers(const std::vector<std::byte>& register_data) = 0;
virtual emulator_hook* hook_memory_violation(uint64_t address, size_t size, virtual emulator_hook* hook_memory_violation(uint64_t address, size_t size,
memory_violation_hook_callback callback) = 0; memory_violation_hook_callback callback) = 0;
virtual emulator_hook* hook_memory_access(uint64_t address, size_t size, memory_operation filter, virtual emulator_hook* hook_memory_access(uint64_t address, size_t size, memory_operation filter,
complex_memory_hook_callback callback) = 0; complex_memory_hook_callback callback) = 0;
virtual emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) = 0; virtual emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) = 0;
virtual emulator_hook* hook_interrupt(interrupt_hook_callback callback) = 0; virtual emulator_hook* hook_interrupt(interrupt_hook_callback callback) = 0;
virtual emulator_hook* hook_edge_generation(edge_generation_hook_callback callback) = 0; virtual emulator_hook* hook_edge_generation(edge_generation_hook_callback callback) = 0;
virtual emulator_hook* hook_basic_block(basic_block_hook_callback callback) = 0; virtual emulator_hook* hook_basic_block(basic_block_hook_callback callback) = 0;
virtual void delete_hook(emulator_hook* hook) = 0; virtual void delete_hook(emulator_hook* hook) = 0;
emulator_hook* hook_memory_violation(memory_violation_hook_callback callback) emulator_hook* hook_memory_violation(memory_violation_hook_callback callback)
{ {
return this->hook_memory_violation(0, std::numeric_limits<size_t>::max(), std::move(callback)); return this->hook_memory_violation(0, std::numeric_limits<size_t>::max(), std::move(callback));
} }
emulator_hook* hook_memory_read(const uint64_t address, const size_t size, simple_memory_hook_callback callback) emulator_hook* hook_memory_read(const uint64_t address, const size_t size, simple_memory_hook_callback callback)
{ {
return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::read); return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::read);
} }
emulator_hook* hook_memory_write(const uint64_t address, const size_t size, simple_memory_hook_callback callback) emulator_hook* hook_memory_write(const uint64_t address, const size_t size, simple_memory_hook_callback callback)
{ {
return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::write); return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::write);
} }
emulator_hook* hook_memory_execution(const uint64_t address, const size_t size, emulator_hook* hook_memory_execution(const uint64_t address, const size_t size,
simple_memory_hook_callback callback) simple_memory_hook_callback callback)
{ {
return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::exec); return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::exec);
} }
void serialize(utils::buffer_serializer& buffer) const void serialize(utils::buffer_serializer& buffer) const
{ {
this->perform_serialization(buffer, false); this->perform_serialization(buffer, false);
} }
void deserialize(utils::buffer_deserializer& buffer) void deserialize(utils::buffer_deserializer& buffer)
{ {
this->perform_deserialization(buffer, false); this->perform_deserialization(buffer, false);
} }
void save_snapshot() void save_snapshot()
{ {
utils::buffer_serializer serializer{}; utils::buffer_serializer serializer{};
this->perform_serialization(serializer, true); this->perform_serialization(serializer, true);
this->last_snapshot_data_ = serializer.move_buffer(); this->last_snapshot_data_ = serializer.move_buffer();
} }
void restore_snapshot() void restore_snapshot()
{ {
if (this->last_snapshot_data_.empty()) if (this->last_snapshot_data_.empty())
{ {
return; return;
} }
utils::buffer_deserializer deserializer{this->last_snapshot_data_}; utils::buffer_deserializer deserializer{this->last_snapshot_data_};
this->perform_deserialization(deserializer, true); this->perform_deserialization(deserializer, true);
} }
virtual bool has_violation() const = 0; virtual bool has_violation() const = 0;
private: private:
std::vector<std::byte> last_snapshot_data_{}; std::vector<std::byte> last_snapshot_data_{};
emulator_hook* hook_simple_memory_access(const uint64_t address, const size_t size, emulator_hook* hook_simple_memory_access(const uint64_t address, const size_t size,
simple_memory_hook_callback callback, const memory_operation operation) simple_memory_hook_callback callback, const memory_operation operation)
{ {
assert((static_cast<uint8_t>(operation) & (static_cast<uint8_t>(operation) - 1)) == 0); assert((static_cast<uint8_t>(operation) & (static_cast<uint8_t>(operation) - 1)) == 0);
return this->hook_memory_access(address, size, operation, return this->hook_memory_access(address, size, operation,
[c = std::move(callback)](const uint64_t a, const size_t s, [c = std::move(callback)](const uint64_t a, const size_t s,
const uint64_t value, const uint64_t value,
memory_operation) memory_operation) { c(a, s, value); });
{ }
c(a, s, value);
});
}
void perform_serialization(utils::buffer_serializer& buffer, const bool is_snapshot) const void perform_serialization(utils::buffer_serializer& buffer, const bool is_snapshot) const
{ {
this->serialize_state(buffer, is_snapshot); this->serialize_state(buffer, is_snapshot);
this->serialize_memory_state(buffer, is_snapshot); this->serialize_memory_state(buffer, is_snapshot);
} }
void perform_deserialization(utils::buffer_deserializer& buffer, const bool is_snapshot) void perform_deserialization(utils::buffer_deserializer& buffer, const bool is_snapshot)
{ {
this->deserialize_state(buffer, is_snapshot); this->deserialize_state(buffer, is_snapshot);
this->deserialize_memory_state(buffer, is_snapshot); this->deserialize_memory_state(buffer, is_snapshot);
} }
virtual void serialize_state(utils::buffer_serializer& buffer, bool is_snapshot) const = 0; virtual void serialize_state(utils::buffer_serializer& buffer, bool is_snapshot) const = 0;
virtual void deserialize_state(utils::buffer_deserializer& buffer, bool is_snapshot) = 0; virtual void deserialize_state(utils::buffer_deserializer& buffer, bool is_snapshot) = 0;
}; };

View File

@@ -10,541 +10,547 @@
namespace namespace
{ {
constexpr auto MIN_ALLOCATION_ADDRESS = 0x0000000000010000ULL; constexpr auto MIN_ALLOCATION_ADDRESS = 0x0000000000010000ULL;
constexpr auto MAX_ALLOCATION_ADDRESS = 0x00007ffffffeffffULL; constexpr auto MAX_ALLOCATION_ADDRESS = 0x00007ffffffeffffULL;
void split_regions(memory_manager::committed_region_map& regions, const std::vector<uint64_t>& split_points) void split_regions(memory_manager::committed_region_map& regions, const std::vector<uint64_t>& split_points)
{ {
for (auto i = regions.begin(); i != regions.end(); ++i) for (auto i = regions.begin(); i != regions.end(); ++i)
{ {
for (const auto split_point : split_points) for (const auto split_point : split_points)
{ {
if (is_within_start_and_length(split_point, i->first, i->second.length) && i->first != split_point) if (is_within_start_and_length(split_point, i->first, i->second.length) && i->first != split_point)
{ {
const auto first_length = split_point - i->first; const auto first_length = split_point - i->first;
const auto second_length = i->second.length - first_length; const auto second_length = i->second.length - first_length;
i->second.length = first_length; i->second.length = first_length;
regions[split_point] = memory_manager::committed_region{second_length, i->second.pemissions}; regions[split_point] = memory_manager::committed_region{second_length, i->second.pemissions};
} }
} }
} }
} }
void merge_regions(memory_manager::committed_region_map& regions) void merge_regions(memory_manager::committed_region_map& regions)
{ {
for (auto i = regions.begin(); i != regions.end();) for (auto i = regions.begin(); i != regions.end();)
{ {
assert(i->second.length > 0); assert(i->second.length > 0);
auto next = i; auto next = i;
std::advance(next, 1); std::advance(next, 1);
if (next == regions.end()) if (next == regions.end())
{ {
break; break;
} }
assert(next->second.length > 0); assert(next->second.length > 0);
const auto end = i->first + i->second.length; const auto end = i->first + i->second.length;
assert(end <= next->first); assert(end <= next->first);
if (end != next->first || i->second.pemissions != next->second.pemissions) if (end != next->first || i->second.pemissions != next->second.pemissions)
{ {
++i; ++i;
continue; continue;
} }
i->second.length += next->second.length; i->second.length += next->second.length;
regions.erase(next); regions.erase(next);
} }
} }
} }
namespace utils namespace utils
{ {
static void serialize(buffer_serializer& buffer, const memory_manager::committed_region& region) static void serialize(buffer_serializer& buffer, const memory_manager::committed_region& region)
{ {
buffer.write<uint64_t>(region.length); buffer.write<uint64_t>(region.length);
buffer.write(region.pemissions); buffer.write(region.pemissions);
} }
static void deserialize(buffer_deserializer& buffer, memory_manager::committed_region& region) static void deserialize(buffer_deserializer& buffer, memory_manager::committed_region& region)
{ {
region.length = static_cast<size_t>(buffer.read<uint64_t>()); region.length = static_cast<size_t>(buffer.read<uint64_t>());
region.pemissions = buffer.read<memory_permission>(); region.pemissions = buffer.read<memory_permission>();
} }
static void serialize(buffer_serializer& buffer, const memory_manager::reserved_region& region) static void serialize(buffer_serializer& buffer, const memory_manager::reserved_region& region)
{ {
buffer.write(region.is_mmio); buffer.write(region.is_mmio);
buffer.write<uint64_t>(region.length); buffer.write<uint64_t>(region.length);
buffer.write_map(region.committed_regions); buffer.write_map(region.committed_regions);
} }
static void deserialize(buffer_deserializer& buffer, memory_manager::reserved_region& region) static void deserialize(buffer_deserializer& buffer, memory_manager::reserved_region& region)
{ {
buffer.read(region.is_mmio); buffer.read(region.is_mmio);
region.length = static_cast<size_t>(buffer.read<uint64_t>()); region.length = static_cast<size_t>(buffer.read<uint64_t>());
buffer.read_map(region.committed_regions); buffer.read_map(region.committed_regions);
} }
} }
void memory_manager::serialize_memory_state(utils::buffer_serializer& buffer, const bool is_snapshot) const void memory_manager::serialize_memory_state(utils::buffer_serializer& buffer, const bool is_snapshot) const
{ {
buffer.write_map(this->reserved_regions_); buffer.write_map(this->reserved_regions_);
if (is_snapshot) if (is_snapshot)
{ {
return; return;
} }
std::vector<uint8_t> data{}; std::vector<uint8_t> data{};
for (const auto& reserved_region : this->reserved_regions_) for (const auto& reserved_region : this->reserved_regions_)
{ {
if (reserved_region.second.is_mmio) if (reserved_region.second.is_mmio)
{ {
continue; continue;
} }
for (const auto& region : reserved_region.second.committed_regions) for (const auto& region : reserved_region.second.committed_regions)
{ {
data.resize(region.second.length); data.resize(region.second.length);
this->read_memory(region.first, data.data(), region.second.length); this->read_memory(region.first, data.data(), region.second.length);
buffer.write(data.data(), region.second.length); buffer.write(data.data(), region.second.length);
} }
} }
} }
void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer, const bool is_snapshot) void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer, const bool is_snapshot)
{ {
if (!is_snapshot) if (!is_snapshot)
{ {
for (const auto& reserved_region : this->reserved_regions_) for (const auto& reserved_region : this->reserved_regions_)
{ {
for (const auto& region : reserved_region.second.committed_regions) for (const auto& region : reserved_region.second.committed_regions)
{ {
this->unmap_memory(region.first, region.second.length); this->unmap_memory(region.first, region.second.length);
} }
} }
} }
buffer.read_map(this->reserved_regions_); buffer.read_map(this->reserved_regions_);
if (is_snapshot) if (is_snapshot)
{ {
return; return;
} }
std::vector<uint8_t> data{}; std::vector<uint8_t> data{};
for (auto i = this->reserved_regions_.begin(); i != this->reserved_regions_.end();) for (auto i = this->reserved_regions_.begin(); i != this->reserved_regions_.end();)
{ {
auto& reserved_region = i->second; auto& reserved_region = i->second;
if (reserved_region.is_mmio) if (reserved_region.is_mmio)
{ {
i = this->reserved_regions_.erase(i); i = this->reserved_regions_.erase(i);
continue; continue;
} }
++i; ++i;
for (const auto& region : reserved_region.committed_regions) for (const auto& region : reserved_region.committed_regions)
{ {
data.resize(region.second.length); data.resize(region.second.length);
buffer.read(data.data(), region.second.length); buffer.read(data.data(), region.second.length);
this->map_memory(region.first, region.second.length, region.second.pemissions); this->map_memory(region.first, region.second.length, region.second.pemissions);
this->write_memory(region.first, data.data(), region.second.length); this->write_memory(region.first, data.data(), region.second.length);
} }
} }
} }
bool memory_manager::protect_memory(const uint64_t address, const size_t size, const memory_permission permissions, bool memory_manager::protect_memory(const uint64_t address, const size_t size, const memory_permission permissions,
memory_permission* old_permissions) memory_permission* old_permissions)
{ {
const auto entry = this->find_reserved_region(address); const auto entry = this->find_reserved_region(address);
if (entry == this->reserved_regions_.end()) if (entry == this->reserved_regions_.end())
{ {
return false; return false;
} }
const auto end = address + size; const auto end = address + size;
const auto region_end = entry->first + entry->second.length; const auto region_end = entry->first + entry->second.length;
if (region_end < end) if (region_end < end)
{ {
throw std::runtime_error("Cross region protect not supported yet!"); throw std::runtime_error("Cross region protect not supported yet!");
} }
std::optional<memory_permission> old_first_permissions{}; std::optional<memory_permission> old_first_permissions{};
auto& committed_regions = entry->second.committed_regions; auto& committed_regions = entry->second.committed_regions;
split_regions(committed_regions, {address, end}); split_regions(committed_regions, {address, end});
for (auto& sub_region : committed_regions) for (auto& sub_region : committed_regions)
{ {
if (sub_region.first >= end) if (sub_region.first >= end)
{ {
break; break;
} }
const auto sub_region_end = sub_region.first + sub_region.second.length; const auto sub_region_end = sub_region.first + sub_region.second.length;
if (sub_region.first >= address && sub_region_end <= end) if (sub_region.first >= address && sub_region_end <= end)
{ {
if (!old_first_permissions.has_value()) if (!old_first_permissions.has_value())
{ {
old_first_permissions = sub_region.second.pemissions; old_first_permissions = sub_region.second.pemissions;
} }
this->apply_memory_protection(sub_region.first, sub_region.second.length, permissions); this->apply_memory_protection(sub_region.first, sub_region.second.length, permissions);
sub_region.second.pemissions = permissions; sub_region.second.pemissions = permissions;
} }
} }
if (old_permissions) if (old_permissions)
{ {
*old_permissions = old_first_permissions.value_or(memory_permission::none); *old_permissions = old_first_permissions.value_or(memory_permission::none);
} }
merge_regions(committed_regions); merge_regions(committed_regions);
return true; return true;
} }
bool memory_manager::allocate_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb, bool memory_manager::allocate_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb,
mmio_write_callback write_cb) mmio_write_callback write_cb)
{ {
if (this->overlaps_reserved_region(address, size)) if (this->overlaps_reserved_region(address, size))
{ {
return false; return false;
} }
this->map_mmio(address, size, std::move(read_cb), std::move(write_cb)); this->map_mmio(address, size, std::move(read_cb), std::move(write_cb));
const auto entry = this->reserved_regions_.try_emplace(address, const auto entry = this->reserved_regions_
reserved_region{ .try_emplace(address,
.length = size, reserved_region{
.is_mmio = true, .length = size,
}).first; .is_mmio = true,
})
.first;
entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write}; entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write};
return true; return true;
} }
bool memory_manager::allocate_memory(const uint64_t address, const size_t size, const memory_permission permissions, bool memory_manager::allocate_memory(const uint64_t address, const size_t size, const memory_permission permissions,
const bool reserve_only) const bool reserve_only)
{ {
if (this->overlaps_reserved_region(address, size)) if (this->overlaps_reserved_region(address, size))
{ {
return false; return false;
} }
const auto entry = this->reserved_regions_.try_emplace(address, reserved_region{.length = size,}).first; const auto entry = this->reserved_regions_
.try_emplace(address,
reserved_region{
.length = size,
})
.first;
if (!reserve_only) if (!reserve_only)
{ {
this->map_memory(address, size, permissions); this->map_memory(address, size, permissions);
entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write}; entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write};
} }
return true; return true;
} }
bool memory_manager::commit_memory(const uint64_t address, const size_t size, const memory_permission permissions) bool memory_manager::commit_memory(const uint64_t address, const size_t size, const memory_permission permissions)
{ {
const auto entry = this->find_reserved_region(address); const auto entry = this->find_reserved_region(address);
if (entry == this->reserved_regions_.end()) if (entry == this->reserved_regions_.end())
{ {
return false; return false;
} }
const auto end = address + size; const auto end = address + size;
const auto region_end = entry->first + entry->second.length; const auto region_end = entry->first + entry->second.length;
if (region_end < end) if (region_end < end)
{ {
throw std::runtime_error("Cross region commit not supported yet!"); throw std::runtime_error("Cross region commit not supported yet!");
} }
auto& committed_regions = entry->second.committed_regions; auto& committed_regions = entry->second.committed_regions;
split_regions(committed_regions, {address, end}); split_regions(committed_regions, {address, end});
uint64_t last_region_start{}; uint64_t last_region_start{};
const committed_region* last_region{nullptr}; const committed_region* last_region{nullptr};
for (auto& sub_region : committed_regions) for (auto& sub_region : committed_regions)
{ {
if (sub_region.first >= end) if (sub_region.first >= end)
{ {
break; break;
} }
const auto sub_region_end = sub_region.first + sub_region.second.length; const auto sub_region_end = sub_region.first + sub_region.second.length;
if (sub_region.first >= address && sub_region_end <= end) if (sub_region.first >= address && sub_region_end <= end)
{ {
const auto map_start = last_region ? (last_region_start + last_region->length) : address; const auto map_start = last_region ? (last_region_start + last_region->length) : address;
const auto map_length = sub_region.first - map_start; const auto map_length = sub_region.first - map_start;
if (map_length > 0) if (map_length > 0)
{ {
this->map_memory(map_start, map_length, permissions); this->map_memory(map_start, map_length, permissions);
committed_regions[map_start] = committed_region{map_length, permissions}; committed_regions[map_start] = committed_region{map_length, permissions};
} }
last_region_start = sub_region.first; last_region_start = sub_region.first;
last_region = &sub_region.second; last_region = &sub_region.second;
} }
} }
if (!last_region || (last_region_start + last_region->length) < end) if (!last_region || (last_region_start + last_region->length) < end)
{ {
const auto map_start = last_region ? (last_region_start + last_region->length) : address; const auto map_start = last_region ? (last_region_start + last_region->length) : address;
const auto map_length = end - map_start; const auto map_length = end - map_start;
this->map_memory(map_start, map_length, permissions); this->map_memory(map_start, map_length, permissions);
committed_regions[map_start] = committed_region{map_length, permissions}; committed_regions[map_start] = committed_region{map_length, permissions};
} }
merge_regions(committed_regions); merge_regions(committed_regions);
return true; return true;
} }
bool memory_manager::decommit_memory(const uint64_t address, const size_t size) bool memory_manager::decommit_memory(const uint64_t address, const size_t size)
{ {
const auto entry = this->find_reserved_region(address); const auto entry = this->find_reserved_region(address);
if (entry == this->reserved_regions_.end()) if (entry == this->reserved_regions_.end())
{ {
return false; return false;
} }
if (entry->second.is_mmio) if (entry->second.is_mmio)
{ {
throw std::runtime_error("Not allowed to decommit MMIO!"); throw std::runtime_error("Not allowed to decommit MMIO!");
} }
const auto end = address + size; const auto end = address + size;
const auto region_end = entry->first + entry->second.length; const auto region_end = entry->first + entry->second.length;
if (region_end < end) if (region_end < end)
{ {
throw std::runtime_error("Cross region decommit not supported yet!"); throw std::runtime_error("Cross region decommit not supported yet!");
} }
auto& committed_regions = entry->second.committed_regions; auto& committed_regions = entry->second.committed_regions;
split_regions(committed_regions, {address, end}); split_regions(committed_regions, {address, end});
for (auto i = committed_regions.begin(); i != committed_regions.end();) for (auto i = committed_regions.begin(); i != committed_regions.end();)
{ {
if (i->first >= end) if (i->first >= end)
{ {
break; break;
} }
const auto sub_region_end = i->first + i->second.length; const auto sub_region_end = i->first + i->second.length;
if (i->first >= address && sub_region_end <= end) if (i->first >= address && sub_region_end <= end)
{ {
this->unmap_memory(i->first, i->second.length); this->unmap_memory(i->first, i->second.length);
i = committed_regions.erase(i); i = committed_regions.erase(i);
continue; continue;
} }
++i; ++i;
} }
return true; return true;
} }
bool memory_manager::release_memory(const uint64_t address, size_t size) bool memory_manager::release_memory(const uint64_t address, size_t size)
{ {
const auto entry = this->reserved_regions_.find(address); const auto entry = this->reserved_regions_.find(address);
if (entry == this->reserved_regions_.end()) if (entry == this->reserved_regions_.end())
{ {
return false; return false;
} }
if (!size) if (!size)
{ {
size = entry->second.length; size = entry->second.length;
} }
if (size > entry->second.length) if (size > entry->second.length)
{ {
throw std::runtime_error("Cross region release not supported yet!"); throw std::runtime_error("Cross region release not supported yet!");
} }
const auto end = address + size; const auto end = address + size;
auto& committed_regions = entry->second.committed_regions; auto& committed_regions = entry->second.committed_regions;
split_regions(committed_regions, {end}); split_regions(committed_regions, {end});
for (auto i = committed_regions.begin(); i != committed_regions.end();) for (auto i = committed_regions.begin(); i != committed_regions.end();)
{ {
if (i->first >= end) if (i->first >= end)
{ {
break; break;
} }
const auto sub_region_end = i->first + i->second.length; const auto sub_region_end = i->first + i->second.length;
if (i->first >= address && sub_region_end <= end) if (i->first >= address && sub_region_end <= end)
{ {
this->unmap_memory(i->first, i->second.length); this->unmap_memory(i->first, i->second.length);
i = committed_regions.erase(i); i = committed_regions.erase(i);
} }
else else
{ {
++i; ++i;
} }
} }
entry->second.length -= size; entry->second.length -= size;
if (entry->second.length > 0) if (entry->second.length > 0)
{ {
this->reserved_regions_[address + size] = std::move(entry->second); this->reserved_regions_[address + size] = std::move(entry->second);
} }
this->reserved_regions_.erase(entry); this->reserved_regions_.erase(entry);
return true; return true;
} }
uint64_t memory_manager::find_free_allocation_base(const size_t size, const uint64_t start) const uint64_t memory_manager::find_free_allocation_base(const size_t size, const uint64_t start) const
{ {
uint64_t start_address = uint64_t start_address = std::max(MIN_ALLOCATION_ADDRESS, start ? start : 0x100000000ULL);
std::max(MIN_ALLOCATION_ADDRESS, start ? start : 0x100000000ULL);
for (const auto& region : this->reserved_regions_) for (const auto& region : this->reserved_regions_)
{ {
const auto region_end = region.first + region.second.length; const auto region_end = region.first + region.second.length;
if (region_end < start_address) if (region_end < start_address)
{ {
continue; continue;
} }
if (!regions_with_length_intersect(start_address, size, region.first, region.second.length)) if (!regions_with_length_intersect(start_address, size, region.first, region.second.length))
{ {
return start_address; return start_address;
} }
start_address = page_align_up(region_end); start_address = page_align_up(region_end);
} }
if (start_address + size <= MAX_ALLOCATION_ADDRESS) if (start_address + size <= MAX_ALLOCATION_ADDRESS)
{ {
return start_address; return start_address;
} }
return 0; return 0;
} }
region_info memory_manager::get_region_info(const uint64_t address) region_info memory_manager::get_region_info(const uint64_t address)
{ {
region_info result{}; region_info result{};
result.start = MIN_ALLOCATION_ADDRESS; result.start = MIN_ALLOCATION_ADDRESS;
result.length = MAX_ALLOCATION_ADDRESS - result.start; result.length = MAX_ALLOCATION_ADDRESS - result.start;
result.permissions = memory_permission::none; result.permissions = memory_permission::none;
result.allocation_base = {}; result.allocation_base = {};
result.allocation_length = result.length; result.allocation_length = result.length;
result.is_committed = false; result.is_committed = false;
result.is_reserved = false; result.is_reserved = false;
if (this->reserved_regions_.empty()) if (this->reserved_regions_.empty())
{ {
return result; return result;
} }
auto upper_bound = this->reserved_regions_.upper_bound(address); auto upper_bound = this->reserved_regions_.upper_bound(address);
if (upper_bound == this->reserved_regions_.begin()) if (upper_bound == this->reserved_regions_.begin())
{ {
result.length = upper_bound->first - result.start; result.length = upper_bound->first - result.start;
return result; return result;
} }
const auto entry = --upper_bound; const auto entry = --upper_bound;
const auto lower_end = entry->first + entry->second.length; const auto lower_end = entry->first + entry->second.length;
if (lower_end <= address) if (lower_end <= address)
{ {
result.start = lower_end; result.start = lower_end;
result.length = MAX_ALLOCATION_ADDRESS - result.start; result.length = MAX_ALLOCATION_ADDRESS - result.start;
return result; return result;
} }
// We have a reserved region // We have a reserved region
const auto& reserved_region = entry->second; const auto& reserved_region = entry->second;
const auto& committed_regions = reserved_region.committed_regions; const auto& committed_regions = reserved_region.committed_regions;
result.is_reserved = true; result.is_reserved = true;
result.allocation_base = entry->first; result.allocation_base = entry->first;
result.allocation_length = reserved_region.length; result.allocation_length = reserved_region.length;
result.start = result.allocation_base; result.start = result.allocation_base;
result.length = result.allocation_length; result.length = result.allocation_length;
if (committed_regions.empty()) if (committed_regions.empty())
{ {
return result; return result;
} }
auto committed_bound = committed_regions.upper_bound(address); auto committed_bound = committed_regions.upper_bound(address);
if (committed_bound == committed_regions.begin()) if (committed_bound == committed_regions.begin())
{ {
result.length = committed_bound->first - result.start; result.length = committed_bound->first - result.start;
return result; return result;
} }
const auto committed_entry = --committed_bound; const auto committed_entry = --committed_bound;
const auto committed_lower_end = committed_entry->first + committed_entry->second.length; const auto committed_lower_end = committed_entry->first + committed_entry->second.length;
if (committed_lower_end <= address) if (committed_lower_end <= address)
{ {
result.start = committed_lower_end; result.start = committed_lower_end;
result.length = lower_end - result.start; result.length = lower_end - result.start;
return result; return result;
} }
result.is_committed = true; result.is_committed = true;
result.start = committed_entry->first; result.start = committed_entry->first;
result.length = committed_entry->second.length; result.length = committed_entry->second.length;
result.permissions = committed_entry->second.pemissions; result.permissions = committed_entry->second.pemissions;
return result; return result;
} }
memory_manager::reserved_region_map::iterator memory_manager::find_reserved_region(const uint64_t address) memory_manager::reserved_region_map::iterator memory_manager::find_reserved_region(const uint64_t address)
{ {
if (this->reserved_regions_.empty()) if (this->reserved_regions_.empty())
{ {
return this->reserved_regions_.end(); return this->reserved_regions_.end();
} }
auto upper_bound = this->reserved_regions_.upper_bound(address); auto upper_bound = this->reserved_regions_.upper_bound(address);
if (upper_bound == this->reserved_regions_.begin()) if (upper_bound == this->reserved_regions_.begin())
{ {
return this->reserved_regions_.end(); return this->reserved_regions_.end();
} }
const auto entry = --upper_bound; const auto entry = --upper_bound;
if (entry->first + entry->second.length <= address) if (entry->first + entry->second.length <= address)
{ {
return this->reserved_regions_.end(); return this->reserved_regions_.end();
} }
return entry; return entry;
} }
bool memory_manager::overlaps_reserved_region(const uint64_t address, const size_t size) const bool memory_manager::overlaps_reserved_region(const uint64_t address, const size_t size) const
{ {
for (const auto& region : this->reserved_regions_) for (const auto& region : this->reserved_regions_)
{ {
if (regions_with_length_intersect(address, size, region.first, region.second.length)) if (regions_with_length_intersect(address, size, region.first, region.second.length))
{ {
return true; return true;
} }
} }
return false; return false;
} }

View File

@@ -7,10 +7,10 @@
struct region_info : basic_memory_region struct region_info : basic_memory_region
{ {
uint64_t allocation_base{}; uint64_t allocation_base{};
size_t allocation_length{}; size_t allocation_length{};
bool is_reserved{}; bool is_reserved{};
bool is_committed{}; bool is_committed{};
}; };
using mmio_read_callback = std::function<uint64_t(uint64_t addr, size_t size)>; using mmio_read_callback = std::function<uint64_t(uint64_t addr, size_t size)>;
@@ -18,115 +18,114 @@ using mmio_write_callback = std::function<void(uint64_t addr, size_t size, uint6
class memory_manager class memory_manager
{ {
public: public:
struct committed_region struct committed_region
{ {
size_t length{}; size_t length{};
memory_permission pemissions{}; memory_permission pemissions{};
}; };
using committed_region_map = std::map<uint64_t, committed_region>; using committed_region_map = std::map<uint64_t, committed_region>;
struct reserved_region struct reserved_region
{ {
size_t length{}; size_t length{};
committed_region_map committed_regions{}; committed_region_map committed_regions{};
bool is_mmio{false}; bool is_mmio{false};
}; };
virtual ~memory_manager() = default; virtual ~memory_manager() = default;
template <typename T> template <typename T>
T read_memory(const uint64_t address) const T read_memory(const uint64_t address) const
{ {
T value{}; T value{};
this->read_memory(address, &value, sizeof(value)); this->read_memory(address, &value, sizeof(value));
return value; return value;
} }
template <typename T> template <typename T>
T read_memory(const void* address) const T read_memory(const void* address) const
{ {
return this->read_memory<T>(reinterpret_cast<uint64_t>(address)); return this->read_memory<T>(reinterpret_cast<uint64_t>(address));
} }
std::vector<std::byte> read_memory(const uint64_t address, const size_t size) const std::vector<std::byte> read_memory(const uint64_t address, const size_t size) const
{ {
std::vector<std::byte> data{}; std::vector<std::byte> data{};
data.resize(size); data.resize(size);
this->read_memory(address, data.data(), data.size()); this->read_memory(address, data.data(), data.size());
return data; return data;
} }
std::vector<std::byte> read_memory(const void* address, const size_t size) const std::vector<std::byte> read_memory(const void* address, const size_t size) const
{ {
return this->read_memory(reinterpret_cast<uint64_t>(address), size); return this->read_memory(reinterpret_cast<uint64_t>(address), size);
} }
template <typename T> template <typename T>
void write_memory(const uint64_t address, const T& value) void write_memory(const uint64_t address, const T& value)
{ {
this->write_memory(address, &value, sizeof(value)); this->write_memory(address, &value, sizeof(value));
} }
template <typename T> template <typename T>
void write_memory(void* address, const T& value) void write_memory(void* address, const T& value)
{ {
this->write_memory(reinterpret_cast<uint64_t>(address), &value, sizeof(value)); this->write_memory(reinterpret_cast<uint64_t>(address), &value, sizeof(value));
} }
void write_memory(void* address, const void* data, const size_t size) void write_memory(void* address, const void* data, const size_t size)
{ {
this->write_memory(reinterpret_cast<uint64_t>(address), data, size); this->write_memory(reinterpret_cast<uint64_t>(address), data, size);
} }
virtual void read_memory(uint64_t address, void* data, size_t size) const = 0; virtual void read_memory(uint64_t address, void* data, size_t size) const = 0;
virtual bool try_read_memory(uint64_t address, void* data, size_t size) const = 0; virtual bool try_read_memory(uint64_t address, void* data, size_t size) const = 0;
virtual void write_memory(uint64_t address, const void* data, size_t size) = 0; virtual void write_memory(uint64_t address, const void* data, size_t size) = 0;
bool protect_memory(uint64_t address, size_t size, memory_permission permissions, bool protect_memory(uint64_t address, size_t size, memory_permission permissions,
memory_permission* old_permissions = nullptr); memory_permission* old_permissions = nullptr);
bool allocate_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb); bool allocate_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb);
bool allocate_memory(uint64_t address, size_t size, memory_permission permissions, bool allocate_memory(uint64_t address, size_t size, memory_permission permissions, bool reserve_only = false);
bool reserve_only = false);
bool commit_memory(uint64_t address, size_t size, memory_permission permissions); bool commit_memory(uint64_t address, size_t size, memory_permission permissions);
bool decommit_memory(uint64_t address, size_t size); bool decommit_memory(uint64_t address, size_t size);
bool release_memory(uint64_t address, size_t size); bool release_memory(uint64_t address, size_t size);
uint64_t find_free_allocation_base(size_t size, uint64_t start = 0) const; uint64_t find_free_allocation_base(size_t size, uint64_t start = 0) const;
region_info get_region_info(uint64_t address); region_info get_region_info(uint64_t address);
uint64_t allocate_memory(const size_t size, const memory_permission permissions, const bool reserve_only = false) uint64_t allocate_memory(const size_t size, const memory_permission permissions, const bool reserve_only = false)
{ {
const auto allocation_base = this->find_free_allocation_base(size); const auto allocation_base = this->find_free_allocation_base(size);
if (!allocate_memory(allocation_base, size, permissions, reserve_only)) if (!allocate_memory(allocation_base, size, permissions, reserve_only))
{ {
return 0; return 0;
} }
return allocation_base; return allocation_base;
} }
private: private:
using reserved_region_map = std::map<uint64_t, reserved_region>; using reserved_region_map = std::map<uint64_t, reserved_region>;
reserved_region_map reserved_regions_{}; reserved_region_map reserved_regions_{};
reserved_region_map::iterator find_reserved_region(uint64_t address); reserved_region_map::iterator find_reserved_region(uint64_t address);
bool overlaps_reserved_region(uint64_t address, size_t size) const; bool overlaps_reserved_region(uint64_t address, size_t size) const;
virtual void map_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb) = 0; virtual void map_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb) = 0;
virtual void map_memory(uint64_t address, size_t size, memory_permission permissions) = 0; virtual void map_memory(uint64_t address, size_t size, memory_permission permissions) = 0;
virtual void unmap_memory(uint64_t address, size_t size) = 0; virtual void unmap_memory(uint64_t address, size_t size) = 0;
virtual void apply_memory_protection(uint64_t address, size_t size, memory_permission permissions) = 0; virtual void apply_memory_protection(uint64_t address, size_t size, memory_permission permissions) = 0;
protected: protected:
void serialize_memory_state(utils::buffer_serializer& buffer, bool is_snapshot) const; void serialize_memory_state(utils::buffer_serializer& buffer, bool is_snapshot) const;
void deserialize_memory_state(utils::buffer_deserializer& buffer, bool is_snapshot); void deserialize_memory_state(utils::buffer_deserializer& buffer, bool is_snapshot);
}; };

View File

@@ -3,62 +3,52 @@
enum class memory_permission : uint8_t enum class memory_permission : uint8_t
{ {
none = 0, none = 0,
read = 1 << 0, read = 1 << 0,
write = 1 << 1, write = 1 << 1,
exec = 1 << 2, exec = 1 << 2,
read_write = read | write, read_write = read | write,
all = read | write | exec all = read | write | exec
}; };
/***************************************************************************** /*****************************************************************************
* *
****************************************************************************/ ****************************************************************************/
inline constexpr memory_permission inline constexpr memory_permission operator&(const memory_permission x, const memory_permission y)
operator&(const memory_permission x, const memory_permission y)
{ {
return static_cast<memory_permission> return static_cast<memory_permission>(static_cast<uint8_t>(x) & static_cast<uint8_t>(y));
(static_cast<uint8_t>(x) & static_cast<uint8_t>(y));
} }
inline constexpr memory_permission inline constexpr memory_permission operator|(const memory_permission x, const memory_permission y)
operator|(const memory_permission x, const memory_permission y)
{ {
return static_cast<memory_permission> return static_cast<memory_permission>(static_cast<uint8_t>(x) | static_cast<uint8_t>(y));
(static_cast<uint8_t>(x) | static_cast<uint8_t>(y));
} }
inline constexpr memory_permission inline constexpr memory_permission operator^(const memory_permission x, const memory_permission y)
operator^(const memory_permission x, const memory_permission y)
{ {
return static_cast<memory_permission> return static_cast<memory_permission>(static_cast<uint8_t>(x) ^ static_cast<uint8_t>(y));
(static_cast<uint8_t>(x) ^ static_cast<uint8_t>(y));
} }
inline constexpr memory_permission inline constexpr memory_permission operator~(memory_permission x)
operator~(memory_permission x)
{ {
return static_cast<memory_permission>(~static_cast<uint8_t>(x)); return static_cast<memory_permission>(~static_cast<uint8_t>(x));
} }
inline memory_permission& inline memory_permission& operator&=(memory_permission& x, const memory_permission y)
operator&=(memory_permission& x, const memory_permission y)
{ {
x = x & y; x = x & y;
return x; return x;
} }
inline memory_permission& inline memory_permission& operator|=(memory_permission& x, const memory_permission y)
operator|=(memory_permission& x, const memory_permission y)
{ {
x = x | y; x = x | y;
return x; return x;
} }
inline memory_permission& inline memory_permission& operator^=(memory_permission& x, const memory_permission y)
operator^=(memory_permission& x, const memory_permission y)
{ {
x = x ^ y; x = x ^ y;
return x; return x;
} }

View File

@@ -4,12 +4,12 @@
struct basic_memory_region struct basic_memory_region
{ {
uint64_t start{}; uint64_t start{};
size_t length{}; size_t length{};
memory_permission permissions{}; memory_permission permissions{};
}; };
struct memory_region : basic_memory_region struct memory_region : basic_memory_region
{ {
bool committed{}; bool committed{};
}; };

View File

@@ -3,52 +3,52 @@
class scoped_hook class scoped_hook
{ {
public: public:
scoped_hook() = default; scoped_hook() = default;
scoped_hook(emulator& emu, emulator_hook* hook) scoped_hook(emulator& emu, emulator_hook* hook)
: emu_(&emu) : emu_(&emu),
, hook_(hook) hook_(hook)
{ {
} }
~scoped_hook() ~scoped_hook()
{ {
this->remove(); this->remove();
} }
scoped_hook(const scoped_hook&) = delete; scoped_hook(const scoped_hook&) = delete;
scoped_hook& operator=(const scoped_hook&) = delete; scoped_hook& operator=(const scoped_hook&) = delete;
scoped_hook(scoped_hook&& obj) noexcept scoped_hook(scoped_hook&& obj) noexcept
{ {
this->operator=(std::move(obj)); this->operator=(std::move(obj));
} }
scoped_hook& operator=(scoped_hook&& obj) noexcept scoped_hook& operator=(scoped_hook&& obj) noexcept
{ {
if (this != &obj) if (this != &obj)
{ {
this->remove(); this->remove();
this->emu_ = obj.emu_; this->emu_ = obj.emu_;
this->hook_ = obj.hook_; this->hook_ = obj.hook_;
obj.hook_ = {}; obj.hook_ = {};
} }
return *this; return *this;
} }
void remove() void remove()
{ {
if (this->hook_) if (this->hook_)
{ {
this->emu_->delete_hook(this->hook_); this->emu_->delete_hook(this->hook_);
this->hook_ = {}; this->hook_ = {};
} }
} }
private: private:
emulator* emu_{}; emulator* emu_{};
emulator_hook* hook_{}; emulator_hook* hook_{};
}; };

View File

@@ -11,488 +11,483 @@
namespace utils namespace utils
{ {
class buffer_serializer; class buffer_serializer;
class buffer_deserializer; class buffer_deserializer;
template <typename T> template <typename T>
concept Serializable = requires(T a, const T ac, buffer_serializer& serializer, buffer_deserializer& deserializer) concept Serializable = requires(T a, const T ac, buffer_serializer& serializer, buffer_deserializer& deserializer) {
{ { ac.serialize(serializer) } -> std::same_as<void>;
{ ac.serialize(serializer) } -> std::same_as<void>; { a.deserialize(deserializer) } -> std::same_as<void>;
{ a.deserialize(deserializer) } -> std::same_as<void>; };
};
/* Use concept instead, to prevent overhead of virtual function calls /* Use concept instead, to prevent overhead of virtual function calls
struct serializable struct serializable
{ {
virtual ~serializable() = default; virtual ~serializable() = default;
virtual void serialize(buffer_serializer& buffer) const = 0; virtual void serialize(buffer_serializer& buffer) const = 0;
virtual void deserialize(buffer_deserializer& buffer) = 0; virtual void deserialize(buffer_deserializer& buffer) = 0;
}; };
*/ */
namespace detail namespace detail
{ {
template <typename, typename = void> template <typename, typename = void>
struct has_serialize_function : std::false_type struct has_serialize_function : std::false_type
{ {
}; };
template <typename T> template <typename T>
struct has_serialize_function<T, std::void_t<decltype(serialize(std::declval<buffer_serializer&>(), struct has_serialize_function<T,
std::declval<const std::remove_cvref_t<T>&>()) std::void_t<decltype(serialize(std::declval<buffer_serializer&>(),
)>> std::declval<const std::remove_cvref_t<T>&>()))>>
: std::true_type : std::true_type
{ {
}; };
template <typename, typename = void> template <typename, typename = void>
struct has_deserialize_function : std::false_type struct has_deserialize_function : std::false_type
{ {
}; };
template <typename T> template <typename T>
struct has_deserialize_function<T, std::void_t<decltype(deserialize( struct has_deserialize_function<T, std::void_t<decltype(deserialize(std::declval<buffer_deserializer&>(),
std::declval<buffer_deserializer&>(), std::declval<std::remove_cvref_t<T>&>()))>>
std::declval<std::remove_cvref_t<T>&>()))>> : std::true_type
: std::true_type {
{ };
};
template <typename T> template <typename T>
struct has_deserializer_constructor struct has_deserializer_constructor : std::bool_constant<std::is_constructible_v<T, buffer_deserializer&>>
: std::bool_constant<std::is_constructible_v<T, buffer_deserializer&>> {
{ };
}; }
}
class buffer_deserializer class buffer_deserializer
{ {
public: public:
template <typename T> template <typename T>
buffer_deserializer(const std::span<T> buffer, bool no_debugging = false) buffer_deserializer(const std::span<T> buffer, bool no_debugging = false)
: no_debugging_(no_debugging) : no_debugging_(no_debugging),
, buffer_(reinterpret_cast<const std::byte*>(buffer.data()), buffer.size() * sizeof(T)) buffer_(reinterpret_cast<const std::byte*>(buffer.data()), buffer.size() * sizeof(T))
{ {
static_assert(std::is_trivially_copyable_v<T>, "Type must be trivially copyable"); static_assert(std::is_trivially_copyable_v<T>, "Type must be trivially copyable");
} }
template <typename T> template <typename T>
buffer_deserializer(const std::vector<T>& buffer, bool no_debugging = false) buffer_deserializer(const std::vector<T>& buffer, bool no_debugging = false)
: buffer_deserializer(std::span(buffer), no_debugging) : buffer_deserializer(std::span(buffer), no_debugging)
{ {
} }
std::span<const std::byte> read_data(const size_t length) std::span<const std::byte> read_data(const size_t length)
{ {
#ifndef NDEBUG #ifndef NDEBUG
const uint64_t real_old_size = this->offset_; const uint64_t real_old_size = this->offset_;
(void)real_old_size; (void)real_old_size;
#endif #endif
if (this->offset_ + length > this->buffer_.size()) if (this->offset_ + length > this->buffer_.size())
{ {
throw std::runtime_error("Out of bounds read from byte buffer"); throw std::runtime_error("Out of bounds read from byte buffer");
} }
const std::span result(this->buffer_.data() + this->offset_, length); const std::span result(this->buffer_.data() + this->offset_, length);
this->offset_ += length; this->offset_ += length;
(void)this->no_debugging_; (void)this->no_debugging_;
#ifndef NDEBUG #ifndef NDEBUG
if (!this->no_debugging_) if (!this->no_debugging_)
{ {
uint64_t old_size{}; uint64_t old_size{};
if (this->offset_ + sizeof(old_size) > this->buffer_.size()) if (this->offset_ + sizeof(old_size) > this->buffer_.size())
{ {
throw std::runtime_error("Out of bounds read from byte buffer"); throw std::runtime_error("Out of bounds read from byte buffer");
} }
memcpy(&old_size, this->buffer_.data() + this->offset_, sizeof(old_size)); memcpy(&old_size, this->buffer_.data() + this->offset_, sizeof(old_size));
if (old_size != real_old_size) if (old_size != real_old_size)
{ {
throw std::runtime_error("Reading from serialized buffer mismatches written data!"); throw std::runtime_error("Reading from serialized buffer mismatches written data!");
} }
this->offset_ += sizeof(old_size); this->offset_ += sizeof(old_size);
} }
#endif #endif
return result; return result;
} }
void read(void* data, const size_t length) void read(void* data, const size_t length)
{ {
const auto span = this->read_data(length); const auto span = this->read_data(length);
memcpy(data, span.data(), length); memcpy(data, span.data(), length);
} }
template <typename T> template <typename T>
void read(T& object) void read(T& object)
{ {
constexpr auto is_trivially_copyable = std::is_trivially_copyable_v<T>; constexpr auto is_trivially_copyable = std::is_trivially_copyable_v<T>;
if constexpr (Serializable<T>) if constexpr (Serializable<T>)
{ {
object.deserialize(*this); object.deserialize(*this);
} }
else if constexpr (detail::has_deserialize_function<T>::value) else if constexpr (detail::has_deserialize_function<T>::value)
{ {
deserialize(*this, object); deserialize(*this, object);
} }
else if constexpr (is_trivially_copyable) else if constexpr (is_trivially_copyable)
{ {
union union
{ {
T* type_{}; T* type_{};
void* void_; void* void_;
} pointers; } pointers;
pointers.type_ = &object; pointers.type_ = &object;
this->read(pointers.void_, sizeof(object)); this->read(pointers.void_, sizeof(object));
} }
else else
{ {
static_assert(!is_trivially_copyable, "Key must be trivially copyable or implement serializable!"); static_assert(!is_trivially_copyable, "Key must be trivially copyable or implement serializable!");
std::abort(); std::abort();
} }
} }
template <typename T> template <typename T>
T read() T read()
{ {
auto object = this->construct_object<T>(); auto object = this->construct_object<T>();
this->read(object); this->read(object);
return object; return object;
} }
template <typename T> template <typename T>
void read_optional(std::optional<T>& val) void read_optional(std::optional<T>& val)
{ {
if (this->read<bool>()) if (this->read<bool>())
{ {
val.emplace(this->read<T>()); val.emplace(this->read<T>());
} }
else else
{ {
val = std::nullopt; val = std::nullopt;
} }
} }
template <typename T, typename F> template <typename T, typename F>
requires(std::is_invocable_r_v<T, F>) requires(std::is_invocable_r_v<T, F>)
void read_optional(std::optional<T>& val, const F& factory) void read_optional(std::optional<T>& val, const F& factory)
{ {
if (this->read<bool>()) if (this->read<bool>())
{ {
val.emplace(factory()); val.emplace(factory());
this->read<T>(*val); this->read<T>(*val);
} }
else else
{ {
val = {}; val = {};
} }
} }
template <typename T> template <typename T>
void read_vector(std::vector<T>& result) void read_vector(std::vector<T>& result)
{ {
const auto size = this->read<uint64_t>(); const auto size = this->read<uint64_t>();
result.clear(); result.clear();
result.reserve(size); result.reserve(size);
for (uint64_t i = 0; i < size; ++i) for (uint64_t i = 0; i < size; ++i)
{ {
result.emplace_back(this->read<T>()); result.emplace_back(this->read<T>());
} }
} }
template <typename T> template <typename T>
std::vector<T> read_vector() std::vector<T> read_vector()
{ {
std::vector<T> result{}; std::vector<T> result{};
this->read_vector(result); this->read_vector(result);
return result; return result;
} }
template <typename Map> template <typename Map>
void read_map(Map& map) void read_map(Map& map)
{ {
using key_type = typename Map::key_type; using key_type = typename Map::key_type;
using value_type = typename Map::mapped_type; using value_type = typename Map::mapped_type;
map.clear(); map.clear();
const auto size = this->read<uint64_t>(); const auto size = this->read<uint64_t>();
for (uint64_t i = 0; i < size; ++i) for (uint64_t i = 0; i < size; ++i)
{ {
auto key = this->read<key_type>(); auto key = this->read<key_type>();
auto value = this->read<value_type>(); auto value = this->read<value_type>();
map.emplace(std::move(key), std::move(value)); map.emplace(std::move(key), std::move(value));
} }
} }
template <typename Map> template <typename Map>
Map read_map() Map read_map()
{ {
Map map{}; Map map{};
this->read_map(map); this->read_map(map);
return map; return map;
} }
template <typename T = char> template <typename T = char>
void read_string(std::basic_string<T>& result) void read_string(std::basic_string<T>& result)
{ {
const auto size = this->read<uint64_t>(); const auto size = this->read<uint64_t>();
result.clear(); result.clear();
result.reserve(size); result.reserve(size);
for (uint64_t i = 0; i < size; ++i) for (uint64_t i = 0; i < size; ++i)
{ {
result.push_back(this->read<T>()); result.push_back(this->read<T>());
} }
} }
template <typename T= char> template <typename T = char>
std::basic_string<T> read_string() std::basic_string<T> read_string()
{ {
std::basic_string<T> result{}; std::basic_string<T> result{};
this->read_string(result); this->read_string(result);
return result; return result;
} }
size_t get_remaining_size() const size_t get_remaining_size() const
{ {
return this->buffer_.size() - offset_; return this->buffer_.size() - offset_;
} }
std::span<const std::byte> get_remaining_data() std::span<const std::byte> get_remaining_data()
{ {
return this->read_data(this->get_remaining_size()); return this->read_data(this->get_remaining_size());
} }
size_t get_offset() const size_t get_offset() const
{ {
return this->offset_; return this->offset_;
} }
template <typename T, typename F> template <typename T, typename F>
requires(std::is_invocable_r_v<T, F>) requires(std::is_invocable_r_v<T, F>)
void register_factory(F factory) void register_factory(F factory)
{ {
this->factories_[std::type_index(typeid(T))] = [f = std::move(factory)]() -> T* { this->factories_[std::type_index(typeid(T))] = [f = std::move(factory)]() -> T* { return new T(f()); };
return new T(f()); }
};
}
private: private:
bool no_debugging_{false}; bool no_debugging_{false};
size_t offset_{0}; size_t offset_{0};
std::span<const std::byte> buffer_{}; std::span<const std::byte> buffer_{};
std::unordered_map<std::type_index, std::function<void*()>> factories_{}; std::unordered_map<std::type_index, std::function<void*()>> factories_{};
template <typename T> template <typename T>
T construct_object() T construct_object()
{ {
if constexpr (detail::has_deserializer_constructor<T>::value) if constexpr (detail::has_deserializer_constructor<T>::value)
{ {
return T(*this); return T(*this);
} }
else if constexpr (std::is_default_constructible_v<T>) else if constexpr (std::is_default_constructible_v<T>)
{ {
return {}; return {};
} }
else else
{ {
const auto factory = this->factories_.find(std::type_index(typeid(T))); const auto factory = this->factories_.find(std::type_index(typeid(T)));
if (factory == this->factories_.end()) if (factory == this->factories_.end())
{ {
throw std::runtime_error( throw std::runtime_error("Object construction failed. Missing factory for type: " +
"Object construction failed. Missing factory for type: " + std::string(typeid(T).name())); std::string(typeid(T).name()));
} }
auto* object = static_cast<T*>(factory->second()); auto* object = static_cast<T*>(factory->second());
auto obj = std::move(*object); auto obj = std::move(*object);
delete object; delete object;
return obj; return obj;
} }
} }
}; };
class buffer_serializer class buffer_serializer
{ {
public: public:
buffer_serializer() = default; buffer_serializer() = default;
void write(const void* buffer, const size_t length) void write(const void* buffer, const size_t length)
{ {
#ifndef NDEBUG #ifndef NDEBUG
const uint64_t old_size = this->buffer_.size(); const uint64_t old_size = this->buffer_.size();
#endif #endif
const auto* byte_buffer = static_cast<const std::byte*>(buffer); const auto* byte_buffer = static_cast<const std::byte*>(buffer);
this->buffer_.insert(this->buffer_.end(), byte_buffer, byte_buffer + length); this->buffer_.insert(this->buffer_.end(), byte_buffer, byte_buffer + length);
#ifndef NDEBUG #ifndef NDEBUG
const auto* security_buffer = reinterpret_cast<const std::byte*>(&old_size); const auto* security_buffer = reinterpret_cast<const std::byte*>(&old_size);
this->buffer_.insert(this->buffer_.end(), security_buffer, security_buffer + sizeof(old_size)); this->buffer_.insert(this->buffer_.end(), security_buffer, security_buffer + sizeof(old_size));
#endif #endif
} }
void write(const buffer_serializer& object) void write(const buffer_serializer& object)
{ {
const auto& buffer = object.get_buffer(); const auto& buffer = object.get_buffer();
this->write(buffer.data(), buffer.size()); this->write(buffer.data(), buffer.size());
} }
template <typename T> template <typename T>
void write(const T& object) void write(const T& object)
{ {
constexpr auto is_trivially_copyable = std::is_trivially_copyable_v<T>; constexpr auto is_trivially_copyable = std::is_trivially_copyable_v<T>;
if constexpr (Serializable<T>) if constexpr (Serializable<T>)
{ {
object.serialize(*this); object.serialize(*this);
} }
else if constexpr (detail::has_serialize_function<T>::value) else if constexpr (detail::has_serialize_function<T>::value)
{ {
serialize(*this, object); serialize(*this, object);
} }
else if constexpr (is_trivially_copyable) else if constexpr (is_trivially_copyable)
{ {
union union
{ {
const T* type_{}; const T* type_{};
const void* void_; const void* void_;
} pointers; } pointers;
pointers.type_ = &object; pointers.type_ = &object;
this->write(pointers.void_, sizeof(object)); this->write(pointers.void_, sizeof(object));
} }
else else
{ {
static_assert(!is_trivially_copyable, "Key must be trivially copyable or implement serializable!"); static_assert(!is_trivially_copyable, "Key must be trivially copyable or implement serializable!");
std::abort(); std::abort();
} }
} }
template <typename T> template <typename T>
void write_optional(const std::optional<T>& val) void write_optional(const std::optional<T>& val)
{ {
this->write(val.has_value()); this->write(val.has_value());
if (val.has_value()) if (val.has_value())
{ {
this->write(*val); this->write(*val);
} }
} }
template <typename T> template <typename T>
void write_span(const std::span<T> vec) void write_span(const std::span<T> vec)
{ {
this->write(static_cast<uint64_t>(vec.size())); this->write(static_cast<uint64_t>(vec.size()));
for (const auto& v : vec) for (const auto& v : vec)
{ {
this->write(v); this->write(v);
} }
} }
template <typename T> template <typename T>
void write_vector(const std::vector<T> vec) void write_vector(const std::vector<T> vec)
{ {
this->write_span(std::span(vec)); this->write_span(std::span(vec));
} }
template <typename T> template <typename T>
void write_string(const std::basic_string_view<T> str) void write_string(const std::basic_string_view<T> str)
{ {
this->write_span<const T>(str); this->write_span<const T>(str);
} }
template <typename T> template <typename T>
void write_string(const std::basic_string<T>& str) void write_string(const std::basic_string<T>& str)
{ {
this->write_string(std::basic_string_view<T>(str)); this->write_string(std::basic_string_view<T>(str));
} }
template <typename Map> template <typename Map>
void write_map(const Map& map) void write_map(const Map& map)
{ {
this->write<uint64_t>(map.size()); this->write<uint64_t>(map.size());
for (const auto& entry : map) for (const auto& entry : map)
{ {
this->write(entry.first); this->write(entry.first);
this->write(entry.second); this->write(entry.second);
} }
} }
const std::vector<std::byte>& get_buffer() const const std::vector<std::byte>& get_buffer() const
{ {
return this->buffer_; return this->buffer_;
} }
std::vector<std::byte> move_buffer() std::vector<std::byte> move_buffer()
{ {
return std::move(this->buffer_); return std::move(this->buffer_);
} }
private: private:
std::vector<std::byte> buffer_{}; std::vector<std::byte> buffer_{};
}; };
template <> template <>
inline void buffer_deserializer::read<bool>(bool& object) inline void buffer_deserializer::read<bool>(bool& object)
{ {
object = this->read<uint8_t>() != 0; object = this->read<uint8_t>() != 0;
} }
template <> template <>
inline void buffer_deserializer::read<std::string>(std::string& object) inline void buffer_deserializer::read<std::string>(std::string& object)
{ {
object = this->read_string<char>(); object = this->read_string<char>();
} }
template <> template <>
inline void buffer_deserializer::read<std::wstring>(std::wstring& object) inline void buffer_deserializer::read<std::wstring>(std::wstring& object)
{ {
object = this->read_string<wchar_t>(); object = this->read_string<wchar_t>();
} }
template <> template <>
inline void buffer_deserializer::read<std::u16string>(std::u16string& object) inline void buffer_deserializer::read<std::u16string>(std::u16string& object)
{ {
object = this->read_string<char16_t>(); object = this->read_string<char16_t>();
} }
template <> template <>
inline void buffer_serializer::write<bool>(const bool& object) inline void buffer_serializer::write<bool>(const bool& object)
{ {
this->write<uint8_t>(object ? 1 : 0); this->write<uint8_t>(object ? 1 : 0);
} }
template <> template <>
inline void buffer_serializer::write<std::string>(const std::string& object) inline void buffer_serializer::write<std::string>(const std::string& object)
{ {
this->write_string(object); this->write_string(object);
} }
template <> template <>
inline void buffer_serializer::write<std::wstring>(const std::wstring& object) inline void buffer_serializer::write<std::wstring>(const std::wstring& object)
{ {
this->write_string(object); this->write_string(object);
} }
template <> template <>
inline void buffer_serializer::write<std::u16string>(const std::u16string& object) inline void buffer_serializer::write<std::u16string>(const std::u16string& object)
{ {
this->write_string(object); this->write_string(object);
} }
} }

View File

@@ -7,41 +7,41 @@
namespace utils namespace utils
{ {
inline void serialize(buffer_serializer& buffer, const std::chrono::steady_clock::time_point& tp) inline void serialize(buffer_serializer& buffer, const std::chrono::steady_clock::time_point& tp)
{ {
buffer.write(tp.time_since_epoch().count()); buffer.write(tp.time_since_epoch().count());
} }
inline void deserialize(buffer_deserializer& buffer, std::chrono::steady_clock::time_point& tp) inline void deserialize(buffer_deserializer& buffer, std::chrono::steady_clock::time_point& tp)
{ {
using time_point = std::chrono::steady_clock::time_point; using time_point = std::chrono::steady_clock::time_point;
using duration = time_point::duration; using duration = time_point::duration;
const auto count = buffer.read<duration::rep>(); const auto count = buffer.read<duration::rep>();
tp = time_point{duration{count}}; tp = time_point{duration{count}};
} }
inline void serialize(buffer_serializer& buffer, const std::chrono::system_clock::time_point& tp) inline void serialize(buffer_serializer& buffer, const std::chrono::system_clock::time_point& tp)
{ {
buffer.write(tp.time_since_epoch().count()); buffer.write(tp.time_since_epoch().count());
} }
inline void deserialize(buffer_deserializer& buffer, std::chrono::system_clock::time_point& tp) inline void deserialize(buffer_deserializer& buffer, std::chrono::system_clock::time_point& tp)
{ {
using time_point = std::chrono::system_clock::time_point; using time_point = std::chrono::system_clock::time_point;
using duration = time_point::duration; using duration = time_point::duration;
const auto count = buffer.read<duration::rep>(); const auto count = buffer.read<duration::rep>();
tp = time_point{duration{count}}; tp = time_point{duration{count}};
} }
inline void serialize(buffer_serializer& buffer, const std::filesystem::path& path) inline void serialize(buffer_serializer& buffer, const std::filesystem::path& path)
{ {
buffer.write_string<char16_t>(path.u16string()); buffer.write_string<char16_t>(path.u16string());
} }
inline void deserialize(buffer_deserializer& buffer, std::filesystem::path& path) inline void deserialize(buffer_deserializer& buffer, std::filesystem::path& path)
{ {
path = buffer.read_string<char16_t>(); path = buffer.read_string<char16_t>();
} }
} }

View File

@@ -2,94 +2,94 @@
#include "emulator.hpp" #include "emulator.hpp"
template <typename PointerType, typename Register, Register InstructionPointer, Register template <typename PointerType, typename Register, Register InstructionPointer, Register StackPointer,
StackPointer, typename HookableInstructions> typename HookableInstructions>
class typed_emulator : public emulator class typed_emulator : public emulator
{ {
public: public:
using registers = Register; using registers = Register;
using pointer_type = PointerType; using pointer_type = PointerType;
using hookable_instructions = HookableInstructions; using hookable_instructions = HookableInstructions;
static constexpr size_t pointer_size = sizeof(pointer_type); static constexpr size_t pointer_size = sizeof(pointer_type);
static constexpr registers stack_pointer = StackPointer; static constexpr registers stack_pointer = StackPointer;
static constexpr registers instruction_pointer = InstructionPointer; static constexpr registers instruction_pointer = InstructionPointer;
void start_from_ip(const std::chrono::nanoseconds timeout = {}, const size_t count = 0) void start_from_ip(const std::chrono::nanoseconds timeout = {}, const size_t count = 0)
{ {
this->start(this->read_instruction_pointer(), 0, timeout, count); this->start(this->read_instruction_pointer(), 0, timeout, count);
} }
void write_register(registers reg, const void* value, const size_t size) void write_register(registers reg, const void* value, const size_t size)
{ {
this->write_raw_register(static_cast<int>(reg), value, size); this->write_raw_register(static_cast<int>(reg), value, size);
} }
void read_register(registers reg, void* value, const size_t size) void read_register(registers reg, void* value, const size_t size)
{ {
this->read_raw_register(static_cast<int>(reg), value, size); this->read_raw_register(static_cast<int>(reg), value, size);
} }
template <typename T = pointer_type> template <typename T = pointer_type>
T reg(const registers regid) T reg(const registers regid)
{ {
T value{}; T value{};
this->read_register(regid, &value, sizeof(value)); this->read_register(regid, &value, sizeof(value));
return value; return value;
} }
template <typename T = pointer_type, typename S> template <typename T = pointer_type, typename S>
void reg(const registers regid, const S& maybe_value) void reg(const registers regid, const S& maybe_value)
{ {
T value = static_cast<T>(maybe_value); T value = static_cast<T>(maybe_value);
this->write_register(regid, &value, sizeof(value)); this->write_register(regid, &value, sizeof(value));
} }
pointer_type read_instruction_pointer() pointer_type read_instruction_pointer()
{ {
return this->reg(instruction_pointer); return this->reg(instruction_pointer);
} }
pointer_type read_stack_pointer() pointer_type read_stack_pointer()
{ {
return this->reg(stack_pointer); return this->reg(stack_pointer);
} }
pointer_type read_stack(const size_t index) pointer_type read_stack(const size_t index)
{ {
pointer_type result{}; pointer_type result{};
const auto sp = this->read_stack_pointer(); const auto sp = this->read_stack_pointer();
this->read_memory(sp + (index * pointer_size), &result, sizeof(result)); this->read_memory(sp + (index * pointer_size), &result, sizeof(result));
return result; return result;
} }
void push_stack(const pointer_type& value) void push_stack(const pointer_type& value)
{ {
const auto sp = this->read_stack_pointer() - pointer_size; const auto sp = this->read_stack_pointer() - pointer_size;
this->reg(stack_pointer, sp); this->reg(stack_pointer, sp);
this->write_memory(sp, &value, sizeof(value)); this->write_memory(sp, &value, sizeof(value));
} }
pointer_type pop_stack() pointer_type pop_stack()
{ {
pointer_type result{}; pointer_type result{};
const auto sp = this->read_stack_pointer(); const auto sp = this->read_stack_pointer();
this->read_memory(sp, &result, sizeof(result)); this->read_memory(sp, &result, sizeof(result));
this->reg(stack_pointer, sp + pointer_size); this->reg(stack_pointer, sp + pointer_size);
return result; return result;
} }
emulator_hook* hook_instruction(hookable_instructions instruction_type, instruction_hook_callback callback) emulator_hook* hook_instruction(hookable_instructions instruction_type, instruction_hook_callback callback)
{ {
return this->hook_instruction(static_cast<int>(instruction_type), std::move(callback)); return this->hook_instruction(static_cast<int>(instruction_type), std::move(callback));
} }
private: private:
emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) override = 0; emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) override = 0;
void read_raw_register(int reg, void* value, size_t size) override = 0; void read_raw_register(int reg, void* value, size_t size) override = 0;
void write_raw_register(int reg, const void* value, size_t size) override = 0; void write_raw_register(int reg, const void* value, size_t size) override = 0;
}; };

View File

@@ -4,12 +4,12 @@
enum class x64_hookable_instructions enum class x64_hookable_instructions
{ {
invalid, invalid,
syscall, syscall,
cpuid, cpuid,
rdtsc, rdtsc,
rdtscp, rdtscp,
}; };
using x64_emulator = typed_emulator<uint64_t, x64_register, x64_register::rip, using x64_emulator =
x64_register::rsp, x64_hookable_instructions>; typed_emulator<uint64_t, x64_register, x64_register::rip, x64_register::rsp, x64_hookable_instructions>;

View File

@@ -2,244 +2,244 @@
enum class x64_register enum class x64_register
{ {
invalid = 0, invalid = 0,
ah, ah,
al, al,
ax, ax,
bh, bh,
bl, bl,
bp, bp,
bpl, bpl,
bx, bx,
ch, ch,
cl, cl,
cs, cs,
cx, cx,
dh, dh,
di, di,
dil, dil,
dl, dl,
ds, ds,
dx, dx,
eax, eax,
ebp, ebp,
ebx, ebx,
ecx, ecx,
edi, edi,
edx, edx,
eflags, eflags,
eip, eip,
es = eip + 2, es = eip + 2,
esi, esi,
esp, esp,
fpsw, fpsw,
fs, fs,
gs, gs,
ip, ip,
rax, rax,
rbp, rbp,
rbx, rbx,
rcx, rcx,
rdi, rdi,
rdx, rdx,
rip, rip,
rsi = rip + 2, rsi = rip + 2,
rsp, rsp,
si, si,
sil, sil,
sp, sp,
spl, spl,
ss, ss,
cr0, cr0,
cr1, cr1,
cr2, cr2,
cr3, cr3,
cr4, cr4,
cr8 = cr4 + 4, cr8 = cr4 + 4,
dr0 = cr8 + 8, dr0 = cr8 + 8,
dr1, dr1,
dr2, dr2,
dr3, dr3,
dr4, dr4,
dr5, dr5,
dr6, dr6,
dr7, dr7,
fp0 = dr7 + 9, fp0 = dr7 + 9,
fp1, fp1,
fp2, fp2,
fp3, fp3,
fp4, fp4,
fp5, fp5,
fp6, fp6,
fp7, fp7,
k0, k0,
k1, k1,
k2, k2,
k3, k3,
k4, k4,
k5, k5,
k6, k6,
k7, k7,
mm0, mm0,
mm1, mm1,
mm2, mm2,
mm3, mm3,
mm4, mm4,
mm5, mm5,
mm6, mm6,
mm7, mm7,
r8, r8,
r9, r9,
r10, r10,
r11, r11,
r12, r12,
r13, r13,
r14, r14,
r15, r15,
st0, st0,
st1, st1,
st2, st2,
st3, st3,
st4, st4,
st5, st5,
st6, st6,
st7, st7,
xmm0, xmm0,
xmm1, xmm1,
xmm2, xmm2,
xmm3, xmm3,
xmm4, xmm4,
xmm5, xmm5,
xmm6, xmm6,
xmm7, xmm7,
xmm8, xmm8,
xmm9, xmm9,
xmm10, xmm10,
xmm11, xmm11,
xmm12, xmm12,
xmm13, xmm13,
xmm14, xmm14,
xmm15, xmm15,
xmm16, xmm16,
xmm17, xmm17,
xmm18, xmm18,
xmm19, xmm19,
xmm20, xmm20,
xmm21, xmm21,
xmm22, xmm22,
xmm23, xmm23,
xmm24, xmm24,
xmm25, xmm25,
xmm26, xmm26,
xmm27, xmm27,
xmm28, xmm28,
xmm29, xmm29,
xmm30, xmm30,
xmm31, xmm31,
ymm0, ymm0,
ymm1, ymm1,
ymm2, ymm2,
ymm3, ymm3,
ymm4, ymm4,
ymm5, ymm5,
ymm6, ymm6,
ymm7, ymm7,
ymm8, ymm8,
ymm9, ymm9,
ymm10, ymm10,
ymm11, ymm11,
ymm12, ymm12,
ymm13, ymm13,
ymm14, ymm14,
ymm15, ymm15,
ymm16, ymm16,
ymm17, ymm17,
ymm18, ymm18,
ymm19, ymm19,
ymm20, ymm20,
ymm21, ymm21,
ymm22, ymm22,
ymm23, ymm23,
ymm24, ymm24,
ymm25, ymm25,
ymm26, ymm26,
ymm27, ymm27,
ymm28, ymm28,
ymm29, ymm29,
ymm30, ymm30,
ymm31, ymm31,
zmm0, zmm0,
zmm1, zmm1,
zmm2, zmm2,
zmm3, zmm3,
zmm4, zmm4,
zmm5, zmm5,
zmm6, zmm6,
zmm7, zmm7,
zmm8, zmm8,
zmm9, zmm9,
zmm10, zmm10,
zmm11, zmm11,
zmm12, zmm12,
zmm13, zmm13,
zmm14, zmm14,
zmm15, zmm15,
zmm16, zmm16,
zmm17, zmm17,
zmm18, zmm18,
zmm19, zmm19,
zmm20, zmm20,
zmm21, zmm21,
zmm22, zmm22,
zmm23, zmm23,
zmm24, zmm24,
zmm25, zmm25,
zmm26, zmm26,
zmm27, zmm27,
zmm28, zmm28,
zmm29, zmm29,
zmm30, zmm30,
zmm31, zmm31,
r8b, r8b,
r9b, r9b,
r10b, r10b,
r11b, r11b,
r12b, r12b,
r13b, r13b,
r14b, r14b,
r15b, r15b,
r8d, r8d,
r9d, r9d,
r10d, r10d,
r11d, r11d,
r12d, r12d,
r13d, r13d,
r14d, r14d,
r15d, r15d,
r8w, r8w,
r9w, r9w,
r10w, r10w,
r11w, r11w,
r12w, r12w,
r13w, r13w,
r14w, r14w,
r15w, r15w,
idtr, idtr,
gdtr, gdtr,
ldtr, ldtr,
tr, tr,
fpcw, fpcw,
fptag, fptag,
msr, msr,
mxcsr, mxcsr,
fs_base, fs_base,
gs_base, gs_base,
flags, flags,
rflags, rflags,
fip, fip,
fcs, fcs,
fdp, fdp,
fds, fds,
fop, fop,
end, // Must be last end, // Must be last
}; };

View File

@@ -9,192 +9,183 @@ bool use_gdb = false;
namespace namespace
{ {
void run_emulation(windows_emulator& win_emu) void run_emulation(windows_emulator& win_emu)
{ {
try try
{ {
win_emu.log.disable_output(true); win_emu.log.disable_output(true);
win_emu.start(); win_emu.start();
if (win_emu.process().exception_rip.has_value()) if (win_emu.process().exception_rip.has_value())
{ {
throw std::runtime_error("Exception!"); throw std::runtime_error("Exception!");
} }
} }
catch (...) catch (...)
{ {
win_emu.log.disable_output(false); win_emu.log.disable_output(false);
win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 "\n", win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 "\n",
win_emu.emu().read_instruction_pointer()); win_emu.emu().read_instruction_pointer());
throw; throw;
} }
win_emu.log.disable_output(false); win_emu.log.disable_output(false);
} }
void forward_emulator(windows_emulator& win_emu) void forward_emulator(windows_emulator& win_emu)
{ {
const auto target = win_emu.process().executable->find_export("vulnerable"); const auto target = win_emu.process().executable->find_export("vulnerable");
win_emu.emu().hook_memory_execution(target, 1, [&](uint64_t, size_t, uint64_t) win_emu.emu().hook_memory_execution(target, 1, [&](uint64_t, size_t, uint64_t) { win_emu.emu().stop(); });
{
win_emu.emu().stop();
});
run_emulation(win_emu); run_emulation(win_emu);
} }
struct fuzzer_executer : fuzzer::executer struct fuzzer_executer : fuzzer::executer
{ {
windows_emulator emu{}; windows_emulator emu{};
std::span<const std::byte> emulator_data{}; std::span<const std::byte> emulator_data{};
std::unordered_set<uint64_t> visited_blocks{}; std::unordered_set<uint64_t> visited_blocks{};
const std::function<fuzzer::coverage_functor>* handler{nullptr}; const std::function<fuzzer::coverage_functor>* handler{nullptr};
fuzzer_executer(std::span<const std::byte> data)
: emulator_data(data)
{
emu.fuzzing = true;
emu.emu().hook_basic_block([&](const basic_block& block) {
if (this->handler && visited_blocks.emplace(block.address).second)
{
(*this->handler)(block.address);
}
});
fuzzer_executer(std::span<const std::byte> data) utils::buffer_deserializer deserializer{emulator_data};
: emulator_data(data) emu.deserialize(deserializer);
{ emu.save_snapshot();
emu.fuzzing = true;
emu.emu().hook_basic_block([&](const basic_block& block)
{
if (this->handler && visited_blocks.emplace(block.address).second)
{
(*this->handler)(block.address);
}
});
utils::buffer_deserializer deserializer{emulator_data}; const auto ret = emu.emu().read_stack(0);
emu.deserialize(deserializer);
emu.save_snapshot();
const auto ret = emu.emu().read_stack(0); emu.emu().hook_memory_execution(ret, 1, [&](uint64_t, size_t, uint64_t) { emu.emu().stop(); });
}
emu.emu().hook_memory_execution(ret, 1, [&](uint64_t, size_t, uint64_t) void restore_emulator()
{ {
emu.emu().stop(); /*utils::buffer_deserializer deserializer{ emulator_data };
}); emu.deserialize(deserializer);*/
} emu.restore_snapshot();
}
void restore_emulator() fuzzer::execution_result execute(std::span<const uint8_t> data,
{ const std::function<fuzzer::coverage_functor>& coverage_handler) override
/*utils::buffer_deserializer deserializer{ emulator_data }; {
emu.deserialize(deserializer);*/ // printf("Input size: %zd\n", data.size());
emu.restore_snapshot(); this->handler = &coverage_handler;
} this->visited_blocks.clear();
fuzzer::execution_result execute(std::span<const uint8_t> data, restore_emulator();
const std::function<fuzzer::coverage_functor>& coverage_handler) override
{
//printf("Input size: %zd\n", data.size());
this->handler = &coverage_handler;
this->visited_blocks.clear();
restore_emulator(); const auto memory = emu.emu().allocate_memory(page_align_up(std::max(data.size(), size_t(1))),
memory_permission::read_write);
emu.emu().write_memory(memory, data.data(), data.size());
const auto memory = emu.emu().allocate_memory(page_align_up(std::max(data.size(), size_t(1))), emu.emu().reg(x64_register::rcx, memory);
memory_permission::read_write); emu.emu().reg<uint64_t>(x64_register::rdx, data.size());
emu.emu().write_memory(memory, data.data(), data.size());
emu.emu().reg(x64_register::rcx, memory); try
emu.emu().reg<uint64_t>(x64_register::rdx, data.size()); {
run_emulation(emu);
return fuzzer::execution_result::success;
}
catch (...)
{
return fuzzer::execution_result::error;
}
}
};
try struct my_fuzzing_handler : fuzzer::fuzzing_handler
{ {
run_emulation(emu); std::vector<std::byte> emulator_state{};
return fuzzer::execution_result::success; std::atomic_bool stop_fuzzing{false};
}
catch (...)
{
return fuzzer::execution_result::error;
}
}
};
struct my_fuzzing_handler : fuzzer::fuzzing_handler my_fuzzing_handler(std::vector<std::byte> emulator_state)
{ : emulator_state(std::move(emulator_state))
std::vector<std::byte> emulator_state{}; {
std::atomic_bool stop_fuzzing{false}; }
my_fuzzing_handler(std::vector<std::byte> emulator_state) std::unique_ptr<fuzzer::executer> make_executer() override
: emulator_state(std::move(emulator_state)) {
{ return std::make_unique<fuzzer_executer>(emulator_state);
} }
std::unique_ptr<fuzzer::executer> make_executer() override bool stop() override
{ {
return std::make_unique<fuzzer_executer>(emulator_state); return stop_fuzzing;
} }
};
bool stop() override void run_fuzzer(const windows_emulator& base_emulator)
{ {
return stop_fuzzing; const auto concurrency = std::thread::hardware_concurrency() + 2;
}
};
void run_fuzzer(const windows_emulator& base_emulator) utils::buffer_serializer serializer{};
{ base_emulator.serialize(serializer);
const auto concurrency = std::thread::hardware_concurrency() + 2;
utils::buffer_serializer serializer{}; my_fuzzing_handler handler{serializer.move_buffer()};
base_emulator.serialize(serializer);
my_fuzzing_handler handler{serializer.move_buffer()}; fuzzer::run(handler, concurrency);
}
fuzzer::run(handler, concurrency); void run(const std::string_view application)
} {
emulator_settings settings{
.application = application,
};
void run(const std::string_view application) windows_emulator win_emu{std::move(settings)};
{
emulator_settings settings{
.application = application,
};
windows_emulator win_emu{std::move(settings)}; forward_emulator(win_emu);
run_fuzzer(win_emu);
forward_emulator(win_emu); }
run_fuzzer(win_emu);
}
} }
int main(const int argc, char** argv) int main(const int argc, char** argv)
{ {
if (argc <= 1) if (argc <= 1)
{ {
puts("Application not specified!"); puts("Application not specified!");
return 1; return 1;
} }
//setvbuf(stdout, nullptr, _IOFBF, 0x10000); // setvbuf(stdout, nullptr, _IOFBF, 0x10000);
if (argc > 2 && argv[1] == "-d"s) if (argc > 2 && argv[1] == "-d"s)
{ {
use_gdb = true; use_gdb = true;
} }
try try
{ {
do do
{ {
run(argv[use_gdb ? 2 : 1]); run(argv[use_gdb ? 2 : 1]);
} } while (use_gdb);
while (use_gdb);
return 0; return 0;
} }
catch (std::exception& e) catch (std::exception& e)
{ {
puts(e.what()); puts(e.what());
#if defined(_WIN32) && 0 #if defined(_WIN32) && 0
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR); MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
#endif #endif
} }
return 1; return 1;
} }
#ifdef _WIN32 #ifdef _WIN32
int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int) int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int)
{ {
return main(__argc, __argv); return main(__argc, __argv);
} }
#endif #endif

View File

@@ -5,128 +5,121 @@
namespace fuzzer namespace fuzzer
{ {
namespace namespace
{ {
class fuzzing_context class fuzzing_context
{ {
public: public:
fuzzing_context(input_generator& generator, fuzzing_handler& handler) fuzzing_context(input_generator& generator, fuzzing_handler& handler)
: generator(generator) : generator(generator),
, handler(handler) handler(handler)
{ {
} }
void stop() void stop()
{ {
this->stop_ = true; this->stop_ = true;
} }
bool should_stop() bool should_stop()
{ {
if (this->stop_) if (this->stop_)
{ {
return true; return true;
} }
if (!handler.stop()) if (!handler.stop())
{ {
return false; return false;
} }
this->stop_ = true; this->stop_ = true;
return true; return true;
} }
input_generator& generator; input_generator& generator;
fuzzing_handler& handler; fuzzing_handler& handler;
std::atomic_uint64_t executions{0}; std::atomic_uint64_t executions{0};
private: private:
std::atomic_bool stop_{false}; std::atomic_bool stop_{false};
}; };
void perform_fuzzing_iteration(fuzzing_context& context, executer& executer) void perform_fuzzing_iteration(fuzzing_context& context, executer& executer)
{ {
++context.executions; ++context.executions;
context.generator.access_input([&](const std::span<const uint8_t> input) context.generator.access_input([&](const std::span<const uint8_t> input) {
{ uint64_t score{0};
uint64_t score{0}; const auto result = executer.execute(input, [&](uint64_t) { ++score; });
const auto result = executer.execute(input, [&](uint64_t)
{
++score;
});
if (result == execution_result::error) if (result == execution_result::error)
{ {
printf("Found error!\n"); printf("Found error!\n");
context.stop(); context.stop();
} }
return score; return score;
}); });
} }
void worker(fuzzing_context& context) void worker(fuzzing_context& context)
{ {
const auto executer = context.handler.make_executer(); const auto executer = context.handler.make_executer();
while (!context.should_stop()) while (!context.should_stop())
{ {
perform_fuzzing_iteration(context, *executer); perform_fuzzing_iteration(context, *executer);
} }
} }
struct worker_pool struct worker_pool
{ {
fuzzing_context* context_{nullptr}; fuzzing_context* context_{nullptr};
std::vector<std::thread> workers_{}; std::vector<std::thread> workers_{};
worker_pool(fuzzing_context& context, const size_t concurrency) worker_pool(fuzzing_context& context, const size_t concurrency)
: context_(&context) : context_(&context)
{ {
this->workers_.reserve(concurrency); this->workers_.reserve(concurrency);
for (size_t i = 0; i < concurrency; ++i) for (size_t i = 0; i < concurrency; ++i)
{ {
this->workers_.emplace_back([&context] this->workers_.emplace_back([&context] { worker(context); });
{ }
worker(context); }
});
}
}
~worker_pool() ~worker_pool()
{ {
if (this->workers_.empty()) if (this->workers_.empty())
{ {
return; return;
} }
this->context_->stop(); this->context_->stop();
for (auto& w : this->workers_) for (auto& w : this->workers_)
{ {
w.join(); w.join();
} }
} }
}; };
} }
void run(fuzzing_handler& handler, const size_t concurrency) void run(fuzzing_handler& handler, const size_t concurrency)
{ {
input_generator generator{}; input_generator generator{};
fuzzing_context context{generator, handler}; fuzzing_context context{generator, handler};
worker_pool pool{context, concurrency}; worker_pool pool{context, concurrency};
while (!context.should_stop()) while (!context.should_stop())
{ {
std::this_thread::sleep_for(std::chrono::seconds{1}); std::this_thread::sleep_for(std::chrono::seconds{1});
const auto executions = context.executions.exchange(0); const auto executions = context.executions.exchange(0);
const auto highest_scorer = context.generator.get_highest_scorer(); const auto highest_scorer = context.generator.get_highest_scorer();
const auto avg_score = context.generator.get_average_score(); const auto avg_score = context.generator.get_average_score();
printf("Executions/s: %" PRIu64 " - Score: %" PRIx64 " - Avg: %.3f\n", executions, highest_scorer.score, printf("Executions/s: %" PRIu64 " - Score: %" PRIx64 " - Avg: %.3f\n", executions, highest_scorer.score,
avg_score); avg_score);
} }
} }
} }

View File

@@ -7,33 +7,33 @@
namespace fuzzer namespace fuzzer
{ {
using coverage_functor = void(uint64_t address); using coverage_functor = void(uint64_t address);
enum class execution_result enum class execution_result
{ {
success, success,
error, error,
}; };
struct executer struct executer
{ {
virtual ~executer() = default; virtual ~executer() = default;
virtual execution_result execute(std::span<const uint8_t> data, virtual execution_result execute(std::span<const uint8_t> data,
const std::function<coverage_functor>& coverage_handler) = 0; const std::function<coverage_functor>& coverage_handler) = 0;
}; };
struct fuzzing_handler struct fuzzing_handler
{ {
virtual ~fuzzing_handler() = default; virtual ~fuzzing_handler() = default;
virtual std::unique_ptr<executer> make_executer() = 0; virtual std::unique_ptr<executer> make_executer() = 0;
virtual bool stop() virtual bool stop()
{ {
return false; return false;
} }
}; };
void run(fuzzing_handler& handler, size_t concurrency = std::thread::hardware_concurrency()); void run(fuzzing_handler& handler, size_t concurrency = std::thread::hardware_concurrency());
} }

View File

@@ -4,120 +4,118 @@
namespace fuzzer namespace fuzzer
{ {
namespace namespace
{ {
constexpr size_t MAX_TOP_SCORER = 20; constexpr size_t MAX_TOP_SCORER = 20;
void mutate_input(random_generator& rng, std::vector<uint8_t>& input) void mutate_input(random_generator& rng, std::vector<uint8_t>& input)
{ {
if (input.empty() || rng.get(3) == 0) if (input.empty() || rng.get(3) == 0)
{ {
const auto new_bytes = rng.get_geometric<size_t>() + 1; const auto new_bytes = rng.get_geometric<size_t>() + 1;
input.resize(input.size() + new_bytes); input.resize(input.size() + new_bytes);
} }
else if (rng.get(10) == 0) else if (rng.get(10) == 0)
{ {
const auto remove_bytes = rng.get_geometric<size_t>() % input.size(); const auto remove_bytes = rng.get_geometric<size_t>() % input.size();
input.resize(input.size() - remove_bytes); input.resize(input.size() - remove_bytes);
} }
const auto mutations = (rng.get_geometric<size_t>() + 1) % input.size(); const auto mutations = (rng.get_geometric<size_t>() + 1) % input.size();
for (size_t i = 0; i < mutations; ++i)
{
const auto index = rng.get<size_t>(input.size());
input[index] = rng.get<uint8_t>();
}
}
}
for (size_t i = 0; i < mutations; ++i) input_generator::input_generator() = default;
{
const auto index = rng.get<size_t>(input.size());
input[index] = rng.get<uint8_t>();
}
}
}
input_generator::input_generator() = default; std::vector<uint8_t> input_generator::generate_next_input()
{
std::vector<uint8_t> input{};
std::unique_lock lock{this->mutex_};
std::vector<uint8_t> input_generator::generate_next_input() if (!this->top_scorer_.empty())
{ {
std::vector<uint8_t> input{}; const auto index = this->rng.get<size_t>() % this->top_scorer_.size();
std::unique_lock lock{this->mutex_}; input = this->top_scorer_[index].data;
}
if (!this->top_scorer_.empty()) mutate_input(this->rng, input);
{
const auto index = this->rng.get<size_t>() % this->top_scorer_.size();
input = this->top_scorer_[index].data;
}
mutate_input(this->rng, input); return input;
}
return input; void input_generator::access_input(const std::function<input_handler>& handler)
} {
auto next_input = this->generate_next_input();
const auto score = handler(next_input);
void input_generator::access_input(const std::function<input_handler>& handler) input_entry e{};
{ e.data = std::move(next_input);
auto next_input = this->generate_next_input(); e.score = score;
const auto score = handler(next_input);
input_entry e{}; this->store_input_entry(std::move(e));
e.data = std::move(next_input); }
e.score = score;
this->store_input_entry(std::move(e)); input_entry input_generator::get_highest_scorer()
} {
std::unique_lock lock{this->mutex_};
return this->highest_scorer_;
}
input_entry input_generator::get_highest_scorer() double input_generator::get_average_score()
{ {
std::unique_lock lock{this->mutex_}; std::unique_lock lock{this->mutex_};
return this->highest_scorer_;
}
double input_generator::get_average_score() double score{0.0};
{ for (const auto& e : this->top_scorer_)
std::unique_lock lock{this->mutex_}; {
score += static_cast<double>(e.score);
}
double score{0.0}; return score / static_cast<double>(this->top_scorer_.size());
for (const auto& e : this->top_scorer_) }
{
score += static_cast<double>(e.score);
}
return score / static_cast<double>(this->top_scorer_.size()); void input_generator::store_input_entry(input_entry entry)
} {
std::unique_lock lock{this->mutex_};
void input_generator::store_input_entry(input_entry entry) if (entry.score < this->lowest_score && this->rng.get(40) != 0)
{ {
std::unique_lock lock{this->mutex_}; return;
}
if (entry.score < this->lowest_score && this->rng.get(40) != 0) if (entry.score > this->highest_scorer_.score)
{ {
return; this->highest_scorer_ = entry;
} }
if (entry.score > this->highest_scorer_.score) if (this->top_scorer_.size() < MAX_TOP_SCORER)
{ {
this->highest_scorer_ = entry; this->top_scorer_.emplace_back(std::move(entry));
} return;
}
if (this->top_scorer_.size() < MAX_TOP_SCORER) const auto insert_at_random = this->rng.get(10) == 0;
{ const auto index =
this->top_scorer_.emplace_back(std::move(entry)); insert_at_random ? (this->rng.get<size_t>() % this->top_scorer_.size()) : this->lowest_scorer;
return;
}
const auto insert_at_random = this->rng.get(10) == 0; this->top_scorer_[index] = std::move(entry);
const auto index = insert_at_random
? (this->rng.get<size_t>() % this->top_scorer_.size())
: this->lowest_scorer;
this->top_scorer_[index] = std::move(entry); this->lowest_score = this->top_scorer_[0].score;
this->lowest_scorer = 0;
this->lowest_score = this->top_scorer_[0].score; for (size_t i = 1; i < this->top_scorer_.size(); ++i)
this->lowest_scorer = 0; {
if (this->top_scorer_[i].score < this->lowest_score)
for (size_t i = 1; i < this->top_scorer_.size(); ++i) {
{ this->lowest_score = this->top_scorer_[i].score;
if (this->top_scorer_[i].score < this->lowest_score) this->lowest_scorer = i;
{ }
this->lowest_score = this->top_scorer_[i].score; }
this->lowest_scorer = i; }
}
}
}
} }

View File

@@ -8,37 +8,37 @@
namespace fuzzer namespace fuzzer
{ {
using input_score = uint64_t; using input_score = uint64_t;
using input_handler = input_score(std::span<const uint8_t>); using input_handler = input_score(std::span<const uint8_t>);
struct input_entry struct input_entry
{ {
std::vector<uint8_t> data{}; std::vector<uint8_t> data{};
input_score score{}; input_score score{};
}; };
class input_generator class input_generator
{ {
public: public:
input_generator(); input_generator();
void access_input(const std::function<input_handler>& handler); void access_input(const std::function<input_handler>& handler);
input_entry get_highest_scorer(); input_entry get_highest_scorer();
double get_average_score(); double get_average_score();
private: private:
std::mutex mutex_{}; std::mutex mutex_{};
random_generator rng{}; random_generator rng{};
std::vector<input_entry> top_scorer_{}; std::vector<input_entry> top_scorer_{};
input_score lowest_score{0}; input_score lowest_score{0};
size_t lowest_scorer{0}; size_t lowest_scorer{0};
input_entry highest_scorer_{}; input_entry highest_scorer_{};
std::vector<uint8_t> generate_next_input(); std::vector<uint8_t> generate_next_input();
void store_input_entry(input_entry entry); void store_input_entry(input_entry entry);
}; };
} }

View File

@@ -3,33 +3,33 @@
namespace fuzzer namespace fuzzer
{ {
random_generator::random_generator() random_generator::random_generator()
: rng_(std::random_device()()) : rng_(std::random_device()())
{ {
} }
std::mt19937::result_type random_generator::generate_number() std::mt19937::result_type random_generator::generate_number()
{ {
return this->distribution_(this->rng_); return this->distribution_(this->rng_);
} }
void random_generator::fill(void* data, const size_t size) void random_generator::fill(void* data, const size_t size)
{ {
this->fill(std::span(static_cast<uint8_t*>(data), size)); this->fill(std::span(static_cast<uint8_t*>(data), size));
} }
void random_generator::fill(std::span<uint8_t> data) void random_generator::fill(std::span<uint8_t> data)
{ {
size_t i = 0; size_t i = 0;
while (i < data.size()) while (i < data.size())
{ {
const auto number = this->generate_number(); const auto number = this->generate_number();
const auto remaining_data = data.size() - i; const auto remaining_data = data.size() - i;
const auto data_to_fill = std::min(remaining_data, sizeof(number)); const auto data_to_fill = std::min(remaining_data, sizeof(number));
memcpy(data.data() + i, &number, data_to_fill); memcpy(data.data() + i, &number, data_to_fill);
i += data_to_fill; i += data_to_fill;
} }
} }
} }

View File

@@ -6,65 +6,65 @@
namespace fuzzer namespace fuzzer
{ {
class random_generator class random_generator
{ {
public: public:
random_generator(); random_generator();
void fill(std::span<uint8_t> data); void fill(std::span<uint8_t> data);
void fill(void* data, size_t size); void fill(void* data, size_t size);
template <typename T> template <typename T>
requires(std::is_trivially_copyable_v<T>) requires(std::is_trivially_copyable_v<T>)
T get() T get()
{ {
T value{}; T value{};
this->fill(&value, sizeof(value)); this->fill(&value, sizeof(value));
return value; return value;
} }
template <typename T> template <typename T>
T get(const T& max) T get(const T& max)
{ {
return this->get<T>() % max; return this->get<T>() % max;
} }
template <typename T> template <typename T>
T get(T min, T max) T get(T min, T max)
{ {
if (max < min) if (max < min)
{ {
std::swap(max, min); std::swap(max, min);
} }
const auto diff = max - min; const auto diff = max - min;
return (this->get<T>() % diff) + min; return (this->get<T>() % diff) + min;
} }
template <typename T> template <typename T>
T get_geometric() T get_geometric()
{ {
T value{0}; T value{0};
while (this->get<bool>()) while (this->get<bool>())
{ {
++value; ++value;
} }
return value; return value;
} }
private: private:
std::mt19937 rng_; std::mt19937 rng_;
std::uniform_int_distribution<std::mt19937::result_type> distribution_{}; std::uniform_int_distribution<std::mt19937::result_type> distribution_{};
std::mt19937::result_type generate_number(); std::mt19937::result_type generate_number();
}; };
template <> template <>
inline bool random_generator::get<bool>() inline bool random_generator::get<bool>()
{ {
return (this->generate_number() & 1) != 0; return (this->generate_number() & 1) != 0;
} }
} }

View File

@@ -19,12 +19,12 @@ __declspec(dllexport) bool do_the_task = true;
struct tls_struct struct tls_struct
{ {
DWORD num = 1337; DWORD num = 1337;
tls_struct() tls_struct()
{ {
num = GetCurrentThreadId(); num = GetCurrentThreadId();
} }
}; };
thread_local tls_struct tls_var{}; thread_local tls_struct tls_var{};
@@ -32,297 +32,293 @@ thread_local tls_struct tls_var{};
// getenv is broken right now :( // getenv is broken right now :(
std::string read_env(const char* env) std::string read_env(const char* env)
{ {
char buffer[0x1000] = {}; char buffer[0x1000] = {};
if (!GetEnvironmentVariableA(env, buffer, sizeof(buffer))) if (!GetEnvironmentVariableA(env, buffer, sizeof(buffer)))
{ {
return {}; return {};
} }
return buffer; return buffer;
} }
bool test_threads() bool test_threads()
{ {
constexpr auto thread_count = 5ULL; constexpr auto thread_count = 5ULL;
std::atomic<uint64_t> counter{0}; std::atomic<uint64_t> counter{0};
std::vector<std::thread> threads{}; std::vector<std::thread> threads{};
threads.reserve(thread_count); threads.reserve(thread_count);
for (auto i = 0ULL; i < thread_count; ++i) for (auto i = 0ULL; i < thread_count; ++i)
{ {
threads.emplace_back([&counter] threads.emplace_back([&counter] {
{ ++counter;
++counter; std::this_thread::yield();
std::this_thread::yield(); ++counter;
++counter; // Host scheduling/cpu performance can have impact on emulator scheduling
// Host scheduling/cpu performance can have impact on emulator scheduling // std::this_thread::sleep_for(std::chrono::milliseconds(100));
//std::this_thread::sleep_for(std::chrono::milliseconds(100)); ++counter;
++counter; });
}); }
}
for (auto& t : threads) for (auto& t : threads)
{ {
t.join(); t.join();
} }
return counter == (thread_count * 3ULL); return counter == (thread_count * 3ULL);
} }
bool test_tls() bool test_tls()
{ {
std::atomic_bool kill{false}; std::atomic_bool kill{false};
std::atomic_uint32_t successes{0}; std::atomic_uint32_t successes{0};
constexpr uint32_t thread_count = 2; constexpr uint32_t thread_count = 2;
std::vector<std::thread> ts{}; std::vector<std::thread> ts{};
kill = false; kill = false;
for (size_t i = 0; i < thread_count; ++i) for (size_t i = 0; i < thread_count; ++i)
{ {
ts.emplace_back([&] ts.emplace_back([&] {
{ while (!kill)
while (!kill) {
{ std::this_thread::yield();
std::this_thread::yield(); }
}
if (tls_var.num == GetCurrentThreadId()) if (tls_var.num == GetCurrentThreadId())
{ {
++successes; ++successes;
} }
}); });
} }
LoadLibraryA("d3dcompiler_47.dll"); LoadLibraryA("d3dcompiler_47.dll");
LoadLibraryA("dsound.dll"); LoadLibraryA("dsound.dll");
/*LoadLibraryA("d3d9.dll"); /*LoadLibraryA("d3d9.dll");
LoadLibraryA("dxgi.dll"); LoadLibraryA("dxgi.dll");
LoadLibraryA("wlanapi.dll");*/ LoadLibraryA("wlanapi.dll");*/
kill = true; kill = true;
for (auto& t : ts) for (auto& t : ts)
{ {
if (t.joinable()) if (t.joinable())
{ {
t.join(); t.join();
} }
} }
return successes == thread_count; return successes == thread_count;
} }
bool test_env() bool test_env()
{ {
const auto computername = read_env("COMPUTERNAME"); const auto computername = read_env("COMPUTERNAME");
SetEnvironmentVariableA("BLUB", "LUL"); SetEnvironmentVariableA("BLUB", "LUL");
const auto blub = read_env("BLUB"); const auto blub = read_env("BLUB");
return !computername.empty() && blub == "LUL"; return !computername.empty() && blub == "LUL";
} }
bool test_io() bool test_io()
{ {
const auto* filename = "a.txt"; const auto* filename = "a.txt";
FILE* fp{}; FILE* fp{};
(void)fopen_s(&fp, filename, "wb"); (void)fopen_s(&fp, filename, "wb");
if (!fp) if (!fp)
{ {
puts("Bad file"); puts("Bad file");
return false; return false;
} }
const std::string text = "Blub"; const std::string text = "Blub";
(void)fwrite(text.data(), 1, text.size(), fp); (void)fwrite(text.data(), 1, text.size(), fp);
(void)fclose(fp); (void)fclose(fp);
std::ifstream t(filename); std::ifstream t(filename);
t.seekg(0, std::ios::end); t.seekg(0, std::ios::end);
const size_t size = t.tellg(); const size_t size = t.tellg();
std::string buffer(size, ' '); std::string buffer(size, ' ');
t.seekg(0); t.seekg(0);
t.read(buffer.data(), static_cast<std::streamsize>(size)); t.read(buffer.data(), static_cast<std::streamsize>(size));
return text == buffer; return text == buffer;
} }
bool test_dir_io() bool test_dir_io()
{ {
size_t count = 0; size_t count = 0;
for (auto i : std::filesystem::directory_iterator(R"(C:\Windows\System32\)")) for (auto i : std::filesystem::directory_iterator(R"(C:\Windows\System32\)"))
{ {
++count; ++count;
if (count > 30) if (count > 30)
{ {
return true; return true;
} }
} }
return count > 30; return count > 30;
} }
std::optional<std::string> read_registry_string(const HKEY root, const char* path, const char* value) std::optional<std::string> read_registry_string(const HKEY root, const char* path, const char* value)
{ {
HKEY key{}; HKEY key{};
if (RegOpenKeyExA(root, path, 0, KEY_READ, &key) != if (RegOpenKeyExA(root, path, 0, KEY_READ, &key) != ERROR_SUCCESS)
ERROR_SUCCESS) {
{ return std::nullopt;
return std::nullopt; }
}
char data[MAX_PATH]{}; char data[MAX_PATH]{};
DWORD length = sizeof(data); DWORD length = sizeof(data);
const auto res = RegQueryValueExA(key, value, nullptr, nullptr, reinterpret_cast<uint8_t*>(data), &length); const auto res = RegQueryValueExA(key, value, nullptr, nullptr, reinterpret_cast<uint8_t*>(data), &length);
if (RegCloseKey(key) != ERROR_SUCCESS) if (RegCloseKey(key) != ERROR_SUCCESS)
{ {
return std::nullopt; return std::nullopt;
} }
if (res != ERROR_SUCCESS) if (res != ERROR_SUCCESS)
{ {
return std::nullopt; return std::nullopt;
} }
if (length == 0) if (length == 0)
{ {
return ""; return "";
} }
return {std::string(data, min(length - 1, sizeof(data)))}; return {std::string(data, min(length - 1, sizeof(data)))};
} }
bool test_registry() bool test_registry()
{ {
const auto val = read_registry_string(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows\CurrentVersion)", const auto val =
"ProgramFilesDir"); read_registry_string(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows\CurrentVersion)", "ProgramFilesDir");
if (!val) if (!val)
{ {
return false; return false;
} }
return *val == "C:\\Program Files"; return *val == "C:\\Program Files";
} }
void throw_exception() void throw_exception()
{ {
if (do_the_task) if (do_the_task)
{ {
throw std::runtime_error("OK"); throw std::runtime_error("OK");
} }
} }
bool test_exceptions() bool test_exceptions()
{ {
try try
{ {
throw_exception(); throw_exception();
return false; return false;
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
return e.what() == std::string("OK"); return e.what() == std::string("OK");
} }
} }
void throw_access_violation() void throw_access_violation()
{ {
if (do_the_task) if (do_the_task)
{ {
*reinterpret_cast<int*>(1) = 1; *reinterpret_cast<int*>(1) = 1;
} }
} }
bool test_access_violation_exception() bool test_access_violation_exception()
{ {
__try __try
{ {
throw_access_violation(); throw_access_violation();
return false; return false;
} }
__except (EXCEPTION_EXECUTE_HANDLER) __except (EXCEPTION_EXECUTE_HANDLER)
{ {
return GetExceptionCode() == STATUS_ACCESS_VIOLATION; return GetExceptionCode() == STATUS_ACCESS_VIOLATION;
} }
} }
bool test_ud2_exception(void* address) bool test_ud2_exception(void* address)
{ {
__try __try
{ {
static_cast<void(*)()>(address)(); static_cast<void (*)()>(address)();
return false; return false;
} }
__except (EXCEPTION_EXECUTE_HANDLER) __except (EXCEPTION_EXECUTE_HANDLER)
{ {
return GetExceptionCode() == STATUS_ILLEGAL_INSTRUCTION; return GetExceptionCode() == STATUS_ILLEGAL_INSTRUCTION;
} }
} }
bool test_illegal_instruction_exception() bool test_illegal_instruction_exception()
{ {
const auto address = VirtualAlloc(nullptr, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); const auto address = VirtualAlloc(nullptr, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!address) if (!address)
{ {
return false; return false;
} }
memcpy(address, "\x0F\x0B", 2); // ud2 memcpy(address, "\x0F\x0B", 2); // ud2
const auto res = test_ud2_exception(address); const auto res = test_ud2_exception(address);
VirtualFree(address, 0x1000, MEM_RELEASE); VirtualFree(address, 0x1000, MEM_RELEASE);
return res; return res;
} }
bool test_native_exceptions() bool test_native_exceptions()
{ {
return test_access_violation_exception() return test_access_violation_exception() && test_illegal_instruction_exception();
&& test_illegal_instruction_exception();
} }
void print_time() void print_time()
{ {
const auto epoch_time = std::chrono::system_clock::now().time_since_epoch(); const auto epoch_time = std::chrono::system_clock::now().time_since_epoch();
printf("Time: %lld\n", epoch_time.count()); printf("Time: %lld\n", epoch_time.count());
} }
#define RUN_TEST(func, name) \ #define RUN_TEST(func, name) \
{ \ { \
printf("Running test '" name "': "); \ printf("Running test '" name "': "); \
const auto res = func(); \ const auto res = func(); \
valid &= res; \ valid &= res; \
puts(res ? "Success" : "Fail"); \ puts(res ? "Success" : "Fail"); \
} }
int main(int argc, const char* argv[]) int main(int argc, const char* argv[])
{ {
if (argc == 2 && argv[1] == "-time"sv) if (argc == 2 && argv[1] == "-time"sv)
{ {
print_time(); print_time();
return 0; return 0;
} }
bool valid = true; bool valid = true;
RUN_TEST(test_io, "I/O") RUN_TEST(test_io, "I/O")
RUN_TEST(test_dir_io, "Dir I/O") RUN_TEST(test_dir_io, "Dir I/O")
RUN_TEST(test_registry, "Registry") RUN_TEST(test_registry, "Registry")
RUN_TEST(test_threads, "Threads") RUN_TEST(test_threads, "Threads")
RUN_TEST(test_env, "Environment") RUN_TEST(test_env, "Environment")
RUN_TEST(test_exceptions, "Exceptions") RUN_TEST(test_exceptions, "Exceptions")
RUN_TEST(test_native_exceptions, "Native Exceptions") RUN_TEST(test_native_exceptions, "Native Exceptions")
RUN_TEST(test_tls, "TLS") RUN_TEST(test_tls, "TLS")
return valid ? 0 : 1; return valid ? 0 : 1;
} }

View File

@@ -8,36 +8,35 @@
template <typename ReturnType, typename... Args> template <typename ReturnType, typename... Args>
class function_wrapper : public object class function_wrapper : public object
{ {
public: public:
using user_data_pointer = void*; using user_data_pointer = void*;
using c_function_type = ReturnType(Args..., user_data_pointer); using c_function_type = ReturnType(Args..., user_data_pointer);
using functor_type = std::function<ReturnType(Args...)>; using functor_type = std::function<ReturnType(Args...)>;
function_wrapper() = default; function_wrapper() = default;
function_wrapper(functor_type functor) function_wrapper(functor_type functor)
: functor_(std::make_unique<functor_type>(std::move(functor))) : functor_(std::make_unique<functor_type>(std::move(functor)))
{ {
} }
c_function_type* get_c_function() const c_function_type* get_c_function() const
{ {
return +[](Args... args, user_data_pointer user_data) -> ReturnType return +[](Args... args, user_data_pointer user_data) -> ReturnType {
{ return (*static_cast<functor_type*>(user_data))(std::forward<Args>(args)...);
return (*static_cast<functor_type*>(user_data))(std::forward<Args>(args)...); };
}; }
}
void* get_function() const void* get_function() const
{ {
return reinterpret_cast<void*>(this->get_c_function()); return reinterpret_cast<void*>(this->get_c_function());
} }
user_data_pointer get_user_data() const user_data_pointer get_user_data() const
{ {
return this->functor_.get(); return this->functor_.get();
} }
private: private:
std::unique_ptr<functor_type> functor_{}; std::unique_ptr<functor_type> functor_{};
}; };

View File

@@ -2,11 +2,11 @@
struct object struct object
{ {
object() = default; object() = default;
virtual ~object() = default; virtual ~object() = default;
object(object&&) = default; object(object&&) = default;
object(const object&) = default; object(const object&) = default;
object& operator=(object&&) = default; object& operator=(object&&) = default;
object& operator=(const object&) = default; object& operator=(const object&) = default;
}; };

View File

@@ -2,7 +2,7 @@
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(push) #pragma warning(push)
#pragma warning(disable: 4505) #pragma warning(disable : 4505)
#endif #endif
#ifdef __clang__ #ifdef __clang__
@@ -25,27 +25,27 @@
namespace unicorn namespace unicorn
{ {
struct unicorn_error : std::runtime_error struct unicorn_error : std::runtime_error
{ {
unicorn_error(const uc_err error_code) unicorn_error(const uc_err error_code)
: std::runtime_error(uc_strerror(error_code)) : std::runtime_error(uc_strerror(error_code)),
, code(error_code) code(error_code)
{ {
} }
uc_err code{}; uc_err code{};
}; };
inline void throw_if_unicorn_error(const uc_err error_code) inline void throw_if_unicorn_error(const uc_err error_code)
{ {
if (error_code != UC_ERR_OK) if (error_code != UC_ERR_OK)
{ {
throw unicorn_error(error_code); throw unicorn_error(error_code);
} }
} }
inline void uce(const uc_err error_code) inline void uce(const uc_err error_code)
{ {
throw_if_unicorn_error(error_code); throw_if_unicorn_error(error_code);
} }
} }

View File

@@ -4,74 +4,73 @@
namespace unicorn namespace unicorn
{ {
class unicorn_hook class unicorn_hook
{ {
public: public:
unicorn_hook() = default; unicorn_hook() = default;
unicorn_hook(uc_engine* uc) unicorn_hook(uc_engine* uc)
: unicorn_hook(uc, {}) : unicorn_hook(uc, {})
{ {
} }
unicorn_hook(uc_engine* uc, const uc_hook hook) unicorn_hook(uc_engine* uc, const uc_hook hook)
: uc_(uc) : uc_(uc),
, hook_(hook) hook_(hook)
{ {
} }
~unicorn_hook() ~unicorn_hook()
{ {
release(); release();
} }
unicorn_hook(const unicorn_hook&) = delete; unicorn_hook(const unicorn_hook&) = delete;
unicorn_hook& operator=(const unicorn_hook&) = delete; unicorn_hook& operator=(const unicorn_hook&) = delete;
unicorn_hook(unicorn_hook&& obj) noexcept
{
this->operator=(std::move(obj));
}
unicorn_hook(unicorn_hook&& obj) noexcept uc_hook* make_reference()
{ {
this->operator=(std::move(obj)); if (!this->uc_)
} {
throw std::runtime_error("Cannot make reference on default constructed hook");
}
uc_hook* make_reference() this->release();
{ return &this->hook_;
if (!this->uc_) }
{
throw std::runtime_error("Cannot make reference on default constructed hook");
}
this->release(); unicorn_hook& operator=(unicorn_hook&& obj) noexcept
return &this->hook_; {
} if (this != &obj)
{
this->release();
unicorn_hook& operator=(unicorn_hook&& obj) noexcept this->uc_ = obj.uc_;
{ this->hook_ = obj.hook_;
if (this != &obj)
{
this->release();
this->uc_ = obj.uc_; obj.hook_ = {};
this->hook_ = obj.hook_; obj.uc_ = {};
}
obj.hook_ = {}; return *this;
obj.uc_ = {}; }
}
return *this; void release()
} {
if (this->hook_ && this->uc_)
{
uc_hook_del(this->uc_, this->hook_);
this->hook_ = {};
}
}
void release() private:
{ uc_engine* uc_{};
if (this->hook_ && this->uc_) uc_hook hook_{};
{ };
uc_hook_del(this->uc_, this->hook_);
this->hook_ = {};
}
}
private:
uc_engine* uc_{};
uc_hook hook_{};
};
} }

View File

@@ -6,62 +6,61 @@
namespace unicorn namespace unicorn
{ {
class unicorn_memory_regions class unicorn_memory_regions
{ {
public: public:
unicorn_memory_regions(uc_engine* uc) unicorn_memory_regions(uc_engine* uc)
{ {
uce(uc_mem_regions(uc, &this->regions_, &this->count_)); uce(uc_mem_regions(uc, &this->regions_, &this->count_));
} }
~unicorn_memory_regions()
{
this->release();
}
~unicorn_memory_regions() unicorn_memory_regions(const unicorn_memory_regions&) = delete;
{ unicorn_memory_regions& operator=(const unicorn_memory_regions&) = delete;
this->release();
}
unicorn_memory_regions(const unicorn_memory_regions&) = delete; unicorn_memory_regions(unicorn_memory_regions&& obj) noexcept
unicorn_memory_regions& operator=(const unicorn_memory_regions&) = delete; {
this->operator=(std::move(obj));
}
unicorn_memory_regions(unicorn_memory_regions&& obj) noexcept unicorn_memory_regions& operator=(unicorn_memory_regions&& obj) noexcept
{ {
this->operator=(std::move(obj)); if (this != &obj)
} {
this->release();
unicorn_memory_regions& operator=(unicorn_memory_regions&& obj) noexcept this->count_ = obj.count_;
{ this->regions_ = obj.regions_;
if (this != &obj)
{
this->release();
this->count_ = obj.count_; obj.count_ = {};
this->regions_ = obj.regions_; obj.regions_ = nullptr;
}
obj.count_ = {}; return *this;
obj.regions_ = nullptr; }
}
return *this; std::span<uc_mem_region> get_span() const
} {
return {this->regions_, this->count_};
}
std::span<uc_mem_region> get_span() const private:
{ uint32_t count_{};
return {this->regions_, this->count_}; uc_mem_region* regions_{};
}
private: void release()
uint32_t count_{}; {
uc_mem_region* regions_{}; if (this->regions_)
{
uc_free(regions_);
}
void release() this->count_ = {};
{ this->regions_ = nullptr;
if (this->regions_) }
{ };
uc_free(regions_);
}
this->count_ = {};
this->regions_ = nullptr;
}
};
} }

File diff suppressed because it is too large Load Diff

View File

@@ -12,6 +12,6 @@
namespace unicorn namespace unicorn
{ {
UNICORN_EMULATOR_DLL_STORAGE UNICORN_EMULATOR_DLL_STORAGE
std::unique_ptr<x64_emulator> create_x64_emulator(); std::unique_ptr<x64_emulator> create_x64_emulator();
} }

View File

@@ -2,46 +2,46 @@
namespace test namespace test
{ {
TEST(EmulationTest, BasicEmulationWorks) TEST(EmulationTest, BasicEmulationWorks)
{ {
auto emu = create_sample_emulator(); auto emu = create_sample_emulator();
emu.start(); emu.start();
ASSERT_TERMINATED_SUCCESSFULLY(emu); ASSERT_TERMINATED_SUCCESSFULLY(emu);
} }
TEST(EmulationTest, CountedEmulationWorks) TEST(EmulationTest, CountedEmulationWorks)
{ {
constexpr auto count = 200000; constexpr auto count = 200000;
auto emu = create_sample_emulator(); auto emu = create_sample_emulator();
emu.start({}, count); emu.start({}, count);
ASSERT_EQ(emu.process().executed_instructions, count); ASSERT_EQ(emu.process().executed_instructions, count);
} }
TEST(EmulationTest, CountedEmulationIsAccurate) TEST(EmulationTest, CountedEmulationIsAccurate)
{ {
auto emu = create_sample_emulator(); auto emu = create_sample_emulator();
emu.start(); emu.start();
ASSERT_TERMINATED_SUCCESSFULLY(emu); ASSERT_TERMINATED_SUCCESSFULLY(emu);
const auto executedInstructions = emu.process().executed_instructions; const auto executedInstructions = emu.process().executed_instructions;
auto new_emu = create_sample_emulator(); auto new_emu = create_sample_emulator();
constexpr auto offset = 1; constexpr auto offset = 1;
const auto instructionsToExecute = executedInstructions - offset; const auto instructionsToExecute = executedInstructions - offset;
new_emu.start({}, instructionsToExecute); new_emu.start({}, instructionsToExecute);
ASSERT_EQ(new_emu.process().executed_instructions, instructionsToExecute); ASSERT_EQ(new_emu.process().executed_instructions, instructionsToExecute);
ASSERT_NOT_TERMINATED(new_emu); ASSERT_NOT_TERMINATED(new_emu);
new_emu.start({}, offset); new_emu.start({}, offset);
ASSERT_TERMINATED_SUCCESSFULLY(new_emu); ASSERT_TERMINATED_SUCCESSFULLY(new_emu);
ASSERT_EQ(new_emu.process().executed_instructions, executedInstructions); ASSERT_EQ(new_emu.process().executed_instructions, executedInstructions);
} }
} }

View File

@@ -3,37 +3,36 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <windows_emulator.hpp> #include <windows_emulator.hpp>
#define ASSERT_NOT_TERMINATED(win_emu) \ #define ASSERT_NOT_TERMINATED(win_emu) \
do { \ do \
ASSERT_FALSE((win_emu).process().exit_status.has_value()); \ { \
} while(false) ASSERT_FALSE((win_emu).process().exit_status.has_value()); \
} while (false)
#define ASSERT_TERMINATED_WITH_STATUS(win_emu, status) \
do \
{ \
ASSERT_TRUE((win_emu).process().exit_status.has_value()); \
ASSERT_EQ(*(win_emu).process().exit_status, status); \
} while (false)
#define ASSERT_TERMINATED_WITH_STATUS(win_emu, status) \ #define ASSERT_TERMINATED_SUCCESSFULLY(win_emu) ASSERT_TERMINATED_WITH_STATUS(win_emu, STATUS_SUCCESS)
do { \
ASSERT_TRUE((win_emu).process().exit_status.has_value()); \
ASSERT_EQ(*(win_emu).process().exit_status, status); \
} while(false)
#define ASSERT_TERMINATED_SUCCESSFULLY(win_emu) \
ASSERT_TERMINATED_WITH_STATUS(win_emu, STATUS_SUCCESS)
namespace test namespace test
{ {
inline windows_emulator create_sample_emulator(emulator_settings settings) inline windows_emulator create_sample_emulator(emulator_settings settings)
{ {
settings.application = "./test-sample.exe"; settings.application = "./test-sample.exe";
return windows_emulator{std::move(settings)}; return windows_emulator{std::move(settings)};
} }
inline windows_emulator create_sample_emulator() inline windows_emulator create_sample_emulator()
{ {
emulator_settings settings emulator_settings settings{
{ .disable_logging = true,
.disable_logging = true, .use_relative_time = true,
.use_relative_time = true, };
};
return create_sample_emulator(std::move(settings)); return create_sample_emulator(std::move(settings));
} }
} }

View File

@@ -2,6 +2,6 @@
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();
} }

View File

@@ -2,77 +2,77 @@
namespace test namespace test
{ {
TEST(SerializationTest, SerializedDataIsReproducible) TEST(SerializationTest, SerializedDataIsReproducible)
{ {
auto emu1 = create_sample_emulator(); auto emu1 = create_sample_emulator();
emu1.start(); emu1.start();
ASSERT_TERMINATED_SUCCESSFULLY(emu1); ASSERT_TERMINATED_SUCCESSFULLY(emu1);
utils::buffer_serializer serializer1{}; utils::buffer_serializer serializer1{};
emu1.serialize(serializer1); emu1.serialize(serializer1);
utils::buffer_deserializer deserializer{serializer1.get_buffer()}; utils::buffer_deserializer deserializer{serializer1.get_buffer()};
windows_emulator new_emu{}; windows_emulator new_emu{};
new_emu.deserialize(deserializer); new_emu.deserialize(deserializer);
utils::buffer_serializer serializer2{}; utils::buffer_serializer serializer2{};
new_emu.serialize(serializer2); new_emu.serialize(serializer2);
auto buffer1 = serializer1.move_buffer(); auto buffer1 = serializer1.move_buffer();
auto buffer2 = serializer2.move_buffer(); auto buffer2 = serializer2.move_buffer();
ASSERT_EQ(serializer1.get_buffer(), serializer2.get_buffer()); ASSERT_EQ(serializer1.get_buffer(), serializer2.get_buffer());
} }
TEST(SerializationTest, EmulationIsReproducible) TEST(SerializationTest, EmulationIsReproducible)
{ {
auto emu1 = create_sample_emulator(); auto emu1 = create_sample_emulator();
emu1.start(); emu1.start();
ASSERT_TERMINATED_SUCCESSFULLY(emu1); ASSERT_TERMINATED_SUCCESSFULLY(emu1);
utils::buffer_serializer serializer1{}; utils::buffer_serializer serializer1{};
emu1.serialize(serializer1); emu1.serialize(serializer1);
auto emu2 = create_sample_emulator(); auto emu2 = create_sample_emulator();
emu2.start(); emu2.start();
ASSERT_TERMINATED_SUCCESSFULLY(emu2); ASSERT_TERMINATED_SUCCESSFULLY(emu2);
utils::buffer_serializer serializer2{}; utils::buffer_serializer serializer2{};
emu2.serialize(serializer2); emu2.serialize(serializer2);
ASSERT_EQ(serializer1.get_buffer(), serializer2.get_buffer()); ASSERT_EQ(serializer1.get_buffer(), serializer2.get_buffer());
} }
TEST(SerializationTest, DeserializedEmulatorBehavesLikeSource) TEST(SerializationTest, DeserializedEmulatorBehavesLikeSource)
{ {
auto emu = create_sample_emulator(); auto emu = create_sample_emulator();
emu.start({}, 100); emu.start({}, 100);
utils::buffer_serializer serializer{}; utils::buffer_serializer serializer{};
emu.serialize(serializer); emu.serialize(serializer);
utils::buffer_deserializer deserializer{serializer.get_buffer()}; utils::buffer_deserializer deserializer{serializer.get_buffer()};
windows_emulator new_emu{}; windows_emulator new_emu{};
new_emu.log.disable_output(true); new_emu.log.disable_output(true);
new_emu.deserialize(deserializer); new_emu.deserialize(deserializer);
new_emu.start(); new_emu.start();
ASSERT_TERMINATED_SUCCESSFULLY(new_emu); ASSERT_TERMINATED_SUCCESSFULLY(new_emu);
emu.start(); emu.start();
ASSERT_TERMINATED_SUCCESSFULLY(emu); ASSERT_TERMINATED_SUCCESSFULLY(emu);
utils::buffer_serializer serializer1{}; utils::buffer_serializer serializer1{};
utils::buffer_serializer serializer2{}; utils::buffer_serializer serializer2{};
emu.serialize(serializer1); emu.serialize(serializer1);
new_emu.serialize(serializer2); new_emu.serialize(serializer2);
ASSERT_EQ(serializer1.get_buffer(), serializer2.get_buffer()); ASSERT_EQ(serializer1.get_buffer(), serializer2.get_buffer());
} }
} }

View File

@@ -2,44 +2,41 @@
namespace test namespace test
{ {
TEST(TimeTest, SystemTimeIsAccurate) TEST(TimeTest, SystemTimeIsAccurate)
{ {
std::string output_buffer{}; std::string output_buffer{};
const emulator_settings settings{ const emulator_settings settings{
.arguments = {u"-time"}, .arguments = {u"-time"},
.stdout_callback = [&output_buffer](const std::string_view data) .stdout_callback = [&output_buffer](const std::string_view data) { output_buffer.append(data); },
{ .disable_logging = true,
output_buffer.append(data); .use_relative_time = false,
}, };
.disable_logging = true,
.use_relative_time = false,
};
auto emu = create_sample_emulator(settings); auto emu = create_sample_emulator(settings);
emu.start(); emu.start();
constexpr auto prefix = "Time: "sv; constexpr auto prefix = "Time: "sv;
ASSERT_TERMINATED_SUCCESSFULLY(emu); ASSERT_TERMINATED_SUCCESSFULLY(emu);
ASSERT_TRUE(output_buffer.starts_with(prefix)); ASSERT_TRUE(output_buffer.starts_with(prefix));
output_buffer = output_buffer.substr(prefix.size()); output_buffer = output_buffer.substr(prefix.size());
while (!output_buffer.empty() && (output_buffer.back() == '\n' || output_buffer.back() == '\r')) while (!output_buffer.empty() && (output_buffer.back() == '\n' || output_buffer.back() == '\r'))
{ {
output_buffer.pop_back(); output_buffer.pop_back();
} }
const auto time = strtoll(output_buffer.c_str(), nullptr, 10); const auto time = strtoll(output_buffer.c_str(), nullptr, 10);
using time_point = std::chrono::system_clock::time_point; using time_point = std::chrono::system_clock::time_point;
const time_point::duration time_duration(time); const time_point::duration time_duration(time);
const time_point tp(time_duration); const time_point tp(time_duration);
const auto now = std::chrono::system_clock::now(); const auto now = std::chrono::system_clock::now();
const auto diff = now - tp; const auto diff = now - tp;
ASSERT_LE(diff, std::chrono::hours(1)); ASSERT_LE(diff, std::chrono::hours(1));
} }
} }

View File

@@ -3,149 +3,149 @@
namespace context_frame namespace context_frame
{ {
void restore(x64_emulator& emu, const CONTEXT64& context) void restore(x64_emulator& emu, const CONTEXT64& context)
{ {
if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS_64) if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS_64)
{ {
emu.reg(x64_register::dr0, context.Dr0); emu.reg(x64_register::dr0, context.Dr0);
emu.reg(x64_register::dr1, context.Dr1); emu.reg(x64_register::dr1, context.Dr1);
emu.reg(x64_register::dr2, context.Dr2); emu.reg(x64_register::dr2, context.Dr2);
emu.reg(x64_register::dr3, context.Dr3); emu.reg(x64_register::dr3, context.Dr3);
emu.reg(x64_register::dr6, context.Dr6); emu.reg(x64_register::dr6, context.Dr6);
emu.reg(x64_register::dr7, context.Dr7); emu.reg(x64_register::dr7, context.Dr7);
} }
if (context.ContextFlags & CONTEXT_CONTROL_64) if (context.ContextFlags & CONTEXT_CONTROL_64)
{ {
emu.reg<uint16_t>(x64_register::ss, context.SegSs); emu.reg<uint16_t>(x64_register::ss, context.SegSs);
emu.reg<uint16_t>(x64_register::cs, context.SegCs); emu.reg<uint16_t>(x64_register::cs, context.SegCs);
emu.reg(x64_register::rip, context.Rip); emu.reg(x64_register::rip, context.Rip);
emu.reg(x64_register::rsp, context.Rsp); emu.reg(x64_register::rsp, context.Rsp);
emu.reg<uint32_t>(x64_register::eflags, context.EFlags); emu.reg<uint32_t>(x64_register::eflags, context.EFlags);
} }
if (context.ContextFlags & CONTEXT_INTEGER_64) if (context.ContextFlags & CONTEXT_INTEGER_64)
{ {
emu.reg(x64_register::rax, context.Rax); emu.reg(x64_register::rax, context.Rax);
emu.reg(x64_register::rbx, context.Rbx); emu.reg(x64_register::rbx, context.Rbx);
emu.reg(x64_register::rcx, context.Rcx); emu.reg(x64_register::rcx, context.Rcx);
emu.reg(x64_register::rdx, context.Rdx); emu.reg(x64_register::rdx, context.Rdx);
emu.reg(x64_register::rbp, context.Rbp); emu.reg(x64_register::rbp, context.Rbp);
emu.reg(x64_register::rsi, context.Rsi); emu.reg(x64_register::rsi, context.Rsi);
emu.reg(x64_register::rdi, context.Rdi); emu.reg(x64_register::rdi, context.Rdi);
emu.reg(x64_register::r8, context.R8); emu.reg(x64_register::r8, context.R8);
emu.reg(x64_register::r9, context.R9); emu.reg(x64_register::r9, context.R9);
emu.reg(x64_register::r10, context.R10); emu.reg(x64_register::r10, context.R10);
emu.reg(x64_register::r11, context.R11); emu.reg(x64_register::r11, context.R11);
emu.reg(x64_register::r12, context.R12); emu.reg(x64_register::r12, context.R12);
emu.reg(x64_register::r13, context.R13); emu.reg(x64_register::r13, context.R13);
emu.reg(x64_register::r14, context.R14); emu.reg(x64_register::r14, context.R14);
emu.reg(x64_register::r15, context.R15); emu.reg(x64_register::r15, context.R15);
} }
/*if (context.ContextFlags & CONTEXT_SEGMENTS) /*if (context.ContextFlags & CONTEXT_SEGMENTS)
{ {
emu.reg<uint16_t>(x64_register::ds, context.SegDs); emu.reg<uint16_t>(x64_register::ds, context.SegDs);
emu.reg<uint16_t>(x64_register::es, context.SegEs); emu.reg<uint16_t>(x64_register::es, context.SegEs);
emu.reg<uint16_t>(x64_register::fs, context.SegFs); emu.reg<uint16_t>(x64_register::fs, context.SegFs);
emu.reg<uint16_t>(x64_register::gs, context.SegGs); emu.reg<uint16_t>(x64_register::gs, context.SegGs);
}*/ }*/
if (context.ContextFlags & CONTEXT_FLOATING_POINT_64) if (context.ContextFlags & CONTEXT_FLOATING_POINT_64)
{ {
emu.reg<uint16_t>(x64_register::fpcw, context.FltSave.ControlWord); emu.reg<uint16_t>(x64_register::fpcw, context.FltSave.ControlWord);
emu.reg<uint16_t>(x64_register::fpsw, context.FltSave.StatusWord); emu.reg<uint16_t>(x64_register::fpsw, context.FltSave.StatusWord);
emu.reg<uint16_t>(x64_register::fptag, context.FltSave.TagWord); emu.reg<uint16_t>(x64_register::fptag, context.FltSave.TagWord);
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::st0) + i); const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::st0) + i);
emu.reg<M128A>(reg, context.FltSave.FloatRegisters[i]); emu.reg<M128A>(reg, context.FltSave.FloatRegisters[i]);
} }
} }
if (context.ContextFlags & CONTEXT_XSTATE_64) if (context.ContextFlags & CONTEXT_XSTATE_64)
{ {
emu.reg<uint32_t>(x64_register::mxcsr, context.MxCsr); emu.reg<uint32_t>(x64_register::mxcsr, context.MxCsr);
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
{ {
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::xmm0) + i); const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::xmm0) + i);
emu.reg<M128A>(reg, (&context.Xmm0)[i]); emu.reg<M128A>(reg, (&context.Xmm0)[i]);
} }
} }
} }
void save(x64_emulator& emu, CONTEXT64& context) void save(x64_emulator& emu, CONTEXT64& context)
{ {
if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS_64) if (context.ContextFlags & CONTEXT_DEBUG_REGISTERS_64)
{ {
context.Dr0 = emu.reg(x64_register::dr0); context.Dr0 = emu.reg(x64_register::dr0);
context.Dr1 = emu.reg(x64_register::dr1); context.Dr1 = emu.reg(x64_register::dr1);
context.Dr2 = emu.reg(x64_register::dr2); context.Dr2 = emu.reg(x64_register::dr2);
context.Dr3 = emu.reg(x64_register::dr3); context.Dr3 = emu.reg(x64_register::dr3);
context.Dr6 = emu.reg(x64_register::dr6); context.Dr6 = emu.reg(x64_register::dr6);
context.Dr7 = emu.reg(x64_register::dr7); context.Dr7 = emu.reg(x64_register::dr7);
} }
if (context.ContextFlags & CONTEXT_CONTROL_64) if (context.ContextFlags & CONTEXT_CONTROL_64)
{ {
context.SegSs = emu.reg<uint16_t>(x64_register::ss); context.SegSs = emu.reg<uint16_t>(x64_register::ss);
context.SegCs = emu.reg<uint16_t>(x64_register::cs); context.SegCs = emu.reg<uint16_t>(x64_register::cs);
context.Rip = emu.reg(x64_register::rip); context.Rip = emu.reg(x64_register::rip);
context.Rsp = emu.reg(x64_register::rsp); context.Rsp = emu.reg(x64_register::rsp);
context.EFlags = emu.reg<uint32_t>(x64_register::eflags); context.EFlags = emu.reg<uint32_t>(x64_register::eflags);
} }
if (context.ContextFlags & CONTEXT_INTEGER_64) if (context.ContextFlags & CONTEXT_INTEGER_64)
{ {
context.Rax = emu.reg(x64_register::rax); context.Rax = emu.reg(x64_register::rax);
context.Rbx = emu.reg(x64_register::rbx); context.Rbx = emu.reg(x64_register::rbx);
context.Rcx = emu.reg(x64_register::rcx); context.Rcx = emu.reg(x64_register::rcx);
context.Rdx = emu.reg(x64_register::rdx); context.Rdx = emu.reg(x64_register::rdx);
context.Rbp = emu.reg(x64_register::rbp); context.Rbp = emu.reg(x64_register::rbp);
context.Rsi = emu.reg(x64_register::rsi); context.Rsi = emu.reg(x64_register::rsi);
context.Rdi = emu.reg(x64_register::rdi); context.Rdi = emu.reg(x64_register::rdi);
context.R8 = emu.reg(x64_register::r8); context.R8 = emu.reg(x64_register::r8);
context.R9 = emu.reg(x64_register::r9); context.R9 = emu.reg(x64_register::r9);
context.R10 = emu.reg(x64_register::r10); context.R10 = emu.reg(x64_register::r10);
context.R11 = emu.reg(x64_register::r11); context.R11 = emu.reg(x64_register::r11);
context.R12 = emu.reg(x64_register::r12); context.R12 = emu.reg(x64_register::r12);
context.R13 = emu.reg(x64_register::r13); context.R13 = emu.reg(x64_register::r13);
context.R14 = emu.reg(x64_register::r14); context.R14 = emu.reg(x64_register::r14);
context.R15 = emu.reg(x64_register::r15); context.R15 = emu.reg(x64_register::r15);
} }
if (context.ContextFlags & CONTEXT_SEGMENTS_64) if (context.ContextFlags & CONTEXT_SEGMENTS_64)
{ {
context.SegDs = emu.reg<uint16_t>(x64_register::ds); context.SegDs = emu.reg<uint16_t>(x64_register::ds);
context.SegEs = emu.reg<uint16_t>(x64_register::es); context.SegEs = emu.reg<uint16_t>(x64_register::es);
context.SegFs = emu.reg<uint16_t>(x64_register::fs); context.SegFs = emu.reg<uint16_t>(x64_register::fs);
context.SegGs = emu.reg<uint16_t>(x64_register::gs); context.SegGs = emu.reg<uint16_t>(x64_register::gs);
} }
if (context.ContextFlags & CONTEXT_FLOATING_POINT_64) if (context.ContextFlags & CONTEXT_FLOATING_POINT_64)
{ {
context.FltSave.ControlWord = emu.reg<uint16_t>(x64_register::fpcw); context.FltSave.ControlWord = emu.reg<uint16_t>(x64_register::fpcw);
context.FltSave.StatusWord = emu.reg<uint16_t>(x64_register::fpsw); context.FltSave.StatusWord = emu.reg<uint16_t>(x64_register::fpsw);
context.FltSave.TagWord = static_cast<BYTE>(emu.reg<uint16_t>(x64_register::fptag)); context.FltSave.TagWord = static_cast<BYTE>(emu.reg<uint16_t>(x64_register::fptag));
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::st0) + i); const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::st0) + i);
context.FltSave.FloatRegisters[i] = emu.reg<M128A>(reg); context.FltSave.FloatRegisters[i] = emu.reg<M128A>(reg);
} }
} }
if (context.ContextFlags & CONTEXT_XSTATE_64) if (context.ContextFlags & CONTEXT_XSTATE_64)
{ {
context.MxCsr = emu.reg<uint32_t>(x64_register::mxcsr); context.MxCsr = emu.reg<uint32_t>(x64_register::mxcsr);
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
{ {
const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::xmm0) + i); const auto reg = static_cast<x64_register>(static_cast<int>(x64_register::xmm0) + i);
(&context.Xmm0)[i] = emu.reg<M128A>(reg); (&context.Xmm0)[i] = emu.reg<M128A>(reg);
} }
} }
} }
} }

View File

@@ -3,6 +3,6 @@
namespace context_frame namespace context_frame
{ {
void save(x64_emulator& emu, CONTEXT64& context); void save(x64_emulator& emu, CONTEXT64& context);
void restore(x64_emulator& emu, const CONTEXT64& context); void restore(x64_emulator& emu, const CONTEXT64& context);
} }

View File

@@ -3,137 +3,134 @@
#include <utils/finally.hpp> #include <utils/finally.hpp>
extern "C" { extern "C"
{
#include <gdbstub.h> #include <gdbstub.h>
} }
namespace namespace
{ {
gdb_action_t map_gdb_action(const gdb_action action) gdb_action_t map_gdb_action(const gdb_action action)
{ {
switch (action) switch (action)
{ {
case gdb_action::none: case gdb_action::none:
return ACT_NONE; return ACT_NONE;
case gdb_action::resume: case gdb_action::resume:
return ACT_RESUME; return ACT_RESUME;
case gdb_action::shutdown: case gdb_action::shutdown:
return ACT_SHUTDOWN; return ACT_SHUTDOWN;
} }
throw std::runtime_error("Bad action"); throw std::runtime_error("Bad action");
} }
breakpoint_type map_breakpoint_type(const bp_type_t type) breakpoint_type map_breakpoint_type(const bp_type_t type)
{ {
switch (type) switch (type)
{ {
case BP_SOFTWARE: case BP_SOFTWARE:
return breakpoint_type::software; return breakpoint_type::software;
case BP_HARDWARE_EXEC: case BP_HARDWARE_EXEC:
return breakpoint_type::hardware_exec; return breakpoint_type::hardware_exec;
case BP_HARDWARE_WRITE: case BP_HARDWARE_WRITE:
return breakpoint_type::hardware_write; return breakpoint_type::hardware_write;
case BP_HARDWARE_READ: case BP_HARDWARE_READ:
return breakpoint_type::hardware_read; return breakpoint_type::hardware_read;
case BP_HARDWARE_READ_WRITE: case BP_HARDWARE_READ_WRITE:
return breakpoint_type::hardware_read_write; return breakpoint_type::hardware_read_write;
} }
throw std::runtime_error("Bad breakpoint type"); throw std::runtime_error("Bad breakpoint type");
} }
gdb_stub_handler& get_handler(void* args) gdb_stub_handler& get_handler(void* args)
{ {
return *static_cast<gdb_stub_handler*>(args); return *static_cast<gdb_stub_handler*>(args);
} }
gdb_action_t cont(void* args) gdb_action_t cont(void* args)
{ {
return map_gdb_action(get_handler(args).cont()); return map_gdb_action(get_handler(args).cont());
} }
gdb_action_t stepi(void* args) gdb_action_t stepi(void* args)
{ {
return map_gdb_action(get_handler(args).stepi()); return map_gdb_action(get_handler(args).stepi());
} }
int read_reg(void* args, const int regno, size_t* value) int read_reg(void* args, const int regno, size_t* value)
{ {
return get_handler(args).read_reg(regno, value) ? 0 : 1; return get_handler(args).read_reg(regno, value) ? 0 : 1;
} }
int write_reg(void* args, const int regno, const size_t value) int write_reg(void* args, const int regno, const size_t value)
{ {
return get_handler(args).write_reg(regno, value) ? 0 : 1; return get_handler(args).write_reg(regno, value) ? 0 : 1;
} }
int read_mem(void* args, const size_t addr, const size_t len, void* val) int read_mem(void* args, const size_t addr, const size_t len, void* val)
{ {
return get_handler(args).read_mem(addr, len, val) ? 0 : 1; return get_handler(args).read_mem(addr, len, val) ? 0 : 1;
} }
int write_mem(void* args, const size_t addr, const size_t len, void* val) int write_mem(void* args, const size_t addr, const size_t len, void* val)
{ {
return get_handler(args).write_mem(addr, len, val) ? 0 : 1; return get_handler(args).write_mem(addr, len, val) ? 0 : 1;
} }
bool set_bp(void* args, const size_t addr, const bp_type_t type, const size_t size) bool set_bp(void* args, const size_t addr, const bp_type_t type, const size_t size)
{ {
return get_handler(args).set_bp(map_breakpoint_type(type), addr, size); return get_handler(args).set_bp(map_breakpoint_type(type), addr, size);
} }
bool del_bp(void* args, const size_t addr, const bp_type_t type, const size_t size) bool del_bp(void* args, const size_t addr, const bp_type_t type, const size_t size)
{ {
return get_handler(args).del_bp(map_breakpoint_type(type), addr, size); return get_handler(args).del_bp(map_breakpoint_type(type), addr, size);
} }
void on_interrupt(void* args) void on_interrupt(void* args)
{ {
get_handler(args).on_interrupt(); get_handler(args).on_interrupt();
} }
target_ops get_target_ops() target_ops get_target_ops()
{ {
target_ops ops{}; target_ops ops{};
ops.cont = cont; ops.cont = cont;
ops.stepi = stepi; ops.stepi = stepi;
ops.read_reg = read_reg; ops.read_reg = read_reg;
ops.write_reg = write_reg; ops.write_reg = write_reg;
ops.read_mem = read_mem; ops.read_mem = read_mem;
ops.write_mem = write_mem; ops.write_mem = write_mem;
ops.set_bp = set_bp; ops.set_bp = set_bp;
ops.del_bp = del_bp; ops.del_bp = del_bp;
ops.on_interrupt = on_interrupt; ops.on_interrupt = on_interrupt;
return ops; return ops;
} }
} }
bool run_gdb_stub(gdb_stub_handler& handler, std::string target_description, const size_t register_count, bool run_gdb_stub(gdb_stub_handler& handler, std::string target_description, const size_t register_count,
std::string bind_address) std::string bind_address)
{ {
const arch_info_t info const arch_info_t info{
{ target_description.data(),
target_description.data(), static_cast<int>(register_count),
static_cast<int>(register_count), sizeof(uint64_t),
sizeof(uint64_t), };
};
auto ops = get_target_ops(); auto ops = get_target_ops();
gdbstub_t stub{}; gdbstub_t stub{};
if (!gdbstub_init(&stub, &ops, info, bind_address.data())) if (!gdbstub_init(&stub, &ops, info, bind_address.data()))
{ {
return false; return false;
} }
const auto _ = utils::finally([&] const auto _ = utils::finally([&] { gdbstub_close(&stub); });
{
gdbstub_close(&stub);
});
return gdbstub_run(&stub, &handler); return gdbstub_run(&stub, &handler);
} }

View File

@@ -2,37 +2,37 @@
enum class gdb_action : uint8_t enum class gdb_action : uint8_t
{ {
none, none,
resume, resume,
shutdown, shutdown,
}; };
enum class breakpoint_type : uint8_t enum class breakpoint_type : uint8_t
{ {
software, software,
hardware_exec, hardware_exec,
hardware_write, hardware_write,
hardware_read, hardware_read,
hardware_read_write, hardware_read_write,
}; };
struct gdb_stub_handler struct gdb_stub_handler
{ {
virtual ~gdb_stub_handler() = default; virtual ~gdb_stub_handler() = default;
virtual gdb_action cont() = 0; virtual gdb_action cont() = 0;
virtual gdb_action stepi() = 0; virtual gdb_action stepi() = 0;
virtual bool read_reg(int regno, size_t* value) = 0; virtual bool read_reg(int regno, size_t* value) = 0;
virtual bool write_reg(int regno, size_t value) = 0; virtual bool write_reg(int regno, size_t value) = 0;
virtual bool read_mem(size_t addr, size_t len, void* val) = 0; virtual bool read_mem(size_t addr, size_t len, void* val) = 0;
virtual bool write_mem(size_t addr, size_t len, void* val) = 0; virtual bool write_mem(size_t addr, size_t len, void* val) = 0;
virtual bool set_bp(breakpoint_type type, size_t addr, size_t size) = 0; virtual bool set_bp(breakpoint_type type, size_t addr, size_t size) = 0;
virtual bool del_bp(breakpoint_type type, size_t addr, size_t size) = 0; virtual bool del_bp(breakpoint_type type, size_t addr, size_t size) = 0;
virtual void on_interrupt() = 0; virtual void on_interrupt() = 0;
}; };
bool run_gdb_stub(gdb_stub_handler& handler, std::string target_description, size_t register_count, bool run_gdb_stub(gdb_stub_handler& handler, std::string target_description, size_t register_count,

View File

@@ -5,41 +5,41 @@
class win_x64_gdb_stub_handler : public x64_gdb_stub_handler class win_x64_gdb_stub_handler : public x64_gdb_stub_handler
{ {
public: public:
win_x64_gdb_stub_handler(windows_emulator& win_emu) win_x64_gdb_stub_handler(windows_emulator& win_emu)
: x64_gdb_stub_handler(win_emu.emu()) : x64_gdb_stub_handler(win_emu.emu()),
, win_emu_(&win_emu) win_emu_(&win_emu)
{ {
} }
gdb_action cont() override gdb_action cont() override
{ {
try try
{ {
this->win_emu_->start(); this->win_emu_->start();
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
puts(e.what()); puts(e.what());
} }
return gdb_action::resume; return gdb_action::resume;
} }
gdb_action stepi() override gdb_action stepi() override
{ {
try try
{ {
this->win_emu_->start({}, 1); this->win_emu_->start({}, 1);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
puts(e.what()); puts(e.what());
} }
return gdb_action::resume; return gdb_action::resume;
} }
private: private:
windows_emulator* win_emu_{}; windows_emulator* win_emu_{};
}; };

View File

@@ -4,212 +4,194 @@
#include "scoped_hook.hpp" #include "scoped_hook.hpp"
inline std::vector gdb_registers{ inline std::vector gdb_registers{
x64_register::rax, x64_register::rax, x64_register::rbx, x64_register::rcx, x64_register::rdx, x64_register::rsi, x64_register::rdi,
x64_register::rbx, x64_register::rbp, x64_register::rsp, x64_register::r8, x64_register::r9, x64_register::r10, x64_register::r11,
x64_register::rcx, x64_register::r12, x64_register::r13, x64_register::r14, x64_register::r15, x64_register::rip, x64_register::rflags,
x64_register::rdx, /*x64_register::cs,
x64_register::rsi, x64_register::ss,
x64_register::rdi, x64_register::ds,
x64_register::rbp, x64_register::es,
x64_register::rsp, x64_register::fs,
x64_register::r8, x64_register::gs,*/
x64_register::r9,
x64_register::r10,
x64_register::r11,
x64_register::r12,
x64_register::r13,
x64_register::r14,
x64_register::r15,
x64_register::rip,
x64_register::rflags,
/*x64_register::cs,
x64_register::ss,
x64_register::ds,
x64_register::es,
x64_register::fs,
x64_register::gs,*/
}; };
inline memory_operation map_breakpoint_type(const breakpoint_type type) inline memory_operation map_breakpoint_type(const breakpoint_type type)
{ {
switch (type) switch (type)
{ {
case breakpoint_type::software: case breakpoint_type::software:
case breakpoint_type::hardware_exec: case breakpoint_type::hardware_exec:
return memory_operation::exec; return memory_operation::exec;
case breakpoint_type::hardware_read: case breakpoint_type::hardware_read:
return memory_permission::read; return memory_permission::read;
case breakpoint_type::hardware_write: case breakpoint_type::hardware_write:
return memory_permission::write; return memory_permission::write;
case breakpoint_type::hardware_read_write: case breakpoint_type::hardware_read_write:
return memory_permission::read_write; return memory_permission::read_write;
default: default:
throw std::runtime_error("Bad bp type"); throw std::runtime_error("Bad bp type");
} }
} }
struct breakpoint_key struct breakpoint_key
{ {
size_t addr{}; size_t addr{};
size_t size{}; size_t size{};
breakpoint_type type{}; breakpoint_type type{};
bool operator==(const breakpoint_key& other) const bool operator==(const breakpoint_key& other) const
{ {
return this->addr == other.addr && this->size == other.size && this->type == other.type; return this->addr == other.addr && this->size == other.size && this->type == other.type;
} }
}; };
template <> template <>
struct std::hash<breakpoint_key> struct std::hash<breakpoint_key>
{ {
std::size_t operator()(const breakpoint_key& k) const noexcept std::size_t operator()(const breakpoint_key& k) const noexcept
{ {
return ((std::hash<size_t>()(k.addr) return ((std::hash<size_t>()(k.addr) ^ (std::hash<size_t>()(k.size) << 1)) >> 1) ^
^ (std::hash<size_t>()(k.size) << 1)) >> 1) (std::hash<size_t>()(static_cast<size_t>(k.type)) << 1);
^ (std::hash<size_t>()(static_cast<size_t>(k.type)) << 1); }
}
}; };
class x64_gdb_stub_handler : public gdb_stub_handler class x64_gdb_stub_handler : public gdb_stub_handler
{ {
public: public:
x64_gdb_stub_handler(x64_emulator& emu) x64_gdb_stub_handler(x64_emulator& emu)
: emu_(&emu) : emu_(&emu)
{ {
} }
~x64_gdb_stub_handler() override = default; ~x64_gdb_stub_handler() override = default;
gdb_action cont() override gdb_action cont() override
{ {
try try
{ {
this->emu_->start_from_ip(); this->emu_->start_from_ip();
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
puts(e.what()); puts(e.what());
} }
return gdb_action::resume; return gdb_action::resume;
} }
gdb_action stepi() override gdb_action stepi() override
{ {
try try
{ {
this->emu_->start_from_ip({}, 1); this->emu_->start_from_ip({}, 1);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
puts(e.what()); puts(e.what());
} }
return gdb_action::resume; return gdb_action::resume;
} }
bool read_reg(const int regno, size_t* value) override bool read_reg(const int regno, size_t* value) override
{ {
*value = 0; *value = 0;
try try
{ {
if (static_cast<size_t>(regno) >= gdb_registers.size()) if (static_cast<size_t>(regno) >= gdb_registers.size())
{ {
return true; return true;
} }
this->emu_->read_register(gdb_registers[regno], value, sizeof(*value)); this->emu_->read_register(gdb_registers[regno], value, sizeof(*value));
return true; return true;
} }
catch (...) catch (...)
{ {
return true; return true;
} }
} }
bool write_reg(const int regno, const size_t value) override bool write_reg(const int regno, const size_t value) override
{ {
try try
{ {
if (static_cast<size_t>(regno) >= gdb_registers.size()) if (static_cast<size_t>(regno) >= gdb_registers.size())
{ {
return true; return true;
} }
this->emu_->write_register(gdb_registers[regno], &value, sizeof(value)); this->emu_->write_register(gdb_registers[regno], &value, sizeof(value));
return true; return true;
} }
catch (...) catch (...)
{ {
return false; return false;
} }
} }
bool read_mem(const size_t addr, const size_t len, void* val) override bool read_mem(const size_t addr, const size_t len, void* val) override
{ {
return this->emu_->try_read_memory(addr, val, len); return this->emu_->try_read_memory(addr, val, len);
} }
bool write_mem(const size_t addr, const size_t len, void* val) override bool write_mem(const size_t addr, const size_t len, void* val) override
{ {
try try
{ {
this->emu_->write_memory(addr, val, len); this->emu_->write_memory(addr, val, len);
return true; return true;
} }
catch (...) catch (...)
{ {
return false; return false;
} }
} }
bool set_bp(const breakpoint_type type, const size_t addr, const size_t size) override bool set_bp(const breakpoint_type type, const size_t addr, const size_t size) override
{ {
try try
{ {
this->hooks_[{addr, size, type}] = scoped_hook(*this->emu_, this->emu_->hook_memory_access( this->hooks_[{addr, size, type}] = scoped_hook(
addr, size, map_breakpoint_type(type), *this->emu_, this->emu_->hook_memory_access(
[this](uint64_t, size_t, uint64_t, memory_operation) addr, size, map_breakpoint_type(type),
{ [this](uint64_t, size_t, uint64_t, memory_operation) { this->on_interrupt(); }));
this->on_interrupt();
}));
return true; return true;
} }
catch (...) catch (...)
{ {
return false; return false;
} }
} }
bool del_bp(const breakpoint_type type, const size_t addr, const size_t size) override bool del_bp(const breakpoint_type type, const size_t addr, const size_t size) override
{ {
try try
{ {
const auto entry = this->hooks_.find({addr, size, type}); const auto entry = this->hooks_.find({addr, size, type});
if (entry == this->hooks_.end()) if (entry == this->hooks_.end())
{ {
return false; return false;
} }
this->hooks_.erase(entry); this->hooks_.erase(entry);
return true; return true;
} }
catch (...) catch (...)
{ {
return false; return false;
} }
} }
void on_interrupt() override void on_interrupt() override
{ {
this->emu_->stop(); this->emu_->stop();
} }
private: private:
x64_emulator* emu_{}; x64_emulator* emu_{};
std::unordered_map<breakpoint_key, scoped_hook> hooks_{}; std::unordered_map<breakpoint_key, scoped_hook> hooks_{};
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -7,88 +7,88 @@ typedef LONG TDI_STATUS;
template <typename Traits> template <typename Traits>
struct TDI_CONNECTION_INFORMATION struct TDI_CONNECTION_INFORMATION
{ {
LONG UserDataLength; LONG UserDataLength;
typename Traits::PVOID UserData; typename Traits::PVOID UserData;
LONG OptionsLength; LONG OptionsLength;
typename Traits::PVOID Options; typename Traits::PVOID Options;
LONG RemoteAddressLength; LONG RemoteAddressLength;
typename Traits::PVOID RemoteAddress; typename Traits::PVOID RemoteAddress;
}; };
template <typename Traits> template <typename Traits>
struct TDI_REQUEST struct TDI_REQUEST
{ {
union union
{ {
typename Traits::HANDLE AddressHandle; typename Traits::HANDLE AddressHandle;
EMULATOR_CAST(typename Traits::PVOID, CONNECTION_CONTEXT) ConnectionContext; EMULATOR_CAST(typename Traits::PVOID, CONNECTION_CONTEXT) ConnectionContext;
typename Traits::HANDLE ControlChannel; typename Traits::HANDLE ControlChannel;
} Handle; } Handle;
typename Traits::PVOID RequestNotifyObject; typename Traits::PVOID RequestNotifyObject;
typename Traits::PVOID RequestContext; typename Traits::PVOID RequestContext;
TDI_STATUS TdiStatus; TDI_STATUS TdiStatus;
}; };
template <typename Traits> template <typename Traits>
struct TDI_REQUEST_SEND_DATAGRAM struct TDI_REQUEST_SEND_DATAGRAM
{ {
TDI_REQUEST<Traits> Request; TDI_REQUEST<Traits> Request;
EMULATOR_CAST(typename Traits::PVOID, PTDI_CONNECTION_INFORMATION) SendDatagramInformation; EMULATOR_CAST(typename Traits::PVOID, PTDI_CONNECTION_INFORMATION) SendDatagramInformation;
}; };
template <typename Traits> template <typename Traits>
struct AFD_SEND_INFO struct AFD_SEND_INFO
{ {
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray; EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
ULONG BufferCount; ULONG BufferCount;
ULONG AfdFlags; ULONG AfdFlags;
ULONG TdiFlags; ULONG TdiFlags;
}; };
template <typename Traits> template <typename Traits>
struct AFD_SEND_DATAGRAM_INFO struct AFD_SEND_DATAGRAM_INFO
{ {
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray; EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
ULONG BufferCount; ULONG BufferCount;
ULONG AfdFlags; ULONG AfdFlags;
TDI_REQUEST_SEND_DATAGRAM<Traits> TdiRequest; TDI_REQUEST_SEND_DATAGRAM<Traits> TdiRequest;
TDI_CONNECTION_INFORMATION<Traits> TdiConnInfo; TDI_CONNECTION_INFORMATION<Traits> TdiConnInfo;
}; };
template <typename Traits> template <typename Traits>
struct AFD_RECV_INFO struct AFD_RECV_INFO
{ {
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray; EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
ULONG BufferCount; ULONG BufferCount;
ULONG AfdFlags; ULONG AfdFlags;
ULONG TdiFlags; ULONG TdiFlags;
}; };
template <typename Traits> template <typename Traits>
struct AFD_RECV_DATAGRAM_INFO struct AFD_RECV_DATAGRAM_INFO
{ {
EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray; EMULATOR_CAST(typename Traits::PVOID, LPWSABUF) BufferArray;
ULONG BufferCount; ULONG BufferCount;
ULONG AfdFlags; ULONG AfdFlags;
ULONG TdiFlags; ULONG TdiFlags;
typename Traits::PVOID Address; typename Traits::PVOID Address;
EMULATOR_CAST(typename Traits::PVOID, PULONG) AddressLength; EMULATOR_CAST(typename Traits::PVOID, PULONG) AddressLength;
}; };
struct AFD_POLL_HANDLE_INFO64 struct AFD_POLL_HANDLE_INFO64
{ {
EmulatorTraits<Emu64>::HANDLE Handle; EmulatorTraits<Emu64>::HANDLE Handle;
ULONG PollEvents; ULONG PollEvents;
NTSTATUS Status; NTSTATUS Status;
}; };
struct AFD_POLL_INFO64 struct AFD_POLL_INFO64
{ {
LARGE_INTEGER Timeout; LARGE_INTEGER Timeout;
ULONG NumberOfHandles; ULONG NumberOfHandles;
BOOLEAN Unique; BOOLEAN Unique;
AFD_POLL_HANDLE_INFO64 Handles[1]; AFD_POLL_HANDLE_INFO64 Handles[1];
}; };
#define AFD_POLL_RECEIVE_BIT 0 #define AFD_POLL_RECEIVE_BIT 0
@@ -117,59 +117,57 @@ struct AFD_POLL_INFO64
#define AFD_NUM_POLL_EVENTS 11 #define AFD_NUM_POLL_EVENTS 11
#define AFD_POLL_ALL ((1 << AFD_NUM_POLL_EVENTS) - 1) #define AFD_POLL_ALL ((1 << AFD_NUM_POLL_EVENTS) - 1)
#define _AFD_REQUEST(ioctl) \ #define _AFD_REQUEST(ioctl) ((((ULONG)(ioctl)) >> 2) & 0x03FF)
((((ULONG)(ioctl)) >> 2) & 0x03FF) #define _AFD_BASE(ioctl) ((((ULONG)(ioctl)) >> 12) & 0xFFFFF)
#define _AFD_BASE(ioctl) \
((((ULONG)(ioctl)) >> 12) & 0xFFFFF)
#define FSCTL_AFD_BASE FILE_DEVICE_NETWORK #define FSCTL_AFD_BASE FILE_DEVICE_NETWORK
#define AFD_BIND 0 #define AFD_BIND 0
#define AFD_CONNECT 1 #define AFD_CONNECT 1
#define AFD_START_LISTEN 2 #define AFD_START_LISTEN 2
#define AFD_WAIT_FOR_LISTEN 3 #define AFD_WAIT_FOR_LISTEN 3
#define AFD_ACCEPT 4 #define AFD_ACCEPT 4
#define AFD_RECEIVE 5 #define AFD_RECEIVE 5
#define AFD_RECEIVE_DATAGRAM 6 #define AFD_RECEIVE_DATAGRAM 6
#define AFD_SEND 7 #define AFD_SEND 7
#define AFD_SEND_DATAGRAM 8 #define AFD_SEND_DATAGRAM 8
#define AFD_POLL 9 #define AFD_POLL 9
#define AFD_PARTIAL_DISCONNECT 10 #define AFD_PARTIAL_DISCONNECT 10
#define AFD_GET_ADDRESS 11 #define AFD_GET_ADDRESS 11
#define AFD_QUERY_RECEIVE_INFO 12 #define AFD_QUERY_RECEIVE_INFO 12
#define AFD_QUERY_HANDLES 13 #define AFD_QUERY_HANDLES 13
#define AFD_SET_INFORMATION 14 #define AFD_SET_INFORMATION 14
#define AFD_GET_CONTEXT_LENGTH 15 #define AFD_GET_CONTEXT_LENGTH 15
#define AFD_GET_CONTEXT 16 #define AFD_GET_CONTEXT 16
#define AFD_SET_CONTEXT 17 #define AFD_SET_CONTEXT 17
#define AFD_SET_CONNECT_DATA 18 #define AFD_SET_CONNECT_DATA 18
#define AFD_SET_CONNECT_OPTIONS 19 #define AFD_SET_CONNECT_OPTIONS 19
#define AFD_SET_DISCONNECT_DATA 20 #define AFD_SET_DISCONNECT_DATA 20
#define AFD_SET_DISCONNECT_OPTIONS 21 #define AFD_SET_DISCONNECT_OPTIONS 21
#define AFD_GET_CONNECT_DATA 22 #define AFD_GET_CONNECT_DATA 22
#define AFD_GET_CONNECT_OPTIONS 23 #define AFD_GET_CONNECT_OPTIONS 23
#define AFD_GET_DISCONNECT_DATA 24 #define AFD_GET_DISCONNECT_DATA 24
#define AFD_GET_DISCONNECT_OPTIONS 25 #define AFD_GET_DISCONNECT_OPTIONS 25
#define AFD_SIZE_CONNECT_DATA 26 #define AFD_SIZE_CONNECT_DATA 26
#define AFD_SIZE_CONNECT_OPTIONS 27 #define AFD_SIZE_CONNECT_OPTIONS 27
#define AFD_SIZE_DISCONNECT_DATA 28 #define AFD_SIZE_DISCONNECT_DATA 28
#define AFD_SIZE_DISCONNECT_OPTIONS 29 #define AFD_SIZE_DISCONNECT_OPTIONS 29
#define AFD_GET_INFORMATION 30 #define AFD_GET_INFORMATION 30
#define AFD_TRANSMIT_FILE 31 #define AFD_TRANSMIT_FILE 31
#define AFD_SUPER_ACCEPT 32 #define AFD_SUPER_ACCEPT 32
#define AFD_EVENT_SELECT 33 #define AFD_EVENT_SELECT 33
#define AFD_ENUM_NETWORK_EVENTS 34 #define AFD_ENUM_NETWORK_EVENTS 34
#define AFD_DEFER_ACCEPT 35 #define AFD_DEFER_ACCEPT 35
#define AFD_WAIT_FOR_LISTEN_LIFO 36 #define AFD_WAIT_FOR_LISTEN_LIFO 36
#define AFD_SET_QOS 37 #define AFD_SET_QOS 37
#define AFD_GET_QOS 38 #define AFD_GET_QOS 38
#define AFD_NO_OPERATION 39 #define AFD_NO_OPERATION 39
#define AFD_VALIDATE_GROUP 40 #define AFD_VALIDATE_GROUP 40
#define AFD_GET_UNACCEPTED_CONNECT_DATA 41 #define AFD_GET_UNACCEPTED_CONNECT_DATA 41

View File

@@ -8,31 +8,31 @@ using emulator_pointer = uint64_t;
template <typename T> template <typename T>
class object_wrapper class object_wrapper
{ {
T* obj_; T* obj_;
public: public:
object_wrapper(T& obj) object_wrapper(T& obj)
: obj_(&obj) : obj_(&obj)
{ {
} }
T& get() const T& get() const
{ {
return *this->obj_; return *this->obj_;
} }
operator T&() const operator T&() const
{ {
return this->get(); return this->get();
} }
void serialize(utils::buffer_serializer&) const void serialize(utils::buffer_serializer&) const
{ {
} }
void deserialize(utils::buffer_deserializer&) void deserialize(utils::buffer_deserializer&)
{ {
} }
}; };
class windows_emulator; class windows_emulator;
@@ -45,256 +45,250 @@ using windows_emulator_wrapper = object_wrapper<windows_emulator>;
template <typename T> template <typename T>
class emulator_object class emulator_object
{ {
public: public:
using value_type = T; using value_type = T;
emulator_object(const x64_emulator_wrapper& wrapper, const uint64_t address = 0) emulator_object(const x64_emulator_wrapper& wrapper, const uint64_t address = 0)
: emulator_object(wrapper.get(), address) : emulator_object(wrapper.get(), address)
{ {
} }
emulator_object(emulator& emu, const uint64_t address = 0) emulator_object(emulator& emu, const uint64_t address = 0)
: emu_(&emu) : emu_(&emu),
, address_(address) address_(address)
{ {
} }
emulator_object(emulator& emu, const void* address) emulator_object(emulator& emu, const void* address)
: emulator_object(emu, reinterpret_cast<uint64_t>(address)) : emulator_object(emu, reinterpret_cast<uint64_t>(address))
{ {
} }
uint64_t value() const uint64_t value() const
{ {
return this->address_; return this->address_;
} }
constexpr uint64_t size() const constexpr uint64_t size() const
{ {
return sizeof(T); return sizeof(T);
} }
uint64_t end() const uint64_t end() const
{ {
return this->value() + this->size(); return this->value() + this->size();
} }
T* ptr() const T* ptr() const
{ {
return reinterpret_cast<T*>(this->address_); return reinterpret_cast<T*>(this->address_);
} }
operator bool() const operator bool() const
{ {
return this->address_ != 0; return this->address_ != 0;
} }
T read(const size_t index = 0) const T read(const size_t index = 0) const
{ {
T obj{}; T obj{};
this->emu_->read_memory(this->address_ + index * this->size(), &obj, sizeof(obj)); this->emu_->read_memory(this->address_ + index * this->size(), &obj, sizeof(obj));
return obj; return obj;
} }
void write(const T& value, const size_t index = 0) const void write(const T& value, const size_t index = 0) const
{ {
this->emu_->write_memory(this->address_ + index * this->size(), &value, sizeof(value)); this->emu_->write_memory(this->address_ + index * this->size(), &value, sizeof(value));
} }
void write_if_valid(const T& value, const size_t index = 0) const void write_if_valid(const T& value, const size_t index = 0) const
{ {
if (this->operator bool()) if (this->operator bool())
{ {
this->write(value, index); this->write(value, index);
} }
} }
template <typename F> template <typename F>
void access(const F& accessor, const size_t index = 0) const void access(const F& accessor, const size_t index = 0) const
{ {
T obj{}; T obj{};
this->emu_->read_memory(this->address_ + index * this->size(), &obj, sizeof(obj)); this->emu_->read_memory(this->address_ + index * this->size(), &obj, sizeof(obj));
accessor(obj); accessor(obj);
this->write(obj, index); this->write(obj, index);
} }
void serialize(utils::buffer_serializer& buffer) const void serialize(utils::buffer_serializer& buffer) const
{ {
buffer.write(this->address_); buffer.write(this->address_);
} }
void deserialize(utils::buffer_deserializer& buffer) void deserialize(utils::buffer_deserializer& buffer)
{ {
buffer.read(this->address_); buffer.read(this->address_);
} }
void set_address(const uint64_t address) void set_address(const uint64_t address)
{ {
this->address_ = address; this->address_ = address;
} }
private: private:
emulator* emu_{}; emulator* emu_{};
uint64_t address_{}; uint64_t address_{};
}; };
// TODO: warning emulator_utils is hardcoded for 64bit unicode_string usage // TODO: warning emulator_utils is hardcoded for 64bit unicode_string usage
class emulator_allocator class emulator_allocator
{ {
public: public:
emulator_allocator(emulator& emu) emulator_allocator(emulator& emu)
: emu_(&emu) : emu_(&emu)
{ {
} }
emulator_allocator(emulator& emu, const uint64_t address, const uint64_t size) emulator_allocator(emulator& emu, const uint64_t address, const uint64_t size)
: emu_(&emu) : emu_(&emu),
, address_(address) address_(address),
, size_(size) size_(size),
, active_address_(address) active_address_(address)
{ {
} }
uint64_t reserve(const uint64_t count, const uint64_t alignment = 1) uint64_t reserve(const uint64_t count, const uint64_t alignment = 1)
{ {
const auto potential_start = align_up(this->active_address_, alignment); const auto potential_start = align_up(this->active_address_, alignment);
const auto potential_end = potential_start + count; const auto potential_end = potential_start + count;
const auto total_end = this->address_ + this->size_; const auto total_end = this->address_ + this->size_;
if (potential_end > total_end) if (potential_end > total_end)
{ {
throw std::runtime_error("Out of memory"); throw std::runtime_error("Out of memory");
} }
this->active_address_ = potential_end; this->active_address_ = potential_end;
return potential_start; return potential_start;
} }
template <typename T> template <typename T>
emulator_object<T> reserve(const size_t count = 1) emulator_object<T> reserve(const size_t count = 1)
{ {
const auto potential_start = this->reserve(sizeof(T) * count, alignof(T)); const auto potential_start = this->reserve(sizeof(T) * count, alignof(T));
return emulator_object<T>(*this->emu_, potential_start); return emulator_object<T>(*this->emu_, potential_start);
} }
char16_t* copy_string(const std::u16string_view str)
{
UNICODE_STRING<EmulatorTraits<Emu64>> uc_str{};
this->make_unicode_string(uc_str, str);
return reinterpret_cast<char16_t*>(uc_str.Buffer);
}
char16_t* copy_string(const std::u16string_view str) void make_unicode_string(UNICODE_STRING<EmulatorTraits<Emu64>>& result, const std::u16string_view str)
{ {
UNICODE_STRING<EmulatorTraits<Emu64>> uc_str{}; constexpr auto element_size = sizeof(str[0]);
this->make_unicode_string(uc_str, str); constexpr auto required_alignment = alignof(decltype(str[0]));
return reinterpret_cast<char16_t*>(uc_str.Buffer); const auto total_length = str.size() * element_size;
}
void make_unicode_string(UNICODE_STRING<EmulatorTraits<Emu64>>& result, const std::u16string_view str) const auto string_buffer = this->reserve(total_length + element_size, required_alignment);
{
constexpr auto element_size = sizeof(str[0]);
constexpr auto required_alignment = alignof(decltype(str[0]));
const auto total_length = str.size() * element_size;
const auto string_buffer = this->reserve(total_length + element_size, required_alignment); this->emu_->write_memory(string_buffer, str.data(), total_length);
this->emu_->write_memory(string_buffer, str.data(), total_length); constexpr std::array<char, element_size> nullbyte{};
this->emu_->write_memory(string_buffer + total_length, nullbyte.data(), nullbyte.size());
constexpr std::array<char, element_size> nullbyte{}; result.Buffer = string_buffer;
this->emu_->write_memory(string_buffer + total_length, nullbyte.data(), nullbyte.size()); result.Length = static_cast<USHORT>(total_length);
result.MaximumLength = static_cast<USHORT>(total_length + element_size);
}
result.Buffer = string_buffer; emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> make_unicode_string(const std::u16string_view str)
result.Length = static_cast<USHORT>(total_length); {
result.MaximumLength = static_cast<USHORT>(total_length + element_size); const auto unicode_string = this->reserve<UNICODE_STRING<EmulatorTraits<Emu64>>>();
}
emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> make_unicode_string(const std::u16string_view str) unicode_string.access(
{ [&](UNICODE_STRING<EmulatorTraits<Emu64>>& unicode_str) { this->make_unicode_string(unicode_str, str); });
const auto unicode_string = this->reserve<UNICODE_STRING<EmulatorTraits<Emu64>>>();
unicode_string.access([&](UNICODE_STRING<EmulatorTraits<Emu64>>& unicode_str) return unicode_string;
{ }
this->make_unicode_string(unicode_str, str);
});
return unicode_string; uint64_t get_base() const
} {
return this->address_;
}
uint64_t get_base() const uint64_t get_size() const
{ {
return this->address_; return this->size_;
} }
uint64_t get_size() const uint64_t get_next_address() const
{ {
return this->size_; return this->active_address_;
} }
uint64_t get_next_address() const emulator& get_emulator() const
{ {
return this->active_address_; return *this->emu_;
} }
emulator& get_emulator() const void serialize(utils::buffer_serializer& buffer) const
{ {
return *this->emu_; buffer.write(this->address_);
} buffer.write(this->size_);
buffer.write(this->active_address_);
}
void serialize(utils::buffer_serializer& buffer) const void deserialize(utils::buffer_deserializer& buffer)
{ {
buffer.write(this->address_); buffer.read(this->address_);
buffer.write(this->size_); buffer.read(this->size_);
buffer.write(this->active_address_); buffer.read(this->active_address_);
} }
void deserialize(utils::buffer_deserializer& buffer) void release()
{ {
buffer.read(this->address_); if (this->emu_ && this->address_ && this->size_)
buffer.read(this->size_); {
buffer.read(this->active_address_); this->emu_->release_memory(this->address_, this->size_);
} this->address_ = 0;
this->size_ = 0;
}
}
void release() private:
{ emulator* emu_{};
if (this->emu_ && this->address_ && this->size_) uint64_t address_{};
{ uint64_t size_{};
this->emu_->release_memory(this->address_, this->size_); uint64_t active_address_{0};
this->address_ = 0;
this->size_ = 0;
}
}
private:
emulator* emu_{};
uint64_t address_{};
uint64_t size_{};
uint64_t active_address_{0};
}; };
inline std::u16string read_unicode_string(const emulator& emu, const UNICODE_STRING<EmulatorTraits<Emu64>> ucs) inline std::u16string read_unicode_string(const emulator& emu, const UNICODE_STRING<EmulatorTraits<Emu64>> ucs)
{ {
static_assert(offsetof(UNICODE_STRING<EmulatorTraits<Emu64>>, Length) == 0); static_assert(offsetof(UNICODE_STRING<EmulatorTraits<Emu64>>, Length) == 0);
static_assert(offsetof(UNICODE_STRING<EmulatorTraits<Emu64>>, MaximumLength) == 2); static_assert(offsetof(UNICODE_STRING<EmulatorTraits<Emu64>>, MaximumLength) == 2);
static_assert(offsetof(UNICODE_STRING<EmulatorTraits<Emu64>>, Buffer) == 8); static_assert(offsetof(UNICODE_STRING<EmulatorTraits<Emu64>>, Buffer) == 8);
static_assert(sizeof(UNICODE_STRING<EmulatorTraits<Emu64>>) == 16); static_assert(sizeof(UNICODE_STRING<EmulatorTraits<Emu64>>) == 16);
std::u16string result{}; std::u16string result{};
result.resize(ucs.Length / 2); result.resize(ucs.Length / 2);
emu.read_memory(ucs.Buffer, result.data(), ucs.Length); emu.read_memory(ucs.Buffer, result.data(), ucs.Length);
return result; return result;
} }
inline std::u16string read_unicode_string(const emulator& emu, inline std::u16string read_unicode_string(const emulator& emu,
const emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> uc_string) const emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> uc_string)
{ {
const auto ucs = uc_string.read(); const auto ucs = uc_string.read();
return read_unicode_string(emu, ucs); return read_unicode_string(emu, ucs);
} }
inline std::u16string read_unicode_string(emulator& emu, const UNICODE_STRING<EmulatorTraits<Emu64>>* uc_string) inline std::u16string read_unicode_string(emulator& emu, const UNICODE_STRING<EmulatorTraits<Emu64>>* uc_string)
{ {
return read_unicode_string(emu, emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>>{emu, uc_string}); return read_unicode_string(emu, emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>>{emu, uc_string});
} }

View File

@@ -2,33 +2,33 @@
struct handle_types struct handle_types
{ {
enum type : uint16_t enum type : uint16_t
{ {
reserved = 0, reserved = 0,
file, file,
device, device,
event, event,
section, section,
symlink, symlink,
directory, directory,
semaphore, semaphore,
port, port,
thread, thread,
registry, registry,
mutant, mutant,
token, token,
}; };
}; };
#pragma pack(push) #pragma pack(push)
#pragma pack(1) #pragma pack(1)
struct handle_value struct handle_value
{ {
uint64_t id : 32; uint64_t id : 32;
uint64_t type : 16; uint64_t type : 16;
uint64_t padding : 14; uint64_t padding : 14;
uint64_t is_system : 1; uint64_t is_system : 1;
uint64_t is_pseudo : 1; uint64_t is_pseudo : 1;
}; };
#pragma pack(pop) #pragma pack(pop)
@@ -37,315 +37,315 @@ static_assert(sizeof(handle_value) == 8);
// TODO: this is a concrete 64bit handle // TODO: this is a concrete 64bit handle
union handle union handle
{ {
handle_value value; handle_value value;
uint64_t bits; uint64_t bits;
std::uint64_t h; std::uint64_t h;
}; };
namespace utils namespace utils
{ {
inline void serialize(buffer_serializer& buffer, const handle& h) inline void serialize(buffer_serializer& buffer, const handle& h)
{ {
buffer.write(h.bits); buffer.write(h.bits);
} }
inline void deserialize(buffer_deserializer& buffer, handle& h) inline void deserialize(buffer_deserializer& buffer, handle& h)
{ {
buffer.read(h.bits); buffer.read(h.bits);
} }
} }
inline bool operator==(const handle& h1, const handle& h2) inline bool operator==(const handle& h1, const handle& h2)
{ {
return h1.bits == h2.bits; return h1.bits == h2.bits;
} }
inline bool operator==(const handle& h1, const uint64_t& h2) inline bool operator==(const handle& h1, const uint64_t& h2)
{ {
return h1.bits == h2; return h1.bits == h2;
} }
inline handle_value get_handle_value(const uint64_t h) inline handle_value get_handle_value(const uint64_t h)
{ {
handle hh{}; handle hh{};
hh.bits = h; hh.bits = h;
return hh.value; return hh.value;
} }
constexpr handle make_handle(const uint32_t id, const handle_types::type type, const bool is_pseudo) constexpr handle make_handle(const uint32_t id, const handle_types::type type, const bool is_pseudo)
{ {
handle_value value{}; handle_value value{};
value.padding = 0; value.padding = 0;
value.id = id; value.id = id;
value.type = type; value.type = type;
value.is_system = false; value.is_system = false;
value.is_pseudo = is_pseudo; value.is_pseudo = is_pseudo;
return {value}; return {value};
} }
constexpr handle make_handle(const uint64_t value) constexpr handle make_handle(const uint64_t value)
{ {
handle h{}; handle h{};
h.bits = value; h.bits = value;
return h; return h;
} }
constexpr handle make_pseudo_handle(const uint32_t id, const handle_types::type type) constexpr handle make_pseudo_handle(const uint32_t id, const handle_types::type type)
{ {
return make_handle(id, type, true); return make_handle(id, type, true);
} }
namespace handle_detail namespace handle_detail
{ {
template <typename, typename = void> template <typename, typename = void>
struct has_deleter_function : std::false_type struct has_deleter_function : std::false_type
{ {
}; };
template <typename T> template <typename T>
struct has_deleter_function<T, std::void_t<decltype(T::deleter(std::declval<T&>()))>> struct has_deleter_function<T, std::void_t<decltype(T::deleter(std::declval<T&>()))>>
: std::is_same<decltype(T::deleter(std::declval<T&>())), bool> : std::is_same<decltype(T::deleter(std::declval<T&>())), bool>
{ {
}; };
} }
struct generic_handle_store struct generic_handle_store
{ {
virtual ~generic_handle_store() = default; virtual ~generic_handle_store() = default;
virtual bool erase(const handle h) = 0; virtual bool erase(const handle h) = 0;
}; };
template <handle_types::type Type, typename T, uint32_t IndexShift = 0> template <handle_types::type Type, typename T, uint32_t IndexShift = 0>
requires(utils::Serializable<T>) requires(utils::Serializable<T>)
class handle_store : public generic_handle_store class handle_store : public generic_handle_store
{ {
public: public:
using index_type = uint32_t; using index_type = uint32_t;
using value_map = std::map<index_type, T>; using value_map = std::map<index_type, T>;
bool block_mutation(bool blocked) bool block_mutation(bool blocked)
{ {
std::swap(this->block_mutation_, blocked); std::swap(this->block_mutation_, blocked);
return blocked; return blocked;
} }
handle store(T value) handle store(T value)
{ {
if (this->block_mutation_) if (this->block_mutation_)
{ {
throw std::runtime_error("Mutation of handle store is blocked!"); throw std::runtime_error("Mutation of handle store is blocked!");
} }
auto index = this->find_free_index(); auto index = this->find_free_index();
this->store_.emplace(index, std::move(value)); this->store_.emplace(index, std::move(value));
return make_handle(index); return make_handle(index);
} }
handle make_handle(const index_type index) const handle make_handle(const index_type index) const
{ {
handle h{}; handle h{};
h.bits = 0; h.bits = 0;
h.value.is_pseudo = false; h.value.is_pseudo = false;
h.value.type = Type; h.value.type = Type;
h.value.id = index << IndexShift; h.value.id = index << IndexShift;
return h; return h;
} }
T* get_by_index(const uint32_t index) T* get_by_index(const uint32_t index)
{ {
return this->get(this->make_handle(index)); return this->get(this->make_handle(index));
} }
T* get(const handle_value h) T* get(const handle_value h)
{ {
const auto entry = this->get_iterator(h); const auto entry = this->get_iterator(h);
if (entry == this->store_.end()) if (entry == this->store_.end())
{ {
return nullptr; return nullptr;
} }
return &entry->second; return &entry->second;
} }
T* get(const handle h) T* get(const handle h)
{ {
return this->get(h.value); return this->get(h.value);
} }
T* get(const uint64_t h) T* get(const uint64_t h)
{ {
handle hh{}; handle hh{};
hh.bits = h; hh.bits = h;
return this->get(hh); return this->get(hh);
} }
size_t size() const size_t size() const
{ {
return this->store_.size(); return this->store_.size();
} }
bool erase(const typename value_map::iterator& entry) bool erase(const typename value_map::iterator& entry)
{ {
if (this->block_mutation_) if (this->block_mutation_)
{ {
throw std::runtime_error("Mutation of handle store is blocked!"); throw std::runtime_error("Mutation of handle store is blocked!");
} }
if (entry == this->store_.end()) if (entry == this->store_.end())
{ {
return false; return false;
} }
if constexpr (handle_detail::has_deleter_function<T>()) if constexpr (handle_detail::has_deleter_function<T>())
{ {
if (!T::deleter(entry->second)) if (!T::deleter(entry->second))
{ {
return false; return false;
} }
} }
this->store_.erase(entry); this->store_.erase(entry);
return true; return true;
} }
bool erase(const handle_value h) bool erase(const handle_value h)
{ {
const auto entry = this->get_iterator(h); const auto entry = this->get_iterator(h);
return this->erase(entry); return this->erase(entry);
} }
bool erase(const handle h) override bool erase(const handle h) override
{ {
return this->erase(h.value); return this->erase(h.value);
} }
bool erase(const uint64_t h) bool erase(const uint64_t h)
{ {
handle hh{}; handle hh{};
hh.bits = h; hh.bits = h;
return this->erase(hh); return this->erase(hh);
} }
bool erase(const T& value) bool erase(const T& value)
{ {
const auto entry = this->find(value); const auto entry = this->find(value);
return this->erase(entry); return this->erase(entry);
} }
void serialize(utils::buffer_serializer& buffer) const void serialize(utils::buffer_serializer& buffer) const
{ {
buffer.write(this->block_mutation_); buffer.write(this->block_mutation_);
buffer.write_map(this->store_); buffer.write_map(this->store_);
} }
void deserialize(utils::buffer_deserializer& buffer) void deserialize(utils::buffer_deserializer& buffer)
{ {
buffer.read(this->block_mutation_); buffer.read(this->block_mutation_);
buffer.read_map(this->store_); buffer.read_map(this->store_);
} }
typename value_map::iterator find(const T& value) typename value_map::iterator find(const T& value)
{ {
auto i = this->store_.begin(); auto i = this->store_.begin();
for (; i != this->store_.end(); ++i) for (; i != this->store_.end(); ++i)
{ {
if (&i->second == &value) if (&i->second == &value)
{ {
break; break;
} }
} }
return i; return i;
} }
typename value_map::const_iterator find(const T& value) const typename value_map::const_iterator find(const T& value) const
{ {
auto i = this->store_.begin(); auto i = this->store_.begin();
for (; i != this->store_.end(); ++i) for (; i != this->store_.end(); ++i)
{ {
if (&i->second == &value) if (&i->second == &value)
{ {
break; break;
} }
} }
return i; return i;
} }
handle find_handle(const T& value) const handle find_handle(const T& value) const
{ {
const auto entry = this->find(value); const auto entry = this->find(value);
if (entry == this->end()) if (entry == this->end())
{ {
return {}; return {};
} }
return this->make_handle(entry->first); return this->make_handle(entry->first);
} }
handle find_handle(const T* value) const handle find_handle(const T* value) const
{ {
if (!value) if (!value)
{ {
return {}; return {};
} }
return this->find_handle(*value); return this->find_handle(*value);
} }
typename value_map::iterator begin() typename value_map::iterator begin()
{ {
return this->store_.begin(); return this->store_.begin();
} }
typename value_map::const_iterator begin() const typename value_map::const_iterator begin() const
{ {
return this->store_.begin(); return this->store_.begin();
} }
typename value_map::iterator end() typename value_map::iterator end()
{ {
return this->store_.end(); return this->store_.end();
} }
typename value_map::const_iterator end() const typename value_map::const_iterator end() const
{ {
return this->store_.end(); return this->store_.end();
} }
private: private:
typename value_map::iterator get_iterator(const handle_value h) typename value_map::iterator get_iterator(const handle_value h)
{ {
if (h.type != Type || h.is_pseudo) if (h.type != Type || h.is_pseudo)
{ {
return this->store_.end(); return this->store_.end();
} }
return this->store_.find(static_cast<uint32_t>(h.id) >> IndexShift); return this->store_.find(static_cast<uint32_t>(h.id) >> IndexShift);
} }
uint32_t find_free_index() uint32_t find_free_index()
{ {
uint32_t index = 1; uint32_t index = 1;
for (; index > 0; ++index) for (; index > 0; ++index)
{ {
if (!this->store_.contains(index)) if (!this->store_.contains(index))
{ {
break; break;
} }
} }
return index; return index;
} }
bool block_mutation_{false}; bool block_mutation_{false};
value_map store_{}; value_map store_{};
}; };
constexpr auto KNOWN_DLLS_DIRECTORY = make_pseudo_handle(0x1, handle_types::directory); constexpr auto KNOWN_DLLS_DIRECTORY = make_pseudo_handle(0x1, handle_types::directory);

View File

@@ -3,30 +3,27 @@
namespace namespace
{ {
struct dummy_device : stateless_device struct dummy_device : stateless_device
{ {
NTSTATUS io_control(windows_emulator&, const io_device_context&) override NTSTATUS io_control(windows_emulator&, const io_device_context&) override
{ {
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
}; };
} }
std::unique_ptr<io_device> create_device(const std::u16string_view device) std::unique_ptr<io_device> create_device(const std::u16string_view device)
{ {
if (device == u"CNG" if (device == u"CNG" || device == u"KsecDD" || device == u"PcwDrv" || device == u"DeviceApi\\CMApi" ||
|| device == u"KsecDD" device == u"ConDrv\\Server")
|| device == u"PcwDrv" {
|| device == u"DeviceApi\\CMApi" return std::make_unique<dummy_device>();
|| device == u"ConDrv\\Server") }
{
return std::make_unique<dummy_device>();
}
if (device == u"Afd\\Endpoint") if (device == u"Afd\\Endpoint")
{ {
return create_afd_endpoint(); return create_afd_endpoint();
} }
throw std::runtime_error("Unsupported device: " + u16_to_u8(device)); throw std::runtime_error("Unsupported device: " + u16_to_u8(device));
} }

View File

@@ -12,190 +12,188 @@ struct process_context;
struct io_device_context struct io_device_context
{ {
handle event{}; handle event{};
emulator_pointer /*PIO_APC_ROUTINE*/ apc_routine{}; emulator_pointer /*PIO_APC_ROUTINE*/ apc_routine{};
emulator_pointer apc_context{}; emulator_pointer apc_context{};
emulator_object<IO_STATUS_BLOCK<EmulatorTraits<Emu64>>> io_status_block; emulator_object<IO_STATUS_BLOCK<EmulatorTraits<Emu64>>> io_status_block;
ULONG io_control_code{}; ULONG io_control_code{};
emulator_pointer input_buffer{}; emulator_pointer input_buffer{};
ULONG input_buffer_length{}; ULONG input_buffer_length{};
emulator_pointer output_buffer{}; emulator_pointer output_buffer{};
ULONG output_buffer_length{}; ULONG output_buffer_length{};
io_device_context(x64_emulator& emu) io_device_context(x64_emulator& emu)
: io_status_block(emu) : io_status_block(emu)
{ {
} }
io_device_context(utils::buffer_deserializer& buffer) io_device_context(utils::buffer_deserializer& buffer)
: io_device_context(buffer.read<x64_emulator_wrapper>().get()) : io_device_context(buffer.read<x64_emulator_wrapper>().get())
{ {
} }
void serialize(utils::buffer_serializer& buffer) const void serialize(utils::buffer_serializer& buffer) const
{ {
buffer.write(event); buffer.write(event);
buffer.write(apc_routine); buffer.write(apc_routine);
buffer.write(apc_context); buffer.write(apc_context);
buffer.write(io_status_block); buffer.write(io_status_block);
buffer.write(io_control_code); buffer.write(io_control_code);
buffer.write(input_buffer); buffer.write(input_buffer);
buffer.write(input_buffer_length); buffer.write(input_buffer_length);
buffer.write(output_buffer); buffer.write(output_buffer);
buffer.write(output_buffer_length); buffer.write(output_buffer_length);
} }
void deserialize(utils::buffer_deserializer& buffer) void deserialize(utils::buffer_deserializer& buffer)
{ {
buffer.read(event); buffer.read(event);
buffer.read(apc_routine); buffer.read(apc_routine);
buffer.read(apc_context); buffer.read(apc_context);
buffer.read(io_status_block); buffer.read(io_status_block);
buffer.read(io_control_code); buffer.read(io_control_code);
buffer.read(input_buffer); buffer.read(input_buffer);
buffer.read(input_buffer_length); buffer.read(input_buffer_length);
buffer.read(output_buffer); buffer.read(output_buffer);
buffer.read(output_buffer_length); buffer.read(output_buffer_length);
} }
}; };
struct io_device_creation_data struct io_device_creation_data
{ {
uint64_t buffer; uint64_t buffer;
uint32_t length; uint32_t length;
}; };
inline void write_io_status(const emulator_object<IO_STATUS_BLOCK<EmulatorTraits<Emu64>>> io_status_block, inline void write_io_status(const emulator_object<IO_STATUS_BLOCK<EmulatorTraits<Emu64>>> io_status_block,
const NTSTATUS status) const NTSTATUS status)
{ {
if (io_status_block) if (io_status_block)
{ {
io_status_block.access([&](IO_STATUS_BLOCK<EmulatorTraits<Emu64>>& status_block) io_status_block.access(
{ [&](IO_STATUS_BLOCK<EmulatorTraits<Emu64>>& status_block) { status_block.Status = status; });
status_block.Status = status; }
});
}
} }
struct io_device struct io_device
{ {
io_device() = default; io_device() = default;
virtual ~io_device() = default; virtual ~io_device() = default;
io_device(io_device&&) = default; io_device(io_device&&) = default;
io_device& operator=(io_device&&) = default; io_device& operator=(io_device&&) = default;
io_device(const io_device&) = delete; io_device(const io_device&) = delete;
io_device& operator=(const io_device&) = delete; io_device& operator=(const io_device&) = delete;
virtual NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& context) = 0; virtual NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& context) = 0;
virtual void create(windows_emulator& win_emu, const io_device_creation_data& data) virtual void create(windows_emulator& win_emu, const io_device_creation_data& data)
{ {
(void)win_emu; (void)win_emu;
(void)data; (void)data;
} }
virtual void work(windows_emulator& win_emu) virtual void work(windows_emulator& win_emu)
{ {
(void)win_emu; (void)win_emu;
} }
virtual void serialize(utils::buffer_serializer& buffer) const = 0; virtual void serialize(utils::buffer_serializer& buffer) const = 0;
virtual void deserialize(utils::buffer_deserializer& buffer) = 0; virtual void deserialize(utils::buffer_deserializer& buffer) = 0;
NTSTATUS execute_ioctl(windows_emulator& win_emu, const io_device_context& c) NTSTATUS execute_ioctl(windows_emulator& win_emu, const io_device_context& c)
{ {
if (c.io_status_block) if (c.io_status_block)
{ {
c.io_status_block.write({}); c.io_status_block.write({});
} }
const auto result = this->io_control(win_emu, c); const auto result = this->io_control(win_emu, c);
write_io_status(c.io_status_block, result); write_io_status(c.io_status_block, result);
return result; return result;
} }
}; };
struct stateless_device : io_device struct stateless_device : io_device
{ {
void create(windows_emulator&, const io_device_creation_data&) final void create(windows_emulator&, const io_device_creation_data&) final
{ {
} }
void serialize(utils::buffer_serializer&) const override void serialize(utils::buffer_serializer&) const override
{ {
} }
void deserialize(utils::buffer_deserializer&) override void deserialize(utils::buffer_deserializer&) override
{ {
} }
}; };
std::unique_ptr<io_device> create_device(std::u16string_view device); std::unique_ptr<io_device> create_device(std::u16string_view device);
class io_device_container : public io_device class io_device_container : public io_device
{ {
public: public:
io_device_container() = default; io_device_container() = default;
io_device_container(std::u16string device, windows_emulator& win_emu, const io_device_creation_data& data) io_device_container(std::u16string device, windows_emulator& win_emu, const io_device_creation_data& data)
: device_name_(std::move(device)) : device_name_(std::move(device))
{ {
this->setup(); this->setup();
this->device_->create(win_emu, data); this->device_->create(win_emu, data);
} }
NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& context) override NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& context) override
{ {
this->assert_validity(); this->assert_validity();
return this->device_->io_control(win_emu, context); return this->device_->io_control(win_emu, context);
} }
void work(windows_emulator& win_emu) override void work(windows_emulator& win_emu) override
{ {
this->assert_validity(); this->assert_validity();
return this->device_->work(win_emu); return this->device_->work(win_emu);
} }
void serialize(utils::buffer_serializer& buffer) const override void serialize(utils::buffer_serializer& buffer) const override
{ {
this->assert_validity(); this->assert_validity();
buffer.write_string(this->device_name_); buffer.write_string(this->device_name_);
this->device_->serialize(buffer); this->device_->serialize(buffer);
} }
void deserialize(utils::buffer_deserializer& buffer) override void deserialize(utils::buffer_deserializer& buffer) override
{ {
buffer.read_string(this->device_name_); buffer.read_string(this->device_name_);
this->setup(); this->setup();
this->device_->deserialize(buffer); this->device_->deserialize(buffer);
} }
template <typename T = io_device> template <typename T = io_device>
requires(std::is_base_of_v<io_device, T> || std::is_same_v<io_device, T>) requires(std::is_base_of_v<io_device, T> || std::is_same_v<io_device, T>)
T* get_internal_device() T* get_internal_device()
{ {
this->assert_validity(); this->assert_validity();
auto* value = this->device_.get(); auto* value = this->device_.get();
return dynamic_cast<T*>(value); return dynamic_cast<T*>(value);
} }
private: private:
std::u16string device_name_{}; std::u16string device_name_{};
std::unique_ptr<io_device> device_{}; std::unique_ptr<io_device> device_{};
void setup() void setup()
{ {
this->device_ = create_device(this->device_name_); this->device_ = create_device(this->device_name_);
} }
void assert_validity() const void assert_validity() const
{ {
if (!this->device_) if (!this->device_)
{ {
throw std::runtime_error("Device not created!"); throw std::runtime_error("Device not created!");
} }
} }
}; };

View File

@@ -10,216 +10,214 @@ constexpr auto KUSD_BUFFER_SIZE = page_align_up(KUSD_SIZE);
namespace namespace
{ {
void setup_kusd(KUSER_SHARED_DATA64& kusd, const bool use_relative_time) void setup_kusd(KUSER_SHARED_DATA64& kusd, const bool use_relative_time)
{ {
memset(reinterpret_cast<void*>(&kusd), 0, sizeof(kusd)); memset(reinterpret_cast<void*>(&kusd), 0, sizeof(kusd));
kusd.TickCountMultiplier = 0x0fa00000; kusd.TickCountMultiplier = 0x0fa00000;
kusd.InterruptTime.LowPart = 0x17bd9547; kusd.InterruptTime.LowPart = 0x17bd9547;
kusd.InterruptTime.High1Time = 0x0000004b; kusd.InterruptTime.High1Time = 0x0000004b;
kusd.InterruptTime.High2Time = 0x0000004b; kusd.InterruptTime.High2Time = 0x0000004b;
kusd.SystemTime.LowPart = 0x7af9da99; kusd.SystemTime.LowPart = 0x7af9da99;
kusd.SystemTime.High1Time = 0x01db27b9; kusd.SystemTime.High1Time = 0x01db27b9;
kusd.SystemTime.High2Time = 0x01db27b9; kusd.SystemTime.High2Time = 0x01db27b9;
kusd.TimeZoneBias.LowPart = 0x3c773000; kusd.TimeZoneBias.LowPart = 0x3c773000;
kusd.TimeZoneBias.High1Time = -17; kusd.TimeZoneBias.High1Time = -17;
kusd.TimeZoneBias.High2Time = -17; kusd.TimeZoneBias.High2Time = -17;
kusd.TimeZoneId = 0x00000002; kusd.TimeZoneId = 0x00000002;
kusd.LargePageMinimum = 0x00200000; kusd.LargePageMinimum = 0x00200000;
kusd.RNGSeedVersion = 0x0000000000000013; kusd.RNGSeedVersion = 0x0000000000000013;
kusd.TimeZoneBiasStamp = 0x00000004; kusd.TimeZoneBiasStamp = 0x00000004;
kusd.NtBuildNumber = 0x00006c51; kusd.NtBuildNumber = 0x00006c51;
kusd.NtProductType = NtProductWinNt; kusd.NtProductType = NtProductWinNt;
kusd.ProductTypeIsValid = 0x01; kusd.ProductTypeIsValid = 0x01;
kusd.NativeProcessorArchitecture = 0x0009; kusd.NativeProcessorArchitecture = 0x0009;
kusd.NtMajorVersion = 0x0000000a; kusd.NtMajorVersion = 0x0000000a;
kusd.BootId = 0x0000000b; kusd.BootId = 0x0000000b;
kusd.SystemExpirationDate.QuadPart = 0x01dc26860a9ff300; kusd.SystemExpirationDate.QuadPart = 0x01dc26860a9ff300;
kusd.SuiteMask = 0x00000110; kusd.SuiteMask = 0x00000110;
kusd.MitigationPolicies.MitigationPolicies = 0x0a; kusd.MitigationPolicies.MitigationPolicies = 0x0a;
kusd.MitigationPolicies.NXSupportPolicy = 0x02; kusd.MitigationPolicies.NXSupportPolicy = 0x02;
kusd.MitigationPolicies.SEHValidationPolicy = 0x02; kusd.MitigationPolicies.SEHValidationPolicy = 0x02;
kusd.CyclesPerYield = 0x0064; kusd.CyclesPerYield = 0x0064;
kusd.DismountCount = 0x00000006; kusd.DismountCount = 0x00000006;
kusd.ComPlusPackage = 0x00000001; kusd.ComPlusPackage = 0x00000001;
kusd.LastSystemRITEventTickCount = 0x01ec1fd3; kusd.LastSystemRITEventTickCount = 0x01ec1fd3;
kusd.NumberOfPhysicalPages = 0x00bf0958; kusd.NumberOfPhysicalPages = 0x00bf0958;
kusd.FullNumberOfPhysicalPages = 0x0000000000bf0958; kusd.FullNumberOfPhysicalPages = 0x0000000000bf0958;
kusd.TickCount.TickCount.LowPart = 0x001f7f05; kusd.TickCount.TickCount.LowPart = 0x001f7f05;
kusd.TickCount.TickCountQuad = 0x00000000001f7f05; kusd.TickCount.TickCountQuad = 0x00000000001f7f05;
kusd.Cookie = 0x1c3471da; kusd.Cookie = 0x1c3471da;
kusd.ConsoleSessionForegroundProcessId = 0x00000000000028f4; kusd.ConsoleSessionForegroundProcessId = 0x00000000000028f4;
kusd.TimeUpdateLock = 0x0000000002b28586; kusd.TimeUpdateLock = 0x0000000002b28586;
kusd.BaselineSystemTimeQpc = 0x0000004b17cd596c; kusd.BaselineSystemTimeQpc = 0x0000004b17cd596c;
kusd.BaselineInterruptTimeQpc = 0x0000004b17cd596c; kusd.BaselineInterruptTimeQpc = 0x0000004b17cd596c;
kusd.QpcSystemTimeIncrement = 0x8000000000000000; kusd.QpcSystemTimeIncrement = 0x8000000000000000;
kusd.QpcInterruptTimeIncrement = 0x8000000000000000; kusd.QpcInterruptTimeIncrement = 0x8000000000000000;
kusd.QpcSystemTimeIncrementShift = 0x01; kusd.QpcSystemTimeIncrementShift = 0x01;
kusd.QpcInterruptTimeIncrementShift = 0x01; kusd.QpcInterruptTimeIncrementShift = 0x01;
kusd.UnparkedProcessorCount = 0x000c; kusd.UnparkedProcessorCount = 0x000c;
kusd.TelemetryCoverageRound = 0x00000001; kusd.TelemetryCoverageRound = 0x00000001;
kusd.LangGenerationCount = 0x00000003; kusd.LangGenerationCount = 0x00000003;
kusd.InterruptTimeBias = 0x00000015a5d56406; kusd.InterruptTimeBias = 0x00000015a5d56406;
kusd.QpcBias = 0x000000159530c4af; kusd.QpcBias = 0x000000159530c4af;
kusd.ActiveProcessorCount = 0x0000000c; kusd.ActiveProcessorCount = 0x0000000c;
kusd.ActiveGroupCount = 0x01; kusd.ActiveGroupCount = 0x01;
kusd.QpcData.QpcData = 0x0083; kusd.QpcData.QpcData = 0x0083;
kusd.QpcData.QpcBypassEnabled = 0x83; kusd.QpcData.QpcBypassEnabled = 0x83;
kusd.TimeZoneBiasEffectiveStart.QuadPart = 0x01db276e654cb2ff; kusd.TimeZoneBiasEffectiveStart.QuadPart = 0x01db276e654cb2ff;
kusd.TimeZoneBiasEffectiveEnd.QuadPart = 0x01db280b8c3b2800; kusd.TimeZoneBiasEffectiveEnd.QuadPart = 0x01db280b8c3b2800;
kusd.XState.EnabledFeatures = 0x000000000000001f; kusd.XState.EnabledFeatures = 0x000000000000001f;
kusd.XState.EnabledVolatileFeatures = 0x000000000000000f; kusd.XState.EnabledVolatileFeatures = 0x000000000000000f;
kusd.XState.Size = 0x000003c0; kusd.XState.Size = 0x000003c0;
if (use_relative_time) if (use_relative_time)
{ {
kusd.QpcFrequency = 1000; kusd.QpcFrequency = 1000;
} }
else else
{ {
kusd.QpcFrequency = std::chrono::steady_clock::period::den; kusd.QpcFrequency = std::chrono::steady_clock::period::den;
} }
constexpr std::wstring_view root_dir{L"C:\\WINDOWS"}; constexpr std::wstring_view root_dir{L"C:\\WINDOWS"};
memcpy(&kusd.NtSystemRoot.arr[0], root_dir.data(), root_dir.size() * 2); memcpy(&kusd.NtSystemRoot.arr[0], root_dir.data(), root_dir.size() * 2);
kusd.ImageNumberLow = IMAGE_FILE_MACHINE_I386; kusd.ImageNumberLow = IMAGE_FILE_MACHINE_I386;
kusd.ImageNumberHigh = IMAGE_FILE_MACHINE_AMD64; kusd.ImageNumberHigh = IMAGE_FILE_MACHINE_AMD64;
} }
} }
namespace utils namespace utils
{ {
inline void serialize(buffer_serializer& buffer, const KUSER_SHARED_DATA64& kusd) inline void serialize(buffer_serializer& buffer, const KUSER_SHARED_DATA64& kusd)
{ {
static_assert(KUSD_SIZE == sizeof(kusd)); static_assert(KUSD_SIZE == sizeof(kusd));
buffer.write(&kusd, KUSD_SIZE); buffer.write(&kusd, KUSD_SIZE);
} }
inline void deserialize(buffer_deserializer& buffer, KUSER_SHARED_DATA64& kusd) inline void deserialize(buffer_deserializer& buffer, KUSER_SHARED_DATA64& kusd)
{ {
buffer.read(&kusd, KUSD_SIZE); buffer.read(&kusd, KUSD_SIZE);
} }
} }
kusd_mmio::kusd_mmio(x64_emulator& emu, process_context& process) kusd_mmio::kusd_mmio(x64_emulator& emu, process_context& process)
: emu_(&emu) : emu_(&emu),
, process_(&process) process_(&process)
{ {
} }
kusd_mmio::~kusd_mmio() kusd_mmio::~kusd_mmio()
{ {
this->deregister_mmio(); this->deregister_mmio();
} }
kusd_mmio::kusd_mmio(utils::buffer_deserializer& buffer) kusd_mmio::kusd_mmio(utils::buffer_deserializer& buffer)
: kusd_mmio(buffer.read<x64_emulator_wrapper>(), buffer.read<process_context_wrapper>()) : kusd_mmio(buffer.read<x64_emulator_wrapper>(), buffer.read<process_context_wrapper>())
{ {
} }
void kusd_mmio::setup(const bool use_relative_time) void kusd_mmio::setup(const bool use_relative_time)
{ {
this->use_relative_time_ = use_relative_time; this->use_relative_time_ = use_relative_time;
setup_kusd(this->kusd_, use_relative_time); setup_kusd(this->kusd_, use_relative_time);
this->start_time_ = convert_from_ksystem_time(this->kusd_.SystemTime); this->start_time_ = convert_from_ksystem_time(this->kusd_.SystemTime);
this->register_mmio(); this->register_mmio();
} }
void kusd_mmio::serialize(utils::buffer_serializer& buffer) const void kusd_mmio::serialize(utils::buffer_serializer& buffer) const
{ {
buffer.write(this->use_relative_time_); buffer.write(this->use_relative_time_);
buffer.write(this->kusd_); buffer.write(this->kusd_);
buffer.write(this->start_time_); buffer.write(this->start_time_);
} }
void kusd_mmio::deserialize(utils::buffer_deserializer& buffer) void kusd_mmio::deserialize(utils::buffer_deserializer& buffer)
{ {
buffer.read(this->use_relative_time_); buffer.read(this->use_relative_time_);
buffer.read(this->kusd_); buffer.read(this->kusd_);
buffer.read(this->start_time_); buffer.read(this->start_time_);
this->deregister_mmio(); this->deregister_mmio();
this->register_mmio(); this->register_mmio();
} }
uint64_t kusd_mmio::read(const uint64_t addr, const size_t size) uint64_t kusd_mmio::read(const uint64_t addr, const size_t size)
{ {
uint64_t result{}; uint64_t result{};
this->update(); this->update();
if (addr >= KUSD_SIZE) if (addr >= KUSD_SIZE)
{ {
return result; return result;
} }
const auto end = addr + size; const auto end = addr + size;
const auto valid_end = std::min(end, static_cast<uint64_t>(KUSD_SIZE)); const auto valid_end = std::min(end, static_cast<uint64_t>(KUSD_SIZE));
const auto real_size = valid_end - addr; const auto real_size = valid_end - addr;
if (real_size > sizeof(result)) if (real_size > sizeof(result))
{ {
return result; return result;
} }
const auto* kusd_buffer = reinterpret_cast<uint8_t*>(&this->kusd_); const auto* kusd_buffer = reinterpret_cast<uint8_t*>(&this->kusd_);
memcpy(&result, kusd_buffer + addr, real_size); memcpy(&result, kusd_buffer + addr, real_size);
return result; return result;
} }
uint64_t kusd_mmio::address() uint64_t kusd_mmio::address()
{ {
return KUSD_ADDRESS; return KUSD_ADDRESS;
} }
void kusd_mmio::update() void kusd_mmio::update()
{ {
auto time = this->start_time_; auto time = this->start_time_;
if (this->use_relative_time_) if (this->use_relative_time_)
{ {
const auto passed_time = this->process_->executed_instructions; const auto passed_time = this->process_->executed_instructions;
const auto clock_frequency = static_cast<uint64_t>(this->kusd_.QpcFrequency); const auto clock_frequency = static_cast<uint64_t>(this->kusd_.QpcFrequency);
using duration = std::chrono::system_clock::duration; using duration = std::chrono::system_clock::duration;
time += duration(passed_time * duration::period::den / clock_frequency); time += duration(passed_time * duration::period::den / clock_frequency);
} }
else else
{ {
time = std::chrono::system_clock::now(); time = std::chrono::system_clock::now();
} }
convert_to_ksystem_time(&this->kusd_.SystemTime, time); convert_to_ksystem_time(&this->kusd_.SystemTime, time);
} }
void kusd_mmio::register_mmio() void kusd_mmio::register_mmio()
{ {
if (this->registered_) if (this->registered_)
{ {
return; return;
} }
this->registered_ = true; this->registered_ = true;
this->emu_->allocate_mmio(KUSD_ADDRESS, KUSD_BUFFER_SIZE, this->emu_->allocate_mmio(
[this](const uint64_t addr, const size_t size) KUSD_ADDRESS, KUSD_BUFFER_SIZE,
{ [this](const uint64_t addr, const size_t size) { return this->read(addr, size); },
return this->read(addr, size); [](const uint64_t, const size_t, const uint64_t) {
}, [](const uint64_t, const size_t, const uint64_t) // Writing not supported!
{ });
// Writing not supported!
});
} }
void kusd_mmio::deregister_mmio() void kusd_mmio::deregister_mmio()
{ {
if (this->registered_) if (this->registered_)
{ {
this->registered_ = false; this->registered_ = false;
this->emu_->release_memory(KUSD_ADDRESS, KUSD_BUFFER_SIZE); this->emu_->release_memory(KUSD_ADDRESS, KUSD_BUFFER_SIZE);
} }
} }

View File

@@ -10,48 +10,48 @@ class windows_emulator;
class kusd_mmio class kusd_mmio
{ {
public: public:
kusd_mmio(x64_emulator& emu, process_context& process); kusd_mmio(x64_emulator& emu, process_context& process);
~kusd_mmio(); ~kusd_mmio();
kusd_mmio(utils::buffer_deserializer& buffer); kusd_mmio(utils::buffer_deserializer& buffer);
kusd_mmio(kusd_mmio&&) = delete; kusd_mmio(kusd_mmio&&) = delete;
kusd_mmio(const kusd_mmio&) = delete; kusd_mmio(const kusd_mmio&) = delete;
kusd_mmio& operator=(kusd_mmio&& obj) = delete; kusd_mmio& operator=(kusd_mmio&& obj) = delete;
kusd_mmio& operator=(const kusd_mmio&) = delete; kusd_mmio& operator=(const kusd_mmio&) = delete;
void serialize(utils::buffer_serializer& buffer) const; void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer); void deserialize(utils::buffer_deserializer& buffer);
KUSER_SHARED_DATA64& get() KUSER_SHARED_DATA64& get()
{ {
return this->kusd_; return this->kusd_;
} }
const KUSER_SHARED_DATA64& get() const const KUSER_SHARED_DATA64& get() const
{ {
return this->kusd_; return this->kusd_;
} }
static uint64_t address(); static uint64_t address();
void setup(bool use_relative_time); void setup(bool use_relative_time);
private: private:
x64_emulator* emu_{}; x64_emulator* emu_{};
process_context* process_{}; process_context* process_{};
bool registered_{}; bool registered_{};
bool use_relative_time_{}; bool use_relative_time_{};
KUSER_SHARED_DATA64 kusd_{}; KUSER_SHARED_DATA64 kusd_{};
std::chrono::system_clock::time_point start_time_{}; std::chrono::system_clock::time_point start_time_{};
uint64_t read(uint64_t addr, size_t size); uint64_t read(uint64_t addr, size_t size);
void update(); void update();
void register_mmio(); void register_mmio();
void deregister_mmio(); void deregister_mmio();
}; };

View File

@@ -7,130 +7,141 @@ namespace
{ {
#ifdef _WIN32 #ifdef _WIN32
#define COLOR(win, posix) win #define COLOR(win, posix) win
using color_type = WORD; using color_type = WORD;
#else #else
#define COLOR(win, posix) posix #define COLOR(win, posix) posix
using color_type = const char*; using color_type = const char*;
#endif #endif
color_type get_reset_color() color_type get_reset_color()
{ {
return COLOR(7, "\033[0m"); return COLOR(7, "\033[0m");
} }
color_type get_color_type(const color c) color_type get_color_type(const color c)
{ {
using enum color; using enum color;
switch (c) switch (c)
{ {
case black: return COLOR(0x8, "\033[0;90m"); case black:
case red: return COLOR(0xC, "\033[0;91m"); return COLOR(0x8, "\033[0;90m");
case green: return COLOR(0xA, "\033[0;92m"); case red:
case yellow: return COLOR(0xE, "\033[0;93m"); return COLOR(0xC, "\033[0;91m");
case blue: return COLOR(0x9, "\033[0;94m"); case green:
case cyan: return COLOR(0xB, "\033[0;96m"); return COLOR(0xA, "\033[0;92m");
case pink: return COLOR(0xD, "\033[0;95m"); case yellow:
case white: return COLOR(0xF, "\033[0;97m"); return COLOR(0xE, "\033[0;93m");
case dark_gray: return COLOR(0x8, "\033[0;97m"); case blue:
case gray: return COLOR(0x9, "\033[0;94m");
default: return get_reset_color(); case cyan:
} return COLOR(0xB, "\033[0;96m");
} case pink:
return COLOR(0xD, "\033[0;95m");
case white:
return COLOR(0xF, "\033[0;97m");
case dark_gray:
return COLOR(0x8, "\033[0;97m");
case gray:
default:
return get_reset_color();
}
}
#ifdef _WIN32 #ifdef _WIN32
HANDLE get_console_handle() HANDLE get_console_handle()
{ {
return GetStdHandle(STD_OUTPUT_HANDLE); return GetStdHandle(STD_OUTPUT_HANDLE);
} }
#endif #endif
void set_color(const color_type color) void set_color(const color_type color)
{ {
#ifdef _WIN32 #ifdef _WIN32
SetConsoleTextAttribute(get_console_handle(), color); SetConsoleTextAttribute(get_console_handle(), color);
#else #else
printf("%s", color); printf("%s", color);
#endif #endif
} }
void reset_color() void reset_color()
{ {
(void)fflush(stdout); (void)fflush(stdout);
set_color(get_reset_color()); set_color(get_reset_color());
(void)fflush(stdout); (void)fflush(stdout);
} }
std::string_view format(va_list* ap, const char* message) std::string_view format(va_list* ap, const char* message)
{ {
thread_local char buffer[0x1000]; thread_local char buffer[0x1000];
#ifdef _WIN32 #ifdef _WIN32
const int count = _vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer), message, *ap); const int count = _vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer), message, *ap);
#else #else
const int count = vsnprintf(buffer, sizeof(buffer), message, *ap); const int count = vsnprintf(buffer, sizeof(buffer), message, *ap);
#endif #endif
if (count < 0) return {}; if (count < 0)
return {buffer, static_cast<size_t>(count)}; return {};
} return {buffer, static_cast<size_t>(count)};
}
#define format_to_string(msg, str)\ #define format_to_string(msg, str) \
va_list ap;\ va_list ap; \
va_start(ap, msg);\ va_start(ap, msg); \
const auto str = format(&ap, msg);\ const auto str = format(&ap, msg); \
va_end(ap); va_end(ap);
void print_colored(const std::string_view& line, const color_type base_color) void print_colored(const std::string_view& line, const color_type base_color)
{ {
const auto _ = utils::finally(&reset_color); const auto _ = utils::finally(&reset_color);
set_color(base_color); set_color(base_color);
(void)fwrite(line.data(), 1, line.size(), stdout); (void)fwrite(line.data(), 1, line.size(), stdout);
} }
} }
void logger::print(const color c, const std::string_view message) const void logger::print(const color c, const std::string_view message) const
{ {
if (this->disable_output_) if (this->disable_output_)
{ {
return; return;
} }
print_colored(message, get_color_type(c)); print_colored(message, get_color_type(c));
} }
void logger::print(const color c, const char* message, ...) const void logger::print(const color c, const char* message, ...) const
{ {
format_to_string(message, data); format_to_string(message, data);
this->print(c, data); this->print(c, data);
} }
void logger::info(const char* message, ...) const void logger::info(const char* message, ...) const
{ {
format_to_string(message, data); format_to_string(message, data);
this->print(color::cyan, data); this->print(color::cyan, data);
} }
void logger::warn(const char* message, ...) const void logger::warn(const char* message, ...) const
{ {
format_to_string(message, data); format_to_string(message, data);
this->print(color::yellow, data); this->print(color::yellow, data);
} }
void logger::error(const char* message, ...) const void logger::error(const char* message, ...) const
{ {
format_to_string(message, data); format_to_string(message, data);
this->print(color::red, data); this->print(color::red, data);
} }
void logger::success(const char* message, ...) const void logger::success(const char* message, ...) const
{ {
format_to_string(message, data); format_to_string(message, data);
this->print(color::green, data); this->print(color::green, data);
} }
void logger::log(const char* message, ...) const void logger::log(const char* message, ...) const
{ {
format_to_string(message, data); format_to_string(message, data);
this->print(color::gray, data); this->print(color::gray, data);
} }

View File

@@ -3,44 +3,44 @@
#ifdef OS_WINDOWS #ifdef OS_WINDOWS
#define FORMAT_ATTRIBUTE(fmt_pos, var_pos) #define FORMAT_ATTRIBUTE(fmt_pos, var_pos)
#else #else
#define FORMAT_ATTRIBUTE(fmt_pos, var_pos) __attribute__((format( printf, fmt_pos, var_pos))) #define FORMAT_ATTRIBUTE(fmt_pos, var_pos) __attribute__((format(printf, fmt_pos, var_pos)))
#endif #endif
enum class color enum class color
{ {
black, black,
red, red,
green, green,
yellow, yellow,
blue, blue,
cyan, cyan,
pink, pink,
white, white,
gray, gray,
dark_gray, dark_gray,
}; };
class logger class logger
{ {
public: public:
void print(color c, std::string_view message) const; void print(color c, std::string_view message) const;
void print(color c, const char* message, ...) const FORMAT_ATTRIBUTE(3, 4); void print(color c, const char* message, ...) const FORMAT_ATTRIBUTE(3, 4);
void info(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3); void info(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
void warn(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3); void warn(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
void error(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3); void error(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
void success(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3); void success(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
void log(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3); void log(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
void disable_output(const bool value) void disable_output(const bool value)
{ {
this->disable_output_ = value; this->disable_output_ = value;
} }
bool is_output_disabled() const bool is_output_disabled() const
{ {
return this->disable_output_; return this->disable_output_;
} }
private: private:
bool disable_output_{false}; bool disable_output_{false};
}; };

View File

@@ -5,69 +5,69 @@
inline std::string get_permission_string(const memory_permission permission) inline std::string get_permission_string(const memory_permission permission)
{ {
const bool has_exec = (permission & memory_permission::exec) != memory_permission::none; const bool has_exec = (permission & memory_permission::exec) != memory_permission::none;
const bool has_read = (permission & memory_permission::read) != memory_permission::none; const bool has_read = (permission & memory_permission::read) != memory_permission::none;
const bool has_write = (permission & memory_permission::write) != memory_permission::none; const bool has_write = (permission & memory_permission::write) != memory_permission::none;
std::string res = {}; std::string res = {};
res.reserve(3); res.reserve(3);
res.push_back(has_read ? 'r' : '-'); res.push_back(has_read ? 'r' : '-');
res.push_back(has_write ? 'w' : '-'); res.push_back(has_write ? 'w' : '-');
res.push_back(has_exec ? 'x' : '-'); res.push_back(has_exec ? 'x' : '-');
return res; return res;
} }
inline memory_permission map_nt_to_emulator_protection(uint32_t nt_protection) inline memory_permission map_nt_to_emulator_protection(uint32_t nt_protection)
{ {
nt_protection &= ~static_cast<uint32_t>(PAGE_GUARD); // TODO: Implement that nt_protection &= ~static_cast<uint32_t>(PAGE_GUARD); // TODO: Implement that
switch (nt_protection) switch (nt_protection)
{ {
case PAGE_NOACCESS: case PAGE_NOACCESS:
return memory_permission::none; return memory_permission::none;
case PAGE_READONLY: case PAGE_READONLY:
return memory_permission::read; return memory_permission::read;
case PAGE_READWRITE: case PAGE_READWRITE:
case PAGE_WRITECOPY: case PAGE_WRITECOPY:
return memory_permission::read | memory_permission::write; return memory_permission::read | memory_permission::write;
case PAGE_EXECUTE: case PAGE_EXECUTE:
case PAGE_EXECUTE_READ: case PAGE_EXECUTE_READ:
return memory_permission::read | memory_permission::exec; return memory_permission::read | memory_permission::exec;
case PAGE_EXECUTE_READWRITE: case PAGE_EXECUTE_READWRITE:
return memory_permission::all; return memory_permission::all;
case PAGE_EXECUTE_WRITECOPY: case PAGE_EXECUTE_WRITECOPY:
default: default:
throw std::runtime_error("Failed to map protection"); throw std::runtime_error("Failed to map protection");
} }
} }
inline uint32_t map_emulator_to_nt_protection(const memory_permission permission) inline uint32_t map_emulator_to_nt_protection(const memory_permission permission)
{ {
const bool has_exec = (permission & memory_permission::exec) != memory_permission::none; const bool has_exec = (permission & memory_permission::exec) != memory_permission::none;
const bool has_read = (permission & memory_permission::read) != memory_permission::none; const bool has_read = (permission & memory_permission::read) != memory_permission::none;
const bool has_write = (permission & memory_permission::write) != memory_permission::none; const bool has_write = (permission & memory_permission::write) != memory_permission::none;
if (!has_read) if (!has_read)
{ {
return PAGE_NOACCESS; return PAGE_NOACCESS;
} }
if (has_exec && has_write) if (has_exec && has_write)
{ {
return PAGE_EXECUTE_READWRITE; return PAGE_EXECUTE_READWRITE;
} }
if (has_exec) if (has_exec)
{ {
return PAGE_EXECUTE_READ; return PAGE_EXECUTE_READ;
} }
if (has_write) if (has_write)
{ {
return PAGE_READWRITE; return PAGE_READWRITE;
} }
return PAGE_READONLY; return PAGE_READONLY;
} }

View File

@@ -3,10 +3,10 @@
struct exported_symbol struct exported_symbol
{ {
std::string name{}; std::string name{};
uint64_t ordinal{}; uint64_t ordinal{};
uint64_t rva{}; uint64_t rva{};
uint64_t address{}; uint64_t address{};
}; };
using exported_symbols = std::vector<exported_symbol>; using exported_symbols = std::vector<exported_symbol>;
@@ -14,39 +14,39 @@ using address_name_mapping = std::map<uint64_t, std::string>;
struct mapped_section struct mapped_section
{ {
std::string name{}; std::string name{};
basic_memory_region region{}; basic_memory_region region{};
}; };
struct mapped_module struct mapped_module
{ {
std::string name{}; std::string name{};
std::filesystem::path path{}; std::filesystem::path path{};
uint64_t image_base{}; uint64_t image_base{};
uint64_t size_of_image{}; uint64_t size_of_image{};
uint64_t entry_point{}; uint64_t entry_point{};
exported_symbols exports{}; exported_symbols exports{};
address_name_mapping address_names{}; address_name_mapping address_names{};
std::vector<mapped_section> sections{}; std::vector<mapped_section> sections{};
bool is_within(const uint64_t address) const bool is_within(const uint64_t address) const
{ {
return address >= this->image_base && address < (this->image_base + this->size_of_image); return address >= this->image_base && address < (this->image_base + this->size_of_image);
} }
uint64_t find_export(const std::string_view export_name) const uint64_t find_export(const std::string_view export_name) const
{ {
for (auto& symbol : this->exports) for (auto& symbol : this->exports)
{ {
if (symbol.name == export_name) if (symbol.name == export_name)
{ {
return symbol.address; return symbol.address;
} }
} }
return 0; return 0;
} }
}; };

View File

@@ -5,124 +5,124 @@
namespace namespace
{ {
std::filesystem::path canonicalize_module_path(const std::filesystem::path& file) std::filesystem::path canonicalize_module_path(const std::filesystem::path& file)
{ {
constexpr std::u16string_view nt_prefix = u"\\??\\"; constexpr std::u16string_view nt_prefix = u"\\??\\";
const auto wide_file = file.u16string(); const auto wide_file = file.u16string();
if (!wide_file.starts_with(nt_prefix)) if (!wide_file.starts_with(nt_prefix))
{ {
return canonical(absolute(file)); return canonical(absolute(file));
} }
return canonicalize_module_path(wide_file.substr(nt_prefix.size())); return canonicalize_module_path(wide_file.substr(nt_prefix.size()));
} }
} }
namespace utils namespace utils
{ {
static void serialize(buffer_serializer& buffer, const exported_symbol& sym) static void serialize(buffer_serializer& buffer, const exported_symbol& sym)
{ {
buffer.write(sym.name); buffer.write(sym.name);
buffer.write(sym.ordinal); buffer.write(sym.ordinal);
buffer.write(sym.rva); buffer.write(sym.rva);
buffer.write(sym.address); buffer.write(sym.address);
} }
static void deserialize(buffer_deserializer& buffer, exported_symbol& sym) static void deserialize(buffer_deserializer& buffer, exported_symbol& sym)
{ {
buffer.read(sym.name); buffer.read(sym.name);
buffer.read(sym.ordinal); buffer.read(sym.ordinal);
buffer.read(sym.rva); buffer.read(sym.rva);
buffer.read(sym.address); buffer.read(sym.address);
} }
static void serialize(buffer_serializer& buffer, const mapped_module& mod) static void serialize(buffer_serializer& buffer, const mapped_module& mod)
{ {
buffer.write_string(mod.name); buffer.write_string(mod.name);
buffer.write(mod.path.u16string()); buffer.write(mod.path.u16string());
buffer.write(mod.image_base); buffer.write(mod.image_base);
buffer.write(mod.size_of_image); buffer.write(mod.size_of_image);
buffer.write(mod.entry_point); buffer.write(mod.entry_point);
buffer.write_vector(mod.exports); buffer.write_vector(mod.exports);
buffer.write_map(mod.address_names); buffer.write_map(mod.address_names);
} }
static void deserialize(buffer_deserializer& buffer, mapped_module& mod) static void deserialize(buffer_deserializer& buffer, mapped_module& mod)
{ {
mod.name = buffer.read_string(); mod.name = buffer.read_string();
mod.path = buffer.read_string<std::u16string::value_type>(); mod.path = buffer.read_string<std::u16string::value_type>();
buffer.read(mod.image_base); buffer.read(mod.image_base);
buffer.read(mod.size_of_image); buffer.read(mod.size_of_image);
buffer.read(mod.entry_point); buffer.read(mod.entry_point);
buffer.read_vector(mod.exports); buffer.read_vector(mod.exports);
buffer.read_map(mod.address_names); buffer.read_map(mod.address_names);
} }
} }
module_manager::module_manager(emulator& emu) module_manager::module_manager(emulator& emu)
: emu_(&emu) : emu_(&emu)
{ {
} }
mapped_module* module_manager::map_module(const std::filesystem::path& file, logger& logger) mapped_module* module_manager::map_module(const std::filesystem::path& file, logger& logger)
{ {
auto canonical_file = canonicalize_module_path(file); auto canonical_file = canonicalize_module_path(file);
for (auto& mod : this->modules_) for (auto& mod : this->modules_)
{ {
if (mod.second.path == canonical_file) if (mod.second.path == canonical_file)
{ {
return &mod.second; return &mod.second;
} }
} }
try try
{ {
auto mod = map_module_from_file(*this->emu_, std::move(canonical_file)); auto mod = map_module_from_file(*this->emu_, std::move(canonical_file));
logger.log("Mapped %s at 0x%" PRIx64 "\n", mod.path.generic_string().c_str(), mod.image_base); logger.log("Mapped %s at 0x%" PRIx64 "\n", mod.path.generic_string().c_str(), mod.image_base);
const auto image_base = mod.image_base; const auto image_base = mod.image_base;
const auto entry = this->modules_.try_emplace(image_base, std::move(mod)); const auto entry = this->modules_.try_emplace(image_base, std::move(mod));
return &entry.first->second; return &entry.first->second;
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
logger.error("Failed to map %s: %s\n", file.generic_string().c_str(), e.what()); logger.error("Failed to map %s: %s\n", file.generic_string().c_str(), e.what());
return nullptr; return nullptr;
} }
catch (...) catch (...)
{ {
logger.error("Failed to map %s: Unknown error\n", file.generic_string().c_str()); logger.error("Failed to map %s: Unknown error\n", file.generic_string().c_str());
return nullptr; return nullptr;
} }
} }
void module_manager::serialize(utils::buffer_serializer& buffer) const void module_manager::serialize(utils::buffer_serializer& buffer) const
{ {
buffer.write_map(this->modules_); buffer.write_map(this->modules_);
} }
void module_manager::deserialize(utils::buffer_deserializer& buffer) void module_manager::deserialize(utils::buffer_deserializer& buffer)
{ {
buffer.read_map(this->modules_); buffer.read_map(this->modules_);
} }
bool module_manager::unmap(const uint64_t address) bool module_manager::unmap(const uint64_t address)
{ {
const auto mod = this->modules_.find(address); const auto mod = this->modules_.find(address);
if (mod == this->modules_.end()) if (mod == this->modules_.end())
{ {
return false; return false;
} }
unmap_module(*this->emu_, mod->second); unmap_module(*this->emu_, mod->second);
this->modules_.erase(mod); this->modules_.erase(mod);
return true; return true;
} }

View File

@@ -6,58 +6,58 @@ class logger;
class module_manager class module_manager
{ {
public: public:
module_manager(emulator& emu); module_manager(emulator& emu);
mapped_module* map_module(const std::filesystem::path& file, logger& logger); mapped_module* map_module(const std::filesystem::path& file, logger& logger);
mapped_module* find_by_address(const uint64_t address) mapped_module* find_by_address(const uint64_t address)
{ {
const auto entry = this->get_module(address); const auto entry = this->get_module(address);
if (entry != this->modules_.end()) if (entry != this->modules_.end())
{ {
return &entry->second; return &entry->second;
} }
return nullptr; return nullptr;
} }
const char* find_name(const uint64_t address) const char* find_name(const uint64_t address)
{ {
const auto* mod = this->find_by_address(address); const auto* mod = this->find_by_address(address);
if (!mod) if (!mod)
{ {
return "<N/A>"; return "<N/A>";
} }
return mod->name.c_str(); return mod->name.c_str();
} }
void serialize(utils::buffer_serializer& buffer) const; void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer); void deserialize(utils::buffer_deserializer& buffer);
bool unmap(const uint64_t address); bool unmap(const uint64_t address);
private: private:
emulator* emu_{}; emulator* emu_{};
using module_map = std::map<uint64_t, mapped_module>; using module_map = std::map<uint64_t, mapped_module>;
module_map modules_{}; module_map modules_{};
module_map::iterator get_module(const uint64_t address) module_map::iterator get_module(const uint64_t address)
{ {
if (this->modules_.empty()) if (this->modules_.empty())
{ {
return this->modules_.end(); return this->modules_.end();
} }
auto upper_bound = this->modules_.upper_bound(address); auto upper_bound = this->modules_.upper_bound(address);
if (upper_bound == this->modules_.begin()) if (upper_bound == this->modules_.begin())
{ {
return this->modules_.end(); return this->modules_.end();
} }
std::advance(upper_bound, -1); std::advance(upper_bound, -1);
return upper_bound; return upper_bound;
} }
}; };

View File

@@ -7,265 +7,259 @@
namespace namespace
{ {
uint64_t get_first_section_offset(const PENTHeaders_t<std::uint64_t>& nt_headers, const uint64_t nt_headers_offset) uint64_t get_first_section_offset(const PENTHeaders_t<std::uint64_t>& nt_headers, const uint64_t nt_headers_offset)
{ {
const uint8_t* nt_headers_addr = reinterpret_cast<const uint8_t*>(&nt_headers); const uint8_t* nt_headers_addr = reinterpret_cast<const uint8_t*>(&nt_headers);
size_t optional_header_offset = reinterpret_cast<uintptr_t>(&(nt_headers.OptionalHeader)) - reinterpret_cast< size_t optional_header_offset =
uintptr_t>(&nt_headers); reinterpret_cast<uintptr_t>(&(nt_headers.OptionalHeader)) - reinterpret_cast<uintptr_t>(&nt_headers);
size_t optional_header_size = nt_headers.FileHeader.SizeOfOptionalHeader; size_t optional_header_size = nt_headers.FileHeader.SizeOfOptionalHeader;
const uint8_t* first_section_addr = nt_headers_addr + optional_header_offset + optional_header_size; const uint8_t* first_section_addr = nt_headers_addr + optional_header_offset + optional_header_size;
const auto first_section_absolute = reinterpret_cast<uint64_t>(first_section_addr); const auto first_section_absolute = reinterpret_cast<uint64_t>(first_section_addr);
const auto absolute_base = reinterpret_cast<uint64_t>(&nt_headers); const auto absolute_base = reinterpret_cast<uint64_t>(&nt_headers);
return nt_headers_offset + (first_section_absolute - absolute_base); return nt_headers_offset + (first_section_absolute - absolute_base);
} }
std::vector<uint8_t> read_mapped_memory(const emulator& emu, const mapped_module& binary) std::vector<uint8_t> read_mapped_memory(const emulator& emu, const mapped_module& binary)
{ {
std::vector<uint8_t> memory{}; std::vector<uint8_t> memory{};
memory.resize(binary.size_of_image); memory.resize(binary.size_of_image);
emu.read_memory(binary.image_base, memory.data(), memory.size()); emu.read_memory(binary.image_base, memory.data(), memory.size());
return memory; return memory;
} }
void collect_exports(mapped_module& binary, const utils::safe_buffer_accessor<const uint8_t> buffer, void collect_exports(mapped_module& binary, const utils::safe_buffer_accessor<const uint8_t> buffer,
const PEOptionalHeader_t<std::uint64_t>& optional_header) const PEOptionalHeader_t<std::uint64_t>& optional_header)
{ {
auto& export_directory_entry = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; auto& export_directory_entry = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (export_directory_entry.VirtualAddress == 0 || export_directory_entry.Size == 0) if (export_directory_entry.VirtualAddress == 0 || export_directory_entry.Size == 0)
{ {
return; return;
} }
const auto export_directory = buffer.as<IMAGE_EXPORT_DIRECTORY>(export_directory_entry. const auto export_directory = buffer.as<IMAGE_EXPORT_DIRECTORY>(export_directory_entry.VirtualAddress).get();
VirtualAddress).get();
const auto names_count = export_directory.NumberOfNames; const auto names_count = export_directory.NumberOfNames;
//const auto function_count = export_directory.NumberOfFunctions; // const auto function_count = export_directory.NumberOfFunctions;
const auto names = buffer.as<DWORD>(export_directory.AddressOfNames); const auto names = buffer.as<DWORD>(export_directory.AddressOfNames);
const auto ordinals = buffer.as<WORD>(export_directory.AddressOfNameOrdinals); const auto ordinals = buffer.as<WORD>(export_directory.AddressOfNameOrdinals);
const auto functions = buffer.as<DWORD>(export_directory.AddressOfFunctions); const auto functions = buffer.as<DWORD>(export_directory.AddressOfFunctions);
binary.exports.reserve(names_count); binary.exports.reserve(names_count);
for (DWORD i = 0; i < names_count; i++) for (DWORD i = 0; i < names_count; i++)
{ {
const auto ordinal = ordinals.get(i); const auto ordinal = ordinals.get(i);
exported_symbol symbol{}; exported_symbol symbol{};
symbol.ordinal = export_directory.Base + ordinal; symbol.ordinal = export_directory.Base + ordinal;
symbol.rva = functions.get(ordinal); symbol.rva = functions.get(ordinal);
symbol.address = binary.image_base + symbol.rva; symbol.address = binary.image_base + symbol.rva;
symbol.name = buffer.as_string(names.get(i)); symbol.name = buffer.as_string(names.get(i));
binary.exports.push_back(std::move(symbol)); binary.exports.push_back(std::move(symbol));
} }
for (const auto& symbol : binary.exports) for (const auto& symbol : binary.exports)
{ {
binary.address_names.try_emplace(symbol.address, symbol.name); binary.address_names.try_emplace(symbol.address, symbol.name);
} }
} }
template <typename T> template <typename T>
requires(std::is_integral_v<T>) requires(std::is_integral_v<T>)
void apply_relocation(const utils::safe_buffer_accessor<uint8_t> buffer, const uint64_t offset, void apply_relocation(const utils::safe_buffer_accessor<uint8_t> buffer, const uint64_t offset,
const uint64_t delta) const uint64_t delta)
{ {
const auto obj = buffer.as<T>(offset); const auto obj = buffer.as<T>(offset);
const auto value = obj.get(); const auto value = obj.get();
const auto new_value = value + static_cast<T>(delta); const auto new_value = value + static_cast<T>(delta);
obj.set(new_value); obj.set(new_value);
} }
void apply_relocations(const mapped_module& binary, const utils::safe_buffer_accessor<uint8_t> buffer, void apply_relocations(const mapped_module& binary, const utils::safe_buffer_accessor<uint8_t> buffer,
const PEOptionalHeader_t<std::uint64_t>& optional_header) const PEOptionalHeader_t<std::uint64_t>& optional_header)
{ {
const auto delta = binary.image_base - optional_header.ImageBase; const auto delta = binary.image_base - optional_header.ImageBase;
if (delta == 0) if (delta == 0)
{ {
return; return;
} }
const auto directory = &optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; const auto directory = &optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (directory->Size == 0) if (directory->Size == 0)
{ {
return; return;
} }
auto relocation_offset = directory->VirtualAddress; auto relocation_offset = directory->VirtualAddress;
const auto relocation_end = relocation_offset + directory->Size; const auto relocation_end = relocation_offset + directory->Size;
while (relocation_offset < relocation_end) while (relocation_offset < relocation_end)
{ {
const auto relocation = buffer.as<IMAGE_BASE_RELOCATION>(relocation_offset).get(); const auto relocation = buffer.as<IMAGE_BASE_RELOCATION>(relocation_offset).get();
if (relocation.VirtualAddress <= 0 || relocation.SizeOfBlock <= sizeof(IMAGE_BASE_RELOCATION)) if (relocation.VirtualAddress <= 0 || relocation.SizeOfBlock <= sizeof(IMAGE_BASE_RELOCATION))
{ {
break; break;
} }
const auto data_size = relocation.SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION); const auto data_size = relocation.SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION);
const auto entry_count = data_size / sizeof(uint16_t); const auto entry_count = data_size / sizeof(uint16_t);
const auto entries = buffer.as<uint16_t>(relocation_offset + sizeof(IMAGE_BASE_RELOCATION)); const auto entries = buffer.as<uint16_t>(relocation_offset + sizeof(IMAGE_BASE_RELOCATION));
relocation_offset += relocation.SizeOfBlock; relocation_offset += relocation.SizeOfBlock;
for (size_t i = 0; i < entry_count; ++i) for (size_t i = 0; i < entry_count; ++i)
{ {
const auto entry = entries.get(i); const auto entry = entries.get(i);
const int type = entry >> 12; const int type = entry >> 12;
const auto offset = static_cast<uint16_t>(entry & 0xfff); const auto offset = static_cast<uint16_t>(entry & 0xfff);
const auto total_offset = relocation.VirtualAddress + offset; const auto total_offset = relocation.VirtualAddress + offset;
switch (type) switch (type)
{ {
case IMAGE_REL_BASED_ABSOLUTE: case IMAGE_REL_BASED_ABSOLUTE:
break; break;
case IMAGE_REL_BASED_HIGHLOW: case IMAGE_REL_BASED_HIGHLOW:
apply_relocation<DWORD>(buffer, total_offset, delta); apply_relocation<DWORD>(buffer, total_offset, delta);
break; break;
case IMAGE_REL_BASED_DIR64: case IMAGE_REL_BASED_DIR64:
apply_relocation<ULONGLONG>(buffer, total_offset, delta); apply_relocation<ULONGLONG>(buffer, total_offset, delta);
break; break;
default: default:
throw std::runtime_error("Unknown relocation type: " + std::to_string(type)); throw std::runtime_error("Unknown relocation type: " + std::to_string(type));
} }
} }
} }
} }
void map_sections(emulator& emu, mapped_module& binary, void map_sections(emulator& emu, mapped_module& binary, const utils::safe_buffer_accessor<const uint8_t> buffer,
const utils::safe_buffer_accessor<const uint8_t> buffer, const PENTHeaders_t<std::uint64_t>& nt_headers, const uint64_t nt_headers_offset)
const PENTHeaders_t<std::uint64_t>& nt_headers, const uint64_t nt_headers_offset) {
{ const auto first_section_offset = get_first_section_offset(nt_headers, nt_headers_offset);
const auto first_section_offset = get_first_section_offset(nt_headers, nt_headers_offset); const auto sections = buffer.as<IMAGE_SECTION_HEADER>(first_section_offset);
const auto sections = buffer.as<IMAGE_SECTION_HEADER>(first_section_offset);
for (size_t i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i) for (size_t i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i)
{ {
const auto section = sections.get(i); const auto section = sections.get(i);
const auto target_ptr = binary.image_base + section.VirtualAddress; const auto target_ptr = binary.image_base + section.VirtualAddress;
if (section.SizeOfRawData > 0) if (section.SizeOfRawData > 0)
{ {
const auto size_of_data = std::min(section.SizeOfRawData, section.Misc.VirtualSize); const auto size_of_data = std::min(section.SizeOfRawData, section.Misc.VirtualSize);
const auto* source_ptr = buffer.get_pointer_for_range(section.PointerToRawData, size_of_data); const auto* source_ptr = buffer.get_pointer_for_range(section.PointerToRawData, size_of_data);
emu.write_memory(target_ptr, source_ptr, size_of_data); emu.write_memory(target_ptr, source_ptr, size_of_data);
} }
auto permissions = memory_permission::none; auto permissions = memory_permission::none;
if (section.Characteristics & IMAGE_SCN_MEM_EXECUTE) if (section.Characteristics & IMAGE_SCN_MEM_EXECUTE)
{ {
permissions |= memory_permission::exec; permissions |= memory_permission::exec;
} }
if (section.Characteristics & IMAGE_SCN_MEM_READ) if (section.Characteristics & IMAGE_SCN_MEM_READ)
{ {
permissions |= memory_permission::read; permissions |= memory_permission::read;
} }
if (section.Characteristics & IMAGE_SCN_MEM_WRITE) if (section.Characteristics & IMAGE_SCN_MEM_WRITE)
{ {
permissions |= memory_permission::write; permissions |= memory_permission::write;
} }
const auto size_of_section = page_align_up(std::max(section.SizeOfRawData, section.Misc.VirtualSize)); const auto size_of_section = page_align_up(std::max(section.SizeOfRawData, section.Misc.VirtualSize));
emu.protect_memory(target_ptr, size_of_section, permissions, nullptr); emu.protect_memory(target_ptr, size_of_section, permissions, nullptr);
mapped_section section_info{}; mapped_section section_info{};
section_info.region.start = target_ptr; section_info.region.start = target_ptr;
section_info.region.length = size_of_section; section_info.region.length = size_of_section;
section_info.region.permissions = permissions; section_info.region.permissions = permissions;
for (size_t j = 0; j < sizeof(section.Name) && section.Name[j]; ++j) for (size_t j = 0; j < sizeof(section.Name) && section.Name[j]; ++j)
{ {
section_info.name.push_back(static_cast<char>(section.Name[j])); section_info.name.push_back(static_cast<char>(section.Name[j]));
} }
binary.sections.push_back(std::move(section_info)); binary.sections.push_back(std::move(section_info));
} }
} }
} }
mapped_module map_module_from_data(emulator& emu, const std::span<const uint8_t> data, mapped_module map_module_from_data(emulator& emu, const std::span<const uint8_t> data, std::filesystem::path file)
std::filesystem::path file)
{ {
mapped_module binary{}; mapped_module binary{};
binary.path = std::move(file); binary.path = std::move(file);
binary.name = binary.path.filename().string(); binary.name = binary.path.filename().string();
utils::safe_buffer_accessor buffer{data}; utils::safe_buffer_accessor buffer{data};
const auto dos_header = buffer.as<PEDosHeader_t>(0).get(); const auto dos_header = buffer.as<PEDosHeader_t>(0).get();
const auto nt_headers_offset = dos_header.e_lfanew; const auto nt_headers_offset = dos_header.e_lfanew;
const auto nt_headers = buffer.as<PENTHeaders_t<std::uint64_t>>(nt_headers_offset).get(); const auto nt_headers = buffer.as<PENTHeaders_t<std::uint64_t>>(nt_headers_offset).get();
auto& optional_header = nt_headers.OptionalHeader; auto& optional_header = nt_headers.OptionalHeader;
if (nt_headers.FileHeader.Machine != PEMachineType::AMD64) if (nt_headers.FileHeader.Machine != PEMachineType::AMD64)
{ {
throw std::runtime_error("Unsupported architecture!"); throw std::runtime_error("Unsupported architecture!");
} }
binary.image_base = optional_header.ImageBase; binary.image_base = optional_header.ImageBase;
binary.size_of_image = page_align_up(optional_header.SizeOfImage); // TODO: Sanitize binary.size_of_image = page_align_up(optional_header.SizeOfImage); // TODO: Sanitize
if (!emu.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::read)) if (!emu.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::read))
{ {
binary.image_base = emu.find_free_allocation_base(binary.size_of_image); binary.image_base = emu.find_free_allocation_base(binary.size_of_image);
const auto is_dll = nt_headers.FileHeader.Characteristics & IMAGE_FILE_DLL; const auto is_dll = nt_headers.FileHeader.Characteristics & IMAGE_FILE_DLL;
const auto has_dynamic_base = const auto has_dynamic_base = optional_header.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
optional_header.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE; const auto is_relocatable = is_dll || has_dynamic_base;
const auto is_relocatable = is_dll || has_dynamic_base;
if (!is_relocatable || !emu.allocate_memory(binary.image_base, binary.size_of_image, if (!is_relocatable || !emu.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::read))
memory_permission::read)) {
{ throw std::runtime_error("Memory range not allocatable");
throw std::runtime_error("Memory range not allocatable"); }
} }
}
binary.entry_point = binary.image_base + optional_header.AddressOfEntryPoint; binary.entry_point = binary.image_base + optional_header.AddressOfEntryPoint;
const auto* header_buffer = buffer.get_pointer_for_range(0, optional_header.SizeOfHeaders); const auto* header_buffer = buffer.get_pointer_for_range(0, optional_header.SizeOfHeaders);
emu.write_memory(binary.image_base, header_buffer, emu.write_memory(binary.image_base, header_buffer, optional_header.SizeOfHeaders);
optional_header.SizeOfHeaders);
map_sections(emu, binary, buffer, nt_headers, nt_headers_offset); map_sections(emu, binary, buffer, nt_headers, nt_headers_offset);
auto mapped_memory = read_mapped_memory(emu, binary); auto mapped_memory = read_mapped_memory(emu, binary);
utils::safe_buffer_accessor<uint8_t> mapped_buffer{mapped_memory}; utils::safe_buffer_accessor<uint8_t> mapped_buffer{mapped_memory};
apply_relocations(binary, mapped_buffer, optional_header); apply_relocations(binary, mapped_buffer, optional_header);
collect_exports(binary, mapped_buffer, optional_header); collect_exports(binary, mapped_buffer, optional_header);
emu.write_memory(binary.image_base, mapped_memory.data(), mapped_memory.size()); emu.write_memory(binary.image_base, mapped_memory.data(), mapped_memory.size());
return binary; return binary;
} }
mapped_module map_module_from_file(emulator& emu, std::filesystem::path file) mapped_module map_module_from_file(emulator& emu, std::filesystem::path file)
{ {
const auto data = utils::io::read_file(file); const auto data = utils::io::read_file(file);
if (data.empty()) if (data.empty())
{ {
throw std::runtime_error("Bad file data"); throw std::runtime_error("Bad file data");
} }
return map_module_from_data(emu, data, std::move(file)); return map_module_from_data(emu, data, std::move(file));
} }
bool unmap_module(emulator& emu, const mapped_module& mod) bool unmap_module(emulator& emu, const mapped_module& mod)
{ {
return emu.release_memory(mod.image_base, mod.size_of_image); return emu.release_memory(mod.image_base, mod.size_of_image);
} }

View File

@@ -3,8 +3,7 @@
#include <x64_emulator.hpp> #include <x64_emulator.hpp>
#include "mapped_module.hpp" #include "mapped_module.hpp"
mapped_module map_module_from_data(emulator& emu, std::span<const uint8_t> data, mapped_module map_module_from_data(emulator& emu, std::span<const uint8_t> data, std::filesystem::path file);
std::filesystem::path file);
mapped_module map_module_from_file(emulator& emu, std::filesystem::path file); mapped_module map_module_from_file(emulator& emu, std::filesystem::path file);
bool unmap_module(emulator& emu, const mapped_module& mod); bool unmap_module(emulator& emu, const mapped_module& mod);

File diff suppressed because it is too large Load Diff

View File

@@ -5,216 +5,216 @@
namespace namespace
{ {
constexpr uint64_t MAIN_ROOT_OFFSET = 0x1000; constexpr uint64_t MAIN_ROOT_OFFSET = 0x1000;
constexpr uint64_t MAIN_KEY_BLOCK_OFFSET = MAIN_ROOT_OFFSET + 0x20; constexpr uint64_t MAIN_KEY_BLOCK_OFFSET = MAIN_ROOT_OFFSET + 0x20;
struct offset_entry_t struct offset_entry_t
{ {
int32_t offset; int32_t offset;
int32_t hash; int32_t hash;
}; };
struct offsets_t struct offsets_t
{ {
int32_t block_size; int32_t block_size;
char block_type[2]; char block_type[2];
int16_t count; int16_t count;
offset_entry_t entries[1]; offset_entry_t entries[1];
}; };
struct key_block_t struct key_block_t
{ {
int32_t block_size; int32_t block_size;
char block_type[2]; char block_type[2];
uint8_t dummya[18]; uint8_t dummya[18];
int32_t subkey_count; int32_t subkey_count;
uint8_t dummyb[4]; uint8_t dummyb[4];
int32_t subkeys; int32_t subkeys;
uint8_t dummyc[4]; uint8_t dummyc[4];
int32_t value_count; int32_t value_count;
int32_t offsets; int32_t offsets;
uint8_t dummyd[28]; uint8_t dummyd[28];
int16_t len; int16_t len;
int16_t du; int16_t du;
char name[255]; char name[255];
}; };
struct value_block_t struct value_block_t
{ {
int32_t block_size; int32_t block_size;
char block_type[2]; char block_type[2];
int16_t name_len; int16_t name_len;
int32_t size; int32_t size;
int32_t offset; int32_t offset;
int32_t value_type; int32_t value_type;
int16_t flags; int16_t flags;
int16_t dummy; int16_t dummy;
char name[255]; char name[255];
}; };
bool read_file_data_safe(std::ifstream& file, const uint64_t offset, void* buffer, const size_t size) bool read_file_data_safe(std::ifstream& file, const uint64_t offset, void* buffer, const size_t size)
{ {
if (file.bad()) if (file.bad())
{ {
return false; return false;
} }
file.clear(); file.clear();
if (!file.good()) if (!file.good())
{ {
return false; return false;
} }
file.seekg(static_cast<std::streamoff>(offset)); file.seekg(static_cast<std::streamoff>(offset));
if (!file.good()) if (!file.good())
{ {
return false; return false;
} }
file.read(static_cast<char*>(buffer), static_cast<std::streamsize>(size)); file.read(static_cast<char*>(buffer), static_cast<std::streamsize>(size));
return file.good(); return file.good();
} }
void read_file_data(std::ifstream& file, const uint64_t offset, void* buffer, const size_t size) void read_file_data(std::ifstream& file, const uint64_t offset, void* buffer, const size_t size)
{ {
if (!read_file_data_safe(file, offset, buffer, size)) if (!read_file_data_safe(file, offset, buffer, size))
{ {
throw std::runtime_error("Failed to read file data"); throw std::runtime_error("Failed to read file data");
} }
} }
std::vector<std::byte> read_file_data(std::ifstream& file, const uint64_t offset, const size_t size) std::vector<std::byte> read_file_data(std::ifstream& file, const uint64_t offset, const size_t size)
{ {
std::vector<std::byte> result{}; std::vector<std::byte> result{};
result.resize(size); result.resize(size);
read_file_data(file, offset, result.data(), size); read_file_data(file, offset, result.data(), size);
return result; return result;
} }
std::string read_file_data_string(std::ifstream& file, const uint64_t offset, const size_t size) std::string read_file_data_string(std::ifstream& file, const uint64_t offset, const size_t size)
{ {
std::string result{}; std::string result{};
result.resize(size); result.resize(size);
read_file_data(file, offset, result.data(), size); read_file_data(file, offset, result.data(), size);
return result; return result;
} }
template <typename T> template <typename T>
requires(std::is_trivially_copyable_v<T>) requires(std::is_trivially_copyable_v<T>)
T read_file_object(std::ifstream& file, const uint64_t offset, const size_t array_index = 0) T read_file_object(std::ifstream& file, const uint64_t offset, const size_t array_index = 0)
{ {
T obj{}; T obj{};
read_file_data(file, offset + (array_index * sizeof(T)), &obj, sizeof(T)); read_file_data(file, offset + (array_index * sizeof(T)), &obj, sizeof(T));
return obj; return obj;
} }
hive_key parse_root_block(std::ifstream& file, const std::filesystem::path& file_path) hive_key parse_root_block(std::ifstream& file, const std::filesystem::path& file_path)
{ {
try try
{ {
if (read_file_data_string(file, 0, 4) != "regf") if (read_file_data_string(file, 0, 4) != "regf")
{ {
throw std::runtime_error("Invalid signature"); throw std::runtime_error("Invalid signature");
} }
const auto key_block = read_file_object<key_block_t>(file, MAIN_KEY_BLOCK_OFFSET); const auto key_block = read_file_object<key_block_t>(file, MAIN_KEY_BLOCK_OFFSET);
return {key_block.subkeys, key_block.value_count, key_block.offsets}; return {key_block.subkeys, key_block.value_count, key_block.offsets};
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
throw std::runtime_error("Bad hive file '" + file_path.string() + "': " + e.what()); throw std::runtime_error("Bad hive file '" + file_path.string() + "': " + e.what());
} }
} }
} }
const hive_value* hive_key::get_value(std::ifstream& file, const std::string_view name) const hive_value* hive_key::get_value(std::ifstream& file, const std::string_view name)
{ {
this->parse(file); this->parse(file);
const auto entry = this->values_.find(name); const auto entry = this->values_.find(name);
if (entry == this->values_.end()) if (entry == this->values_.end())
{ {
return nullptr; return nullptr;
} }
auto& value = entry->second; auto& value = entry->second;
if (!value.parsed) if (!value.parsed)
{ {
value.data = read_file_data(file, MAIN_ROOT_OFFSET + value.data_offset, value.data_length); value.data = read_file_data(file, MAIN_ROOT_OFFSET + value.data_offset, value.data_length);
value.parsed = true; value.parsed = true;
} }
return &value; return &value;
} }
void hive_key::parse(std::ifstream& file) void hive_key::parse(std::ifstream& file)
{ {
if (this->parsed_) if (this->parsed_)
{ {
return; return;
} }
this->parsed_ = true; this->parsed_ = true;
// Values // Values
for (auto i = 0; i < this->value_count_; i++) for (auto i = 0; i < this->value_count_; i++)
{ {
const auto offset = read_file_object<int>(file, MAIN_ROOT_OFFSET + this->value_offsets_ + 4, i); const auto offset = read_file_object<int>(file, MAIN_ROOT_OFFSET + this->value_offsets_ + 4, i);
const auto value = read_file_object<value_block_t>(file, MAIN_ROOT_OFFSET + offset); const auto value = read_file_object<value_block_t>(file, MAIN_ROOT_OFFSET + offset);
std::string value_name(value.name, std::min(value.name_len, static_cast<short>(sizeof(value.name)))); std::string value_name(value.name, std::min(value.name_len, static_cast<short>(sizeof(value.name))));
raw_hive_value raw_value{}; raw_hive_value raw_value{};
raw_value.parsed = false; raw_value.parsed = false;
raw_value.type = value.value_type; raw_value.type = value.value_type;
raw_value.name = value_name; raw_value.name = value_name;
raw_value.data_length = value.size & 0xffff; raw_value.data_length = value.size & 0xffff;
raw_value.data_offset = value.offset + 4; raw_value.data_offset = value.offset + 4;
if (value.size & 1 << 31) if (value.size & 1 << 31)
{ {
raw_value.data_offset = offset + static_cast<int>(offsetof(value_block_t, offset)); raw_value.data_offset = offset + static_cast<int>(offsetof(value_block_t, offset));
} }
utils::string::to_lower_inplace(value_name); utils::string::to_lower_inplace(value_name);
this->values_[std::move(value_name)] = std::move(raw_value); this->values_[std::move(value_name)] = std::move(raw_value);
} }
// Subkeys // Subkeys
const auto item = read_file_object<offsets_t>(file, MAIN_ROOT_OFFSET + this->subkey_block_offset_); const auto item = read_file_object<offsets_t>(file, MAIN_ROOT_OFFSET + this->subkey_block_offset_);
if (item.block_type[1] != 'f' && item.block_type[1] != 'h') if (item.block_type[1] != 'f' && item.block_type[1] != 'h')
{ {
return; return;
} }
const auto entry_offsets = this->subkey_block_offset_ + offsetof(offsets_t, entries); const auto entry_offsets = this->subkey_block_offset_ + offsetof(offsets_t, entries);
for (short i = 0; i < item.count; ++i) for (short i = 0; i < item.count; ++i)
{ {
const auto offset_entry = read_file_object<offset_entry_t>(file, MAIN_ROOT_OFFSET + entry_offsets, i); const auto offset_entry = read_file_object<offset_entry_t>(file, MAIN_ROOT_OFFSET + entry_offsets, i);
const auto subkey_block_offset = MAIN_ROOT_OFFSET + offset_entry.offset; const auto subkey_block_offset = MAIN_ROOT_OFFSET + offset_entry.offset;
const auto subkey = read_file_object<key_block_t>(file, subkey_block_offset); const auto subkey = read_file_object<key_block_t>(file, subkey_block_offset);
std::string subkey_name(subkey.name, std::min(subkey.len, static_cast<int16_t>(sizeof(subkey.name)))); std::string subkey_name(subkey.name, std::min(subkey.len, static_cast<int16_t>(sizeof(subkey.name))));
utils::string::to_lower_inplace(subkey_name); utils::string::to_lower_inplace(subkey_name);
this->sub_keys_.emplace(std::move(subkey_name), hive_key{subkey.subkeys, subkey.value_count, subkey.offsets}); this->sub_keys_.emplace(std::move(subkey_name), hive_key{subkey.subkeys, subkey.value_count, subkey.offsets});
} }
} }
hive_parser::hive_parser(const std::filesystem::path& file_path) hive_parser::hive_parser(const std::filesystem::path& file_path)
: file_(file_path, std::ios::binary) : file_(file_path, std::ios::binary),
, root_key_(parse_root_block(file_, file_path)) root_key_(parse_root_block(file_, file_path))
{ {
} }

View File

@@ -8,95 +8,95 @@
struct hive_value struct hive_value
{ {
uint32_t type{}; uint32_t type{};
std::string name{}; std::string name{};
std::vector<std::byte> data{}; std::vector<std::byte> data{};
}; };
class hive_key class hive_key
{ {
public: public:
hive_key(const int subkey_block_offset, const int value_count, const int value_offsets) hive_key(const int subkey_block_offset, const int value_count, const int value_offsets)
: subkey_block_offset_(subkey_block_offset) : subkey_block_offset_(subkey_block_offset),
, value_count_(value_count) value_count_(value_count),
, value_offsets_(value_offsets) value_offsets_(value_offsets)
{ {
} }
utils::unordered_string_map<hive_key>& get_sub_keys(std::ifstream& file) utils::unordered_string_map<hive_key>& get_sub_keys(std::ifstream& file)
{ {
this->parse(file); this->parse(file);
return this->sub_keys_; return this->sub_keys_;
} }
hive_key* get_sub_key(std::ifstream& file, const std::string_view name) hive_key* get_sub_key(std::ifstream& file, const std::string_view name)
{ {
auto& sub_keys = this->get_sub_keys(file); auto& sub_keys = this->get_sub_keys(file);
const auto entry = sub_keys.find(name); const auto entry = sub_keys.find(name);
if (entry == sub_keys.end()) if (entry == sub_keys.end())
{ {
return nullptr; return nullptr;
} }
return &entry->second; return &entry->second;
} }
const hive_value* get_value(std::ifstream& file, const std::string_view name); const hive_value* get_value(std::ifstream& file, const std::string_view name);
private: private:
struct raw_hive_value : hive_value struct raw_hive_value : hive_value
{ {
bool parsed{false}; bool parsed{false};
int data_offset{}; int data_offset{};
size_t data_length{}; size_t data_length{};
}; };
bool parsed_{false}; bool parsed_{false};
utils::unordered_string_map<hive_key> sub_keys_{}; utils::unordered_string_map<hive_key> sub_keys_{};
utils::unordered_string_map<raw_hive_value> values_{}; utils::unordered_string_map<raw_hive_value> values_{};
const int subkey_block_offset_{}; const int subkey_block_offset_{};
const int value_count_{}; const int value_count_{};
const int value_offsets_{}; const int value_offsets_{};
void parse(std::ifstream& file); void parse(std::ifstream& file);
}; };
class hive_parser class hive_parser
{ {
public: public:
explicit hive_parser(const std::filesystem::path& file_path); explicit hive_parser(const std::filesystem::path& file_path);
[[nodiscard]] hive_key* get_sub_key(const std::filesystem::path& key) [[nodiscard]] hive_key* get_sub_key(const std::filesystem::path& key)
{ {
hive_key* current_key = &this->root_key_; hive_key* current_key = &this->root_key_;
for (const auto& key_part : key) for (const auto& key_part : key)
{ {
if (!current_key) if (!current_key)
{ {
return nullptr; return nullptr;
} }
current_key = current_key->get_sub_key(this->file_, key_part.string()); current_key = current_key->get_sub_key(this->file_, key_part.string());
} }
return current_key; return current_key;
} }
[[nodiscard]] const hive_value* get_value(const std::filesystem::path& key, const std::string_view name) [[nodiscard]] const hive_value* get_value(const std::filesystem::path& key, const std::string_view name)
{ {
auto* sub_key = this->get_sub_key(key); auto* sub_key = this->get_sub_key(key);
if (!sub_key) if (!sub_key)
{ {
return nullptr; return nullptr;
} }
return sub_key->get_value(this->file_, name); return sub_key->get_value(this->file_, name);
} }
private: private:
std::ifstream file_{}; std::ifstream file_{};
hive_key root_key_; hive_key root_key_;
}; };

View File

@@ -7,33 +7,33 @@
namespace namespace
{ {
std::filesystem::path canonicalize_path(const std::filesystem::path& key) std::filesystem::path canonicalize_path(const std::filesystem::path& key)
{ {
auto path = key.lexically_normal().wstring(); auto path = key.lexically_normal().wstring();
return utils::string::to_lower_consume(path); return utils::string::to_lower_consume(path);
} }
bool is_subpath(const std::filesystem::path& root, const std::filesystem::path& p) bool is_subpath(const std::filesystem::path& root, const std::filesystem::path& p)
{ {
auto root_it = root.begin(); auto root_it = root.begin();
auto p_it = p.begin(); auto p_it = p.begin();
for (; root_it != root.end(); ++root_it, ++p_it) for (; root_it != root.end(); ++root_it, ++p_it)
{ {
if (p_it == p.end() || *root_it != *p_it) if (p_it == p.end() || *root_it != *p_it)
{ {
return false; return false;
} }
} }
return true; return true;
} }
void register_hive(registry_manager::hive_map& hives, void register_hive(registry_manager::hive_map& hives, const std::filesystem::path& key,
const std::filesystem::path& key, const std::filesystem::path& file) const std::filesystem::path& file)
{ {
hives[canonicalize_path(key)] = std::make_unique<hive_parser>(file); hives[canonicalize_path(key)] = std::make_unique<hive_parser>(file);
} }
} }
registry_manager::registry_manager() = default; registry_manager::registry_manager() = default;
@@ -42,130 +42,130 @@ registry_manager::registry_manager(registry_manager&&) noexcept = default;
registry_manager& registry_manager::operator=(registry_manager&&) noexcept = default; registry_manager& registry_manager::operator=(registry_manager&&) noexcept = default;
registry_manager::registry_manager(const std::filesystem::path& hive_path) registry_manager::registry_manager(const std::filesystem::path& hive_path)
: hive_path_(absolute(hive_path)) : hive_path_(absolute(hive_path))
{ {
this->setup(); this->setup();
} }
void registry_manager::setup() void registry_manager::setup()
{ {
this->path_mapping_.clear(); this->path_mapping_.clear();
this->hives_.clear(); this->hives_.clear();
const std::filesystem::path root = R"(\registry)"; const std::filesystem::path root = R"(\registry)";
const std::filesystem::path machine = root / "machine"; const std::filesystem::path machine = root / "machine";
register_hive(this->hives_, machine / "system", this->hive_path_ / "SYSTEM"); register_hive(this->hives_, machine / "system", this->hive_path_ / "SYSTEM");
register_hive(this->hives_, machine / "security", this->hive_path_ / "SECURITY"); register_hive(this->hives_, machine / "security", this->hive_path_ / "SECURITY");
register_hive(this->hives_, machine / "sam", this->hive_path_ / "SAM"); register_hive(this->hives_, machine / "sam", this->hive_path_ / "SAM");
register_hive(this->hives_, machine / "software", this->hive_path_ / "SOFTWARE"); register_hive(this->hives_, machine / "software", this->hive_path_ / "SOFTWARE");
register_hive(this->hives_, machine / "system", this->hive_path_ / "SYSTEM"); register_hive(this->hives_, machine / "system", this->hive_path_ / "SYSTEM");
register_hive(this->hives_, machine / "hardware", this->hive_path_ / "HARDWARE"); register_hive(this->hives_, machine / "hardware", this->hive_path_ / "HARDWARE");
register_hive(this->hives_, root / "user", this->hive_path_ / "NTUSER.dat"); register_hive(this->hives_, root / "user", this->hive_path_ / "NTUSER.dat");
this->add_path_mapping(machine / "system" / "CurrentControlSet", machine / "system" / "ControlSet001"); this->add_path_mapping(machine / "system" / "CurrentControlSet", machine / "system" / "ControlSet001");
} }
void registry_manager::serialize(utils::buffer_serializer& buffer) const void registry_manager::serialize(utils::buffer_serializer& buffer) const
{ {
buffer.write(this->hive_path_); buffer.write(this->hive_path_);
} }
void registry_manager::deserialize(utils::buffer_deserializer& buffer) void registry_manager::deserialize(utils::buffer_deserializer& buffer)
{ {
buffer.read(this->hive_path_); buffer.read(this->hive_path_);
this->setup(); this->setup();
} }
std::filesystem::path registry_manager::normalize_path(const std::filesystem::path& path) const std::filesystem::path registry_manager::normalize_path(const std::filesystem::path& path) const
{ {
auto canonical_path = canonicalize_path(path); auto canonical_path = canonicalize_path(path);
for (const auto& mapping : this->path_mapping_) for (const auto& mapping : this->path_mapping_)
{ {
if (is_subpath(mapping.first, canonical_path)) if (is_subpath(mapping.first, canonical_path))
{ {
return mapping.second / canonical_path.lexically_relative(mapping.first); return mapping.second / canonical_path.lexically_relative(mapping.first);
} }
} }
return canonical_path; return canonical_path;
} }
void registry_manager::add_path_mapping(const std::filesystem::path& key, const std::filesystem::path& value) void registry_manager::add_path_mapping(const std::filesystem::path& key, const std::filesystem::path& value)
{ {
this->path_mapping_[canonicalize_path(key)] = canonicalize_path(value); this->path_mapping_[canonicalize_path(key)] = canonicalize_path(value);
} }
std::optional<registry_key> registry_manager::get_key(const std::filesystem::path& key) std::optional<registry_key> registry_manager::get_key(const std::filesystem::path& key)
{ {
const auto normal_key = this->normalize_path(key); const auto normal_key = this->normalize_path(key);
if (is_subpath(normal_key, "\\registry\\machine")) if (is_subpath(normal_key, "\\registry\\machine"))
{ {
registry_key reg_key{}; registry_key reg_key{};
reg_key.hive = normal_key; reg_key.hive = normal_key;
return {std::move(reg_key)}; return {std::move(reg_key)};
} }
const auto iterator = this->find_hive(normal_key); const auto iterator = this->find_hive(normal_key);
if (iterator == this->hives_.end()) if (iterator == this->hives_.end())
{ {
return {}; return {};
} }
registry_key reg_key{}; registry_key reg_key{};
reg_key.hive = iterator->first; reg_key.hive = iterator->first;
reg_key.path = normal_key.lexically_relative(reg_key.hive); reg_key.path = normal_key.lexically_relative(reg_key.hive);
if (reg_key.path.empty()) if (reg_key.path.empty())
{ {
return {std::move(reg_key)}; return {std::move(reg_key)};
} }
const auto entry = iterator->second->get_sub_key(reg_key.path); const auto entry = iterator->second->get_sub_key(reg_key.path);
if (!entry) if (!entry)
{ {
return std::nullopt; return std::nullopt;
} }
return {std::move(reg_key)}; return {std::move(reg_key)};
} }
std::optional<registry_value> registry_manager::get_value(const registry_key& key, std::string name) std::optional<registry_value> registry_manager::get_value(const registry_key& key, std::string name)
{ {
utils::string::to_lower_inplace(name); utils::string::to_lower_inplace(name);
const auto iterator = this->hives_.find(key.hive); const auto iterator = this->hives_.find(key.hive);
if (iterator == this->hives_.end()) if (iterator == this->hives_.end())
{ {
return std::nullopt; return std::nullopt;
} }
auto* entry = iterator->second->get_value(key.path, name); auto* entry = iterator->second->get_value(key.path, name);
if (!entry) if (!entry)
{ {
return std::nullopt; return std::nullopt;
} }
registry_value v{}; registry_value v{};
v.type = entry->type; v.type = entry->type;
v.name = entry->name; v.name = entry->name;
v.data = entry->data; v.data = entry->data;
return v; return v;
} }
registry_manager::hive_map::iterator registry_manager::find_hive(const std::filesystem::path& key) registry_manager::hive_map::iterator registry_manager::find_hive(const std::filesystem::path& key)
{ {
for (auto i = this->hives_.begin(); i != this->hives_.end(); ++i) for (auto i = this->hives_.begin(); i != this->hives_.end(); ++i)
{ {
if (is_subpath(i->first, key)) if (is_subpath(i->first, key))
{ {
return i; return i;
} }
} }
return this->hives_.end(); return this->hives_.end();
} }

View File

@@ -6,61 +6,60 @@
struct registry_key struct registry_key
{ {
std::filesystem::path hive{}; std::filesystem::path hive{};
std::filesystem::path path{}; std::filesystem::path path{};
void serialize(utils::buffer_serializer& buffer) const void serialize(utils::buffer_serializer& buffer) const
{ {
buffer.write(this->hive); buffer.write(this->hive);
buffer.write(this->path); buffer.write(this->path);
} }
void deserialize(utils::buffer_deserializer& buffer) void deserialize(utils::buffer_deserializer& buffer)
{ {
buffer.read(this->hive); buffer.read(this->hive);
buffer.read(this->path); buffer.read(this->path);
} }
}; };
struct registry_value struct registry_value
{ {
uint32_t type; uint32_t type;
std::string_view name; std::string_view name;
std::span<const std::byte> data; std::span<const std::byte> data;
}; };
class registry_manager class registry_manager
{ {
public: public:
using hive_ptr = std::unique_ptr<hive_parser>; using hive_ptr = std::unique_ptr<hive_parser>;
using hive_map = std::unordered_map<std::filesystem::path, hive_ptr>; using hive_map = std::unordered_map<std::filesystem::path, hive_ptr>;
registry_manager(); registry_manager();
registry_manager(const std::filesystem::path& hive_path); registry_manager(const std::filesystem::path& hive_path);
~registry_manager(); ~registry_manager();
registry_manager(registry_manager&&) noexcept; registry_manager(registry_manager&&) noexcept;
registry_manager& operator=(registry_manager&&) noexcept; registry_manager& operator=(registry_manager&&) noexcept;
registry_manager(const registry_manager&) = delete; registry_manager(const registry_manager&) = delete;
registry_manager& operator=(const registry_manager&) = delete; registry_manager& operator=(const registry_manager&) = delete;
void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer);
void serialize(utils::buffer_serializer& buffer) const; std::optional<registry_key> get_key(const std::filesystem::path& key);
void deserialize(utils::buffer_deserializer& buffer); std::optional<registry_value> get_value(const registry_key& key, std::string name);
std::optional<registry_key> get_key(const std::filesystem::path& key); private:
std::optional<registry_value> get_value(const registry_key& key, std::string name); std::filesystem::path hive_path_{};
hive_map hives_{};
std::unordered_map<std::filesystem::path, std::filesystem::path> path_mapping_{};
private: std::filesystem::path normalize_path(const std::filesystem::path& path) const;
std::filesystem::path hive_path_{}; void add_path_mapping(const std::filesystem::path& key, const std::filesystem::path& value);
hive_map hives_{};
std::unordered_map<std::filesystem::path, std::filesystem::path> path_mapping_{};
std::filesystem::path normalize_path(const std::filesystem::path& path) const; hive_map::iterator find_hive(const std::filesystem::path& key);
void add_path_mapping(const std::filesystem::path& key, const std::filesystem::path& value);
hive_map::iterator find_hive(const std::filesystem::path& key); void setup();
void setup();
}; };

View File

@@ -2,32 +2,32 @@
#ifdef _WIN32 #ifdef _WIN32
#pragma warning(push) #pragma warning(push)
#pragma warning(disable: 4005) #pragma warning(disable : 4005)
#pragma warning(disable: 4127) #pragma warning(disable : 4127)
#pragma warning(disable: 4201) #pragma warning(disable : 4201)
#pragma warning(disable: 4244) #pragma warning(disable : 4244)
#pragma warning(disable: 4245) #pragma warning(disable : 4245)
#pragma warning(disable: 4324) #pragma warning(disable : 4324)
#pragma warning(disable: 4458) #pragma warning(disable : 4458)
#pragma warning(disable: 4471) #pragma warning(disable : 4471)
#pragma warning(disable: 4505) #pragma warning(disable : 4505)
#pragma warning(disable: 4702) #pragma warning(disable : 4702)
#pragma warning(disable: 4996) #pragma warning(disable : 4996)
#pragma warning(disable: 5054) #pragma warning(disable : 5054)
#pragma warning(disable: 6011) #pragma warning(disable : 6011)
#pragma warning(disable: 6297) #pragma warning(disable : 6297)
#pragma warning(disable: 6385) #pragma warning(disable : 6385)
#pragma warning(disable: 6386) #pragma warning(disable : 6386)
#pragma warning(disable: 6387) #pragma warning(disable : 6387)
#pragma warning(disable: 26110) #pragma warning(disable : 26110)
#pragma warning(disable: 26451) #pragma warning(disable : 26451)
#pragma warning(disable: 26444) #pragma warning(disable : 26444)
#pragma warning(disable: 26451) #pragma warning(disable : 26451)
#pragma warning(disable: 26489) #pragma warning(disable : 26489)
#pragma warning(disable: 26495) #pragma warning(disable : 26495)
#pragma warning(disable: 26498) #pragma warning(disable : 26498)
#pragma warning(disable: 26812) #pragma warning(disable : 26812)
#pragma warning(disable: 28020) #pragma warning(disable : 28020)
#pragma warning(pop) #pragma warning(pop)
#endif #endif

View File

@@ -3,145 +3,138 @@
static void serialize(utils::buffer_serializer& buffer, const syscall_handler_entry& obj) static void serialize(utils::buffer_serializer& buffer, const syscall_handler_entry& obj)
{ {
buffer.write(obj.name); buffer.write(obj.name);
} }
static void deserialize(utils::buffer_deserializer& buffer, syscall_handler_entry& obj) static void deserialize(utils::buffer_deserializer& buffer, syscall_handler_entry& obj)
{ {
buffer.read(obj.name); buffer.read(obj.name);
obj.handler = nullptr; obj.handler = nullptr;
} }
void syscall_dispatcher::serialize(utils::buffer_serializer& buffer) const void syscall_dispatcher::serialize(utils::buffer_serializer& buffer) const
{ {
buffer.write_map(this->handlers_); buffer.write_map(this->handlers_);
} }
void syscall_dispatcher::deserialize(utils::buffer_deserializer& buffer) void syscall_dispatcher::deserialize(utils::buffer_deserializer& buffer)
{ {
buffer.read_map(this->handlers_); buffer.read_map(this->handlers_);
this->add_handlers(); this->add_handlers();
} }
void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data, void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data) const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data)
{ {
this->handlers_ = {}; this->handlers_ = {};
const auto ntdll_syscalls = find_syscalls(ntdll_exports, ntdll_data); const auto ntdll_syscalls = find_syscalls(ntdll_exports, ntdll_data);
const auto win32u_syscalls = find_syscalls(win32u_exports, win32u_data); const auto win32u_syscalls = find_syscalls(win32u_exports, win32u_data);
map_syscalls(this->handlers_, ntdll_syscalls); map_syscalls(this->handlers_, ntdll_syscalls);
map_syscalls(this->handlers_, win32u_syscalls); map_syscalls(this->handlers_, win32u_syscalls);
this->add_handlers(); this->add_handlers();
} }
void syscall_dispatcher::add_handlers() void syscall_dispatcher::add_handlers()
{ {
std::map<std::string, syscall_handler> handler_mapping{}; std::map<std::string, syscall_handler> handler_mapping{};
syscall_dispatcher::add_handlers(handler_mapping); syscall_dispatcher::add_handlers(handler_mapping);
for (auto& entry : this->handlers_) for (auto& entry : this->handlers_)
{ {
const auto handler = handler_mapping.find(entry.second.name); const auto handler = handler_mapping.find(entry.second.name);
if (handler == handler_mapping.end()) if (handler == handler_mapping.end())
{ {
continue; continue;
} }
entry.second.handler = handler->second; entry.second.handler = handler->second;
#ifndef NDEBUG #ifndef NDEBUG
handler_mapping.erase(handler); handler_mapping.erase(handler);
#endif #endif
} }
} }
void syscall_dispatcher::dispatch(windows_emulator& win_emu) void syscall_dispatcher::dispatch(windows_emulator& win_emu)
{ {
auto& emu = win_emu.emu(); auto& emu = win_emu.emu();
auto& context = win_emu.process(); auto& context = win_emu.process();
const auto address = emu.read_instruction_pointer(); const auto address = emu.read_instruction_pointer();
const auto syscall_id = emu.reg<uint32_t>(x64_register::eax); const auto syscall_id = emu.reg<uint32_t>(x64_register::eax);
const syscall_context c{win_emu, emu, context, true};
const syscall_context c{win_emu, emu, context, true}; try
{
const auto entry = this->handlers_.find(syscall_id);
if (entry == this->handlers_.end())
{
printf("Unknown syscall: 0x%X\n", syscall_id);
c.emu.reg<uint64_t>(x64_register::rax, STATUS_NOT_SUPPORTED);
c.emu.stop();
return;
}
try if (!entry->second.handler)
{ {
const auto entry = this->handlers_.find(syscall_id); printf("Unimplemented syscall: %s - 0x%X\n", entry->second.name.c_str(), syscall_id);
if (entry == this->handlers_.end()) c.emu.reg<uint64_t>(x64_register::rax, STATUS_NOT_SUPPORTED);
{ c.emu.stop();
printf("Unknown syscall: 0x%X\n", syscall_id); return;
c.emu.reg<uint64_t>(x64_register::rax, STATUS_NOT_SUPPORTED); }
c.emu.stop();
return;
}
if (!entry->second.handler) const auto* mod = context.mod_manager.find_by_address(address);
{ if (mod != context.ntdll && mod != context.win32u)
printf("Unimplemented syscall: %s - 0x%X\n", entry->second.name.c_str(), syscall_id); {
c.emu.reg<uint64_t>(x64_register::rax, STATUS_NOT_SUPPORTED); win_emu.log.print(color::blue, "Executing inline syscall: %s (0x%X) at 0x%" PRIx64 " (%s)\n",
c.emu.stop(); entry->second.name.c_str(), syscall_id, address, mod ? mod->name.c_str() : "<N/A>");
return; }
} else
{
if (mod->is_within(context.previous_ip))
{
const auto rsp = c.emu.read_stack_pointer();
const auto return_address = c.emu.read_memory<uint64_t>(rsp);
const auto* mod_name = context.mod_manager.find_name(return_address);
const auto* mod = context.mod_manager.find_by_address(address); win_emu.log.print(color::dark_gray,
if (mod != context.ntdll && mod != context.win32u) "Executing syscall: %s (0x%X) at 0x%" PRIx64 " via 0x%" PRIx64 " (%s)\n",
{ entry->second.name.c_str(), syscall_id, address, return_address, mod_name);
win_emu.log.print(color::blue, "Executing inline syscall: %s (0x%X) at 0x%" PRIx64 " (%s)\n", }
entry->second.name.c_str(), else
syscall_id, {
address, mod ? mod->name.c_str() : "<N/A>"); const auto* previous_mod = context.mod_manager.find_by_address(context.previous_ip);
} win_emu.log.print(color::blue,
else "Crafted out-of-line syscall: %s (0x%X) at 0x%" PRIx64 " (%s) via 0x%" PRIx64
{ " (%s)\n",
if (mod->is_within(context.previous_ip)) entry->second.name.c_str(), syscall_id, address, mod ? mod->name.c_str() : "<N/A>",
{ context.previous_ip, previous_mod ? previous_mod->name.c_str() : "<N/A>");
const auto rsp = c.emu.read_stack_pointer(); }
const auto return_address = c.emu.read_memory<uint64_t>(rsp); }
const auto* mod_name = context.mod_manager.find_name(return_address);
win_emu.log.print(color::dark_gray, entry->second.handler(c);
"Executing syscall: %s (0x%X) at 0x%" PRIx64 " via 0x%" PRIx64 " (%s)\n", }
entry->second.name.c_str(), catch (std::exception& e)
syscall_id, address, return_address, mod_name); {
} printf("Syscall threw an exception: %X (0x%" PRIx64 ") - %s\n", syscall_id, address, e.what());
else emu.reg<uint64_t>(x64_register::rax, STATUS_UNSUCCESSFUL);
{ emu.stop();
const auto* previous_mod = context.mod_manager.find_by_address(context.previous_ip); }
win_emu.log.print(color::blue, catch (...)
"Crafted out-of-line syscall: %s (0x%X) at 0x%" PRIx64 " (%s) via 0x%" PRIx64 {
" (%s)\n", printf("Syscall threw an unknown exception: %X (0x%" PRIx64 ")\n", syscall_id, address);
entry->second.name.c_str(), emu.reg<uint64_t>(x64_register::rax, STATUS_UNSUCCESSFUL);
syscall_id, emu.stop();
address, mod ? mod->name.c_str() : "<N/A>", context.previous_ip, }
previous_mod ? previous_mod->name.c_str() : "<N/A>");
}
}
entry->second.handler(c);
}
catch (std::exception& e)
{
printf("Syscall threw an exception: %X (0x%" PRIx64 ") - %s\n", syscall_id, address, e.what());
emu.reg<uint64_t>(x64_register::rax, STATUS_UNSUCCESSFUL);
emu.stop();
}
catch (...)
{
printf("Syscall threw an unknown exception: %X (0x%" PRIx64 ")\n", syscall_id, address);
emu.reg<uint64_t>(x64_register::rax, STATUS_UNSUCCESSFUL);
emu.stop();
}
} }
syscall_dispatcher::syscall_dispatcher(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data, syscall_dispatcher::syscall_dispatcher(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data) const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data)
{ {
this->setup(ntdll_exports, ntdll_data, win32u_exports, win32u_data); this->setup(ntdll_exports, ntdll_data, win32u_exports, win32u_data);
} }

View File

@@ -3,39 +3,39 @@
#include "process_context.hpp" #include "process_context.hpp"
struct syscall_context; struct syscall_context;
using syscall_handler = void(*)(const syscall_context& c); using syscall_handler = void (*)(const syscall_context& c);
struct syscall_handler_entry struct syscall_handler_entry
{ {
syscall_handler handler{}; syscall_handler handler{};
std::string name{}; std::string name{};
}; };
class windows_emulator; class windows_emulator;
class syscall_dispatcher class syscall_dispatcher
{ {
public: public:
syscall_dispatcher() = default; syscall_dispatcher() = default;
syscall_dispatcher(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data, syscall_dispatcher(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data); const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data);
void dispatch(windows_emulator& win_emu); void dispatch(windows_emulator& win_emu);
void serialize(utils::buffer_serializer& buffer) const; void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer); void deserialize(utils::buffer_deserializer& buffer);
void setup(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data, void setup(const exported_symbols& ntdll_exports, std::span<const std::byte> ntdll_data,
const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data); const exported_symbols& win32u_exports, std::span<const std::byte> win32u_data);
std::string get_syscall_name(const uint64_t id) std::string get_syscall_name(const uint64_t id)
{ {
return this->handlers_.at(id).name; return this->handlers_.at(id).name;
} }
private: private:
std::map<uint64_t, syscall_handler_entry> handlers_{}; std::map<uint64_t, syscall_handler_entry> handlers_{};
static void add_handlers(std::map<std::string, syscall_handler>& handler_mapping); static void add_handlers(std::map<std::string, syscall_handler>& handler_mapping);
void add_handlers(); void add_handlers();
}; };

View File

@@ -5,202 +5,193 @@
struct syscall_context struct syscall_context
{ {
windows_emulator& win_emu; windows_emulator& win_emu;
x64_emulator& emu; x64_emulator& emu;
process_context& proc; process_context& proc;
mutable bool write_status{true}; mutable bool write_status{true};
mutable bool retrigger_syscall{false}; mutable bool retrigger_syscall{false};
}; };
inline uint64_t get_syscall_argument(x64_emulator& emu, const size_t index) inline uint64_t get_syscall_argument(x64_emulator& emu, const size_t index)
{ {
switch (index) switch (index)
{ {
case 0: case 0:
return emu.reg(x64_register::r10); return emu.reg(x64_register::r10);
case 1: case 1:
return emu.reg(x64_register::rdx); return emu.reg(x64_register::rdx);
case 2: case 2:
return emu.reg(x64_register::r8); return emu.reg(x64_register::r8);
case 3: case 3:
return emu.reg(x64_register::r9); return emu.reg(x64_register::r9);
default: default:
return emu.read_stack(index + 1); return emu.read_stack(index + 1);
} }
} }
inline bool is_uppercase(const char character) inline bool is_uppercase(const char character)
{ {
return toupper(character) == character; return toupper(character) == character;
} }
inline bool is_syscall(const std::string_view name) inline bool is_syscall(const std::string_view name)
{ {
return name.starts_with("Nt") && name.size() > 3 && is_uppercase(name[2]); return name.starts_with("Nt") && name.size() > 3 && is_uppercase(name[2]);
} }
inline std::optional<uint32_t> extract_syscall_id(const exported_symbol& symbol, std::span<const std::byte> data) inline std::optional<uint32_t> extract_syscall_id(const exported_symbol& symbol, std::span<const std::byte> data)
{ {
if (!is_syscall(symbol.name)) if (!is_syscall(symbol.name))
{ {
return std::nullopt; return std::nullopt;
} }
constexpr auto instruction_size = 5; constexpr auto instruction_size = 5;
constexpr auto instruction_offset = 3; constexpr auto instruction_offset = 3;
constexpr auto instruction_operand_offset = 1; constexpr auto instruction_operand_offset = 1;
constexpr auto instruction_opcode = static_cast<std::byte>(0xB8); constexpr auto instruction_opcode = static_cast<std::byte>(0xB8);
const auto instruction_rva = symbol.rva + instruction_offset; const auto instruction_rva = symbol.rva + instruction_offset;
if (data.size() < (instruction_rva + instruction_size) || data[instruction_rva] != instruction_opcode) if (data.size() < (instruction_rva + instruction_size) || data[instruction_rva] != instruction_opcode)
{ {
return std::nullopt; return std::nullopt;
} }
uint32_t syscall_id{0}; uint32_t syscall_id{0};
static_assert(sizeof(syscall_id) <= (instruction_size - instruction_operand_offset)); static_assert(sizeof(syscall_id) <= (instruction_size - instruction_operand_offset));
memcpy(&syscall_id, data.data() + instruction_rva + instruction_operand_offset, sizeof(syscall_id)); memcpy(&syscall_id, data.data() + instruction_rva + instruction_operand_offset, sizeof(syscall_id));
return syscall_id; return syscall_id;
} }
inline std::map<uint64_t, std::string> find_syscalls(const exported_symbols& exports, std::span<const std::byte> data) inline std::map<uint64_t, std::string> find_syscalls(const exported_symbols& exports, std::span<const std::byte> data)
{ {
std::map<uint64_t, std::string> syscalls{}; std::map<uint64_t, std::string> syscalls{};
for (const auto& symbol : exports) for (const auto& symbol : exports)
{ {
const auto id = extract_syscall_id(symbol, data); const auto id = extract_syscall_id(symbol, data);
if (id) if (id)
{ {
auto& entry = syscalls[*id]; auto& entry = syscalls[*id];
if (!entry.empty()) if (!entry.empty())
{ {
throw std::runtime_error( throw std::runtime_error("Syscall with id " + std::to_string(*id) + ", which is mapping to " +
"Syscall with id " + std::to_string(*id) + ", which is mapping to " + symbol.name + symbol.name + ", was already mapped to " + entry);
", was already mapped to " + entry); }
}
entry = symbol.name; entry = symbol.name;
} }
} }
return syscalls; return syscalls;
} }
inline void map_syscalls(std::map<uint64_t, syscall_handler_entry>& handlers, inline void map_syscalls(std::map<uint64_t, syscall_handler_entry>& handlers, std::map<uint64_t, std::string> syscalls)
std::map<uint64_t, std::string> syscalls)
{ {
for (auto& [id, name] : syscalls) for (auto& [id, name] : syscalls)
{ {
auto& entry = handlers[id]; auto& entry = handlers[id];
if (!entry.name.empty()) if (!entry.name.empty())
{ {
throw std::runtime_error( throw std::runtime_error("Syscall with id " + std::to_string(id) + ", which is mapping to " + name +
"Syscall with id " + std::to_string(id) + ", which is mapping to " + name + ", was previously mapped to " + entry.name);
", was previously mapped to " + entry.name); }
}
entry.name = std::move(name); entry.name = std::move(name);
entry.handler = nullptr; entry.handler = nullptr;
} }
} }
template <typename T> template <typename T>
requires(std::is_integral_v<T> || std::is_enum_v<T>) requires(std::is_integral_v<T> || std::is_enum_v<T>)
T resolve_argument(x64_emulator& emu, const size_t index) T resolve_argument(x64_emulator& emu, const size_t index)
{ {
const auto arg = get_syscall_argument(emu, index); const auto arg = get_syscall_argument(emu, index);
return static_cast<T>(arg); return static_cast<T>(arg);
} }
template <typename T> template <typename T>
requires(std::is_same_v<std::remove_cvref_t<T>, handle>) requires(std::is_same_v<std::remove_cvref_t<T>, handle>)
handle resolve_argument(x64_emulator& emu, const size_t index) handle resolve_argument(x64_emulator& emu, const size_t index)
{ {
handle h{}; handle h{};
h.bits = resolve_argument<uint64_t>(emu, index); h.bits = resolve_argument<uint64_t>(emu, index);
return h; return h;
} }
template <typename T> template <typename T>
requires(std::is_same_v<T, emulator_object<typename T::value_type>>) requires(std::is_same_v<T, emulator_object<typename T::value_type>>)
T resolve_argument(x64_emulator& emu, const size_t index) T resolve_argument(x64_emulator& emu, const size_t index)
{ {
const auto arg = get_syscall_argument(emu, index); const auto arg = get_syscall_argument(emu, index);
return T(emu, arg); return T(emu, arg);
} }
template <typename T> template <typename T>
T resolve_indexed_argument(x64_emulator& emu, size_t& index) T resolve_indexed_argument(x64_emulator& emu, size_t& index)
{ {
return resolve_argument<T>(emu, index++); return resolve_argument<T>(emu, index++);
} }
inline void write_status(const syscall_context& c, const NTSTATUS status, const uint64_t initial_ip) inline void write_status(const syscall_context& c, const NTSTATUS status, const uint64_t initial_ip)
{ {
if (c.write_status && !c.retrigger_syscall) if (c.write_status && !c.retrigger_syscall)
{ {
c.emu.reg<uint64_t>(x64_register::rax, static_cast<uint64_t>(status)); c.emu.reg<uint64_t>(x64_register::rax, static_cast<uint64_t>(status));
} }
const auto new_ip = c.emu.read_instruction_pointer(); const auto new_ip = c.emu.read_instruction_pointer();
if (initial_ip != new_ip || c.retrigger_syscall) if (initial_ip != new_ip || c.retrigger_syscall)
{ {
c.emu.reg(x64_register::rip, new_ip - 2); c.emu.reg(x64_register::rip, new_ip - 2);
} }
} }
inline void forward_syscall(const syscall_context& c, NTSTATUS (*handler)()) inline void forward_syscall(const syscall_context& c, NTSTATUS (*handler)())
{ {
const auto ip = c.emu.read_instruction_pointer(); const auto ip = c.emu.read_instruction_pointer();
const auto ret = handler(); const auto ret = handler();
write_status(c, ret, ip); write_status(c, ret, ip);
} }
template <typename... Args> template <typename... Args>
void forward_syscall(const syscall_context& c, NTSTATUS (*handler)(const syscall_context&, Args...)) void forward_syscall(const syscall_context& c, NTSTATUS (*handler)(const syscall_context&, Args...))
{ {
const auto ip = c.emu.read_instruction_pointer(); const auto ip = c.emu.read_instruction_pointer();
size_t index = 0; size_t index = 0;
std::tuple<const syscall_context&, Args...> func_args std::tuple<const syscall_context&, Args...> func_args{
{ c, resolve_indexed_argument<std::remove_cv_t<std::remove_reference_t<Args>>>(c.emu, index)...};
c,
resolve_indexed_argument<std::remove_cv_t<std::remove_reference_t<Args>>>(c.emu, index)...
};
(void)index; (void)index;
const auto ret = std::apply(handler, std::move(func_args)); const auto ret = std::apply(handler, std::move(func_args));
write_status(c, ret, ip); write_status(c, ret, ip);
} }
template <auto Handler> template <auto Handler>
syscall_handler make_syscall_handler() syscall_handler make_syscall_handler()
{ {
return +[](const syscall_context& c) return +[](const syscall_context& c) { forward_syscall(c, Handler); };
{
forward_syscall(c, Handler);
};
} }
template <typename T, typename Traits> template <typename T, typename Traits>
void write_attribute(emulator& emu, const PS_ATTRIBUTE<Traits>& attribute, const T& value) void write_attribute(emulator& emu, const PS_ATTRIBUTE<Traits>& attribute, const T& value)
{ {
if (attribute.ReturnLength) if (attribute.ReturnLength)
{ {
emulator_object<typename Traits::SIZE_T>{emu, attribute.ReturnLength}.write(sizeof(T)); emulator_object<typename Traits::SIZE_T>{emu, attribute.ReturnLength}.write(sizeof(T));
} }
if (attribute.Size >= sizeof(T)) if (attribute.Size >= sizeof(T))
{ {
emulator_object<T>{emu, attribute.Value}.write(value); emulator_object<T>{emu, attribute.Value}.write(value);
} }
} }
constexpr auto HUNDRED_NANOSECONDS_IN_ONE_SECOND = 10000000LL; constexpr auto HUNDRED_NANOSECONDS_IN_ONE_SECOND = 10000000LL;
@@ -209,67 +200,65 @@ constexpr auto WINDOWS_EPOCH_DIFFERENCE = EPOCH_DIFFERENCE_1601_TO_1970_SECONDS
inline std::chrono::steady_clock::time_point convert_delay_interval_to_time_point(const LARGE_INTEGER delay_interval) inline std::chrono::steady_clock::time_point convert_delay_interval_to_time_point(const LARGE_INTEGER delay_interval)
{ {
if (delay_interval.QuadPart <= 0) if (delay_interval.QuadPart <= 0)
{ {
const auto relative_time = -delay_interval.QuadPart; const auto relative_time = -delay_interval.QuadPart;
const auto relative_ticks_in_ms = relative_time / 10; const auto relative_ticks_in_ms = relative_time / 10;
const auto relative_fraction_ns = (relative_time % 10) * 100; const auto relative_fraction_ns = (relative_time % 10) * 100;
const auto relative_duration = std::chrono::microseconds(relative_ticks_in_ms) + const auto relative_duration =
std::chrono::nanoseconds(relative_fraction_ns); std::chrono::microseconds(relative_ticks_in_ms) + std::chrono::nanoseconds(relative_fraction_ns);
return std::chrono::steady_clock::now() + relative_duration; return std::chrono::steady_clock::now() + relative_duration;
} }
const auto delay_seconds_since_1601 = delay_interval.QuadPart / HUNDRED_NANOSECONDS_IN_ONE_SECOND; const auto delay_seconds_since_1601 = delay_interval.QuadPart / HUNDRED_NANOSECONDS_IN_ONE_SECOND;
const auto delay_fraction_ns = (delay_interval.QuadPart % HUNDRED_NANOSECONDS_IN_ONE_SECOND) * 100; const auto delay_fraction_ns = (delay_interval.QuadPart % HUNDRED_NANOSECONDS_IN_ONE_SECOND) * 100;
const auto delay_seconds_since_1970 = delay_seconds_since_1601 - EPOCH_DIFFERENCE_1601_TO_1970_SECONDS; const auto delay_seconds_since_1970 = delay_seconds_since_1601 - EPOCH_DIFFERENCE_1601_TO_1970_SECONDS;
const auto target_time = const auto target_time =
std::chrono::system_clock::from_time_t(delay_seconds_since_1970) + std::chrono::system_clock::from_time_t(delay_seconds_since_1970) + std::chrono::nanoseconds(delay_fraction_ns);
std::chrono::nanoseconds(delay_fraction_ns);
const auto now_system = std::chrono::system_clock::now(); const auto now_system = std::chrono::system_clock::now();
const auto duration_until_target = std::chrono::duration_cast< const auto duration_until_target = std::chrono::duration_cast<std::chrono::microseconds>(target_time - now_system);
std::chrono::microseconds>(target_time - now_system);
return std::chrono::steady_clock::now() + duration_until_target; return std::chrono::steady_clock::now() + duration_until_target;
} }
inline KSYSTEM_TIME convert_to_ksystem_time(const std::chrono::system_clock::time_point& tp) inline KSYSTEM_TIME convert_to_ksystem_time(const std::chrono::system_clock::time_point& tp)
{ {
const auto duration = tp.time_since_epoch(); const auto duration = tp.time_since_epoch();
const auto ns_duration = std::chrono::duration_cast<std::chrono::nanoseconds>(duration); const auto ns_duration = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
const auto total_ticks = ns_duration.count() / 100 + WINDOWS_EPOCH_DIFFERENCE; const auto total_ticks = ns_duration.count() / 100 + WINDOWS_EPOCH_DIFFERENCE;
KSYSTEM_TIME time{}; KSYSTEM_TIME time{};
time.LowPart = static_cast<uint32_t>(total_ticks); time.LowPart = static_cast<uint32_t>(total_ticks);
time.High1Time = static_cast<int32_t>(total_ticks >> 32); time.High1Time = static_cast<int32_t>(total_ticks >> 32);
time.High2Time = time.High1Time; time.High2Time = time.High1Time;
return time; return time;
} }
inline void convert_to_ksystem_time(volatile KSYSTEM_TIME* dest, const std::chrono::system_clock::time_point& tp) inline void convert_to_ksystem_time(volatile KSYSTEM_TIME* dest, const std::chrono::system_clock::time_point& tp)
{ {
const auto time = convert_to_ksystem_time(tp); const auto time = convert_to_ksystem_time(tp);
memcpy(const_cast<KSYSTEM_TIME*>(dest), &time, sizeof(*dest)); memcpy(const_cast<KSYSTEM_TIME*>(dest), &time, sizeof(*dest));
} }
inline std::chrono::system_clock::time_point convert_from_ksystem_time(const KSYSTEM_TIME& time) inline std::chrono::system_clock::time_point convert_from_ksystem_time(const KSYSTEM_TIME& time)
{ {
auto totalTicks = (static_cast<int64_t>(time.High1Time) << 32) | time.LowPart; auto totalTicks = (static_cast<int64_t>(time.High1Time) << 32) | time.LowPart;
totalTicks -= WINDOWS_EPOCH_DIFFERENCE; totalTicks -= WINDOWS_EPOCH_DIFFERENCE;
const auto duration = std::chrono::system_clock::duration(totalTicks * 100); const auto duration = std::chrono::system_clock::duration(totalTicks * 100);
return std::chrono::system_clock::time_point(duration); return std::chrono::system_clock::time_point(duration);
} }
inline std::chrono::system_clock::time_point convert_from_ksystem_time(const volatile KSYSTEM_TIME& time) inline std::chrono::system_clock::time_point convert_from_ksystem_time(const volatile KSYSTEM_TIME& time)
{ {
return convert_from_ksystem_time(*const_cast<const KSYSTEM_TIME*>(&time)); return convert_from_ksystem_time(*const_cast<const KSYSTEM_TIME*>(&time));
} }
#ifndef OS_WINDOWS #ifndef OS_WINDOWS
@@ -278,7 +267,7 @@ using __time64_t = int64_t;
inline LARGE_INTEGER convert_unix_to_windows_time(const __time64_t unix_time) inline LARGE_INTEGER convert_unix_to_windows_time(const __time64_t unix_time)
{ {
LARGE_INTEGER windows_time{}; LARGE_INTEGER windows_time{};
windows_time.QuadPart = (unix_time + EPOCH_DIFFERENCE_1601_TO_1970_SECONDS) * HUNDRED_NANOSECONDS_IN_ONE_SECOND; windows_time.QuadPart = (unix_time + EPOCH_DIFFERENCE_1601_TO_1970_SECONDS) * HUNDRED_NANOSECONDS_IN_ONE_SECOND;
return windows_time; return windows_time;
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -12,120 +12,119 @@ std::unique_ptr<x64_emulator> create_default_x64_emulator();
// TODO: Split up into application and emulator settings // TODO: Split up into application and emulator settings
struct emulator_settings struct emulator_settings
{ {
std::filesystem::path application{}; std::filesystem::path application{};
std::filesystem::path working_directory{}; std::filesystem::path working_directory{};
std::filesystem::path registry_directory{"./registry"}; std::filesystem::path registry_directory{"./registry"};
std::vector<std::u16string> arguments{}; std::vector<std::u16string> arguments{};
std::function<void(std::string_view)> stdout_callback{}; std::function<void(std::string_view)> stdout_callback{};
bool disable_logging{false}; bool disable_logging{false};
bool silent_until_main{false}; bool silent_until_main{false};
bool use_relative_time{false}; bool use_relative_time{false};
}; };
class windows_emulator class windows_emulator
{ {
public: public:
windows_emulator(std::unique_ptr<x64_emulator> emu = create_default_x64_emulator()); windows_emulator(std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
windows_emulator(emulator_settings settings, windows_emulator(emulator_settings settings, std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
windows_emulator(windows_emulator&&) = delete; windows_emulator(windows_emulator&&) = delete;
windows_emulator(const windows_emulator&) = delete; windows_emulator(const windows_emulator&) = delete;
windows_emulator& operator=(windows_emulator&&) = delete; windows_emulator& operator=(windows_emulator&&) = delete;
windows_emulator& operator=(const windows_emulator&) = delete; windows_emulator& operator=(const windows_emulator&) = delete;
~windows_emulator() = default; ~windows_emulator() = default;
x64_emulator& emu() x64_emulator& emu()
{ {
return *this->emu_; return *this->emu_;
} }
const x64_emulator& emu() const const x64_emulator& emu() const
{ {
return *this->emu_; return *this->emu_;
} }
process_context& process() process_context& process()
{ {
return this->process_; return this->process_;
} }
const process_context& process() const const process_context& process() const
{ {
return this->process_; return this->process_;
} }
syscall_dispatcher& dispatcher() syscall_dispatcher& dispatcher()
{ {
return this->dispatcher_; return this->dispatcher_;
} }
const syscall_dispatcher& dispatcher() const const syscall_dispatcher& dispatcher() const
{ {
return this->dispatcher_; return this->dispatcher_;
} }
emulator_thread& current_thread() const emulator_thread& current_thread() const
{ {
if (!this->process_.active_thread) if (!this->process_.active_thread)
{ {
throw std::runtime_error("No active thread!"); throw std::runtime_error("No active thread!");
} }
return *this->process_.active_thread; return *this->process_.active_thread;
} }
void start(std::chrono::nanoseconds timeout = {}, size_t count = 0); void start(std::chrono::nanoseconds timeout = {}, size_t count = 0);
void serialize(utils::buffer_serializer& buffer) const; void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer); void deserialize(utils::buffer_deserializer& buffer);
void save_snapshot(); void save_snapshot();
void restore_snapshot(); void restore_snapshot();
void add_syscall_hook(instruction_hook_callback callback) void add_syscall_hook(instruction_hook_callback callback)
{ {
this->syscall_hooks_.push_back(std::move(callback)); this->syscall_hooks_.push_back(std::move(callback));
} }
void on_stdout(const std::string_view data) const void on_stdout(const std::string_view data) const
{ {
if (this->stdout_callback_) if (this->stdout_callback_)
{ {
this->stdout_callback_(data); this->stdout_callback_(data);
} }
} }
logger log{}; logger log{};
bool verbose{false}; bool verbose{false};
bool verbose_calls{false}; bool verbose_calls{false};
bool buffer_stdout{false}; bool buffer_stdout{false};
bool fuzzing{false}; bool fuzzing{false};
bool switch_thread{false}; bool switch_thread{false};
void yield_thread(); void yield_thread();
void perform_thread_switch(); void perform_thread_switch();
bool time_is_relative() const bool time_is_relative() const
{ {
return this->use_relative_time_; return this->use_relative_time_;
} }
private: private:
bool use_relative_time_{false}; bool use_relative_time_{false};
bool silent_until_main_{false}; bool silent_until_main_{false};
std::unique_ptr<x64_emulator> emu_{}; std::unique_ptr<x64_emulator> emu_{};
std::vector<instruction_hook_callback> syscall_hooks_{}; std::vector<instruction_hook_callback> syscall_hooks_{};
std::function<void(std::string_view)> stdout_callback_{}; std::function<void(std::string_view)> stdout_callback_{};
process_context process_; process_context process_;
syscall_dispatcher dispatcher_; syscall_dispatcher dispatcher_;
std::vector<std::byte> process_snapshot_{}; std::vector<std::byte> process_snapshot_{};
//std::optional<process_context> process_snapshot_{}; // std::optional<process_context> process_snapshot_{};
void setup_hooks(); void setup_hooks();
void setup_process(const emulator_settings& settings); void setup_process(const emulator_settings& settings);
void on_instruction_execution(uint64_t address); void on_instruction_execution(uint64_t address);
}; };