Merge branch 'unicorn-upgrade-2' into unhandled-exceptions

This commit is contained in:
Maurice Heumann
2025-07-13 20:47:01 +02:00
245 changed files with 26970 additions and 2348 deletions

View File

@@ -1,26 +0,0 @@
# Format Style Options - Created with Clang Power Tools
---
BasedOnStyle: Microsoft
AlwaysBreakTemplateDeclarations: Yes
BreakConstructorInitializers: BeforeColon
DerivePointerAlignment: false
FixNamespaceComments: false
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
PointerAlignment: Left
SortIncludes: false
AlignEscapedNewlines: Left
PackConstructorInitializers: Never
IndentPPDirectives: None
AlignConsecutiveMacros:
Enabled: true
AcrossEmptyLines: true
AcrossComments: true
AlignCompound: true
PadOperators: true
BraceWrapping:
AfterStruct: true
AfterUnion: true
...

View File

@@ -3,16 +3,18 @@ add_subdirectory(emulator)
add_subdirectory(gdb-stub)
add_subdirectory(windows-emulator)
add_subdirectory(windows-gdb-stub)
add_subdirectory(backend-selection)
momo_add_subdirectory_and_get_targets("backends" BACKEND_TARGETS)
momo_targets_set_folder("backends" ${BACKEND_TARGETS})
if (NOT MOMO_BUILD_AS_LIBRARY)
add_subdirectory(analyzer)
add_subdirectory(debugger)
add_subdirectory(fuzzing-engine)
add_subdirectory(fuzzer)
add_subdirectory(windows-emulator-test)
if(WIN32)
if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8)
momo_add_subdirectory_and_get_targets("tools" TOOL_TARGETS)
momo_targets_set_folder("tools" ${TOOL_TARGETS})

View File

@@ -16,8 +16,10 @@ endif()
target_link_libraries(analyzer PRIVATE
reflect
debugger
windows-emulator
windows-gdb-stub
backend-selection
)
set_property(GLOBAL PROPERTY VS_STARTUP_PROJECT analyzer)

341
src/analyzer/analysis.cpp Normal file
View File

@@ -0,0 +1,341 @@
#include "std_include.hpp"
#include "analysis.hpp"
#include "windows_emulator.hpp"
#include <utils/lazy_object.hpp>
#ifdef OS_EMSCRIPTEN
#include <event_handler.hpp>
#endif
#define STR_VIEW_VA(str) static_cast<int>((str).size()), (str).data()
namespace
{
template <typename Return, typename... Args>
std::function<Return(Args...)> make_callback(analysis_context& c, Return (*callback)(analysis_context&, Args...))
{
return [&c, callback](Args... args) {
return callback(c, std::forward<Args>(args)...); //
};
}
template <typename Return, typename... Args>
std::function<Return(Args...)> make_callback(analysis_context& c,
Return (*callback)(const analysis_context&, Args...))
{
return [&c, callback](Args... args) {
return callback(c, std::forward<Args>(args)...); //
};
}
void handle_suspicious_activity(const analysis_context& c, const std::string_view details)
{
const auto rip = c.win_emu->emu().read_instruction_pointer();
c.win_emu->log.print(color::pink, "Suspicious: %.*s at 0x%" PRIx64 " (via 0x%" PRIx64 ")\n",
STR_VIEW_VA(details), rip, c.win_emu->process.previous_ip);
}
void handle_generic_activity(const analysis_context& c, const std::string_view details)
{
c.win_emu->log.print(color::dark_gray, "%.*s\n", STR_VIEW_VA(details));
}
void handle_generic_access(const analysis_context& c, const std::string_view type, const std::u16string_view name)
{
c.win_emu->log.print(color::dark_gray, "--> %.*s: %s\n", STR_VIEW_VA(type), u16_to_u8(name).c_str()); //
}
void handle_memory_allocate(const analysis_context& c, const uint64_t address, const uint64_t length,
const memory_permission permission, const bool commit)
{
const auto* action = commit ? "Committed" : "Allocated";
c.win_emu->log.print(is_executable(permission) ? color::gray : color::dark_gray,
"--> %s 0x%" PRIx64 " - 0x%" PRIx64 " (%s)\n", action, address, address + length,
get_permission_string(permission).c_str());
}
void handle_memory_protect(const analysis_context& c, const uint64_t address, const uint64_t length,
const memory_permission permission)
{
c.win_emu->log.print(color::dark_gray, "--> Changing protection at 0x%" PRIx64 "-0x%" PRIx64 " to %s\n",
address, address + length, get_permission_string(permission).c_str());
}
void handle_memory_violate(const analysis_context& c, const uint64_t address, const uint64_t size,
const memory_operation operation, const memory_violation_type type)
{
const auto permission = get_permission_string(operation);
const auto ip = c.win_emu->emu().read_instruction_pointer();
const char* name = c.win_emu->mod_manager.find_name(ip);
if (type == memory_violation_type::protection)
{
c.win_emu->log.print(color::gray,
"Protection violation: 0x%" PRIx64 " (%" PRIx64 ") - %s at 0x%" PRIx64 " (%s)\n",
address, size, permission.c_str(), ip, name);
}
else if (type == memory_violation_type::unmapped)
{
c.win_emu->log.print(color::gray,
"Mapping violation: 0x%" PRIx64 " (%" PRIx64 ") - %s at 0x%" PRIx64 " (%s)\n", address,
size, permission.c_str(), ip, name);
}
}
void handle_ioctrl(const analysis_context& c, const io_device&, const std::u16string_view device_name,
const ULONG code)
{
c.win_emu->log.print(color::dark_gray, "--> %s: 0x%X\n", u16_to_u8(device_name).c_str(),
static_cast<uint32_t>(code));
}
void handle_thread_set_name(const analysis_context& c, const emulator_thread& t)
{
c.win_emu->log.print(color::blue, "Setting thread (%d) name: %s\n", t.id, u16_to_u8(t.name).c_str());
}
void handle_thread_switch(const analysis_context& c, const emulator_thread& current_thread,
const emulator_thread& new_thread)
{
c.win_emu->log.print(color::dark_gray, "Performing thread switch: %X -> %X\n", current_thread.id,
new_thread.id);
}
void handle_module_load(const analysis_context& c, const mapped_module& mod)
{
c.win_emu->log.log("Mapped %s at 0x%" PRIx64 "\n", mod.path.generic_string().c_str(), mod.image_base);
}
void handle_module_unload(const analysis_context& c, const mapped_module& mod)
{
c.win_emu->log.log("Unmapping %s (0x%" PRIx64 ")\n", mod.path.generic_string().c_str(), mod.image_base);
}
void print_string(logger& log, const std::string_view str)
{
log.print(color::dark_gray, "--> %.*s\n", STR_VIEW_VA(str));
}
void print_string(logger& log, const std::u16string_view str)
{
print_string(log, u16_to_u8(str));
}
template <typename CharType = char>
void print_arg_as_string(windows_emulator& win_emu, size_t index)
{
const auto var_ptr = get_function_argument(win_emu.emu(), index);
if (var_ptr)
{
const auto str = read_string<CharType>(win_emu.memory, var_ptr);
print_string(win_emu.log, str);
}
}
void handle_function_details(analysis_context& c, const std::string_view function)
{
if (function == "GetEnvironmentVariableA" || function == "ExpandEnvironmentStringsA")
{
print_arg_as_string(*c.win_emu, 0);
}
else if (function == "MessageBoxA")
{
print_arg_as_string(*c.win_emu, 2);
print_arg_as_string(*c.win_emu, 1);
}
else if (function == "MessageBoxW")
{
print_arg_as_string<char16_t>(*c.win_emu, 2);
print_arg_as_string<char16_t>(*c.win_emu, 1);
}
}
void handle_instruction(analysis_context& c, const uint64_t address)
{
auto& win_emu = *c.win_emu;
#ifdef OS_EMSCRIPTEN
if ((win_emu.get_executed_instructions() % 0x20000) == 0)
{
debugger::event_context ec{.win_emu = win_emu};
debugger::handle_events(ec);
}
#endif
const auto is_main_exe = win_emu.mod_manager.executable->is_within(address);
const auto is_previous_main_exe = win_emu.mod_manager.executable->is_within(c.win_emu->process.previous_ip);
const auto binary = utils::make_lazy([&] {
if (is_main_exe)
{
return win_emu.mod_manager.executable;
}
return win_emu.mod_manager.find_by_address(address); //
});
const auto previous_binary = utils::make_lazy([&] {
if (is_previous_main_exe)
{
return win_emu.mod_manager.executable;
}
return win_emu.mod_manager.find_by_address(win_emu.process.previous_ip); //
});
const auto is_in_interesting_module = [&] {
if (c.settings->modules.empty())
{
return false;
}
return (binary && c.settings->modules.contains(binary->name)) ||
(previous_binary && c.settings->modules.contains(previous_binary->name));
};
const auto is_interesting_call = is_previous_main_exe //
|| is_main_exe //
|| is_in_interesting_module();
if (!c.has_reached_main && c.settings->concise_logging && !c.settings->silent && is_main_exe)
{
c.has_reached_main = true;
win_emu.log.disable_output(false);
}
if ((!c.settings->verbose_logging && !is_interesting_call) || !binary)
{
return;
}
const auto export_entry = binary->address_names.find(address);
if (export_entry != binary->address_names.end() &&
!c.settings->ignored_functions.contains(export_entry->second))
{
const auto rsp = win_emu.emu().read_stack_pointer();
uint64_t return_address{};
win_emu.emu().try_read_memory(rsp, &return_address, sizeof(return_address));
const auto* mod_name = win_emu.mod_manager.find_name(return_address);
win_emu.log.print(is_interesting_call ? color::yellow : color::dark_gray,
"Executing function: %s - %s (0x%" PRIx64 ") via (0x%" PRIx64 ") %s\n",
binary->name.c_str(), export_entry->second.c_str(), address, return_address, mod_name);
if (is_interesting_call)
{
handle_function_details(c, export_entry->second);
}
}
else if (address == binary->entry_point)
{
win_emu.log.print(is_interesting_call ? color::yellow : color::gray,
"Executing entry point: %s (0x%" PRIx64 ")\n", binary->name.c_str(), address);
}
}
emulator_callbacks::continuation handle_syscall(const analysis_context& c, const uint32_t syscall_id,
const std::string_view syscall_name)
{
auto& win_emu = *c.win_emu;
auto& emu = win_emu.emu();
const auto address = emu.read_instruction_pointer();
const auto* mod = win_emu.mod_manager.find_by_address(address);
const auto is_sus_module = mod != win_emu.mod_manager.ntdll && mod != win_emu.mod_manager.win32u;
if (is_sus_module)
{
win_emu.log.print(color::blue, "Executing inline syscall: %.*s (0x%X) at 0x%" PRIx64 " (%s)\n",
STR_VIEW_VA(syscall_name), syscall_id, address, mod ? mod->name.c_str() : "<N/A>");
}
else if (mod->is_within(win_emu.process.previous_ip))
{
const auto rsp = emu.read_stack_pointer();
uint64_t return_address{};
emu.try_read_memory(rsp, &return_address, sizeof(return_address));
const auto* caller_mod_name = win_emu.mod_manager.find_name(return_address);
win_emu.log.print(color::dark_gray,
"Executing syscall: %.*s (0x%X) at 0x%" PRIx64 " via 0x%" PRIx64 " (%s)\n",
STR_VIEW_VA(syscall_name), syscall_id, address, return_address, caller_mod_name);
}
else
{
const auto* previous_mod = win_emu.mod_manager.find_by_address(win_emu.process.previous_ip);
win_emu.log.print(color::blue,
"Crafted out-of-line syscall: %.*s (0x%X) at 0x%" PRIx64 " (%s) via 0x%" PRIx64 " (%s)\n",
STR_VIEW_VA(syscall_name), syscall_id, address, mod ? mod->name.c_str() : "<N/A>",
win_emu.process.previous_ip, previous_mod ? previous_mod->name.c_str() : "<N/A>");
}
return instruction_hook_continuation::run_instruction;
}
void handle_stdout(analysis_context& c, const std::string_view data)
{
if (c.settings->silent)
{
(void)fwrite(data.data(), 1, data.size(), stdout);
}
else if (c.settings->buffer_stdout)
{
c.output.append(data);
}
else
{
c.win_emu->log.info("%.*s%s", static_cast<int>(data.size()), data.data(), data.ends_with("\n") ? "" : "\n");
}
}
}
void register_analysis_callbacks(analysis_context& c)
{
auto& cb = c.win_emu->callbacks;
cb.on_stdout = make_callback(c, handle_stdout);
cb.on_syscall = make_callback(c, handle_syscall);
cb.on_ioctrl = make_callback(c, handle_ioctrl);
cb.on_memory_protect = make_callback(c, handle_memory_protect);
cb.on_memory_violate = make_callback(c, handle_memory_violate);
cb.on_memory_allocate = make_callback(c, handle_memory_allocate);
cb.on_module_load = make_callback(c, handle_module_load);
cb.on_module_unload = make_callback(c, handle_module_unload);
cb.on_thread_switch = make_callback(c, handle_thread_switch);
cb.on_thread_set_name = make_callback(c, handle_thread_set_name);
cb.on_instruction = make_callback(c, handle_instruction);
cb.on_generic_access = make_callback(c, handle_generic_access);
cb.on_generic_activity = make_callback(c, handle_generic_activity);
cb.on_suspicious_activity = make_callback(c, handle_suspicious_activity);
}
mapped_module* get_module_if_interesting(module_manager& manager, const string_set& modules, uint64_t address)
{
if (manager.executable->is_within(address))
{
return manager.executable;
}
if (modules.empty())
{
return nullptr;
}
auto* mod = manager.find_by_address(address);
if (mod && modules.contains(mod->name))
{
return mod;
}
return nullptr;
}

33
src/analyzer/analysis.hpp Normal file
View File

@@ -0,0 +1,33 @@
#pragma once
#include <set>
#include <string>
struct mapped_module;
class module_manager;
class windows_emulator;
using string_set = std::set<std::string, std::less<>>;
struct analysis_settings
{
bool concise_logging{false};
bool verbose_logging{false};
bool silent{false};
bool buffer_stdout{false};
string_set modules{};
string_set ignored_functions{};
};
struct analysis_context
{
const analysis_settings* settings{};
windows_emulator* win_emu{};
std::string output{};
bool has_reached_main{false};
};
void register_analysis_callbacks(analysis_context& c);
mapped_module* get_module_if_interesting(module_manager& manager, const string_set& modules, uint64_t address);

View File

@@ -1,29 +1,33 @@
#include "std_include.hpp"
#include <windows_emulator.hpp>
#include <backend_selection.hpp>
#include <win_x64_gdb_stub_handler.hpp>
#include <minidump_loader.hpp>
#include "object_watching.hpp"
#include "snapshot.hpp"
#include "analysis.hpp"
#include <utils/finally.hpp>
#include <utils/interupt_handler.hpp>
#include <cstdio>
#ifdef OS_EMSCRIPTEN
#include <event_handler.hpp>
#endif
namespace
{
struct analysis_options
struct analysis_options : analysis_settings
{
mutable bool use_gdb{false};
bool concise_logging{false};
bool verbose_logging{false};
bool silent{false};
bool buffer_stdout{false};
bool log_executable_access{false};
std::filesystem::path dump{};
std::filesystem::path minidump_path{};
std::string registry_path{"./registry"};
std::string emulation_root{};
std::set<std::string, std::less<>> modules{};
std::set<std::string, std::less<>> ignored_functions{};
std::unordered_map<windows_path, std::filesystem::path> path_mappings{};
};
@@ -54,23 +58,25 @@ namespace
}
void watch_system_objects(windows_emulator& win_emu, const std::set<std::string, std::less<>>& modules,
const bool cache_logging)
const bool verbose)
{
win_emu.setup_process_if_necessary();
(void)win_emu;
(void)modules;
(void)cache_logging;
(void)verbose;
#ifdef OS_WINDOWS
watch_object(win_emu, modules, *win_emu.current_thread().teb, cache_logging);
watch_object(win_emu, modules, win_emu.process.peb, cache_logging);
#if !defined(__GNUC__) || defined(__clang__)
watch_object(win_emu, modules, *win_emu.current_thread().teb, verbose);
watch_object(win_emu, modules, win_emu.process.peb, verbose);
watch_object(win_emu, modules, emulator_object<KUSER_SHARED_DATA64>{win_emu.emu(), kusd_mmio::address()},
cache_logging);
verbose);
auto* params_hook = watch_object(win_emu, modules, win_emu.process.process_params, cache_logging);
auto* params_hook = watch_object(win_emu, modules, win_emu.process.process_params, verbose);
win_emu.emu().hook_memory_write(
win_emu.process.peb.value() + offsetof(PEB64, ProcessParameters), 0x8,
[&win_emu, cache_logging, params_hook, modules](const uint64_t address, const void*, size_t) mutable {
[&win_emu, verbose, params_hook, modules](const uint64_t address, const void*, size_t) mutable {
const auto target_address = win_emu.process.peb.value() + offsetof(PEB64, ProcessParameters);
if (address == target_address)
@@ -81,7 +87,7 @@ namespace
};
win_emu.emu().delete_hook(params_hook);
params_hook = watch_object(win_emu, modules, obj, cache_logging);
params_hook = watch_object(win_emu, modules, obj, verbose);
}
});
#endif
@@ -104,8 +110,19 @@ namespace
}
}
bool run_emulation(windows_emulator& win_emu, const analysis_options& options)
void do_post_emulation_work(const analysis_context& c)
{
if (c.settings->buffer_stdout)
{
c.win_emu->log.info("%.*s%s", static_cast<int>(c.output.size()), c.output.data(),
c.output.ends_with("\n") ? "" : "\n");
}
}
bool run_emulation(const analysis_context& c, const analysis_options& options)
{
auto& win_emu = *c.win_emu;
std::atomic_uint32_t signals_received{0};
utils::interupt_handler _{[&] {
const auto value = signals_received++;
@@ -118,9 +135,16 @@ namespace
_Exit(1);
}
win_emu.emu().stop();
win_emu.stop();
}};
std::optional<NTSTATUS> exit_status{};
#ifdef OS_EMSCRIPTEN
const auto _1 = utils::finally([&] {
debugger::handle_exit(win_emu, exit_status); //
});
#endif
try
{
if (options.use_gdb)
@@ -133,6 +157,12 @@ namespace
win_x64_gdb_stub_handler handler{win_emu, should_stop};
gdb_stub::run_gdb_stub(network::address{"0.0.0.0:28960", AF_INET}, handler);
}
else if (!options.minidump_path.empty())
{
// For minidumps, don't start execution automatically; just report ready state
win_emu.log.print(color::green, "Minidump loaded successfully. Process state ready for analysis.\n");
return true; // Return success without starting emulation
}
else
{
win_emu.start();
@@ -153,26 +183,36 @@ namespace
}
catch (const std::exception& e)
{
win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 " - %s\n",
win_emu.emu().read_instruction_pointer(), e.what());
do_post_emulation_work(c);
win_emu.log.error("Emulation failed at: 0x%" PRIx64 " - %s\n", win_emu.emu().read_instruction_pointer(),
e.what());
throw;
}
catch (...)
{
win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 "\n",
win_emu.emu().read_instruction_pointer());
do_post_emulation_work(c);
win_emu.log.error("Emulation failed at: 0x%" PRIx64 "\n", win_emu.emu().read_instruction_pointer());
throw;
}
const auto exit_status = win_emu.process.exit_status;
exit_status = win_emu.process.exit_status;
if (!exit_status.has_value())
{
win_emu.log.print(color::red, "Emulation terminated without status!\n");
do_post_emulation_work(c);
win_emu.log.error("Emulation terminated without status!\n");
return false;
}
const auto success = *exit_status == STATUS_SUCCESS;
win_emu.log.print(success ? color::green : color::red, "Emulation terminated with status: %X\n", *exit_status);
if (!options.silent)
{
do_post_emulation_work(c);
win_emu.log.disable_output(false);
win_emu.log.print(success ? color::green : color::red, "Emulation terminated with status: %X\n",
*exit_status);
}
return success;
}
@@ -195,19 +235,14 @@ namespace
return {
.emulation_root = options.emulation_root,
.registry_directory = options.registry_path,
.verbose_calls = options.verbose_logging,
.disable_logging = options.silent,
.silent_until_main = options.concise_logging,
.path_mappings = options.path_mappings,
.modules = options.modules,
.ignored_functions = options.ignored_functions,
};
}
std::unique_ptr<windows_emulator> create_empty_emulator(const analysis_options& options)
{
const auto settings = create_emulator_settings(options);
return std::make_unique<windows_emulator>(settings);
return std::make_unique<windows_emulator>(create_x86_64_emulator(), settings);
}
std::unique_ptr<windows_emulator> create_application_emulator(const analysis_options& options,
@@ -224,98 +259,123 @@ namespace
};
const auto settings = create_emulator_settings(options);
return std::make_unique<windows_emulator>(std::move(app_settings), settings);
return std::make_unique<windows_emulator>(create_x86_64_emulator(), std::move(app_settings), settings);
}
std::unique_ptr<windows_emulator> setup_emulator(const analysis_options& options,
const std::span<const std::string_view> args)
{
if (options.dump.empty())
if (!options.dump.empty())
{
return create_application_emulator(options, args);
// load snapshot
auto win_emu = create_empty_emulator(options);
snapshot::load_emulator_snapshot(*win_emu, options.dump);
return win_emu;
}
if (!options.minidump_path.empty())
{
// load minidump
auto win_emu = create_empty_emulator(options);
minidump_loader::load_minidump_into_emulator(*win_emu, options.minidump_path);
return win_emu;
}
auto win_emu = create_empty_emulator(options);
snapshot::load_emulator_snapshot(*win_emu, options.dump);
return win_emu;
// default: load application
return create_application_emulator(options, args);
}
bool run(const analysis_options& options, const std::span<const std::string_view> args)
{
analysis_context context{
.settings = &options,
};
const auto win_emu = setup_emulator(options, args);
win_emu->log.disable_output(options.concise_logging || options.silent);
context.win_emu = win_emu.get();
win_emu->log.log("Using emulator: %s\n", win_emu->emu().get_name().c_str());
(void)&watch_system_objects;
watch_system_objects(*win_emu, options.modules, options.concise_logging);
win_emu->buffer_stdout = options.buffer_stdout;
if (options.silent)
{
win_emu->buffer_stdout = false;
win_emu->callbacks.on_stdout = [](const std::string_view data) {
(void)fwrite(data.data(), 1, data.size(), stdout);
};
}
register_analysis_callbacks(context);
watch_system_objects(*win_emu, options.modules, options.verbose_logging);
const auto& exe = *win_emu->mod_manager.executable;
const auto concise_logging = options.concise_logging;
const auto concise_logging = !options.verbose_logging;
for (const auto& section : exe.sections)
{
if ((section.region.permissions & memory_permission::exec) != memory_permission::exec)
win_emu->emu().hook_instruction(x86_hookable_instructions::cpuid, [&] {
const auto rip = win_emu->emu().read_instruction_pointer();
auto* mod = get_module_if_interesting(win_emu->mod_manager, options.modules, rip);
if (mod)
{
continue;
const auto leaf = win_emu->emu().reg<uint32_t>(x86_register::eax);
win_emu->log.print(color::blue, "Executing CPUID instruction with leaf 0x%X at 0x%" PRIx64 " (%s)\n",
leaf, rip, mod->name.c_str());
}
auto read_handler = [&, section, concise_logging](const uint64_t address, const void*, size_t) {
const auto rip = win_emu->emu().read_instruction_pointer();
if (!win_emu->mod_manager.executable->is_within(rip))
return instruction_hook_continuation::run_instruction;
});
if (options.log_executable_access)
{
for (const auto& section : exe.sections)
{
if ((section.region.permissions & memory_permission::exec) != memory_permission::exec)
{
return;
continue;
}
if (concise_logging)
{
static uint64_t count{0};
++count;
if (count > 100 && count % 100000 != 0)
auto read_handler = [&, section, concise_logging](const uint64_t address, const void*, size_t) {
const auto rip = win_emu->emu().read_instruction_pointer();
if (!win_emu->mod_manager.executable->is_within(rip))
{
return;
}
}
win_emu->log.print(color::green,
"Reading from executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n",
section.name.c_str(), address, rip);
};
if (concise_logging)
{
static uint64_t count{0};
++count;
if (count > 100 && count % 100000 != 0)
{
return;
}
}
const auto write_handler = [&, section, concise_logging](const uint64_t address, const void*, size_t) {
const auto rip = win_emu->emu().read_instruction_pointer();
if (!win_emu->mod_manager.executable->is_within(rip))
{
return;
}
win_emu->log.print(color::green,
"Reading from executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n",
section.name.c_str(), address, rip);
};
if (concise_logging)
{
static uint64_t count{0};
++count;
if (count > 100 && count % 100000 != 0)
const auto write_handler = [&, section, concise_logging](const uint64_t address, const void*, size_t) {
const auto rip = win_emu->emu().read_instruction_pointer();
if (!win_emu->mod_manager.executable->is_within(rip))
{
return;
}
}
win_emu->log.print(color::blue, "Writing to executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n",
section.name.c_str(), address, rip);
};
if (concise_logging)
{
static uint64_t count{0};
++count;
if (count > 100 && count % 100000 != 0)
{
return;
}
}
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->log.print(color::blue,
"Writing to executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n",
section.name.c_str(), address, rip);
};
win_emu->emu().hook_memory_read(section.region.start, section.region.length, std::move(read_handler));
win_emu->emu().hook_memory_write(section.region.start, section.region.length, std::move(write_handler));
}
}
return run_emulation(*win_emu, options);
return run_emulation(context, options);
}
std::vector<std::string_view> bundle_arguments(const int argc, char** argv)
@@ -330,6 +390,29 @@ namespace
return args;
}
void print_help()
{
printf("Usage: analyzer [options] [application] [args...]\n\n");
printf("Options:\n");
printf(" -h, --help Show this help message\n");
printf(" -d, --debug Enable GDB debugging mode\n");
printf(" -s, --silent Silent mode\n");
printf(" -v, --verbose Verbose logging\n");
printf(" -b, --buffer Buffer stdout\n");
printf(" -c, --concise Concise logging\n");
printf(" -x, --exec Log r/w access to executable memory\n");
printf(" -m, --module <module> Specify module to track\n");
printf(" -e, --emulation <path> Set emulation root path\n");
printf(" -a, --snapshot <path> Load snapshot dump from path\n");
printf(" --minidump <path> Load minidump from path\n");
printf(" -i, --ignore <funcs> Comma-separated list of functions to ignore\n");
printf(" -p, --path <src> <dst> Map Windows path to host path\n");
printf(" -r, --registry <path> Set registry path (default: ./registry)\n\n");
printf("Examples:\n");
printf(" analyzer -v -e path/to/root myapp.exe\n");
printf(" analyzer -e path/to/root -p c:/analysis-sample.exe /path/to/sample.exe c:/analysis-sample.exe\n");
}
analysis_options parse_options(std::vector<std::string_view>& args)
{
analysis_options options{};
@@ -339,68 +422,86 @@ namespace
auto arg_it = args.begin();
const auto& arg = *arg_it;
if (arg == "-d")
if (arg == "-h" || arg == "--help")
{
print_help();
std::exit(0);
}
else if (arg == "-d" || arg == "--debug")
{
options.use_gdb = true;
}
else if (arg == "-s")
else if (arg == "-s" || arg == "--silent")
{
options.silent = true;
}
else if (arg == "-v")
else if (arg == "-v" || arg == "--verbose")
{
options.verbose_logging = true;
}
else if (arg == "-b")
else if (arg == "-b" || arg == "--buffer")
{
options.buffer_stdout = true;
}
else if (arg == "-c")
else if (arg == "-x" || arg == "--exec")
{
options.log_executable_access = true;
}
else if (arg == "-c" || arg == "--concise")
{
options.concise_logging = true;
}
else if (arg == "-m")
else if (arg == "-m" || arg == "--module")
{
if (args.size() < 2)
{
throw std::runtime_error("No module provided after -m");
throw std::runtime_error("No module provided after -m/--module");
}
arg_it = args.erase(arg_it);
options.modules.insert(std::string(args[0]));
}
else if (arg == "-e")
else if (arg == "-e" || arg == "--emulation")
{
if (args.size() < 2)
{
throw std::runtime_error("No emulation root path provided after -e");
throw std::runtime_error("No emulation root path provided after -e/--emulation");
}
arg_it = args.erase(arg_it);
options.emulation_root = args[0];
}
else if (arg == "-a")
else if (arg == "-a" || arg == "--snapshot")
{
if (args.size() < 2)
{
throw std::runtime_error("No dump path provided after -a");
throw std::runtime_error("No dump path provided after -a/--snapshot");
}
arg_it = args.erase(arg_it);
options.dump = args[0];
}
else if (arg == "-i")
else if (arg == "--minidump")
{
if (args.size() < 2)
{
throw std::runtime_error("No ignored function(s) provided after -i");
throw std::runtime_error("No minidump path provided after --minidump");
}
arg_it = args.erase(arg_it);
options.minidump_path = args[0];
}
else if (arg == "-i" || arg == "--ignore")
{
if (args.size() < 2)
{
throw std::runtime_error("No ignored function(s) provided after -i/--ignore");
}
arg_it = args.erase(arg_it);
split_and_insert(options.ignored_functions, args[0]);
}
else if (arg == "-p")
else if (arg == "-p" || arg == "--path")
{
if (args.size() < 3)
{
throw std::runtime_error("No path mapping provided after -p");
throw std::runtime_error("No path mapping provided after -p/--path");
}
arg_it = args.erase(arg_it);
windows_path source = args[0];
@@ -409,11 +510,11 @@ namespace
options.path_mappings[std::move(source)] = std::move(target);
}
else if (arg == "-r")
else if (arg == "-r" || arg == "--registry")
{
if (args.size() < 2)
{
throw std::runtime_error("No registry path provided after -r");
throw std::runtime_error("No registry path provided after -r/--registry");
}
arg_it = args.erase(arg_it);
options.registry_path = args[0];
@@ -428,44 +529,50 @@ namespace
return options;
}
int run_main(const int argc, char** argv)
{
try
{
auto args = bundle_arguments(argc, argv);
if (args.empty())
{
print_help();
return 1;
}
const auto options = parse_options(args);
bool result{};
do
{
result = run(options, args);
} while (options.use_gdb);
return result ? 0 : 1;
}
catch (std::exception& e)
{
puts(e.what());
#if defined(_WIN32) && 0
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
#endif
}
return 1;
}
}
int main(const int argc, char** argv)
{
try
{
auto args = bundle_arguments(argc, argv);
const auto options = parse_options(args);
if (args.empty() && options.dump.empty())
{
throw std::runtime_error("Application not specified!");
}
bool result{};
do
{
result = run(options, args);
} while (options.use_gdb);
return result ? 0 : 1;
}
catch (std::exception& e)
{
puts(e.what());
#if defined(_WIN32) && 0
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
#endif
}
return 1;
return run_main(argc, argv);
}
#ifdef _WIN32
int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int)
{
return main(__argc, __argv);
return run_main(__argc, __argv);
}
#endif

View File

@@ -2,26 +2,27 @@
#include "reflect_type_info.hpp"
#include <set>
#include <cinttypes>
template <typename T>
emulator_hook* watch_object(windows_emulator& emu, const std::set<std::string, std::less<>>& modules,
emulator_object<T> object, const bool cache_logging = false)
emulator_object<T> object, const auto verbose)
{
const reflect_type_info<T> info{};
return emu.emu().hook_memory_read(
object.value(), object.size(),
[i = std::move(info), object, &emu, cache_logging, modules](const uint64_t address, const void*, size_t) {
object.value(), static_cast<size_t>(object.size()),
[i = std::move(info), object, &emu, verbose, modules](const uint64_t address, const void*, size_t) {
const auto rip = emu.emu().read_instruction_pointer();
const auto* mod = emu.mod_manager.find_by_address(rip);
const auto is_main_access = mod == emu.mod_manager.executable || modules.contains(mod->name);
if (!emu.verbose_calls && !is_main_access)
if (!verbose && !is_main_access)
{
return;
}
if (cache_logging)
if (!verbose)
{
static std::unordered_set<uint64_t> logged_addresses{};
if (is_main_access && !logged_addresses.insert(address).second)
@@ -31,8 +32,12 @@ emulator_hook* watch_object(windows_emulator& emu, const std::set<std::string, s
}
const auto offset = address - object.value();
const auto* mod_name = mod ? mod->name.c_str() : "<N/A>";
const auto& type_name = i.get_type_name();
const auto member_name = i.get_member_name(static_cast<size_t>(offset));
emu.log.print(is_main_access ? color::green : color::dark_gray,
"Object access: %s - 0x%llX (%s) at 0x%llX (%s)\n", i.get_type_name().c_str(), offset,
i.get_member_name(offset).c_str(), rip, mod ? mod->name.c_str() : "<N/A>");
"Object access: %s - 0x%" PRIx64 " (%s) at 0x%" PRIx64 " (%s)\n", type_name.c_str(), offset,
member_name.c_str(), rip, mod_name);
});
}

View File

@@ -11,9 +11,18 @@
#pragma clang diagnostic ignored "-Wunused-private-field"
#endif
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4308)
#endif
#include "reflect_extension.hpp"
#include <reflect>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#if defined(__clang__)
#pragma clang diagnostic pop
#endif

View File

@@ -0,0 +1,23 @@
file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS
*.cpp
*.hpp
*.rc
)
list(SORT SRC_FILES)
add_library(backend-selection ${SRC_FILES})
momo_assign_source_group(${SRC_FILES})
target_include_directories(backend-selection INTERFACE "${CMAKE_CURRENT_LIST_DIR}")
target_link_libraries(backend-selection PRIVATE
unicorn-emulator
)
if (MOMO_ENABLE_RUST)
target_link_libraries(backend-selection PRIVATE
icicle-emulator
)
endif()

View File

@@ -0,0 +1,23 @@
#include "backend_selection.hpp"
#include <string_view>
#include <unicorn_x86_64_emulator.hpp>
#if MOMO_ENABLE_RUST_CODE
#include <icicle_x86_64_emulator.hpp>
#endif
using namespace std::literals;
std::unique_ptr<x86_64_emulator> create_x86_64_emulator()
{
#if MOMO_ENABLE_RUST_CODE
const auto* env = getenv("EMULATOR_ICICLE");
if (env && (env == "1"sv || env == "true"sv))
{
return icicle::create_x86_64_emulator();
}
#endif
return unicorn::create_x86_64_emulator();
}

View File

@@ -0,0 +1,6 @@
#pragma once
#include <memory>
#include <arch_emulator.hpp>
std::unique_ptr<x86_64_emulator> create_x86_64_emulator();

View File

@@ -1,5 +1,5 @@
add_subdirectory(unicorn-emulator)
if (MOMO_ENABLE_RUST_CODE)
if (MOMO_ENABLE_RUST)
add_subdirectory(icicle-emulator)
endif()

View File

@@ -13,7 +13,13 @@ endif()
set(CARGO_TRIPLE)
set(CARGO_OPTIONS)
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
if (MINGW)
set(CARGO_TRIPLE "x86_64-pc-windows-gnu")
elseif (CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
set(CARGO_TRIPLE "wasm32-unknown-emscripten")
elseif(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4)
set(CARGO_TRIPLE "i686-pc-windows-msvc")
elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS")
set(CARGO_TRIPLE "aarch64-apple-ios")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Android")
if(CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a")
@@ -37,7 +43,7 @@ ExternalProject_Add(
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}
BINARY_DIR ${CMAKE_CURRENT_LIST_DIR}
CONFIGURE_COMMAND ""
BUILD_COMMAND ${CMAKE_COMMAND} -E env "CARGO_TARGET_DIR=${ICICLE_BUILD_DIR}" cargo build ${CARGO_OPTIONS} --lib --profile $<IF:$<CONFIG:Debug>,dev,release>
BUILD_COMMAND ${CMAKE_COMMAND} -E env "CARGO_TARGET_DIR=${ICICLE_BUILD_DIR}" -- cargo build ${CARGO_OPTIONS} --lib --profile $<IF:$<CONFIG:Debug>,dev,release>
INSTALL_COMMAND ""
USES_TERMINAL_CONFIGURE 1
USES_TERMINAL_BUILD 1

View File

@@ -370,7 +370,7 @@ dependencies = [
[[package]]
name = "icicle-cpu"
version = "0.1.0"
source = "git+https://github.com/icicle-emu/icicle-emu#fe930922ea1ec39578ee545ccced61649f3da00a"
source = "git+https://github.com/icicle-emu/icicle-emu#e80282d3e1f53177b3f5a82fc48ac7538826a7d9"
dependencies = [
"addr2line",
"ahash",
@@ -390,7 +390,7 @@ dependencies = [
[[package]]
name = "icicle-jit"
version = "0.2.0"
source = "git+https://github.com/icicle-emu/icicle-emu#fe930922ea1ec39578ee545ccced61649f3da00a"
source = "git+https://github.com/icicle-emu/icicle-emu#e80282d3e1f53177b3f5a82fc48ac7538826a7d9"
dependencies = [
"cranelift",
"cranelift-codegen",
@@ -408,7 +408,7 @@ dependencies = [
[[package]]
name = "icicle-linux"
version = "0.1.0"
source = "git+https://github.com/icicle-emu/icicle-emu#fe930922ea1ec39578ee545ccced61649f3da00a"
source = "git+https://github.com/icicle-emu/icicle-emu#e80282d3e1f53177b3f5a82fc48ac7538826a7d9"
dependencies = [
"bitflags 2.9.0",
"bstr",
@@ -424,7 +424,7 @@ dependencies = [
[[package]]
name = "icicle-mem"
version = "0.3.0"
source = "git+https://github.com/icicle-emu/icicle-emu#fe930922ea1ec39578ee545ccced61649f3da00a"
source = "git+https://github.com/icicle-emu/icicle-emu#e80282d3e1f53177b3f5a82fc48ac7538826a7d9"
dependencies = [
"ahash",
"tracing",
@@ -433,7 +433,7 @@ dependencies = [
[[package]]
name = "icicle-vm"
version = "0.2.0"
source = "git+https://github.com/icicle-emu/icicle-emu#fe930922ea1ec39578ee545ccced61649f3da00a"
source = "git+https://github.com/icicle-emu/icicle-emu#e80282d3e1f53177b3f5a82fc48ac7538826a7d9"
dependencies = [
"anyhow",
"icicle-cpu",
@@ -550,7 +550,7 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "pcode"
version = "0.2.0"
source = "git+https://github.com/icicle-emu/icicle-emu#fe930922ea1ec39578ee545ccced61649f3da00a"
source = "git+https://github.com/icicle-emu/icicle-emu#e80282d3e1f53177b3f5a82fc48ac7538826a7d9"
[[package]]
name = "pin-project-lite"
@@ -682,7 +682,7 @@ dependencies = [
[[package]]
name = "sleigh-compile"
version = "0.3.0"
source = "git+https://github.com/icicle-emu/icicle-emu#fe930922ea1ec39578ee545ccced61649f3da00a"
source = "git+https://github.com/icicle-emu/icicle-emu#e80282d3e1f53177b3f5a82fc48ac7538826a7d9"
dependencies = [
"pcode",
"serde",
@@ -695,12 +695,12 @@ dependencies = [
[[package]]
name = "sleigh-parse"
version = "0.3.0"
source = "git+https://github.com/icicle-emu/icicle-emu#fe930922ea1ec39578ee545ccced61649f3da00a"
source = "git+https://github.com/icicle-emu/icicle-emu#e80282d3e1f53177b3f5a82fc48ac7538826a7d9"
[[package]]
name = "sleigh-runtime"
version = "0.1.0"
source = "git+https://github.com/icicle-emu/icicle-emu#fe930922ea1ec39578ee545ccced61649f3da00a"
source = "git+https://github.com/icicle-emu/icicle-emu#e80282d3e1f53177b3f5a82fc48ac7538826a7d9"
dependencies = [
"pcode",
"sleigh-parse",

View File

@@ -50,6 +50,7 @@ enum HookType {
ExecuteSpecific,
Violation,
Interrupt,
Block,
Unknown,
}
@@ -101,7 +102,18 @@ impl<Func: ?Sized> HookContainer<Func> {
}
struct InstructionHookInjector {
hook: pcode::HookId,
inst_hook: pcode::HookId,
block_hook: pcode::HookId,
}
fn count_instructions(block: &icicle_cpu::lifter::Block) -> u64 {
return block.pcode.instructions.iter().fold(0u64, |count, &stmt| {
if let pcode::Op::InstructionMarker = stmt.op {
return count + 1;
}
return count;
});
}
impl icicle_vm::CodeInjector for InstructionHookInjector {
@@ -117,10 +129,19 @@ impl icicle_vm::CodeInjector for InstructionHookInjector {
let mut tmp_block = pcode::Block::new();
tmp_block.next_tmp = block.pcode.next_tmp;
let mut is_first_inst = true;
let inst_count = count_instructions(&block);
for stmt in block.pcode.instructions.drain(..) {
tmp_block.push(stmt);
if let pcode::Op::InstructionMarker = stmt.op {
tmp_block.push(pcode::Op::Hook(self.hook));
if is_first_inst {
is_first_inst = false;
tmp_block.push((pcode::Op::Arg(0), pcode::Inputs::one(inst_count)));
tmp_block.push(pcode::Op::Hook(self.block_hook));
}
tmp_block.push(pcode::Op::Hook(self.inst_hook));
code.modified.insert(id);
}
}
@@ -134,6 +155,7 @@ struct ExecutionHooks {
stop: Rc<RefCell<bool>>,
generic_hooks: HookContainer<dyn Fn(u64)>,
specific_hooks: HookContainer<dyn Fn(u64)>,
block_hooks: HookContainer<dyn Fn(u64, u64)>,
address_mapping: HashMap<u64, Vec<u32>>,
}
@@ -143,6 +165,7 @@ impl ExecutionHooks {
stop: stop_value,
generic_hooks: HookContainer::new(),
specific_hooks: HookContainer::new(),
block_hooks: HookContainer::new(),
address_mapping: HashMap::new(),
}
}
@@ -165,6 +188,12 @@ impl ExecutionHooks {
}
}
pub fn on_block(&mut self, address: u64, instructions: u64) {
for (_key, func) in self.block_hooks.get_hooks() {
func(address, instructions);
}
}
pub fn execute(&mut self, cpu: &mut icicle_cpu::Cpu, address: u64) {
self.run_hooks(address);
@@ -174,6 +203,14 @@ impl ExecutionHooks {
}
}
pub fn add_block_hook(&mut self, callback: Box<dyn Fn(u64, u64)>) -> u32 {
self.block_hooks.add_hook(callback)
}
pub fn remove_block_hook(&mut self, id: u32) {
self.block_hooks.remove_hook(id);
}
pub fn add_generic_hook(&mut self, callback: Box<dyn Fn(u64)>) -> u32 {
self.generic_hooks.add_hook(callback)
}
@@ -204,12 +241,13 @@ impl ExecutionHooks {
pub struct IcicleEmulator {
executing_thread: std::thread::ThreadId,
vm: icicle_vm::Vm,
reg: registers::X64RegisterNodes,
reg: registers::X86RegisterNodes,
syscall_hooks: HookContainer<dyn Fn()>,
interrupt_hooks: HookContainer<dyn Fn(i32)>,
violation_hooks: HookContainer<dyn Fn(u64, u8, bool) -> bool>,
execution_hooks: Rc<RefCell<ExecutionHooks>>,
stop: Rc<RefCell<bool>>,
snapshots: Vec<Box<icicle_vm::Snapshot>>,
}
struct MemoryHook {
@@ -263,24 +301,36 @@ impl IcicleEmulator {
let stop_value = Rc::new(RefCell::new(false));
let exec_hooks = Rc::new(RefCell::new(ExecutionHooks::new(stop_value.clone())));
let exec_hooks_clone = Rc::clone(&exec_hooks);
let inst_exec_hooks = Rc::clone(&exec_hooks);
let hook = icicle_cpu::InstHook::new(move |cpu: &mut icicle_cpu::Cpu, addr: u64| {
exec_hooks_clone.borrow_mut().execute(cpu, addr);
let inst_hook = icicle_cpu::InstHook::new(move |cpu: &mut icicle_cpu::Cpu, addr: u64| {
inst_exec_hooks.borrow_mut().execute(cpu, addr);
});
let hook = virtual_machine.cpu.add_hook(hook);
virtual_machine.add_injector(InstructionHookInjector { hook });
let block_exec_hooks = Rc::clone(&exec_hooks);
let block_hook = icicle_cpu::InstHook::new(move |cpu: &mut icicle_cpu::Cpu, addr: u64| {
let instructions = cpu.args[0] as u64;
block_exec_hooks.borrow_mut().on_block(addr, instructions);
});
let inst_hook_id = virtual_machine.cpu.add_hook(inst_hook);
let block_hook_id = virtual_machine.cpu.add_hook(block_hook);
virtual_machine.add_injector(InstructionHookInjector {
inst_hook: inst_hook_id,
block_hook: block_hook_id,
});
Self {
stop: stop_value,
executing_thread: std::thread::current().id(),
reg: registers::X64RegisterNodes::new(&virtual_machine.cpu.arch),
reg: registers::X86RegisterNodes::new(&virtual_machine.cpu.arch),
vm: virtual_machine,
syscall_hooks: HookContainer::new(),
interrupt_hooks: HookContainer::new(),
violation_hooks: HookContainer::new(),
execution_hooks: exec_hooks,
snapshots: Vec::new(),
}
}
@@ -378,6 +428,11 @@ impl IcicleEmulator {
}
}
pub fn add_block_hook(&mut self, callback: Box<dyn Fn(u64, u64)>) -> u32 {
let hook_id = self.execution_hooks.borrow_mut().add_block_hook(callback);
return qualify_hook_id(hook_id, HookType::Block);
}
pub fn add_violation_hook(&mut self, callback: Box<dyn Fn(u64, u8, bool) -> bool>) -> u32 {
let hook_id = self.violation_hooks.add_hook(callback);
return qualify_hook_id(hook_id, HookType::Violation);
@@ -453,6 +508,7 @@ impl IcicleEmulator {
.execution_hooks
.borrow_mut()
.remove_specific_hook(hook_id),
HookType::Block => self.execution_hooks.borrow_mut().remove_block_hook(hook_id),
HookType::Read => {
self.get_mem().remove_read_after_hook(hook_id);
()
@@ -555,7 +611,7 @@ impl IcicleEmulator {
};
}
fn read_generic_register(&mut self, reg: registers::X64Register, buffer: &mut [u8]) -> usize {
fn read_generic_register(&mut self, reg: registers::X86Register, buffer: &mut [u8]) -> usize {
let reg_node = self.reg.get_node(reg);
let res = self.vm.cpu.read_dynamic(pcode::Value::Var(reg_node));
@@ -580,15 +636,29 @@ impl IcicleEmulator {
return limit;
}
pub fn read_register(&mut self, reg: registers::X64Register, data: &mut [u8]) -> usize {
pub fn read_register(&mut self, reg: registers::X86Register, data: &mut [u8]) -> usize {
match reg {
registers::X64Register::Rflags => self.read_flags::<u64>(data),
registers::X64Register::Eflags => self.read_flags::<u32>(data),
registers::X64Register::Flags => self.read_flags::<u16>(data),
registers::X86Register::Rflags => self.read_flags::<u64>(data),
registers::X86Register::Eflags => self.read_flags::<u32>(data),
registers::X86Register::Flags => self.read_flags::<u16>(data),
_ => self.read_generic_register(reg, data),
}
}
pub fn create_snapshot(&mut self) -> u32 {
let snap = self.vm.snapshot();
let id = self.snapshots.len() as u32;
self.snapshots.push(Box::new(snap));
return id;
}
pub fn restore_snapshot(&mut self, id: u32) {
let snap = self.snapshots[id as usize].as_ref();
self.vm.restore(&snap);
}
fn write_flags<T>(&mut self, data: &[u8]) -> usize {
const REAL_SIZE: usize = std::mem::size_of::<u64>();
let limit: usize = std::mem::size_of::<T>();
@@ -606,16 +676,16 @@ impl IcicleEmulator {
return limit;
}
pub fn write_register(&mut self, reg: registers::X64Register, data: &[u8]) -> usize {
pub fn write_register(&mut self, reg: registers::X86Register, data: &[u8]) -> usize {
match reg {
registers::X64Register::Rflags => self.write_flags::<u64>(data),
registers::X64Register::Eflags => self.write_flags::<u32>(data),
registers::X64Register::Flags => self.write_flags::<u16>(data),
registers::X86Register::Rflags => self.write_flags::<u64>(data),
registers::X86Register::Eflags => self.write_flags::<u32>(data),
registers::X86Register::Flags => self.write_flags::<u16>(data),
_ => self.write_generic_register(reg, data),
}
}
fn write_generic_register(&mut self, reg: registers::X64Register, data: &[u8]) -> usize {
fn write_generic_register(&mut self, reg: registers::X86Register, data: &[u8]) -> usize {
let reg_node = self.reg.get_node(reg);
let mut buffer = [0u8; 32];

View File

@@ -2,7 +2,7 @@ mod icicle;
mod registers;
use icicle::IcicleEmulator;
use registers::X64Register;
use registers::X86Register;
use std::os::raw::c_void;
fn to_cbool(value: bool) -> i32 {
@@ -37,6 +37,7 @@ pub fn icicle_stop(ptr: *mut c_void) {
type RawFunction = extern "C" fn(*mut c_void);
type PtrFunction = extern "C" fn(*mut c_void, u64);
type BlockFunction = extern "C" fn(*mut c_void, u64, u64);
type DataFunction = extern "C" fn(*mut c_void, *const c_void, usize);
type MmioReadFunction = extern "C" fn(*mut c_void, u64, *mut c_void, usize);
type MmioWriteFunction = extern "C" fn(*mut c_void, u64, *const c_void, usize);
@@ -148,6 +149,22 @@ pub fn icicle_restore_registers(ptr: *mut c_void, data: *const c_void, size: usi
}
}
#[unsafe(no_mangle)]
pub fn icicle_create_snapshot(ptr: *mut c_void) -> u32 {
unsafe {
let emulator = &mut *(ptr as *mut IcicleEmulator);
return emulator.create_snapshot();
}
}
#[unsafe(no_mangle)]
pub fn icicle_restore_snapshot(ptr: *mut c_void, id: u32) {
unsafe {
let emulator = &mut *(ptr as *mut IcicleEmulator);
emulator.restore_snapshot(id);
}
}
#[unsafe(no_mangle)]
pub fn icicle_read_memory(ptr: *mut c_void, address: u64, data: *mut c_void, size: usize) -> i32 {
if size == 0 {
@@ -167,17 +184,23 @@ pub fn icicle_read_memory(ptr: *mut c_void, address: u64, data: *mut c_void, siz
}
#[unsafe(no_mangle)]
pub fn icicle_add_interrupt_hook(ptr: *mut c_void, callback: InterruptFunction, data: *mut c_void) -> u32 {
pub fn icicle_add_interrupt_hook(
ptr: *mut c_void,
callback: InterruptFunction,
data: *mut c_void,
) -> u32 {
unsafe {
let emulator = &mut *(ptr as *mut IcicleEmulator);
return emulator.add_interrupt_hook(Box::new(
move |code: i32| callback(data, code),
));
return emulator.add_interrupt_hook(Box::new(move |code: i32| callback(data, code)));
}
}
#[unsafe(no_mangle)]
pub fn icicle_add_violation_hook(ptr: *mut c_void, callback: ViolationFunction, data: *mut c_void) -> u32 {
pub fn icicle_add_violation_hook(
ptr: *mut c_void,
callback: ViolationFunction,
data: *mut c_void,
) -> u32 {
unsafe {
let emulator = &mut *(ptr as *mut IcicleEmulator);
return emulator.add_violation_hook(Box::new(
@@ -194,26 +217,42 @@ pub fn icicle_add_violation_hook(ptr: *mut c_void, callback: ViolationFunction,
}
#[unsafe(no_mangle)]
pub fn icicle_add_read_hook(ptr: *mut c_void, start: u64, end: u64, callback: MemoryAccessFunction, user: *mut c_void) -> u32 {
pub fn icicle_add_read_hook(
ptr: *mut c_void,
start: u64,
end: u64,
callback: MemoryAccessFunction,
user: *mut c_void,
) -> u32 {
unsafe {
let emulator = &mut *(ptr as *mut IcicleEmulator);
return emulator.add_read_hook(start, end, Box::new(
move |address: u64, data: &[u8]| {
return emulator.add_read_hook(
start,
end,
Box::new(move |address: u64, data: &[u8]| {
callback(user, address, data.as_ptr() as *const c_void, data.len());
},
));
}),
);
}
}
#[unsafe(no_mangle)]
pub fn icicle_add_write_hook(ptr: *mut c_void, start: u64, end: u64, callback: MemoryAccessFunction, user: *mut c_void) -> u32 {
pub fn icicle_add_write_hook(
ptr: *mut c_void,
start: u64,
end: u64,
callback: MemoryAccessFunction,
user: *mut c_void,
) -> u32 {
unsafe {
let emulator = &mut *(ptr as *mut IcicleEmulator);
return emulator.add_write_hook(start, end, Box::new(
move |address: u64, data: &[u8]| {
return emulator.add_write_hook(
start,
end,
Box::new(move |address: u64, data: &[u8]| {
callback(user, address, data.as_ptr() as *const c_void, data.len());
},
));
}),
);
}
}
@@ -226,7 +265,21 @@ pub fn icicle_add_syscall_hook(ptr: *mut c_void, callback: RawFunction, data: *m
}
#[unsafe(no_mangle)]
pub fn icicle_add_generic_execution_hook(ptr: *mut c_void, callback: PtrFunction, data: *mut c_void) -> u32 {
pub fn icicle_add_block_hook(ptr: *mut c_void, callback: BlockFunction, data: *mut c_void) -> u32 {
unsafe {
let emulator = &mut *(ptr as *mut IcicleEmulator);
return emulator.add_block_hook(Box::new(move |address: u64, instructions: u64| {
callback(data, address, instructions)
}));
}
}
#[unsafe(no_mangle)]
pub fn icicle_add_generic_execution_hook(
ptr: *mut c_void,
callback: PtrFunction,
data: *mut c_void,
) -> u32 {
unsafe {
let emulator = &mut *(ptr as *mut IcicleEmulator);
return emulator.add_generic_execution_hook(Box::new(move |ptr: u64| callback(data, ptr)));
@@ -234,7 +287,12 @@ pub fn icicle_add_generic_execution_hook(ptr: *mut c_void, callback: PtrFunction
}
#[unsafe(no_mangle)]
pub fn icicle_add_execution_hook(ptr: *mut c_void, address: u64, callback: PtrFunction, data: *mut c_void) -> u32 {
pub fn icicle_add_execution_hook(
ptr: *mut c_void,
address: u64,
callback: PtrFunction,
data: *mut c_void,
) -> u32 {
unsafe {
let emulator = &mut *(ptr as *mut IcicleEmulator);
return emulator.add_execution_hook(address, Box::new(move |ptr: u64| callback(data, ptr)));
@@ -252,7 +310,7 @@ pub fn icicle_remove_hook(ptr: *mut c_void, id: u32) {
#[unsafe(no_mangle)]
pub fn icicle_read_register(
ptr: *mut c_void,
reg: X64Register,
reg: X86Register,
data: *mut c_void,
size: usize,
) -> usize {
@@ -274,7 +332,7 @@ pub fn icicle_read_register(
#[unsafe(no_mangle)]
pub fn icicle_write_register(
ptr: *mut c_void,
reg: X64Register,
reg: X86Register,
data: *const c_void,
size: usize,
) -> usize {

View File

@@ -1,6 +1,6 @@
#[repr(i32)]
#[derive(PartialEq)]
pub enum X64Register {
pub enum X86Register {
Invalid = 0,
Ah,
Al,
@@ -244,7 +244,7 @@ pub enum X64Register {
}
#[derive(Clone)]
pub(crate) struct X64RegisterNodes {
pub(crate) struct X86RegisterNodes {
rax: pcode::VarNode,
rbx: pcode::VarNode,
rcx: pcode::VarNode,
@@ -474,9 +474,9 @@ pub(crate) struct X64RegisterNodes {
flags: Vec<pcode::VarNode>,
}
impl X64RegisterNodes {
impl X86RegisterNodes {
pub fn new(arch: &icicle_cpu::Arch) -> Self {
let r = |name: &str| arch.sleigh.get_reg(name).unwrap().var;
let r = |name: &str| arch.sleigh.get_reg(name).unwrap().get_raw_var();
let nodes = [
"CF", "F1", "PF", "F3", "AF", "F5", "ZF", "SF", "TF", "IF", "DF", "OF", "IOPL", "NT",
"F15", "RF", "VM", "AC", "VIF", "VIP", "ID",
@@ -731,234 +731,234 @@ impl X64RegisterNodes {
}
}
pub fn get_node(&self, reg: X64Register) -> pcode::VarNode {
pub fn get_node(&self, reg: X86Register) -> pcode::VarNode {
match reg {
X64Register::Rax => self.rax,
X64Register::Rbx => self.rbx,
X64Register::Rcx => self.rcx,
X64Register::Rdx => self.rdx,
X64Register::Rsi => self.rsi,
X64Register::Rdi => self.rdi,
X64Register::Rbp => self.rbp,
X64Register::Rsp => self.rsp,
X64Register::R8 => self.r8,
X64Register::R9 => self.r9,
X64Register::R10 => self.r10,
X64Register::R11 => self.r11,
X64Register::R12 => self.r12,
X64Register::R13 => self.r13,
X64Register::R14 => self.r14,
X64Register::R15 => self.r15,
X64Register::Rip => self.rip,
X64Register::Cs => self.cs,
X64Register::Ds => self.ds,
X64Register::Es => self.es,
X64Register::Fs => self.fs,
X64Register::Gs => self.gs,
X64Register::Ss => self.ss,
X64Register::Ah => self.ah,
X64Register::Al => self.al,
X64Register::Ax => self.ax,
X64Register::Bh => self.bh,
X64Register::Bl => self.bl,
X64Register::Bpl => self.bpl,
X64Register::Ch => self.ch,
X64Register::Cl => self.cl,
X64Register::Cx => self.cx,
X64Register::Dh => self.dh,
X64Register::Dil => self.dil,
X64Register::Dl => self.dl,
X64Register::Dx => self.dx,
X64Register::Eax => self.eax,
X64Register::Ebp => self.ebp,
X64Register::Ebx => self.ebx,
X64Register::Ecx => self.ecx,
X64Register::Edi => self.edi,
X64Register::Edx => self.edx,
X64Register::Esi => self.esi,
X64Register::Esp => self.esp,
X64Register::Fpsw => self.fpsw,
X64Register::Gdtr => self.gdtr,
X64Register::Idtr => self.idtr,
X64Register::Ldtr => self.ldtr,
X64Register::Tr => self.tr,
X64Register::Cr0 => self.cr0,
X64Register::Cr1 => self.cr1,
X64Register::Cr2 => self.cr2,
X64Register::Cr3 => self.cr3,
X64Register::Cr4 => self.cr4,
X64Register::Cr8 => self.cr8,
X64Register::Dr0 => self.dr0,
X64Register::Dr1 => self.dr1,
X64Register::Dr2 => self.dr2,
X64Register::Dr3 => self.dr3,
X64Register::Dr4 => self.dr4,
X64Register::Dr5 => self.dr5,
X64Register::Dr6 => self.dr6,
X64Register::Dr7 => self.dr7,
X64Register::Fp0 => self.fp0,
X64Register::Fp1 => self.fp1,
X64Register::Fp2 => self.fp2,
X64Register::Fp3 => self.fp3,
X64Register::Fp4 => self.fp4,
X64Register::Fp5 => self.fp5,
X64Register::Fp6 => self.fp6,
X64Register::Fp7 => self.fp7,
/*X64Register::K0 => self.k0,
X64Register::K1 => self.k1,
X64Register::K2 => self.k2,
X64Register::K3 => self.k3,
X64Register::K4 => self.k4,
X64Register::K5 => self.k5,
X64Register::K6 => self.k6,
X64Register::K7 => self.k7,*/
X64Register::Mm0 => self.mm0,
X64Register::Mm1 => self.mm1,
X64Register::Mm2 => self.mm2,
X64Register::Mm3 => self.mm3,
X64Register::Mm4 => self.mm4,
X64Register::Mm5 => self.mm5,
X64Register::Mm6 => self.mm6,
X64Register::Mm7 => self.mm7,
X64Register::St0 => self.st0,
X64Register::St1 => self.st1,
X64Register::St2 => self.st2,
X64Register::St3 => self.st3,
X64Register::St4 => self.st4,
X64Register::St5 => self.st5,
X64Register::St6 => self.st6,
X64Register::St7 => self.st7,
X64Register::Xmm0 => self.xmm0,
X64Register::Xmm1 => self.xmm1,
X64Register::Xmm2 => self.xmm2,
X64Register::Xmm3 => self.xmm3,
X64Register::Xmm4 => self.xmm4,
X64Register::Xmm5 => self.xmm5,
X64Register::Xmm6 => self.xmm6,
X64Register::Xmm7 => self.xmm7,
X64Register::Xmm8 => self.xmm8,
X64Register::Xmm9 => self.xmm9,
X64Register::Xmm10 => self.xmm10,
X64Register::Xmm11 => self.xmm11,
X64Register::Xmm12 => self.xmm12,
X64Register::Xmm13 => self.xmm13,
X64Register::Xmm14 => self.xmm14,
X64Register::Xmm15 => self.xmm15,
/*X64Register::Xmm16 => self.xmm16,
X64Register::Xmm17 => self.xmm17,
X64Register::Xmm18 => self.xmm18,
X64Register::Xmm19 => self.xmm19,
X64Register::Xmm20 => self.xmm20,
X64Register::Xmm21 => self.xmm21,
X64Register::Xmm22 => self.xmm22,
X64Register::Xmm23 => self.xmm23,
X64Register::Xmm24 => self.xmm24,
X64Register::Xmm25 => self.xmm25,
X64Register::Xmm26 => self.xmm26,
X64Register::Xmm27 => self.xmm27,
X64Register::Xmm28 => self.xmm28,
X64Register::Xmm29 => self.xmm29,
X64Register::Xmm30 => self.xmm30,
X64Register::Xmm31 => self.xmm31,*/
X64Register::Ymm0 => self.ymm0,
X64Register::Ymm1 => self.ymm1,
X64Register::Ymm2 => self.ymm2,
X64Register::Ymm3 => self.ymm3,
X64Register::Ymm4 => self.ymm4,
X64Register::Ymm5 => self.ymm5,
X64Register::Ymm6 => self.ymm6,
X64Register::Ymm7 => self.ymm7,
X64Register::Ymm8 => self.ymm8,
X64Register::Ymm9 => self.ymm9,
X64Register::Ymm10 => self.ymm10,
X64Register::Ymm11 => self.ymm11,
X64Register::Ymm12 => self.ymm12,
X64Register::Ymm13 => self.ymm13,
X64Register::Ymm14 => self.ymm14,
X64Register::Ymm15 => self.ymm15,
/*X64Register::Ymm16 => self.ymm16,
X64Register::Ymm17 => self.ymm17,
X64Register::Ymm18 => self.ymm18,
X64Register::Ymm19 => self.ymm19,
X64Register::Ymm20 => self.ymm20,
X64Register::Ymm21 => self.ymm21,
X64Register::Ymm22 => self.ymm22,
X64Register::Ymm23 => self.ymm23,
X64Register::Ymm24 => self.ymm24,
X64Register::Ymm25 => self.ymm25,
X64Register::Ymm26 => self.ymm26,
X64Register::Ymm27 => self.ymm27,
X64Register::Ymm28 => self.ymm28,
X64Register::Ymm29 => self.ymm29,
X64Register::Ymm30 => self.ymm30,
X64Register::Ymm31 => self.ymm31,*/
/*X64Register::Zmm0 => self.zmm0,
X64Register::Zmm1 => self.zmm1,
X64Register::Zmm2 => self.zmm2,
X64Register::Zmm3 => self.zmm3,
X64Register::Zmm4 => self.zmm4,
X64Register::Zmm5 => self.zmm5,
X64Register::Zmm6 => self.zmm6,
X64Register::Zmm7 => self.zmm7,
X64Register::Zmm8 => self.zmm8,
X64Register::Zmm9 => self.zmm9,
X64Register::Zmm10 => self.zmm10,
X64Register::Zmm11 => self.zmm11,
X64Register::Zmm12 => self.zmm12,
X64Register::Zmm13 => self.zmm13,
X64Register::Zmm14 => self.zmm14,
X64Register::Zmm15 => self.zmm15,
X64Register::Zmm16 => self.zmm16,
X64Register::Zmm17 => self.zmm17,
X64Register::Zmm18 => self.zmm18,
X64Register::Zmm19 => self.zmm19,
X64Register::Zmm20 => self.zmm20,
X64Register::Zmm21 => self.zmm21,
X64Register::Zmm22 => self.zmm22,
X64Register::Zmm23 => self.zmm23,
X64Register::Zmm24 => self.zmm24,
X64Register::Zmm25 => self.zmm25,
X64Register::Zmm26 => self.zmm26,
X64Register::Zmm27 => self.zmm27,
X64Register::Zmm28 => self.zmm28,
X64Register::Zmm29 => self.zmm29,
X64Register::Zmm30 => self.zmm30,
X64Register::Zmm31 => self.zmm31,*/
X64Register::R8b => self.r8b,
X64Register::R9b => self.r9b,
X64Register::R10b => self.r10b,
X64Register::R11b => self.r11b,
X64Register::R12b => self.r12b,
X64Register::R13b => self.r13b,
X64Register::R14b => self.r14b,
X64Register::R15b => self.r15b,
X64Register::R8d => self.r8d,
X64Register::R9d => self.r9d,
X64Register::R10d => self.r10d,
X64Register::R11d => self.r11d,
X64Register::R12d => self.r12d,
X64Register::R13d => self.r13d,
X64Register::R14d => self.r14d,
X64Register::R15d => self.r15d,
X64Register::R8w => self.r8w,
X64Register::R9w => self.r9w,
X64Register::R10w => self.r10w,
X64Register::R11w => self.r11w,
X64Register::R12w => self.r12w,
X64Register::R13w => self.r13w,
X64Register::R14w => self.r14w,
X64Register::R15w => self.r15w,
X64Register::Fpcw => self.fpcw,
X64Register::Fptag => self.fptag,
//X64Register::Msr => self.msr,
X64Register::Mxcsr => self.mxcsr,
X64Register::FsBase => self.fs_base,
X64Register::GsBase => self.gs_base,
X64Register::Fip => self.fip,
X64Register::Fcs => self.fcs,
X64Register::Fdp => self.fdp,
X64Register::Fds => self.fds,
X64Register::Fop => self.fop,
X86Register::Rax => self.rax,
X86Register::Rbx => self.rbx,
X86Register::Rcx => self.rcx,
X86Register::Rdx => self.rdx,
X86Register::Rsi => self.rsi,
X86Register::Rdi => self.rdi,
X86Register::Rbp => self.rbp,
X86Register::Rsp => self.rsp,
X86Register::R8 => self.r8,
X86Register::R9 => self.r9,
X86Register::R10 => self.r10,
X86Register::R11 => self.r11,
X86Register::R12 => self.r12,
X86Register::R13 => self.r13,
X86Register::R14 => self.r14,
X86Register::R15 => self.r15,
X86Register::Rip => self.rip,
X86Register::Cs => self.cs,
X86Register::Ds => self.ds,
X86Register::Es => self.es,
X86Register::Fs => self.fs,
X86Register::Gs => self.gs,
X86Register::Ss => self.ss,
X86Register::Ah => self.ah,
X86Register::Al => self.al,
X86Register::Ax => self.ax,
X86Register::Bh => self.bh,
X86Register::Bl => self.bl,
X86Register::Bpl => self.bpl,
X86Register::Ch => self.ch,
X86Register::Cl => self.cl,
X86Register::Cx => self.cx,
X86Register::Dh => self.dh,
X86Register::Dil => self.dil,
X86Register::Dl => self.dl,
X86Register::Dx => self.dx,
X86Register::Eax => self.eax,
X86Register::Ebp => self.ebp,
X86Register::Ebx => self.ebx,
X86Register::Ecx => self.ecx,
X86Register::Edi => self.edi,
X86Register::Edx => self.edx,
X86Register::Esi => self.esi,
X86Register::Esp => self.esp,
X86Register::Fpsw => self.fpsw,
X86Register::Gdtr => self.gdtr,
X86Register::Idtr => self.idtr,
X86Register::Ldtr => self.ldtr,
X86Register::Tr => self.tr,
X86Register::Cr0 => self.cr0,
X86Register::Cr1 => self.cr1,
X86Register::Cr2 => self.cr2,
X86Register::Cr3 => self.cr3,
X86Register::Cr4 => self.cr4,
X86Register::Cr8 => self.cr8,
X86Register::Dr0 => self.dr0,
X86Register::Dr1 => self.dr1,
X86Register::Dr2 => self.dr2,
X86Register::Dr3 => self.dr3,
X86Register::Dr4 => self.dr4,
X86Register::Dr5 => self.dr5,
X86Register::Dr6 => self.dr6,
X86Register::Dr7 => self.dr7,
X86Register::Fp0 => self.fp0,
X86Register::Fp1 => self.fp1,
X86Register::Fp2 => self.fp2,
X86Register::Fp3 => self.fp3,
X86Register::Fp4 => self.fp4,
X86Register::Fp5 => self.fp5,
X86Register::Fp6 => self.fp6,
X86Register::Fp7 => self.fp7,
/*X86Register::K0 => self.k0,
X86Register::K1 => self.k1,
X86Register::K2 => self.k2,
X86Register::K3 => self.k3,
X86Register::K4 => self.k4,
X86Register::K5 => self.k5,
X86Register::K6 => self.k6,
X86Register::K7 => self.k7,*/
X86Register::Mm0 => self.mm0,
X86Register::Mm1 => self.mm1,
X86Register::Mm2 => self.mm2,
X86Register::Mm3 => self.mm3,
X86Register::Mm4 => self.mm4,
X86Register::Mm5 => self.mm5,
X86Register::Mm6 => self.mm6,
X86Register::Mm7 => self.mm7,
X86Register::St0 => self.st0,
X86Register::St1 => self.st1,
X86Register::St2 => self.st2,
X86Register::St3 => self.st3,
X86Register::St4 => self.st4,
X86Register::St5 => self.st5,
X86Register::St6 => self.st6,
X86Register::St7 => self.st7,
X86Register::Xmm0 => self.xmm0,
X86Register::Xmm1 => self.xmm1,
X86Register::Xmm2 => self.xmm2,
X86Register::Xmm3 => self.xmm3,
X86Register::Xmm4 => self.xmm4,
X86Register::Xmm5 => self.xmm5,
X86Register::Xmm6 => self.xmm6,
X86Register::Xmm7 => self.xmm7,
X86Register::Xmm8 => self.xmm8,
X86Register::Xmm9 => self.xmm9,
X86Register::Xmm10 => self.xmm10,
X86Register::Xmm11 => self.xmm11,
X86Register::Xmm12 => self.xmm12,
X86Register::Xmm13 => self.xmm13,
X86Register::Xmm14 => self.xmm14,
X86Register::Xmm15 => self.xmm15,
/*X86Register::Xmm16 => self.xmm16,
X86Register::Xmm17 => self.xmm17,
X86Register::Xmm18 => self.xmm18,
X86Register::Xmm19 => self.xmm19,
X86Register::Xmm20 => self.xmm20,
X86Register::Xmm21 => self.xmm21,
X86Register::Xmm22 => self.xmm22,
X86Register::Xmm23 => self.xmm23,
X86Register::Xmm24 => self.xmm24,
X86Register::Xmm25 => self.xmm25,
X86Register::Xmm26 => self.xmm26,
X86Register::Xmm27 => self.xmm27,
X86Register::Xmm28 => self.xmm28,
X86Register::Xmm29 => self.xmm29,
X86Register::Xmm30 => self.xmm30,
X86Register::Xmm31 => self.xmm31,*/
X86Register::Ymm0 => self.ymm0,
X86Register::Ymm1 => self.ymm1,
X86Register::Ymm2 => self.ymm2,
X86Register::Ymm3 => self.ymm3,
X86Register::Ymm4 => self.ymm4,
X86Register::Ymm5 => self.ymm5,
X86Register::Ymm6 => self.ymm6,
X86Register::Ymm7 => self.ymm7,
X86Register::Ymm8 => self.ymm8,
X86Register::Ymm9 => self.ymm9,
X86Register::Ymm10 => self.ymm10,
X86Register::Ymm11 => self.ymm11,
X86Register::Ymm12 => self.ymm12,
X86Register::Ymm13 => self.ymm13,
X86Register::Ymm14 => self.ymm14,
X86Register::Ymm15 => self.ymm15,
/*X86Register::Ymm16 => self.ymm16,
X86Register::Ymm17 => self.ymm17,
X86Register::Ymm18 => self.ymm18,
X86Register::Ymm19 => self.ymm19,
X86Register::Ymm20 => self.ymm20,
X86Register::Ymm21 => self.ymm21,
X86Register::Ymm22 => self.ymm22,
X86Register::Ymm23 => self.ymm23,
X86Register::Ymm24 => self.ymm24,
X86Register::Ymm25 => self.ymm25,
X86Register::Ymm26 => self.ymm26,
X86Register::Ymm27 => self.ymm27,
X86Register::Ymm28 => self.ymm28,
X86Register::Ymm29 => self.ymm29,
X86Register::Ymm30 => self.ymm30,
X86Register::Ymm31 => self.ymm31,*/
/*X86Register::Zmm0 => self.zmm0,
X86Register::Zmm1 => self.zmm1,
X86Register::Zmm2 => self.zmm2,
X86Register::Zmm3 => self.zmm3,
X86Register::Zmm4 => self.zmm4,
X86Register::Zmm5 => self.zmm5,
X86Register::Zmm6 => self.zmm6,
X86Register::Zmm7 => self.zmm7,
X86Register::Zmm8 => self.zmm8,
X86Register::Zmm9 => self.zmm9,
X86Register::Zmm10 => self.zmm10,
X86Register::Zmm11 => self.zmm11,
X86Register::Zmm12 => self.zmm12,
X86Register::Zmm13 => self.zmm13,
X86Register::Zmm14 => self.zmm14,
X86Register::Zmm15 => self.zmm15,
X86Register::Zmm16 => self.zmm16,
X86Register::Zmm17 => self.zmm17,
X86Register::Zmm18 => self.zmm18,
X86Register::Zmm19 => self.zmm19,
X86Register::Zmm20 => self.zmm20,
X86Register::Zmm21 => self.zmm21,
X86Register::Zmm22 => self.zmm22,
X86Register::Zmm23 => self.zmm23,
X86Register::Zmm24 => self.zmm24,
X86Register::Zmm25 => self.zmm25,
X86Register::Zmm26 => self.zmm26,
X86Register::Zmm27 => self.zmm27,
X86Register::Zmm28 => self.zmm28,
X86Register::Zmm29 => self.zmm29,
X86Register::Zmm30 => self.zmm30,
X86Register::Zmm31 => self.zmm31,*/
X86Register::R8b => self.r8b,
X86Register::R9b => self.r9b,
X86Register::R10b => self.r10b,
X86Register::R11b => self.r11b,
X86Register::R12b => self.r12b,
X86Register::R13b => self.r13b,
X86Register::R14b => self.r14b,
X86Register::R15b => self.r15b,
X86Register::R8d => self.r8d,
X86Register::R9d => self.r9d,
X86Register::R10d => self.r10d,
X86Register::R11d => self.r11d,
X86Register::R12d => self.r12d,
X86Register::R13d => self.r13d,
X86Register::R14d => self.r14d,
X86Register::R15d => self.r15d,
X86Register::R8w => self.r8w,
X86Register::R9w => self.r9w,
X86Register::R10w => self.r10w,
X86Register::R11w => self.r11w,
X86Register::R12w => self.r12w,
X86Register::R13w => self.r13w,
X86Register::R14w => self.r14w,
X86Register::R15w => self.r15w,
X86Register::Fpcw => self.fpcw,
X86Register::Fptag => self.fptag,
//X86Register::Msr => self.msr,
X86Register::Mxcsr => self.mxcsr,
X86Register::FsBase => self.fs_base,
X86Register::GsBase => self.gs_base,
X86Register::Fip => self.fip,
X86Register::Fcs => self.fcs,
X86Register::Fdp => self.fdp,
X86Register::Fds => self.fds,
X86Register::Fop => self.fop,
_ => panic!("Unsupported register"),
}
}

View File

@@ -1,5 +1,5 @@
#define ICICLE_EMULATOR_IMPL
#include "icicle_x64_emulator.hpp"
#include "icicle_x86_64_emulator.hpp"
#include <utils/object.hpp>
@@ -12,6 +12,7 @@ extern "C"
using raw_func = void(void*);
using ptr_func = void(void*, uint64_t);
using block_func = void(void*, uint64_t, uint64_t);
using interrupt_func = void(void*, int32_t);
using violation_func = int32_t(void*, uint64_t address, uint8_t operation, int32_t unmapped);
using data_accessor_func = void(void* user, const void* data, size_t length);
@@ -25,10 +26,13 @@ extern "C"
int32_t icicle_unmap_memory(icicle_emulator*, uint64_t address, uint64_t length);
int32_t icicle_read_memory(icicle_emulator*, uint64_t address, void* data, size_t length);
int32_t icicle_write_memory(icicle_emulator*, uint64_t address, const void* data, size_t length);
int32_t icicle_save_registers(icicle_emulator*, data_accessor_func* accessor, void* accessor_data);
int32_t icicle_restore_registers(icicle_emulator*, const void* data, size_t length);
void icicle_save_registers(icicle_emulator*, data_accessor_func* accessor, void* accessor_data);
void icicle_restore_registers(icicle_emulator*, const void* data, size_t length);
uint32_t icicle_create_snapshot(icicle_emulator*);
void icicle_restore_snapshot(icicle_emulator*, uint32_t id);
uint32_t icicle_add_syscall_hook(icicle_emulator*, raw_func* callback, void* data);
uint32_t icicle_add_interrupt_hook(icicle_emulator*, interrupt_func* callback, void* data);
uint32_t icicle_add_block_hook(icicle_emulator*, block_func* callback, void* data);
uint32_t icicle_add_execution_hook(icicle_emulator*, uint64_t address, ptr_func* callback, void* data);
uint32_t icicle_add_generic_execution_hook(icicle_emulator*, ptr_func* callback, void* data);
uint32_t icicle_add_violation_hook(icicle_emulator*, violation_func* callback, void* data);
@@ -85,10 +89,10 @@ namespace icicle
}
}
class icicle_x64_emulator : public x64_emulator
class icicle_x86_64_emulator : public x86_64_emulator
{
public:
icicle_x64_emulator()
icicle_x86_64_emulator()
: emu_(icicle_create_emulator())
{
if (!this->emu_)
@@ -97,7 +101,7 @@ namespace icicle
}
}
~icicle_x64_emulator() override
~icicle_x86_64_emulator() override
{
if (this->emu_)
{
@@ -128,20 +132,20 @@ namespace icicle
const gdtr entry{.limit = limit, .address = address};
static_assert(sizeof(gdtr) - offsetof(gdtr, limit) == 12);
this->write_register(x64_register::gdtr, &entry.limit, 12);
this->write_register(x86_register::gdtr, &entry.limit, 12);
}
void set_segment_base(const x64_register base, const pointer_type value) override
void set_segment_base(const x86_register base, const pointer_type value) override
{
switch (base)
{
case x64_register::fs:
case x64_register::fs_base:
this->reg(x64_register::fs_base, value);
case x86_register::fs:
case x86_register::fs_base:
this->reg(x86_register::fs_base, value);
break;
case x64_register::gs:
case x64_register::gs_base:
this->reg(x64_register::gs_base, value);
case x86_register::gs:
case x86_register::gs_base:
this->reg(x86_register::gs_base, value);
break;
default:
break;
@@ -226,7 +230,7 @@ namespace icicle
emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) override
{
if (static_cast<x64_hookable_instructions>(instruction_type) != x64_hookable_instructions::syscall)
if (static_cast<x86_hookable_instructions>(instruction_type) != x86_hookable_instructions::syscall)
{
// TODO
return nullptr;
@@ -248,16 +252,21 @@ namespace icicle
emulator_hook* hook_basic_block(basic_block_hook_callback callback) override
{
// TODO
(void)callback;
throw std::runtime_error("Not implemented");
}
auto object = make_function_object(std::move(callback));
auto* ptr = object.get();
auto* wrapper = +[](void* user, const uint64_t addr, const uint64_t instructions) {
basic_block block{};
block.address = addr;
block.instruction_count = static_cast<size_t>(instructions);
emulator_hook* hook_edge_generation(edge_generation_hook_callback callback) override
{
// TODO
(void)callback;
throw std::runtime_error("Not implemented");
const auto& func = *static_cast<decltype(ptr)>(user);
(func)(block);
};
const auto id = icicle_add_block_hook(this->emu_, wrapper, ptr);
this->hooks_[id] = std::move(object);
return wrap_hook(id);
}
emulator_hook* hook_interrupt(interrupt_hook_callback callback) override
@@ -326,7 +335,7 @@ namespace icicle
return wrap_hook(id);
}
emulator_hook* hook_memory_read(const uint64_t address, const size_t size,
emulator_hook* hook_memory_read(const uint64_t address, const uint64_t size,
memory_access_hook_callback callback) override
{
auto obj = make_function_object(std::move(callback));
@@ -342,7 +351,7 @@ namespace icicle
return wrap_hook(id);
}
emulator_hook* hook_memory_write(const uint64_t address, const size_t size,
emulator_hook* hook_memory_write(const uint64_t address, const uint64_t size,
memory_access_hook_callback callback) override
{
auto obj = make_function_object(std::move(callback));
@@ -375,21 +384,27 @@ namespace icicle
{
if (is_snapshot)
{
throw std::runtime_error("Not implemented");
const auto snapshot = icicle_create_snapshot(this->emu_);
buffer.write<uint32_t>(snapshot);
}
else
{
buffer.write_vector(this->save_registers());
}
buffer.write_vector(this->save_registers());
}
void deserialize_state(utils::buffer_deserializer& buffer, const bool is_snapshot) override
{
if (is_snapshot)
{
throw std::runtime_error("Not implemented");
const auto snapshot = buffer.read<uint32_t>();
icicle_restore_snapshot(this->emu_, snapshot);
}
else
{
const auto data = buffer.read_vector<std::byte>();
this->restore_registers(data);
}
const auto data = buffer.read_vector<std::byte>();
this->restore_registers(data);
}
std::vector<std::byte> save_registers() const override
@@ -427,8 +442,8 @@ namespace icicle
icicle_emulator* emu_{};
};
std::unique_ptr<x64_emulator> create_x64_emulator()
std::unique_ptr<x86_64_emulator> create_x86_64_emulator()
{
return std::make_unique<icicle_x64_emulator>();
return std::make_unique<icicle_x86_64_emulator>();
}
}

View File

@@ -1,7 +1,7 @@
#pragma once
#include <memory>
#include <x64_emulator.hpp>
#include <arch_emulator.hpp>
#include "platform/platform.hpp"
#ifdef ICICLE_EMULATOR_IMPL
@@ -15,5 +15,5 @@ namespace icicle
#if !MOMO_BUILD_AS_LIBRARY
ICICLE_EMULATOR_DLL_STORAGE
#endif
std::unique_ptr<x64_emulator> create_x64_emulator();
std::unique_ptr<x86_64_emulator> create_x86_64_emulator();
}

View File

@@ -10,7 +10,9 @@
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <unicorn/unicorn.h>
#ifdef __clang__

View File

@@ -1,13 +1,14 @@
#define UNICORN_EMULATOR_IMPL
#include "unicorn_x64_emulator.hpp"
#include "unicorn_x86_64_emulator.hpp"
#include <array>
#include <ranges>
#include <optional>
#include "unicorn_memory_regions.hpp"
#include "unicorn_hook.hpp"
#include "function_wrapper.hpp"
#include <ranges>
namespace unicorn
{
@@ -18,19 +19,19 @@ namespace unicorn
static_assert(static_cast<uint32_t>(memory_permission::exec) == UC_PROT_EXEC);
static_assert(static_cast<uint32_t>(memory_permission::all) == UC_PROT_ALL);
static_assert(static_cast<uint32_t>(x64_register::end) == UC_X86_REG_ENDING);
static_assert(static_cast<uint32_t>(x86_register::end) == UC_X86_REG_ENDING);
uc_x86_insn map_hookable_instruction(const x64_hookable_instructions instruction)
uc_x86_insn map_hookable_instruction(const x86_hookable_instructions instruction)
{
switch (instruction)
{
case x64_hookable_instructions::syscall:
case x86_hookable_instructions::syscall:
return UC_X86_INS_SYSCALL;
case x64_hookable_instructions::cpuid:
case x86_hookable_instructions::cpuid:
return UC_X86_INS_CPUID;
case x64_hookable_instructions::rdtsc:
case x86_hookable_instructions::rdtsc:
return UC_X86_INS_RDTSC;
case x64_hookable_instructions::rdtscp:
case x86_hookable_instructions::rdtscp:
return UC_X86_INS_RDTSCP;
default:
throw std::runtime_error("Bad instruction for mapping");
@@ -176,17 +177,6 @@ namespace unicorn
size_t size_{};
};
basic_block map_block(const uc_tb& translation_block)
{
basic_block block{};
block.address = translation_block.pc;
block.instruction_count = translation_block.icount;
block.size = translation_block.size;
return block;
}
void assert_64bit_limit(const size_t size)
{
if (size > sizeof(uint64_t))
@@ -195,27 +185,27 @@ namespace unicorn
}
}
class unicorn_x64_emulator : public x64_emulator
class unicorn_x86_64_emulator : public x86_64_emulator
{
public:
unicorn_x64_emulator()
unicorn_x86_64_emulator()
{
uce(uc_open(UC_ARCH_X86, UC_MODE_64, &this->uc_));
uce(uc_ctl_set_cpu_model(this->uc_, UC_CPU_X86_EPYC_ROME));
// uce(uc_ctl_set_cpu_model(this->uc_, UC_CPU_X86_EPYC_ROME));
#ifndef OS_WINDOWS
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#endif
uce(uc_ctl_set_tcg_buffer_size(this->uc_, 2 << 30 /* 2 gb */));
constexpr auto is_64_bit = sizeof(void*) >= 8;
uce(uc_ctl_set_tcg_buffer_size(this->uc_, (is_64_bit ? 2 : 1) << 30 /* 2 gb */));
#ifndef OS_WINDOWS
#pragma GCC diagnostic pop
#endif
}
~unicorn_x64_emulator() override
~unicorn_x86_64_emulator() override
{
this->hooks_.clear();
uc_close(this->uc_);
@@ -223,8 +213,9 @@ namespace unicorn
void start(const size_t count) override
{
this->has_violation_ = false;
const auto start = this->read_instruction_pointer();
const auto start = this->violation_ip_.value_or(this->read_instruction_pointer());
this->violation_ip_ = std::nullopt;
constexpr auto end = std::numeric_limits<uint64_t>::max();
const auto res = uc_emu_start(*this, start, end, 0, count);
if (res == UC_ERR_OK)
@@ -240,7 +231,7 @@ namespace unicorn
res == UC_ERR_WRITE_PROT || //
res == UC_ERR_FETCH_PROT;
if (!is_violation || !this->has_violation_)
if (!is_violation || !this->has_violation())
{
uce(res);
}
@@ -254,18 +245,18 @@ namespace unicorn
void load_gdt(const pointer_type address, const uint32_t limit) override
{
const std::array<uint64_t, 4> gdtr = {0, address, limit, 0};
this->write_register(x64_register::gdtr, gdtr.data(), gdtr.size() * sizeof(uint64_t));
this->write_register(x86_register::gdtr, gdtr.data(), gdtr.size() * sizeof(uint64_t));
}
void set_segment_base(const x64_register base, const pointer_type value) override
void set_segment_base(const x86_register base, const pointer_type value) override
{
constexpr auto IA32_FS_BASE_MSR = 0xC0000100;
constexpr auto IA32_GS_BASE_MSR = 0xC0000101;
struct msr_value
{
uint32_t id;
uint64_t value;
uint64_t id{};
uint64_t value{};
};
msr_value msr_val{
@@ -275,19 +266,19 @@ namespace unicorn
switch (base)
{
case x64_register::fs:
case x64_register::fs_base:
case x86_register::fs:
case x86_register::fs_base:
msr_val.id = IA32_FS_BASE_MSR;
break;
case x64_register::gs:
case x64_register::gs_base:
case x86_register::gs:
case x86_register::gs_base:
msr_val.id = IA32_GS_BASE_MSR;
break;
default:
return;
}
this->write_register(x64_register::msr, &msr_val, sizeof(msr_val));
this->write_register(x86_register::msr, &msr_val, sizeof(msr_val));
}
size_t write_raw_register(const int reg, const void* value, const size_t size) override
@@ -385,29 +376,45 @@ namespace unicorn
emulator_hook* hook_instruction(const int instruction_type, instruction_hook_callback callback) override
{
function_wrapper<int, uc_engine*> wrapper([c = std::move(callback)](uc_engine*) {
return (c() == instruction_hook_continuation::skip_instruction) ? 1 : 0;
});
unicorn_hook hook{*this};
auto container = std::make_unique<hook_container>();
const auto inst_type = static_cast<x64_hookable_instructions>(instruction_type);
const auto inst_type = static_cast<x86_hookable_instructions>(instruction_type);
if (inst_type == x64_hookable_instructions::invalid)
if (inst_type == x86_hookable_instructions::invalid)
{
function_wrapper<int, uc_engine*> wrapper([c = std::move(callback)](uc_engine*) {
return (c() == instruction_hook_continuation::skip_instruction) ? 1 : 0;
});
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN_INVALID, wrapper.get_function(),
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max()));
container->add(std::move(wrapper), std::move(hook));
}
else
else if (inst_type == x86_hookable_instructions::syscall)
{
function_wrapper<void, uc_engine*> wrapper([c = std::move(callback)](uc_engine*) { c(); });
const auto uc_instruction = map_hookable_instruction(inst_type);
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN, wrapper.get_function(),
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max(),
uc_instruction));
}
container->add(std::move(wrapper), std::move(hook));
container->add(std::move(wrapper), std::move(hook));
}
else
{
function_wrapper<int, uc_engine*> wrapper([c = std::move(callback)](uc_engine*) {
return (c() == instruction_hook_continuation::skip_instruction) ? 1 : 0;
});
const auto uc_instruction = map_hookable_instruction(inst_type);
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN, wrapper.get_function(),
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max(),
uc_instruction));
container->add(std::move(wrapper), std::move(hook));
}
auto* result = container->as_opaque_hook();
@@ -440,29 +447,6 @@ namespace unicorn
return result;
}
emulator_hook* hook_edge_generation(edge_generation_hook_callback callback) override
{
function_wrapper<void, uc_engine*, uc_tb*, uc_tb*> wrapper(
[c = std::move(callback)](uc_engine*, const uc_tb* cur_tb, const uc_tb* prev_tb) {
const auto current_block = map_block(*cur_tb);
const auto previous_block = map_block(*prev_tb);
c(current_block, previous_block);
});
unicorn_hook hook{*this};
auto container = std::make_unique<hook_container>();
uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_EDGE_GENERATED, wrapper.get_function(),
wrapper.get_user_data(), 0, std::numeric_limits<pointer_type>::max()));
container->add(std::move(wrapper), std::move(hook));
auto* result = container->as_opaque_hook();
this->hooks_.push_back(std::move(container));
return result;
}
emulator_hook* hook_interrupt(interrupt_hook_callback callback) override
{
function_wrapper<void, uc_engine*, int> wrapper(
@@ -495,14 +479,22 @@ namespace unicorn
const auto resume = c(address, static_cast<uint64_t>(size), operation, violation) ==
memory_violation_continuation::resume;
const auto has_ip_changed = ip != this->read_instruction_pointer();
const auto new_ip = this->read_instruction_pointer();
const auto has_ip_changed = ip != new_ip;
if (!resume)
{
return false;
}
this->has_violation_ = resume && has_ip_changed;
if (resume && has_ip_changed)
{
this->violation_ip_ = new_ip;
}
else
{
this->violation_ip_ = std::nullopt;
}
if (has_ip_changed)
{
@@ -556,16 +548,16 @@ namespace unicorn
return this->hook_memory_execution(address, 1, std::move(callback));
}
emulator_hook* hook_memory_read(const uint64_t address, const size_t size,
emulator_hook* hook_memory_read(const uint64_t address, const uint64_t size,
memory_access_hook_callback callback) override
{
auto read_wrapper = [c = std::move(callback)](uc_engine*, const uc_mem_type type,
const uint64_t address, const int size,
const uint64_t address, const int length,
const uint64_t value) {
const auto operation = map_memory_operation(type);
if (operation == memory_operation::read && size > 0)
if (operation == memory_operation::read && length > 0)
{
c(address, &value, std::min(static_cast<size_t>(size), sizeof(value)));
c(address, &value, std::min(static_cast<size_t>(length), sizeof(value)));
}
};
@@ -581,7 +573,7 @@ namespace unicorn
return container->as_opaque_hook();
}
emulator_hook* hook_memory_write(const uint64_t address, const size_t size,
emulator_hook* hook_memory_write(const uint64_t address, const uint64_t size,
memory_access_hook_callback callback) override
{
auto write_wrapper = [c = std::move(callback)](uc_engine*, const uc_mem_type type, const uint64_t addr,
@@ -675,7 +667,7 @@ namespace unicorn
bool has_violation() const override
{
return this->has_violation_;
return this->violation_ip_.has_value();
}
std::string get_name() const override
@@ -686,14 +678,14 @@ namespace unicorn
private:
mutable bool has_snapshots_{false};
uc_engine* uc_{};
bool has_violation_{false};
std::optional<uint64_t> violation_ip_{};
std::vector<std::unique_ptr<hook_object>> hooks_{};
std::unordered_map<uint64_t, mmio_callbacks> mmio_{};
};
}
std::unique_ptr<x64_emulator> create_x64_emulator()
std::unique_ptr<x86_64_emulator> create_x86_64_emulator()
{
return std::make_unique<unicorn_x64_emulator>();
return std::make_unique<unicorn_x86_64_emulator>();
}
}

View File

@@ -1,7 +1,7 @@
#pragma once
#include <memory>
#include <x64_emulator.hpp>
#include <arch_emulator.hpp>
#include "platform/platform.hpp"
#ifdef UNICORN_EMULATOR_IMPL
@@ -15,5 +15,5 @@ namespace unicorn
#if !MOMO_BUILD_AS_LIBRARY
UNICORN_EMULATOR_DLL_STORAGE
#endif
std::unique_ptr<x64_emulator> create_x64_emulator();
std::unique_ptr<x86_64_emulator> create_x86_64_emulator();
}

View File

@@ -11,7 +11,15 @@ target_include_directories(emulator-common INTERFACE "${CMAKE_CURRENT_LIST_DIR}"
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(emulator-common PUBLIC
Threads::Threads
zlibstatic
minidump::minidump
)
if(WIN)
target_link_libraries(emulator-common PUBLIC
ws2_32
)
endif()

View File

@@ -2,8 +2,14 @@
#if _WIN32
#include "../utils/win.hpp"
#ifdef __MINGW64__
#include <winsock2.h>
#include <ws2tcpip.h>
#include <cstdint>
#else
#include <WinSock2.h>
#include <WS2tcpip.h>
#endif
#else
#include <sys/types.h>
@@ -28,7 +34,6 @@
#ifdef _WIN32
using socklen_t = int;
#pragma comment(lib, "ws2_32.lib")
#endif
namespace network

View File

@@ -2,20 +2,31 @@
#if defined(_WIN32) || defined(_WIN64)
#define OS_WINDOWS
#if defined(_WIN64)
#define OS_WINDOWS_64
#else
#define OS_WINDOWS_32
#endif
#elif defined(__APPLE__) || defined(__MACH__)
#define OS_MAC
#elif defined(__linux__)
#define OS_LINUX
#elif defined(__EMSCRIPTEN__)
#define OS_EMSCRIPTEN
#else
#error "Unsupported platform"
#endif
#ifdef OS_WINDOWS
#define EXPORT_SYMBOL __declspec(dllexport)
#define IMPORT_SYMBOL __declspec(dllimport)
#define NO_INLINE __declspec(noinline)
#define EXPORT_SYMBOL __declspec(dllexport)
#define IMPORT_SYMBOL __declspec(dllimport)
#define NO_INLINE __declspec(noinline)
#ifndef DECLSPEC_ALIGN
#define DECLSPEC_ALIGN(n) __declspec(align(n))
#endif
#define RESTRICTED_POINTER

View File

@@ -72,7 +72,19 @@
#define SL_RETURN_SINGLE_ENTRY 0x02
#define SL_NO_CURSOR_UPDATE 0x10
#define SEC_IMAGE 0x01000000
#ifndef SEC_IMAGE
#define SEC_IMAGE 0x01000000
#define SEC_RESERVE 0x04000000
#endif
#define CTL_CODE(DeviceType, Function, Method, Access) \
(((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
#define METHOD_BUFFERED 0
#define FILE_ANY_ACCESS 0
#define FILE_READ_ACCESS (0x0001) // file & pipe
#define FILE_WRITE_ACCESS (0x0002) // file & pipe
typedef enum _FSINFOCLASS
{
@@ -97,7 +109,7 @@ typedef enum _FSINFOCLASS
FileFsMaximumInformation
} FSINFOCLASS, *PFSINFOCLASS;
typedef enum _FSINFOCLASS FS_INFORMATION_CLASS;
using FS_INFORMATION_CLASS = enum _FSINFOCLASS;
typedef enum _FILE_INFORMATION_CLASS
{
@@ -210,7 +222,7 @@ typedef enum _FILE_INFORMATION_CLASS
FileMaximumInformation
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
typedef enum _OBJECT_INFORMATION_CLASS
using OBJECT_INFORMATION_CLASS = enum _OBJECT_INFORMATION_CLASS
{
ObjectBasicInformation, // q: OBJECT_BASIC_INFORMATION
ObjectNameInformation, // q: OBJECT_NAME_INFORMATION
@@ -220,9 +232,9 @@ typedef enum _OBJECT_INFORMATION_CLASS
ObjectSessionInformation, // s: void // change object session // (requires SeTcbPrivilege)
ObjectSessionObjectInformation, // s: void // change object session // (requires SeTcbPrivilege)
MaxObjectInfoClass
} OBJECT_INFORMATION_CLASS;
};
typedef enum _HARDERROR_RESPONSE_OPTION
using HARDERROR_RESPONSE_OPTION = enum _HARDERROR_RESPONSE_OPTION
{
OptionAbortRetryIgnore,
OptionOk,
@@ -233,9 +245,9 @@ typedef enum _HARDERROR_RESPONSE_OPTION
OptionShutdownSystem,
OptionOkNoWait,
OptionCancelTryContinue
} HARDERROR_RESPONSE_OPTION;
};
typedef enum _HARDERROR_RESPONSE
using HARDERROR_RESPONSE = enum _HARDERROR_RESPONSE
{
ResponseReturnToCaller,
ResponseNotHandled,
@@ -248,9 +260,9 @@ typedef enum _HARDERROR_RESPONSE
ResponseYes,
ResponseTryAgain,
ResponseContinue
} HARDERROR_RESPONSE;
};
typedef USHORT RTL_ATOM;
using RTL_ATOM = USHORT;
template <typename Traits>
struct IO_STATUS_BLOCK
@@ -333,6 +345,17 @@ typedef struct _FILE_BASIC_INFORMATION
ULONG FileAttributes; // Specifies one or more FILE_ATTRIBUTE_XXX flags.
} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
typedef struct _FILE_NETWORK_OPEN_INFORMATION
{
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER AllocationSize;
LARGE_INTEGER EndOfFile;
ULONG FileAttributes;
} FILE_NETWORK_OPEN_INFORMATION, *PFILE_NETWORK_OPEN_INFORMATION;
typedef struct _FILE_DIRECTORY_INFORMATION
{
ULONG NextEntryOffset;
@@ -382,7 +405,20 @@ typedef struct _FILE_BOTH_DIR_INFORMATION
char16_t FileName[1];
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;
typedef struct _FILE_RENAME_INFORMATION
{
BOOLEAN ReplaceIfExists;
EMULATOR_CAST(uint64_t, HANDLE) RootDirectory;
ULONG FileNameLength;
char16_t FileName[1];
} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;
#ifndef OS_WINDOWS
typedef struct _FILE_ID_128
{
BYTE Identifier[16];
} FILE_ID_128, *PFILE_ID_128;
typedef BOOLEAN SECURITY_CONTEXT_TRACKING_MODE, *PSECURITY_CONTEXT_TRACKING_MODE;
typedef struct _SECURITY_QUALITY_OF_SERVICE
{
@@ -394,6 +430,25 @@ typedef struct _SECURITY_QUALITY_OF_SERVICE
#endif
struct EMU_FILE_STAT_BASIC_INFORMATION
{
LARGE_INTEGER FileId;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER AllocationSize;
LARGE_INTEGER EndOfFile;
ULONG FileAttributes;
ULONG ReparseTag;
ULONG NumberOfLinks;
ULONG DeviceType;
ULONG DeviceCharacteristics;
ULONG Reserved;
LARGE_INTEGER VolumeSerialNumber;
FILE_ID_128 FileId128;
};
typedef struct _PORT_VIEW64
{
ULONG Length;
@@ -411,4 +466,10 @@ typedef struct _REMOTE_PORT_VIEW64
EmulatorTraits<Emu64>::PVOID ViewBase;
} REMOTE_PORT_VIEW64, *PREMOTE_PORT_VIEW64;
typedef struct _OBJECT_HANDLE_FLAG_INFORMATION
{
BOOLEAN Inherit;
BOOLEAN ProtectFromClose;
} OBJECT_HANDLE_FLAG_INFORMATION, *POBJECT_HANDLE_FLAG_INFORMATION;
// NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)

View File

@@ -19,13 +19,13 @@
typedef struct _EMU_NT_TIB64
{
struct _EXCEPTION_REGISTRATION_RECORD* ExceptionList;
std::uint64_t* StackBase;
std::uint64_t* StackLimit;
std::uint64_t* SubSystemTib;
std::uint64_t* FibreData;
std::uint64_t* ArbitraryUserPointer;
struct _EMU_NT_TIB64* Self;
EMULATOR_CAST(std::uint64_t, struct _EXCEPTION_REGISTRATION_RECORD*) ExceptionList;
std::uint64_t StackBase;
std::uint64_t StackLimit;
std::uint64_t SubSystemTib;
std::uint64_t FibreData;
std::uint64_t ArbitraryUserPointer;
EMULATOR_CAST(std::uint64_t, struct _EMU_NT_TIB64*) Self;
} EMU_NT_TIB64;
typedef EMU_NT_TIB64* PEMU_NT_TIB64;
@@ -51,8 +51,8 @@ union PEB_BITFIELD_UNION
typedef struct _LIST_ENTRY64
{
struct _LIST_ENTRY* Flink;
struct _LIST_ENTRY* Blink;
ULONGLONG Flink;
ULONGLONG Blink;
} LIST_ENTRY64, *PLIST_ENTRY64, *RESTRICTED_POINTER PRLIST_ENTRY64;
#endif
@@ -65,17 +65,14 @@ typedef struct _PEB_LDR_DATA64
LIST_ENTRY64 InLoadOrderModuleList;
LIST_ENTRY64 InMemoryOrderModuleList;
LIST_ENTRY64 InInitializationOrderModuleList;
std::uint64_t* EntryInProgress;
std::uint64_t EntryInProgress;
BOOLEAN ShutdownInProgress;
EmulatorTraits<Emu64>::HANDLE ShutdownThreadId;
} PEB_LDR_DATA64, *PPEB_LDR_DATA64;
typedef struct _STRING64
{
USHORT Length;
USHORT MaximumLength;
char16_t* Buffer;
} STRING64, *PSTRING64, ANSI_STRING64, *PANSI_STRING64, OEM_STRING64, *POEM_STRING64;
using STRING64 = UNICODE_STRING<EmulatorTraits<Emu64>>;
using ANSI_STRING64 = STRING64;
using OEM_STRING64 = STRING64;
typedef struct _RTL_DRIVE_LETTER_CURDIR64
{
@@ -118,7 +115,7 @@ typedef struct _RTL_USER_PROCESS_PARAMETERS64
UNICODE_STRING<EmulatorTraits<Emu64>> DllPath;
UNICODE_STRING<EmulatorTraits<Emu64>> ImagePathName;
UNICODE_STRING<EmulatorTraits<Emu64>> CommandLine;
std::uint64_t* Environment;
std::uint64_t Environment;
ULONG StartingX;
ULONG StartingY;
@@ -136,21 +133,23 @@ typedef struct _RTL_USER_PROCESS_PARAMETERS64
UNICODE_STRING<EmulatorTraits<Emu64>> RuntimeData;
ARRAY_CONTAINER<RTL_DRIVE_LETTER_CURDIR64, RTL_MAX_DRIVE_LETTERS> CurrentDirectories;
std::uint64_t* EnvironmentSize;
std::uint64_t* EnvironmentVersion;
std::uint64_t EnvironmentSize;
std::uint64_t EnvironmentVersion;
std::uint64_t* PackageDependencyData;
std::uint64_t PackageDependencyData;
ULONG ProcessGroupId;
ULONG LoaderThreads;
UNICODE_STRING<EmulatorTraits<Emu64>> RedirectionDllName; // REDSTONE4
UNICODE_STRING<EmulatorTraits<Emu64>> HeapPartitionName; // 19H1
std::uint64_t* DefaultThreadpoolCpuSetMasks;
std::uint64_t DefaultThreadpoolCpuSetMasks;
ULONG DefaultThreadpoolCpuSetMaskCount;
ULONG DefaultThreadpoolThreadMaximum;
ULONG HeapMemoryTypeMask; // WIN11
} RTL_USER_PROCESS_PARAMETERS64, *PRTL_USER_PROCESS_PARAMETERS64;
static_assert(sizeof(RTL_USER_PROCESS_PARAMETERS64) == 0x448);
union PEB_CROSS_PROCESS_FLAGS_UNION
{
ULONG CrossProcessFlags;
@@ -171,8 +170,8 @@ union PEB_CROSS_PROCESS_FLAGS_UNION
union PEB_KERNEL_CALLBACK_TABLE_UNION64
{
void* KernelCallbackTable;
void* UserSharedInfoPtr;
std::uint64_t KernelCallbackTable;
std::uint64_t UserSharedInfoPtr;
};
typedef struct _API_SET_NAMESPACE
@@ -188,9 +187,9 @@ typedef struct _API_SET_NAMESPACE
union PEB_CONTEXT_DATA_UNION64
{
void* pContextData; // WIN7
void* pUnused; // WIN10
void* EcCodeBitMap; // WIN11
std::uint64_t pContextData; // WIN7
std::uint64_t pUnused; // WIN10
std::uint64_t EcCodeBitMap; // WIN11
};
union PEB_TRACING_FLAGS_UNION
@@ -229,18 +228,18 @@ typedef struct _CPTABLEINFO
USHORT TransUniDefaultChar;
USHORT DBCSCodePage;
UCHAR LeadByte[MAXIMUM_LEADBYTES];
USHORT* MultiByteTable;
void* WideCharTable;
USHORT* DBCSRanges;
USHORT* DBCSOffsets;
EMULATOR_CAST(uint64_t, USHORT*) MultiByteTable;
EMULATOR_CAST(uint64_t, void*) WideCharTable;
EMULATOR_CAST(uint64_t, USHORT*) DBCSRanges;
EMULATOR_CAST(uint64_t, USHORT*) DBCSOffsets;
} CPTABLEINFO, *PCPTABLEINFO;
typedef struct _NLSTABLEINFO
{
CPTABLEINFO OemTableInfo;
CPTABLEINFO AnsiTableInfo;
USHORT* UpperCaseTable;
USHORT* LowerCaseTable;
EMULATOR_CAST(uint64_t, USHORT*) UpperCaseTable;
EMULATOR_CAST(uint64_t, USHORT*) LowerCaseTable;
} NLSTABLEINFO, *PNLSTABLEINFO;
typedef struct _PEB64
@@ -253,26 +252,26 @@ typedef struct _PEB64
EmulatorTraits<Emu64>::HANDLE Mutant;
std::uint64_t ImageBaseAddress;
PPEB_LDR_DATA64 Ldr;
PRTL_USER_PROCESS_PARAMETERS64 ProcessParameters;
std::uint64_t* SubSystemData;
std::uint64_t* ProcessHeap;
EMULATOR_CAST(void*, PRTL_CRITICAL_SECTION) FastPebLock;
EMULATOR_CAST(void*, PSLIST_HEADER) AtlThunkSListPtr;
std::uint64_t* IFEOKey;
EMULATOR_CAST(std::uint64_t, PPEB_LDR_DATA64) Ldr;
EMULATOR_CAST(std::uint64_t, PRTL_USER_PROCESS_PARAMETERS64) ProcessParameters;
std::uint64_t SubSystemData;
std::uint64_t ProcessHeap;
EMULATOR_CAST(std::uint64_t, PRTL_CRITICAL_SECTION) FastPebLock;
EMULATOR_CAST(std::uint64_t, PSLIST_HEADER) AtlThunkSListPtr;
std::uint64_t IFEOKey;
PEB_CROSS_PROCESS_FLAGS_UNION CrossProcessFlags;
PEB_KERNEL_CALLBACK_TABLE_UNION64 KernelCallbackTable;
ULONG SystemReserved;
ULONG AtlThunkSListPtr32;
PAPI_SET_NAMESPACE ApiSetMap;
EMULATOR_CAST(std::uint64_t, PAPI_SET_NAMESPACE) ApiSetMap;
ULONG TlsExpansionCounter;
EMULATOR_CAST(void*, PRTL_BITMAP) TlsBitmap;
EMULATOR_CAST(std::uint64_t, PRTL_BITMAP) TlsBitmap;
ARRAY_CONTAINER<ULONG, 2> TlsBitmapBits; // TLS_MINIMUM_AVAILABLE
void* ReadOnlySharedMemoryBase;
EMULATOR_CAST(void*, PSILO_USER_SHARED_DATA) SharedData; // HotpatchInformation
std::uint64_t** ReadOnlyStaticServerData;
std::uint64_t ReadOnlySharedMemoryBase;
EMULATOR_CAST(std::uint64_t, PSILO_USER_SHARED_DATA) SharedData; // HotpatchInformation
std::uint64_t ReadOnlyStaticServerData;
EMULATOR_CAST(EmulatorTraits<Emu64>::PVOID, PCPTABLEINFO) AnsiCodePageData; // PCPTABLEINFO
EMULATOR_CAST(EmulatorTraits<Emu64>::PVOID, PCPTABLEINFO) OemCodePageData; // PCPTABLEINFO
@@ -289,13 +288,13 @@ typedef struct _PEB64
ULONG NumberOfHeaps;
ULONG MaximumNumberOfHeaps;
std::uint64_t** ProcessHeaps; // PHEAP
std::uint64_t ProcessHeaps; // PHEAP
std::uint64_t GdiSharedHandleTable; // PGDI_SHARED_MEMORY
std::uint64_t* ProcessStarterHelper;
std::uint64_t ProcessStarterHelper;
ULONG GdiDCAttributeList;
EMULATOR_CAST(void*, PRTL_CRITICAL_SECTION) LoaderLock;
EMULATOR_CAST(std::uint64_t, PRTL_CRITICAL_SECTION) LoaderLock;
ULONG OSMajorVersion;
ULONG OSMinorVersion;
@@ -307,30 +306,30 @@ typedef struct _PEB64
ULONG ImageSubsystemMinorVersion;
EMULATOR_CAST(std::uint64_t, KAFFINITY) ActiveProcessAffinityMask;
ARRAY_CONTAINER<ULONG, GDI_HANDLE_BUFFER_SIZE64> GdiHandleBuffer;
std::uint64_t* PostProcessInitRoutine;
std::uint64_t PostProcessInitRoutine;
EMULATOR_CAST(void*, PRTL_BITMAP) TlsExpansionBitmap;
EMULATOR_CAST(std::uint64_t, PRTL_BITMAP) TlsExpansionBitmap;
ARRAY_CONTAINER<ULONG, 32> TlsExpansionBitmapBits; // TLS_EXPANSION_SLOTS
ULONG SessionId;
ULARGE_INTEGER AppCompatFlags; // KACF_*
ULARGE_INTEGER AppCompatFlagsUser;
std::uint64_t* pShimData;
std::uint64_t* AppCompatInfo; // APPCOMPAT_EXE_DATA
std::uint64_t pShimData;
std::uint64_t AppCompatInfo; // APPCOMPAT_EXE_DATA
UNICODE_STRING<EmulatorTraits<Emu64>> CSDVersion;
EMULATOR_CAST(void*, PACTIVATION_CONTEXT_DATA) ActivationContextData;
EMULATOR_CAST(void*, PASSEMBLY_STORAGE_MAP) ProcessAssemblyStorageMap;
EMULATOR_CAST(void*, PACTIVATION_CONTEXT_DATA) SystemDefaultActivationContextData;
EMULATOR_CAST(void*, PASSEMBLY_STORAGE_MAP) SystemAssemblyStorageMap;
EMULATOR_CAST(std::uint64_t, PACTIVATION_CONTEXT_DATA) ActivationContextData;
EMULATOR_CAST(std::uint64_t, PASSEMBLY_STORAGE_MAP) ProcessAssemblyStorageMap;
EMULATOR_CAST(std::uint64_t, PACTIVATION_CONTEXT_DATA) SystemDefaultActivationContextData;
EMULATOR_CAST(std::uint64_t, PASSEMBLY_STORAGE_MAP) SystemAssemblyStorageMap;
EMULATOR_CAST(std::int64_t, SIZE_T) MinimumStackCommit;
EMULATOR_CAST(std::uint64_t, SIZE_T) MinimumStackCommit;
ARRAY_CONTAINER<std::uint64_t*, 2> SparePointers; // 19H1 (previously FlsCallback to FlsHighIndex)
std::uint64_t* PatchLoaderData;
std::uint64_t* ChpeV2ProcessInfo; // _CHPEV2_PROCESS_INFO
ARRAY_CONTAINER<std::uint64_t, 2> SparePointers; // 19H1 (previously FlsCallback to FlsHighIndex)
std::uint64_t PatchLoaderData;
std::uint64_t ChpeV2ProcessInfo; // _CHPEV2_PROCESS_INFO
ULONG AppModelFeatureState;
ARRAY_CONTAINER<ULONG, 2> SpareUlongs;
@@ -340,40 +339,42 @@ typedef struct _PEB64
USHORT UseCaseMapping;
USHORT UnusedNlsField;
std::uint64_t* WerRegistrationData;
std::uint64_t* WerShipAssertPtr;
std::uint64_t WerRegistrationData;
std::uint64_t WerShipAssertPtr;
PEB_CONTEXT_DATA_UNION64 ContextData;
std::uint64_t* pImageHeaderHash;
std::uint64_t pImageHeaderHash;
PEB_TRACING_FLAGS_UNION TracingFlags;
ULONGLONG CsrServerReadOnlySharedMemoryBase;
EMULATOR_CAST(void*, PRTL_CRITICAL_SECTION) TppWorkerpListLock;
EMULATOR_CAST(std::uint64_t, PRTL_CRITICAL_SECTION) TppWorkerpListLock;
LIST_ENTRY64 TppWorkerpList;
ARRAY_CONTAINER<std::uint64_t*, 128> WaitOnAddressHashTable;
EMULATOR_CAST(void*, PTELEMETRY_COVERAGE_HEADER) TelemetryCoverageHeader; // REDSTONE3
ARRAY_CONTAINER<std::uint64_t, 128> WaitOnAddressHashTable;
EMULATOR_CAST(std::uint64_t, PTELEMETRY_COVERAGE_HEADER) TelemetryCoverageHeader; // REDSTONE3
ULONG CloudFileFlags;
ULONG CloudFileDiagFlags; // REDSTONE4
CHAR PlaceholderCompatibilityMode;
ARRAY_CONTAINER<CHAR, 7> PlaceholderCompatibilityModeReserved;
EMULATOR_CAST(void*, PLEAP_SECOND_DATA) LeapSecondData; // REDSTONE5
EMULATOR_CAST(std::uint64_t, PLEAP_SECOND_DATA) LeapSecondData; // REDSTONE5
PEB_LEAP_SECONDS_FLAG_UNION LeapSecondFlags;
ULONG NtGlobalFlag2;
ULONGLONG ExtendedFeatureDisableMask; // since WIN11
} PEB64, *PPEB64;
static_assert(sizeof(PEB64) == 0x7D0);
typedef struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME64
{
struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME* Previous;
EMULATOR_CAST(void*, ACTIVATION_CONTEXT) ActivationContext;
EMULATOR_CAST(std::uint64_t, ACTIVATION_CONTEXT) ActivationContext;
ULONG Flags; // RTL_ACTIVATION_CONTEXT_STACK_FRAME_FLAG_*
} RTL_ACTIVATION_CONTEXT_STACK_FRAME64, *PRTL_ACTIVATION_CONTEXT_STACK_FRAME64;
typedef struct _ACTIVATION_CONTEXT_STACK64
{
PRTL_ACTIVATION_CONTEXT_STACK_FRAME64 ActiveFrame;
EMULATOR_CAST(std::uint64_t, PRTL_ACTIVATION_CONTEXT_STACK_FRAME64) ActiveFrame;
LIST_ENTRY64 FrameListCache;
ULONG Flags; // ACTIVATION_CONTEXT_STACK_FLAG_*
ULONG NextCookieSequenceNumber;
@@ -383,7 +384,7 @@ typedef struct _ACTIVATION_CONTEXT_STACK64
typedef struct _GDI_TEB_BATCH64
{
ULONG Offset;
std::uint64_t* HDC;
std::uint64_t HDC;
ULONG Buffer[GDI_BATCH_BUFFER_SIZE];
} GDI_TEB_BATCH64, *PGDI_TEB_BATCH64;
@@ -452,31 +453,32 @@ union TEB_SAME_TEB_FLAGS_UNION
#ifndef OS_WINDOWS
using LCID = DWORD;
using LANGID = WORD;
#endif
typedef struct _TEB64
{
EMU_NT_TIB64 NtTib;
std::uint64_t* EnvironmentPointer;
std::uint64_t EnvironmentPointer;
CLIENT_ID64 ClientId;
std::uint64_t* ActiveRpcHandle;
std::uint64_t* ThreadLocalStoragePointer;
PPEB64 ProcessEnvironmentBlock;
std::uint64_t ActiveRpcHandle;
std::uint64_t ThreadLocalStoragePointer;
EMULATOR_CAST(std::uint64_t, PPEB64) ProcessEnvironmentBlock;
ULONG LastErrorValue;
ULONG CountOfOwnedCriticalSections;
std::uint64_t* CsrClientThread;
std::uint64_t* Win32ThreadInfo;
std::uint64_t CsrClientThread;
std::uint64_t Win32ThreadInfo;
ARRAY_CONTAINER<ULONG, 26> User32Reserved;
ARRAY_CONTAINER<ULONG, 5> UserReserved;
std::uint64_t* WOW32Reserved;
std::uint64_t WOW32Reserved;
LCID CurrentLocale;
ULONG FpSoftwareStatusRegister;
ARRAY_CONTAINER<void*, 16> ReservedForDebuggerInstrumentation;
ARRAY_CONTAINER<void*, 25> SystemReserved1;
std::uint64_t* HeapFlsData;
ARRAY_CONTAINER<std::uint64_t*, 4> RngState;
ARRAY_CONTAINER<std::uint64_t, 16> ReservedForDebuggerInstrumentation;
ARRAY_CONTAINER<std::uint64_t, 25> SystemReserved1;
std::uint64_t HeapFlsData;
ARRAY_CONTAINER<std::uint64_t, 4> RngState;
CHAR PlaceholderCompatibilityMode;
BOOLEAN PlaceholderHydrationAlwaysExplicit;
ARRAY_CONTAINER<CHAR, 10> PlaceholderReserved;
@@ -488,10 +490,10 @@ typedef struct _TEB64
NTSTATUS ExceptionCode;
PACTIVATION_CONTEXT_STACK64 ActivationContextStackPointer;
std::uint64_t* InstrumentationCallbackSp;
std::uint64_t* InstrumentationCallbackPreviousPc;
std::uint64_t* InstrumentationCallbackPreviousSp;
EMULATOR_CAST(std::uint64_t, PACTIVATION_CONTEXT_STACK64) ActivationContextStackPointer;
std::uint64_t InstrumentationCallbackSp;
std::uint64_t InstrumentationCallbackPreviousPc;
std::uint64_t InstrumentationCallbackPreviousSp;
ULONG TxFsContext;
BOOLEAN InstrumentationCallbackDisabled;
BOOLEAN UnalignedLoadStoreExceptions;
@@ -500,90 +502,92 @@ typedef struct _TEB64
EmulatorTraits<Emu64>::HANDLE GdiCachedProcessHandle;
ULONG GdiClientPID;
ULONG GdiClientTID;
std::uint64_t* GdiThreadLocalInfo;
ARRAY_CONTAINER<std::uint64_t*, WIN32_CLIENT_INFO_LENGTH> Win32ClientInfo;
std::uint64_t GdiThreadLocalInfo;
ARRAY_CONTAINER<std::uint64_t, WIN32_CLIENT_INFO_LENGTH> Win32ClientInfo;
ARRAY_CONTAINER<void*, 233> glDispatchTable;
ARRAY_CONTAINER<std::uint64_t*, 29> glReserved1;
std::uint64_t* glReserved2;
std::uint64_t* glSectionInfo;
std::uint64_t* glSection;
std::uint64_t* glTable;
std::uint64_t* glCurrentRC;
std::uint64_t* glContext;
ARRAY_CONTAINER<std::uint64_t, 233> glDispatchTable;
ARRAY_CONTAINER<std::uint64_t, 29> glReserved1;
std::uint64_t glReserved2;
std::uint64_t glSectionInfo;
std::uint64_t glSection;
std::uint64_t glTable;
std::uint64_t glCurrentRC;
std::uint64_t glContext;
NTSTATUS LastStatusValue;
UNICODE_STRING<EmulatorTraits<Emu64>> StaticUnicodeString;
ARRAY_CONTAINER<char16_t, STATIC_UNICODE_BUFFER_LENGTH> StaticUnicodeBuffer;
std::uint64_t* DeallocationStack;
std::uint64_t DeallocationStack;
ARRAY_CONTAINER<std::uint64_t*, TLS_MINIMUM_AVAILABLE> TlsSlots;
ARRAY_CONTAINER<std::uint64_t, TLS_MINIMUM_AVAILABLE> TlsSlots;
LIST_ENTRY64 TlsLinks;
std::uint64_t* Vdm;
std::uint64_t* ReservedForNtRpc;
ARRAY_CONTAINER<void*, 2> DbgSsReserved;
std::uint64_t Vdm;
std::uint64_t ReservedForNtRpc;
ARRAY_CONTAINER<std::uint64_t, 2> DbgSsReserved;
ULONG HardErrorMode;
ARRAY_CONTAINER<void*, 11> Instrumentation;
ARRAY_CONTAINER<std::uint64_t, 11> Instrumentation;
GUID ActivityId;
std::uint64_t* SubProcessTag;
std::uint64_t* PerflibData;
std::uint64_t* EtwTraceData;
std::uint64_t* WinSockData;
std::uint64_t SubProcessTag;
std::uint64_t PerflibData;
std::uint64_t EtwTraceData;
std::uint64_t WinSockData;
ULONG GdiBatchCount;
TEB_CURRENT_IDEAL_PROCESSOR_UNION CurrentIdealProcessor;
ULONG GuaranteedStackBytes;
std::uint64_t* ReservedForPerf;
std::uint64_t* ReservedForOle; // tagSOleTlsData
std::uint64_t ReservedForPerf;
std::uint64_t ReservedForOle; // tagSOleTlsData
ULONG WaitingOnLoaderLock;
std::uint64_t* SavedPriorityState;
std::uint64_t* ReservedForCodeCoverage;
std::uint64_t* ThreadPoolData;
std::uint64_t** TlsExpansionSlots;
std::uint64_t* ChpeV2CpuAreaInfo; // CHPEV2_CPUAREA_INFO // previously DeallocationBStore
std::uint64_t* Unused; // previously BStoreLimit
std::uint64_t SavedPriorityState;
std::uint64_t ReservedForCodeCoverage;
std::uint64_t ThreadPoolData;
std::uint64_t TlsExpansionSlots;
std::uint64_t ChpeV2CpuAreaInfo; // CHPEV2_CPUAREA_INFO // previously DeallocationBStore
std::uint64_t Unused; // previously BStoreLimit
ULONG MuiGeneration;
ULONG IsImpersonating;
std::uint64_t* NlsCache;
std::uint64_t* pShimData;
std::uint64_t NlsCache;
std::uint64_t pShimData;
ULONG HeapData;
EmulatorTraits<Emu64>::HANDLE CurrentTransactionHandle;
EMULATOR_CAST(void*, PTEB_ACTIVE_FRAME) ActiveFrame;
std::uint64_t* FlsData;
EMULATOR_CAST(std::uint64_t, PTEB_ACTIVE_FRAME) ActiveFrame;
std::uint64_t FlsData;
std::uint64_t* PreferredLanguages;
std::uint64_t* UserPrefLanguages;
std::uint64_t* MergedPrefLanguages;
std::uint64_t PreferredLanguages;
std::uint64_t UserPrefLanguages;
std::uint64_t MergedPrefLanguages;
ULONG MuiImpersonation;
TEB_CROSS_TEB_FLAGS_UNION CrossTebFlags;
TEB_SAME_TEB_FLAGS_UNION SameTebFlags;
std::uint64_t* TxnScopeEnterCallback;
std::uint64_t* TxnScopeExitCallback;
std::uint64_t* TxnScopeContext;
std::uint64_t TxnScopeEnterCallback;
std::uint64_t TxnScopeExitCallback;
std::uint64_t TxnScopeContext;
ULONG LockCount;
LONG WowTebOffset;
std::uint64_t* ResourceRetValue;
std::uint64_t* ReservedForWdf;
std::uint64_t ResourceRetValue;
std::uint64_t ReservedForWdf;
ULONGLONG ReservedForCrt;
GUID EffectiveContainerId;
ULONGLONG LastSleepCounter; // Win11
ULONG SpinCallCount;
ULONGLONG ExtendedFeatureDisableMask;
std::uint64_t* SchedulerSharedDataSlot; // 24H2
std::uint64_t* HeapWalkContext;
std::uint64_t SchedulerSharedDataSlot; // 24H2
std::uint64_t HeapWalkContext;
EMU_GROUP_AFFINITY64 PrimaryGroupAffinity;
ARRAY_CONTAINER<ULONG, 2> Rcu;
} TEB64, *PTEB64;
#ifdef OS_WINDOWS
static_assert(sizeof(TEB64) == 0x1878);
#if defined(OS_WINDOWS) && defined(_WIN64)
inline TEB64* NtCurrentTeb64()
{
return reinterpret_cast<TEB64*>(__readgsqword(FIELD_OFFSET(EMU_NT_TIB64, Self)));
@@ -838,7 +842,7 @@ struct PS_ATTRIBUTE
typename Traits::PVOID ValuePtr;
};
typename Traits::SIZE_T* ReturnLength;
EMULATOR_CAST(uint64_t, typename Traits::SIZE_T*) ReturnLength;
};
template <typename Traits>
@@ -859,10 +863,46 @@ typedef struct _SYSTEM_TIMEOFDAY_INFORMATION64
ULONGLONG SleepTimeBias;
} SYSTEM_TIMEOFDAY_INFORMATION64, *PSYSTEM_TIMEOFDAY_INFORMATION64;
typedef struct _SYSTEMTIME64
{
WORD wYear;
WORD wMonth;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
WORD wDayOfWeek;
} SYSTEMTIME64, *PSYSTEMTIME64, *LPSYSTEMTIME64;
typedef struct _SYSTEM_TIMEZONE_INFORMATION
{
LONG Bias;
ARRAY_CONTAINER<char16_t, 32> StandardName;
SYSTEMTIME64 StandardDate;
LONG StandardBias;
ARRAY_CONTAINER<char16_t, 32> DaylightName;
SYSTEMTIME64 DaylightDate;
LONG DaylightBias;
} SYSTEM_TIMEZONE_INFORMATION, *PSYSTEM_TIMEZONE_INFORMATION;
typedef struct _SYSTEM_DYNAMIC_TIMEZONE_INFORMATION
{
LONG Bias;
ARRAY_CONTAINER<char16_t, 32> StandardName;
SYSTEMTIME64 StandardDate;
LONG StandardBias;
ARRAY_CONTAINER<char16_t, 32> DaylightName;
SYSTEMTIME64 DaylightDate;
LONG DaylightBias;
ARRAY_CONTAINER<char16_t, 128> TimeZoneKeyName;
BOOLEAN DynamicDaylightTimeDisabled;
} SYSTEM_DYNAMIC_TIMEZONE_INFORMATION, *PSYSTEM_DYNAMIC_TIMEZONE_INFORMATION;
typedef struct _PROCESS_BASIC_INFORMATION64
{
NTSTATUS ExitStatus;
PPEB64 PebBaseAddress;
EMULATOR_CAST(uint64_t, PPEB64) PebBaseAddress;
EMULATOR_CAST(std::uint64_t, KAFFINITY) AffinityMask;
EMULATOR_CAST(std::uint32_t, KPRIORITY) BasePriority;
EMULATOR_CAST(std::uint64_t, HANDLE) UniqueProcessId;
@@ -880,10 +920,11 @@ typedef struct _KERNEL_USER_TIMES
struct THREAD_TLS_INFO
{
ULONG Flags;
uint32_t _Padding;
union
{
EmulatorTraits<Emu64>::PVOID* TlsVector;
EmulatorTraits<Emu64>::PVOID TlsVector;
EmulatorTraits<Emu64>::PVOID TlsModulePointer;
};
@@ -916,4 +957,39 @@ struct PROCESS_TLS_INFO
static_assert(sizeof(PROCESS_TLS_INFO) - sizeof(THREAD_TLS_INFO) == 0x10);
struct EMU_GENERIC_MAPPING
{
ACCESS_MASK GenericRead;
ACCESS_MASK GenericWrite;
ACCESS_MASK GenericExecute;
ACCESS_MASK GenericAll;
};
struct OBJECT_TYPE_INFORMATION
{
STRING64 TypeName;
ULONG TotalNumberOfObjects;
ULONG TotalNumberOfHandles;
ULONG TotalPagedPoolUsage;
ULONG TotalNonPagedPoolUsage;
ULONG TotalNamePoolUsage;
ULONG TotalHandleTableUsage;
ULONG HighWaterNumberOfObjects;
ULONG HighWaterNumberOfHandles;
ULONG HighWaterPagedPoolUsage;
ULONG HighWaterNonPagedPoolUsage;
ULONG HighWaterNamePoolUsage;
ULONG HighWaterHandleTableUsage;
ULONG InvalidAttributes;
EMU_GENERIC_MAPPING GenericMapping;
ULONG ValidAccessMask;
BOOLEAN SecurityRequired;
BOOLEAN MaintainHandleCount;
UCHAR TypeIndex; // since WINBLUE
CHAR ReservedByte;
ULONG PoolType;
ULONG DefaultPagedPoolCharge;
ULONG DefaultNonPagedPoolCharge;
};
// NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)

View File

@@ -2,23 +2,24 @@
// NOLINTBEGIN(modernize-use-using)
#define PAGE_EXECUTE 0x10
#define PAGE_EXECUTE_READ 0x20
#define PAGE_EXECUTE_READWRITE 0x40
#define PAGE_EXECUTE_WRITECOPY 0x80
#define PAGE_EXECUTE 0x10
#define PAGE_EXECUTE_READ 0x20
#define PAGE_EXECUTE_READWRITE 0x40
#define PAGE_EXECUTE_WRITECOPY 0x80
#define PAGE_NOACCESS 0x01
#define PAGE_READONLY 0x02
#define PAGE_READWRITE 0x04
#define PAGE_WRITECOPY 0x08
#define PAGE_NOACCESS 0x01
#define PAGE_READONLY 0x02
#define PAGE_READWRITE 0x04
#define PAGE_WRITECOPY 0x08
#define PAGE_TARGETS_INVALID 0x40000000
#define PAGE_TARGETS_NO_UPDATE 0x40000000
#define PAGE_TARGETS_INVALID 0x40000000
#define PAGE_TARGETS_NO_UPDATE 0x40000000
#define PAGE_GUARD 0x100
#define PAGE_NOCACHE 0x200
#define PAGE_WRITECOMBINE 0x400
#define PAGE_GUARD 0x100
#define PAGE_NOCACHE 0x200
#define PAGE_WRITECOMBINE 0x400
#ifndef MEM_64K_PAGES
#define MEM_COMMIT 0x00001000
#define MEM_RESERVE 0x00002000
#define MEM_DECOMMIT 0x00004000
@@ -37,6 +38,7 @@
#define MEM_DOS_LIM 0x40000000
#define MEM_4MB_PAGES 0x80000000
#define MEM_64K_PAGES (MEM_LARGE_PAGES | MEM_PHYSICAL)
#endif
typedef enum _MEMORY_INFORMATION_CLASS
{
@@ -64,10 +66,14 @@ typedef enum _SECTION_INHERIT
ViewUnmap = 2
} SECTION_INHERIT;
typedef struct DECLSPEC_ALIGN(16) _EMU_MEMORY_BASIC_INFORMATION64
typedef struct
#ifndef __MINGW64__
DECLSPEC_ALIGN(16)
#endif
_EMU_MEMORY_BASIC_INFORMATION64
{
void* BaseAddress;
void* AllocationBase;
uint64_t BaseAddress;
uint64_t AllocationBase;
DWORD AllocationProtect;
WORD PartitionId;
std::int64_t RegionSize;
@@ -78,7 +84,7 @@ typedef struct DECLSPEC_ALIGN(16) _EMU_MEMORY_BASIC_INFORMATION64
typedef struct _MEMORY_IMAGE_INFORMATION64
{
void* ImageBase;
uint64_t ImageBase;
std::int64_t SizeOfImage;
union
@@ -98,7 +104,7 @@ typedef struct _MEMORY_IMAGE_INFORMATION64
typedef struct _MEMORY_REGION_INFORMATION
{
void* AllocationBase;
uint64_t AllocationBase;
ULONG AllocationProtect;
union

View File

@@ -1,6 +1,6 @@
#pragma once
#ifdef _WIN32
#if defined(_WIN32) && !defined(__MINGW64__)
#pragma warning(push)
#pragma warning(disable : 4201) // nameless struct/union
#pragma warning(disable : 4702) // unreachable code
@@ -25,11 +25,12 @@
#include "registry.hpp"
#include "network.hpp"
#include "threading.hpp"
#include "window.hpp"
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
#ifdef OS_WINDOWS
#if defined(_WIN32) && !defined(__MINGW64__)
#pragma warning(pop)
#endif

View File

@@ -22,6 +22,8 @@ using ULONG = DWORD;
using DWORD64 = std::uint64_t;
using ULONGLONG = DWORD64;
using LONGLONG = std::int64_t;
using UINT = std::uint32_t;
using BOOL = std::int32_t;
typedef union _ULARGE_INTEGER
{
@@ -64,4 +66,9 @@ using USHORT = WORD;
#define FALSE 0
#endif
static_assert(sizeof(DWORD) == 4);
static_assert(sizeof(ULONG) == 4);
static_assert(sizeof(int) == 4);
static_assert(sizeof(BOOLEAN) == 1);
// NOLINTEND(modernize-use-using)

View File

@@ -25,7 +25,7 @@
(CONTEXT_CONTROL_64 | CONTEXT_INTEGER_64 | CONTEXT_SEGMENTS_64 | CONTEXT_FLOATING_POINT_64 | \
CONTEXT_DEBUG_REGISTERS_64)
typedef enum _SYSTEM_INFORMATION_CLASS
using SYSTEM_INFORMATION_CLASS = enum _SYSTEM_INFORMATION_CLASS
{
SystemBasicInformation, // q: SYSTEM_BASIC_INFORMATION
SystemProcessorInformation, // q: SYSTEM_PROCESSOR_INFORMATION
@@ -323,7 +323,7 @@ typedef enum _SYSTEM_INFORMATION_CLASS
SystemBreakOnContextUnwindFailureInformation, // ULONG (requires SeDebugPrivilege)
SystemOslRamdiskInformation, // SYSTEM_OSL_RAMDISK_INFORMATION
MaxSystemInfoClass
} SYSTEM_INFORMATION_CLASS;
};
#ifndef OS_WINDOWS
typedef enum _TOKEN_INFORMATION_CLASS
@@ -383,7 +383,7 @@ typedef enum _TOKEN_INFORMATION_CLASS
#endif
typedef enum _PROCESSINFOCLASS
using PROCESSINFOCLASS = enum _PROCESSINFOCLASS
{
ProcessBasicInformation, // q: PROCESS_BASIC_INFORMATION, PROCESS_EXTENDED_BASIC_INFORMATION
ProcessQuotaLimits, // qs: QUOTA_LIMITS, QUOTA_LIMITS_EX
@@ -502,9 +502,9 @@ typedef enum _PROCESSINFOCLASS
ProcessNetworkIoCounters, // q: PROCESS_NETWORK_COUNTERS
ProcessFindFirstThreadByTebValue, // PROCESS_TEB_VALUE_INFORMATION
MaxProcessInfoClass
} PROCESSINFOCLASS;
};
typedef enum _PS_ATTRIBUTE_NUM
using PS_ATTRIBUTE_NUM = enum _PS_ATTRIBUTE_NUM
{
PsAttributeParentProcess, // in HANDLE
PsAttributeDebugObject, // in HANDLE
@@ -542,7 +542,7 @@ typedef enum _PS_ATTRIBUTE_NUM
PsAttributeSupportedMachines, // since 24H2
PsAttributeSveVectorLength, // PPS_PROCESS_CREATION_SVE_VECTOR_LENGTH
PsAttributeMax
} PS_ATTRIBUTE_NUM;
};
struct SYSTEM_PROCESSOR_INFORMATION64
{
@@ -553,13 +553,15 @@ struct SYSTEM_PROCESSOR_INFORMATION64
ULONG ProcessorFeatureBits;
};
#ifndef OS_WINDOWS
#if !defined(OS_WINDOWS) || !defined(_WIN64)
#if !defined(OS_WINDOWS)
typedef struct _M128A
{
ULONGLONG Low;
LONGLONG High;
} M128A, *PM128A;
#endif
typedef struct _XMM_SAVE_AREA32
{
@@ -583,13 +585,17 @@ typedef struct _XMM_SAVE_AREA32
#endif
typedef struct _NEON128
using NEON128 = struct _NEON128
{
ULONGLONG Low;
LONGLONG High;
} NEON128;
};
typedef struct DECLSPEC_ALIGN(16) _CONTEXT64
typedef struct
#if !defined(__MINGW64__)
DECLSPEC_ALIGN(16)
#endif
_CONTEXT64
{
DWORD64 P1Home;
DWORD64 P2Home;
@@ -768,6 +774,54 @@ struct TOKEN_USER64
SID_AND_ATTRIBUTES64 User;
};
struct TOKEN_GROUPS64
{
ULONG GroupCount;
SID_AND_ATTRIBUTES64 Groups[1];
};
struct TOKEN_OWNER64
{
EMULATOR_CAST(EmulatorTraits<Emu64>::PVOID, PSID) Owner;
};
struct TOKEN_PRIMARY_GROUP64
{
EMULATOR_CAST(EmulatorTraits<Emu64>::PVOID, PSID) PrimaryGroup;
};
#ifndef OS_WINDOWS
struct ACL
{
BYTE AclRevision;
BYTE Sbz1;
WORD AclSize;
WORD AceCount;
WORD Sbz2;
};
struct ACE_HEADER
{
BYTE AceType;
BYTE AceFlags;
WORD AceSize;
};
typedef DWORD ACCESS_MASK;
struct ACCESS_ALLOWED_ACE
{
ACE_HEADER Header;
ACCESS_MASK Mask;
DWORD SidStart;
};
#endif
struct TOKEN_DEFAULT_DACL64
{
EMULATOR_CAST(EmulatorTraits<Emu64>::PVOID, PACL) DefaultDacl;
};
struct TOKEN_BNO_ISOLATION_INFORMATION64
{
EmulatorTraits<Emu64>::PVOID IsolationPrefix;
@@ -779,6 +833,11 @@ struct TOKEN_MANDATORY_LABEL64
SID_AND_ATTRIBUTES64 Label;
};
struct TOKEN_PROCESS_TRUST_LEVEL64
{
EMULATOR_CAST(EmulatorTraits<Emu64>::PVOID, PSID) TrustLevelSid;
};
#ifndef OS_WINDOWS
typedef enum _TOKEN_TYPE
@@ -835,6 +894,52 @@ typedef struct _TOKEN_SECURITY_ATTRIBUTES_INFORMATION
} Attribute;
} TOKEN_SECURITY_ATTRIBUTES_INFORMATION, *PTOKEN_SECURITY_ATTRIBUTES_INFORMATION;
#ifndef OS_WINDOWS
#define SECURITY_DESCRIPTOR_REVISION 1
#define SECURITY_DESCRIPTOR_REVISION1 1
typedef WORD SECURITY_DESCRIPTOR_CONTROL, *PSECURITY_DESCRIPTOR_CONTROL;
#define SE_OWNER_DEFAULTED 0x0001
#define SE_GROUP_DEFAULTED 0x0002
#define SE_DACL_PRESENT 0x0004
#define SE_DACL_DEFAULTED 0x0008
#define SE_SACL_PRESENT 0x0010
#define SE_SACL_DEFAULTED 0x0020
#define SE_DACL_AUTO_INHERIT_REQ 0x0100
#define SE_SACL_AUTO_INHERIT_REQ 0x0200
#define SE_DACL_AUTO_INHERITED 0x0400
#define SE_SACL_AUTO_INHERITED 0x0800
#define SE_DACL_PROTECTED 0x1000
#define SE_SACL_PROTECTED 0x2000
#define SE_RM_CONTROL_VALID 0x4000
#define SE_SELF_RELATIVE 0x8000
struct SECURITY_DESCRIPTOR_RELATIVE
{
BYTE Revision;
BYTE Sbz1;
SECURITY_DESCRIPTOR_CONTROL Control;
DWORD Owner;
DWORD Group;
DWORD Sacl;
DWORD Dacl;
};
typedef DWORD SECURITY_INFORMATION, *PSECURITY_INFORMATION;
#define OWNER_SECURITY_INFORMATION 0x00000001L
#define GROUP_SECURITY_INFORMATION 0x00000002L
#define DACL_SECURITY_INFORMATION 0x00000004L
#define SACL_SECURITY_INFORMATION 0x00000008L
#define LABEL_SECURITY_INFORMATION 0x00000010L
#define ATTRIBUTE_SECURITY_INFORMATION 0x00000020L
#define SCOPE_SECURITY_INFORMATION 0x00000040L
#define PROCESS_TRUST_LABEL_SECURITY_INFORMATION 0x00000080L
#define ACCESS_FILTER_SECURITY_INFORMATION 0x00000100L
#define BACKUP_SECURITY_INFORMATION 0x00010000L
#endif
struct GDI_HANDLE_ENTRY64
{
union

View File

@@ -34,6 +34,24 @@ struct KEY_NAME_INFORMATION
char16_t Name[1];
};
typedef struct _KEY_BASIC_INFORMATION
{
LARGE_INTEGER LastWriteTime;
ULONG TitleIndex;
ULONG NameLength;
char16_t Name[1];
} KEY_BASIC_INFORMATION, *PKEY_BASIC_INFORMATION;
typedef struct _KEY_NODE_INFORMATION
{
LARGE_INTEGER LastWriteTime;
ULONG TitleIndex;
ULONG ClassOffset;
ULONG ClassLength;
ULONG NameLength;
char16_t Name[1];
} KEY_NODE_INFORMATION, *PKEY_NODE_INFORMATION;
typedef struct _KEY_FULL_INFORMATION
{
LARGE_INTEGER LastWriteTime;

View File

@@ -23,7 +23,6 @@ using NTSTATUS = std::uint32_t;
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#define STATUS_WAIT_1 ((NTSTATUS)0x00000001L)
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0x00000001L)
#define STATUS_ALERTED ((NTSTATUS)0x00000101L)
#define STATUS_OBJECT_NAME_EXISTS ((NTSTATUS)0x40000000L)
@@ -31,6 +30,7 @@ using NTSTATUS = std::uint32_t;
#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L)
#define STATUS_NO_MORE_ENTRIES ((NTSTATUS)0x8000001AL)
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
@@ -43,10 +43,12 @@ using NTSTATUS = std::uint32_t;
#define STATUS_FILE_IS_A_DIRECTORY ((NTSTATUS)0xC00000BAL)
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL)
#define STATUS_INVALID_ADDRESS ((NTSTATUS)0xC0000141L)
#define STATUS_CONNECTION_RESET ((NTSTATUS)0xC000020DL)
#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L)
#define STATUS_CONNECTION_REFUSED ((NTSTATUS)0xC0000236L)
#define STATUS_TIMER_RESOLUTION_NOT_SET ((NTSTATUS)0xC0000245L)
#define STATUS_ADDRESS_ALREADY_ASSOCIATED ((NTSTATUS)0xC0000328L)
#define STATUS_PORT_NOT_SET ((NTSTATUS)0xC0000353L)
#define STATUS_DEBUGGER_INACTIVE ((NTSTATUS)0xC0000354L)
#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)

View File

@@ -76,7 +76,7 @@ struct THREAD_NAME_INFORMATION
typedef struct _THREAD_BASIC_INFORMATION64
{
NTSTATUS ExitStatus;
PTEB64 TebBaseAddress;
EMULATOR_CAST(uint64_t, PTEB64) TebBaseAddress;
CLIENT_ID64 ClientId;
EMULATOR_CAST(std::uint64_t, KAFFINITY) AffinityMask;
EMULATOR_CAST(std::uint32_t, KPRIORITY) Priority;

View File

@@ -8,6 +8,7 @@ struct UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
uint32_t _Padding;
EMULATOR_CAST(typename Traits::PVOID, char16_t*) Buffer;
};

View File

@@ -0,0 +1,55 @@
#pragma once
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
using pointer = uint64_t;
#ifndef OS_WINDOWS
typedef struct tagPOINT
{
LONG x;
LONG y;
} POINT;
#endif
using wparam = pointer;
using lparam = pointer;
using lresult = pointer;
typedef struct _LARGE_STRING
{
ULONG Length;
ULONG MaximumLength : 31;
ULONG bAnsi : 1;
pointer Buffer;
} LARGE_STRING;
using hdc = pointer;
using hwnd = pointer;
using hmenu = pointer;
using hinstance = pointer;
struct msg
{
hwnd window;
UINT message;
wparam wParam;
lparam lParam;
DWORD time;
POINT pt;
#ifdef _MAC
DWORD lPrivate;
#endif
};
struct EMU_DISPLAY_DEVICEW
{
DWORD cb;
char16_t DeviceName[32];
char16_t DeviceString[128];
DWORD StateFlags;
char16_t DeviceID[128];
char16_t DeviceKey[128];
};
// NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)

View File

@@ -4,22 +4,72 @@
#include <string_view>
#include <unordered_set>
#include <unordered_map>
#include "string.hpp"
namespace utils
{
struct string_hash
template <typename Elem, typename Traits>
struct basic_string_hash
{
using is_transparent = void;
size_t operator()(const std::string_view str) const
size_t operator()(const std::basic_string_view<Elem, Traits> str) const
{
constexpr std::hash<std::string_view> hasher{};
constexpr std::hash<std::basic_string_view<Elem, Traits>> hasher{};
return hasher(str);
}
};
template <typename Elem, typename Traits>
struct basic_insensitive_string_hash
{
using is_transparent = void;
size_t operator()(const std::basic_string_view<Elem, Traits> str) const
{
size_t hash = 0;
constexpr std::hash<int> hasher{};
for (const auto c : str)
{
hash ^= hasher(string::char_to_lower(c)) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
}
return hash;
}
};
template <typename Elem, typename Traits>
struct basic_insensitive_string_equal
{
using is_transparent = void;
bool operator()(const std::basic_string_view<Elem, Traits> lhs,
const std::basic_string_view<Elem, Traits> rhs) const
{
return string::equals_ignore_case(lhs, rhs);
}
};
using string_hash = basic_string_hash<char, std::char_traits<char>>;
using u16string_hash = basic_string_hash<char16_t, std::char_traits<char16_t>>;
using insensitive_string_hash = basic_insensitive_string_hash<char, std::char_traits<char>>;
using insensitive_u16string_hash = basic_insensitive_string_hash<char16_t, std::char_traits<char16_t>>;
using insensitive_string_equal = basic_insensitive_string_equal<char, std::char_traits<char>>;
using insensitive_u16string_equal = basic_insensitive_string_equal<char16_t, std::char_traits<char16_t>>;
template <typename T>
using unordered_string_map = std::unordered_map<std::string, T, string_hash, std::equal_to<>>;
template <typename T>
using unordered_u16string_map = std::unordered_map<std::u16string, T, u16string_hash, std::equal_to<>>;
template <typename T>
using unordered_insensitive_string_map =
std::unordered_map<std::string, T, insensitive_string_hash, insensitive_string_equal>;
template <typename T>
using unordered_insensitive_u16string_map =
std::unordered_map<std::u16string, T, insensitive_u16string_hash, insensitive_u16string_equal>;
using unordered_string_set = std::unordered_set<std::string, string_hash, std::equal_to<>>;
using unordered_u16string_set = std::unordered_set<std::u16string, u16string_hash, std::equal_to<>>;
}

View File

@@ -0,0 +1,52 @@
#include "string.hpp"
#include <array>
#include <cstdarg>
namespace utils::string
{
// NOLINTNEXTLINE(cert-dcl50-cpp)
const char* va(const char* format, ...)
{
constexpr auto buffer_count = 4;
thread_local std::array<std::vector<char>, buffer_count> buffers{};
thread_local size_t current_index{0};
const auto index = current_index++;
current_index %= buffers.size();
auto& buffer = buffers.at(index);
if (buffer.size() < 10)
{
buffer.resize(10);
}
while (true)
{
va_list ap{};
va_start(ap, format);
#ifdef _WIN32
const int res = vsnprintf_s(buffer.data(), buffer.size(), _TRUNCATE, format, ap);
#else
const int res = vsnprintf(buffer.data(), buffer.size(), format, ap);
#endif
va_end(ap);
if (res > 0 && static_cast<size_t>(res) < buffer.size())
{
break;
}
if (res == 0)
{
return nullptr;
}
buffer.resize(std::max(buffer.size() * 2, static_cast<size_t>(1)));
}
return buffer.data();
}
}

View File

@@ -1,32 +1,61 @@
#pragma once
#include <span>
#include <string>
#include <vector>
#include <cstddef>
#include <cstdint>
#include <cwctype>
#include <algorithm>
#include <string_view>
namespace utils::string
{
#ifdef __clang__
__attribute__((__format__(__printf__, 1, 2)))
#endif
const char*
va(const char* format, ...);
template <typename T, size_t Size>
requires(std::is_trivially_copyable_v<T>)
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
void copy(T (&array)[Size], const std::basic_string_view<T> str)
{
if constexpr (Size == 0)
{
return;
}
const auto size = std::min(Size, str.size());
memcpy(array, str.data(), size * sizeof(T));
array[std::min(Size - 1, size)] = {};
}
template <typename T, size_t Size>
requires(std::is_trivially_copyable_v<T>)
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
void copy(T (&array)[Size], const T* str)
{
copy<T, Size>(array, std::basic_string_view<T>(str));
}
inline char char_to_lower(const char val)
{
return static_cast<char>(std::tolower(static_cast<unsigned char>(val)));
}
inline char16_t char_to_lower(const char16_t val)
{
if (val >= u'A' && val <= u'Z')
{
return val + 32;
}
return val;
}
inline wchar_t char_to_lower(const wchar_t val)
{
return static_cast<wchar_t>(std::towlower(val));
}
inline char16_t char_to_lower(const char16_t val)
{
static_assert(sizeof(char16_t) <= sizeof(wchar_t));
static_assert(sizeof(char16_t) == sizeof(uint16_t));
return static_cast<char16_t>(char_to_lower(static_cast<wchar_t>(static_cast<uint16_t>(val))));
}
template <class Elem, class Traits, class Alloc>
void to_lower_inplace(std::basic_string<Elem, Traits, Alloc>& str)
{
@@ -149,4 +178,20 @@ namespace utils::string
return data;
}
template <class Elem, class Traits, class Alloc>
bool equals_ignore_case(const std::basic_string<Elem, Traits, Alloc>& lhs,
const std::basic_string<Elem, Traits, Alloc>& rhs)
{
return std::ranges::equal(lhs, rhs,
[](const auto c1, const auto c2) { return char_to_lower(c1) == char_to_lower(c2); });
}
template <class Elem, class Traits>
bool equals_ignore_case(const std::basic_string_view<Elem, Traits>& lhs,
const std::basic_string_view<Elem, Traits>& rhs)
{
return std::ranges::equal(lhs, rhs,
[](const auto c1, const auto c2) { return char_to_lower(c1) == char_to_lower(c2); });
}
}

View File

@@ -75,6 +75,11 @@ namespace utils
return this->now(this->steady_start_);
}
uint64_t timestamp_counter() override
{
return this->ticks();
}
virtual uint64_t ticks() = 0;
uint64_t get_frequency() const

View File

@@ -14,10 +14,14 @@ namespace utils
}
bool has_elapsed(typename Clock::duration duration) const
{
return this->elapsed() > duration;
}
typename Clock::duration elapsed() const
{
const auto now = Clock::now();
const auto diff = now - this->point_;
return diff > duration;
return now - this->point_;
}
private:

View File

@@ -0,0 +1,99 @@
#pragma once
#include <string>
namespace utils::wildcard
{
inline bool is_wildcard(char16_t c)
{
return c == '*' || c == '?' || c == '>' || c == '<' || c == '\"';
}
inline bool has_wildcard(const std::u16string_view mask)
{
return std::ranges::any_of(mask, is_wildcard);
}
inline bool match_filename(std::u16string_view name, std::u16string_view mask)
{
if (mask.empty() || mask == u"*" || mask == u"*.*")
{
return true;
}
size_t name_pos = 0;
size_t mask_pos = 0;
size_t star_mask_pos = std::u16string_view::npos;
size_t star_name_pos = 0;
while (name_pos < name.size())
{
if (mask_pos < mask.size())
{
char16_t mask_char = mask[mask_pos];
char16_t name_char = name[name_pos];
bool char_matches = false;
if (mask_char == u'?' || mask_char == u'>')
{
char_matches = true;
}
else if (mask_char == u'"')
{
char_matches = name_char == u'.';
}
else
{
char_matches = string::char_to_lower(name_char) == string::char_to_lower(mask_char);
}
// Advance if current characters match
if (char_matches)
{
name_pos++;
mask_pos++;
continue;
}
// If this is a wildcard, skip all consecutive wildcards and save position for backtracking
if (mask[mask_pos] == u'*' || mask[mask_pos] == u'<')
{
mask_pos++;
while (mask_pos < mask.size() && (mask[mask_pos] == u'*' || mask[mask_pos] == u'<'))
{
mask_pos++;
}
if (mask_pos == mask.size())
{
// There is no need to continue because all that remained were star masks.
return true;
}
star_mask_pos = mask_pos;
star_name_pos = name_pos;
continue;
}
}
// The current characters didn't match...
// If we had a wildcard earlier, backtrack to it and try to match at the next position
if (star_mask_pos != std::u16string_view::npos)
{
mask_pos = star_mask_pos;
name_pos = ++star_name_pos;
continue;
}
return false;
}
// Skip any remaining wildcards in the mask
while (mask_pos < mask.size() && (mask[mask_pos] == u'*' || mask[mask_pos] == u'<'))
{
mask_pos++;
}
return mask_pos == mask.size();
}
}

View File

@@ -18,6 +18,10 @@
#define WIN32_LEAN_AND_MEAN
#endif
#ifdef __MINGW64__
#include <windows.h>
#else
#include <Windows.h>
#endif
#endif

View File

@@ -0,0 +1,25 @@
file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS
*.cpp
*.hpp
*.rc
)
list(SORT SRC_FILES)
add_library(debugger ${SRC_FILES})
momo_assign_source_group(${SRC_FILES})
target_link_libraries(debugger PRIVATE
windows-emulator
flatbuffers
base64
)
target_include_directories(debugger INTERFACE "${CMAKE_CURRENT_LIST_DIR}")
add_custom_target(generate-flatbuffer
DEPENDS flatc
COMMAND "$<TARGET_FILE:flatc>" --gen-mutable --gen-object-api --filename-ext hxx --cpp -o "${CMAKE_CURRENT_LIST_DIR}" "${CMAKE_CURRENT_LIST_DIR}/events.fbs"
COMMAND "$<TARGET_FILE:flatc>" --gen-mutable --gen-object-api --ts -o "${PROJECT_SOURCE_DIR}/page/src/fb" "${CMAKE_CURRENT_LIST_DIR}/events.fbs"
)

View File

@@ -0,0 +1,257 @@
#include "event_handler.hpp"
#include "message_transmitter.hpp"
#include "windows_emulator.hpp"
#include <base64.hpp>
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4244)
#endif
#include "events_generated.hxx"
#ifdef _MSC_VER
#pragma warning(pop)
#endif
namespace debugger
{
namespace
{
std::optional<Debugger::DebugEventT> receive_event()
{
const auto message = receive_message();
if (message.empty())
{
return std::nullopt;
}
const auto data = base64::from_base64(message);
flatbuffers::Verifier verifier(reinterpret_cast<const uint8_t*>(data.data()), data.size());
if (!Debugger::VerifyDebugEventBuffer(verifier))
{
return std::nullopt;
}
Debugger::DebugEventT e{};
Debugger::GetDebugEvent(data.data())->UnPackTo(&e);
return {std::move(e)};
}
void send_event(const Debugger::DebugEventT& event)
{
flatbuffers::FlatBufferBuilder fbb{};
fbb.Finish(Debugger::DebugEvent::Pack(fbb, &event));
const std::string_view buffer(reinterpret_cast<const char*>(fbb.GetBufferPointer()), fbb.GetSize());
const auto message = base64::to_base64(buffer);
send_message(message);
}
template <typename T>
requires(!std::is_same_v<std::remove_cvref_t<T>, Debugger::DebugEventT>)
void send_event(T event)
{
Debugger::DebugEventT e{};
e.event.Set(std::move(event));
send_event(e);
}
Debugger::State translate_state(const emulation_state state)
{
switch (state)
{
case emulation_state::paused:
return Debugger::State_Paused;
case emulation_state::none:
case emulation_state::running:
return Debugger::State_Running;
default:
return Debugger::State_None;
}
}
void handle_get_state(const event_context& c)
{
Debugger::GetStateResponseT response{};
response.state = translate_state(c.state);
send_event(response);
}
void handle_read_memory(const event_context& c, const Debugger::ReadMemoryRequestT& request)
{
std::vector<uint8_t> buffer{};
buffer.resize(request.size);
const auto res = c.win_emu.memory.try_read_memory(request.address, buffer.data(), buffer.size());
Debugger::ReadMemoryResponseT response{};
response.address = request.address;
if (res)
{
response.data = std::move(buffer);
}
send_event(std::move(response));
}
void handle_write_memory(const event_context& c, const Debugger::WriteMemoryRequestT& request)
{
bool success{};
try
{
c.win_emu.memory.write_memory(request.address, request.data.data(), request.data.size());
success = true;
}
catch (...)
{
success = false;
}
Debugger::WriteMemoryResponseT response{};
response.address = request.address;
response.size = static_cast<uint32_t>(request.data.size());
response.success = success;
send_event(response);
}
void handle_read_register(const event_context& c, const Debugger::ReadRegisterRequestT& request)
{
std::array<uint8_t, 512> buffer{};
const auto res = c.win_emu.emu().read_register(static_cast<x86_register>(request.register_), buffer.data(),
buffer.size());
const auto size = std::min(buffer.size(), res);
Debugger::ReadRegisterResponseT response{};
response.register_ = request.register_;
response.data.assign(buffer.data(), buffer.data() + size);
send_event(std::move(response));
}
void handle_write_register(const event_context& c, const Debugger::WriteRegisterRequestT& request)
{
bool success{};
size_t size = request.data.size();
try
{
size = c.win_emu.emu().write_register(static_cast<x86_register>(request.register_), request.data.data(),
request.data.size());
success = true;
}
catch (...)
{
success = false;
}
Debugger::WriteRegisterResponseT response{};
response.register_ = request.register_;
response.size = static_cast<uint32_t>(size);
response.success = success;
send_event(response);
}
void handle_event(event_context& c, const Debugger::DebugEventT& e)
{
switch (e.event.type)
{
case Debugger::Event_PauseRequest:
c.state = emulation_state::paused;
break;
case Debugger::Event_RunRequest:
c.state = emulation_state::running;
break;
case Debugger::Event_GetStateRequest:
handle_get_state(c);
break;
case Debugger::Event_ReadMemoryRequest:
handle_read_memory(c, *e.event.AsReadMemoryRequest());
break;
case Debugger::Event_WriteMemoryRequest:
handle_write_memory(c, *e.event.AsWriteMemoryRequest());
break;
case Debugger::Event_ReadRegisterRequest:
handle_read_register(c, *e.event.AsReadRegisterRequest());
break;
case Debugger::Event_WriteRegisterRequest:
handle_write_register(c, *e.event.AsWriteRegisterRequest());
break;
default:
break;
}
}
}
void handle_events_once(event_context& c)
{
while (true)
{
suspend_execution(0ms);
const auto e = receive_event();
if (!e.has_value())
{
break;
}
handle_event(c, *e);
}
}
void handle_events(event_context& c)
{
update_emulation_status(c.win_emu);
while (true)
{
handle_events_once(c);
if (c.state != emulation_state::paused)
{
break;
}
suspend_execution(2ms);
}
}
void update_emulation_status(const windows_emulator& win_emu)
{
const auto memory_status = win_emu.memory.compute_memory_stats();
Debugger::EmulationStatusT status{};
status.reserved_memory = memory_status.reserved_memory;
status.committed_memory = memory_status.committed_memory;
status.executed_instructions = win_emu.get_executed_instructions();
status.active_threads = static_cast<uint32_t>(win_emu.process.threads.size());
send_event(status);
}
void handle_exit(const windows_emulator& win_emu, std::optional<NTSTATUS> exit_status)
{
update_emulation_status(win_emu);
Debugger::ApplicationExitT response{};
response.exit_status = exit_status;
send_event(response);
}
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include <windows_emulator.hpp>
namespace debugger
{
enum class emulation_state
{
none,
running,
paused,
};
struct event_context
{
windows_emulator& win_emu;
emulation_state state{emulation_state::none};
};
void handle_events(event_context& c);
void handle_exit(const windows_emulator& win_emu, std::optional<NTSTATUS> exit_status);
void update_emulation_status(const windows_emulator& win_emu);
}

94
src/debugger/events.fbs Normal file
View File

@@ -0,0 +1,94 @@
namespace Debugger;
table GetStateRequest {}
enum State : uint32 {
None = 0,
Running,
Paused,
}
table GetStateResponse {
state: State;
}
table PauseRequest {}
table RunRequest {
single_step: bool;
}
table WriteMemoryRequest {
address: uint64;
data: [ubyte];
}
table WriteMemoryResponse {
address: uint64;
size: uint32;
success: bool;
}
table ReadMemoryRequest {
address: uint64;
size: uint32;
}
table ReadMemoryResponse {
address: uint64;
data: [ubyte];
}
table WriteRegisterRequest {
register: uint32;
data: [ubyte];
}
table WriteRegisterResponse {
register: uint32;
size: uint32;
success: bool;
}
table ReadRegisterRequest {
register: uint32;
}
table ReadRegisterResponse {
register: uint32;
data: [ubyte];
}
table ApplicationExit {
exit_status: uint32 = null;
}
table EmulationStatus {
active_threads: uint32;
reserved_memory: uint64;
committed_memory: uint64;
executed_instructions: uint64;
}
union Event {
PauseRequest,
RunRequest,
GetStateRequest,
GetStateResponse,
WriteMemoryRequest,
WriteMemoryResponse,
ReadMemoryRequest,
ReadMemoryResponse,
WriteRegisterRequest,
WriteRegisterResponse,
ReadRegisterRequest,
ReadRegisterResponse,
ApplicationExit,
EmulationStatus,
}
table DebugEvent {
event: Event;
}
root_type DebugEvent;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,76 @@
#include "message_transmitter.hpp"
#include <platform/compiler.hpp>
#include <thread>
#include <utils/finally.hpp>
#ifdef OS_EMSCRIPTEN
#include <emscripten.h>
#endif
using namespace std::literals;
namespace debugger
{
void send_message(const std::string& message)
{
#ifdef OS_EMSCRIPTEN
// clang-format off
EM_ASM_({
handleMessage(UTF8ToString($0));
}, message.c_str());
// clang-format on
#else
(void)message;
#endif
}
std::string receive_message()
{
#ifdef OS_EMSCRIPTEN
// clang-format off
auto* ptr = EM_ASM_PTR({
var message = getMessageFromQueue();
if (!message || message.length == 0)
{
return 0;
}
const length = lengthBytesUTF8(message) + 1;
const buffer = _malloc(length);
stringToUTF8(message, buffer, length);
return buffer;
});
// clang-format on
if (!ptr)
{
return {};
}
const auto _ = utils::finally([&] {
free(ptr); //
});
return {reinterpret_cast<const char*>(ptr)};
#else
return {};
#endif
}
void suspend_execution(const std::chrono::milliseconds ms)
{
#ifdef OS_EMSCRIPTEN
emscripten_sleep(static_cast<uint32_t>(ms.count()));
#else
if (ms > 0ms)
{
std::this_thread::sleep_for(ms);
}
else
{
std::this_thread::yield();
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include <chrono>
#include <string>
namespace debugger
{
void suspend_execution(std::chrono::milliseconds ms = std::chrono::milliseconds(0));
void send_message(const std::string& message);
std::string receive_message();
}

View File

@@ -0,0 +1,66 @@
/*
Design notes:
1. emulator: the root interface (provides CPU, memory, and hook interfaces).
2. typed_emulator<Traits>: a template that adapts to architecture/bitness via the Traits struct.
3. arch_emulator<Traits>: a thin layer for architecture-specific logic, things that are shared by all x86 (32/64), or
all ARM (32/64), etc.
X. x86_emulator<Traits>: x86_emulator<Traits> are specialisations for
x86 and ARM, parameterised by their respective traits (e.g., x86_64_traits) and stuff :)
1. emulator (cpu_interface, memory_interface, hook_interface)
2. └── typed_emulator<address_t, register_t, ...>
3. └── arch_emulator<arch_traits>
├── x86_emulator<x86_32_traits>
├── x86_emulator<x86_64_traits>
├── arm_emulator<arm_32_traits>
└── arm_emulator<arm_64_traits>
*/
#pragma once
#include "typed_emulator.hpp"
#include "x86_register.hpp"
// --[Core]--------------------------------------------------------------------------
template <typename Traits>
struct arch_emulator : typed_emulator<Traits>
{
};
template <typename Traits>
struct x86_emulator : arch_emulator<Traits>
{
using register_type = typename Traits::register_type;
using pointer_type = typename Traits::pointer_type;
virtual void set_segment_base(register_type base, pointer_type value) = 0;
virtual void load_gdt(pointer_type address, uint32_t limit) = 0;
};
template <typename Traits>
struct arm_emulator : arch_emulator<Traits>
{
};
enum class x86_hookable_instructions
{
invalid, // TODO: Get rid of that
syscall,
cpuid,
rdtsc,
rdtscp,
};
// --[x86_64]-------------------------------------------------------------------------
struct x86_64_traits
{
using pointer_type = uint64_t;
using register_type = x86_register;
static constexpr register_type instruction_pointer = x86_register::rip;
static constexpr register_type stack_pointer = x86_register::rsp;
using hookable_instructions = x86_hookable_instructions;
};
using x86_64_emulator = x86_emulator<x86_64_traits>;

View File

@@ -55,15 +55,14 @@ class hook_interface
virtual emulator_hook* hook_memory_execution(memory_execution_hook_callback callback) = 0;
virtual emulator_hook* hook_memory_execution(uint64_t address, memory_execution_hook_callback callback) = 0;
virtual emulator_hook* hook_memory_read(uint64_t address, size_t size, memory_access_hook_callback callback) = 0;
virtual emulator_hook* hook_memory_write(uint64_t address, size_t size, memory_access_hook_callback callback) = 0;
virtual emulator_hook* hook_memory_read(uint64_t address, uint64_t size, memory_access_hook_callback callback) = 0;
virtual emulator_hook* hook_memory_write(uint64_t address, uint64_t size, memory_access_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_memory_violation(memory_violation_hook_callback callback) = 0;
virtual emulator_hook* hook_edge_generation(edge_generation_hook_callback callback) = 0;
virtual emulator_hook* hook_basic_block(basic_block_hook_callback callback) = 0;
virtual void delete_hook(emulator_hook* hook) = 0;

View File

@@ -52,3 +52,22 @@ inline memory_permission& operator^=(memory_permission& x, const memory_permissi
x = x ^ y;
return x;
}
/*****************************************************************************
*
****************************************************************************/
inline bool is_executable(const memory_permission permission)
{
return (permission & memory_permission::exec) != memory_permission::none;
}
inline bool is_readable(const memory_permission permission)
{
return (permission & memory_permission::read) != memory_permission::none;
}
inline bool is_writable(const memory_permission permission)
{
return (permission & memory_permission::write) != memory_permission::none;
}

View File

@@ -122,7 +122,7 @@ namespace utils
}
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();
}
}
@@ -378,7 +378,7 @@ namespace utils
{
const auto size = this->read<uint64_t>();
result.clear();
result.reserve(size);
result.reserve(static_cast<size_t>(size));
for (uint64_t i = 0; i < size; ++i)
{
@@ -447,7 +447,7 @@ namespace utils
const auto size = this->read<uint64_t>();
result.clear();
result.reserve(size);
result.reserve(static_cast<size_t>(size));
for (uint64_t i = 0; i < size; ++i)
{

View File

@@ -2,18 +2,17 @@
#include "emulator.hpp"
template <typename PointerType, typename Register, Register InstructionPointer, Register StackPointer,
typename HookableInstructions>
template <typename Traits>
class typed_emulator : public emulator
{
public:
using registers = Register;
using pointer_type = PointerType;
using hookable_instructions = HookableInstructions;
using registers = typename Traits::register_type;
using pointer_type = typename Traits::pointer_type;
using hookable_instructions = typename Traits::hookable_instructions;
static constexpr size_t pointer_size = sizeof(pointer_type);
static constexpr registers stack_pointer = StackPointer;
static constexpr registers instruction_pointer = InstructionPointer;
static constexpr registers stack_pointer = Traits::stack_pointer;
static constexpr registers instruction_pointer = Traits::instruction_pointer;
size_t write_register(registers reg, const void* value, const size_t size)
{

View File

@@ -1,19 +0,0 @@
#pragma once
#include "typed_emulator.hpp"
#include "x64_register.hpp"
enum class x64_hookable_instructions
{
invalid,
syscall,
cpuid,
rdtsc,
rdtscp,
};
struct x64_emulator
: typed_emulator<uint64_t, x64_register, x64_register::rip, x64_register::rsp, x64_hookable_instructions>
{
virtual void set_segment_base(x64_register base, pointer_type value) = 0;
virtual void load_gdt(pointer_type address, uint32_t limit) = 0;
};

View File

@@ -1,6 +1,7 @@
#pragma once
enum class x64_register
// x86_64 and x86_32 register definitions
enum class x86_register
{
invalid = 0,
ah,

View File

@@ -19,4 +19,10 @@ target_link_libraries(fuzzer PRIVATE
windows-emulator
)
if (MOMO_ENABLE_RUST)
target_link_libraries(fuzzer PRIVATE
icicle-emulator
)
endif()
momo_strip_target(fuzzer)

View File

@@ -3,20 +3,47 @@
#include <windows_emulator.hpp>
#include <fuzzer.hpp>
#include "utils/finally.hpp"
#include <utils/finally.hpp>
#if MOMO_ENABLE_RUST_CODE
#include <icicle_x86_64_emulator.hpp>
#endif
#ifdef _MSC_VER
#pragma warning(disable : 4702)
#endif
bool use_gdb = false;
namespace
{
std::unique_ptr<x86_64_emulator> create_emulator_backend()
{
#if MOMO_ENABLE_RUST_CODE
return icicle::create_x86_64_emulator();
#else
throw std::runtime_error("Fuzzer requires rust code to be enabled");
#endif
}
void run_emulation(windows_emulator& win_emu)
{
bool has_exception = false;
const auto _ = utils::finally([&] {
win_emu.callbacks.on_exception = {}; //
});
try
{
win_emu.callbacks.on_exception = [&] {
has_exception = true;
win_emu.stop();
};
win_emu.log.disable_output(true);
win_emu.start();
if (win_emu.process.exception_rip.has_value())
if (has_exception)
{
throw std::runtime_error("Exception!");
}
@@ -24,8 +51,7 @@ namespace
catch (...)
{
win_emu.log.disable_output(false);
win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 "\n",
win_emu.emu().read_instruction_pointer());
win_emu.log.error("Emulation failed at: 0x%" PRIx64 "\n", win_emu.emu().read_instruction_pointer());
throw;
}
@@ -44,7 +70,7 @@ namespace
struct fuzzer_executer : fuzzer::executer
{
windows_emulator emu{{.emulation_root = "./"}}; // TODO: Fix root directory
windows_emulator emu{create_emulator_backend()};
std::span<const std::byte> emulator_data{};
std::unordered_set<uint64_t> visited_blocks{};
const std::function<fuzzer::coverage_functor>* handler{nullptr};
@@ -52,7 +78,6 @@ namespace
fuzzer_executer(const 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)
{
@@ -64,9 +89,8 @@ namespace
emu.deserialize(deserializer);
emu.save_snapshot();
const auto ret = emu.emu().read_stack(0);
emu.emu().hook_memory_execution(ret, [&](uint64_t) {
const auto return_address = emu.emu().read_stack(0);
emu.emu().hook_memory_execution(return_address, [&](const uint64_t) {
emu.emu().stop(); //
});
}
@@ -78,7 +102,7 @@ namespace
emu.restore_snapshot();
}
fuzzer::execution_result execute(std::span<const uint8_t> data,
fuzzer::execution_result execute(const std::span<const uint8_t> data,
const std::function<fuzzer::coverage_functor>& coverage_handler) override
{
// printf("Input size: %zd\n", data.size());
@@ -87,12 +111,13 @@ namespace
restore_emulator();
const auto memory = emu.memory.allocate_memory(page_align_up(std::max(data.size(), static_cast<size_t>(1))),
memory_permission::read_write);
const auto memory = emu.memory.allocate_memory(
static_cast<size_t>(page_align_up(std::max(data.size(), static_cast<size_t>(1)))),
memory_permission::read_write);
emu.emu().write_memory(memory, data.data(), data.size());
emu.emu().reg(x64_register::rcx, memory);
emu.emu().reg<uint64_t>(x64_register::rdx, data.size());
emu.emu().reg(x86_register::rcx, memory);
emu.emu().reg<uint64_t>(x86_register::rdx, data.size());
try
{
@@ -129,7 +154,7 @@ namespace
void run_fuzzer(const windows_emulator& base_emulator)
{
const auto concurrency = std::thread::hardware_concurrency() + 2;
const auto concurrency = std::thread::hardware_concurrency() + 4;
utils::buffer_serializer serializer{};
base_emulator.serialize(serializer);
@@ -145,51 +170,56 @@ namespace
.application = application,
};
windows_emulator win_emu{std::move(settings)};
windows_emulator win_emu{create_emulator_backend(), std::move(settings)};
forward_emulator(win_emu);
run_fuzzer(win_emu);
}
int run_main(const int argc, char** argv)
{
if (argc <= 1)
{
puts("Application not specified!");
return 1;
}
// setvbuf(stdout, nullptr, _IOFBF, 0x10000);
if (argc > 2 && argv[1] == "-d"s)
{
use_gdb = true;
}
try
{
do
{
run(argv[use_gdb ? 2 : 1]);
} while (use_gdb);
return 0;
}
catch (std::exception& e)
{
puts(e.what());
#if defined(_WIN32) && 0
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
#endif
}
return 1;
}
}
int main(const int argc, char** argv)
{
if (argc <= 1)
{
puts("Application not specified!");
return 1;
}
// setvbuf(stdout, nullptr, _IOFBF, 0x10000);
if (argc > 2 && argv[1] == "-d"s)
{
use_gdb = true;
}
try
{
do
{
run(argv[use_gdb ? 2 : 1]);
} while (use_gdb);
return 0;
}
catch (std::exception& e)
{
puts(e.what());
#if defined(_WIN32) && 0
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
#endif
}
return 1;
return run_main(argc, argv);
}
#ifdef _WIN32
int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int)
{
return main(__argc, __argv);
return run_main(__argc, __argv);
}
#endif

View File

@@ -3,6 +3,10 @@
#include "input_generator.hpp"
#include <utils/timer.hpp>
#include <utils/string.hpp>
#include <utils/finally.hpp>
namespace fuzzer
{
namespace
@@ -45,17 +49,69 @@ namespace fuzzer
std::atomic_bool stop_{false};
};
std::string format_binary_data(const std::span<const uint8_t> input)
{
std::string data;
std::string bytes;
std::string text;
const auto wrap_line = [&] {
if (bytes.empty())
{
return;
}
while (bytes.size() < 16 * 3)
{
bytes.push_back(' ');
}
data += bytes;
data += "| ";
data += text;
data += "\n";
bytes.clear();
text.clear();
};
for (size_t i = 0; i < input.size(); ++i)
{
if (i % 16 == 0)
{
wrap_line();
}
const auto in = input[i];
bytes += utils::string::va("%02X ", static_cast<uint32_t>(in));
text.push_back(isprint(in) ? static_cast<char>(in) : '.');
}
wrap_line();
return data;
}
void print_crash(const std::span<const uint8_t> input)
{
std::string text = utils::string::va("\nFound crash for input (length %zu):\n", input.size());
text += format_binary_data(input);
printf("%.*s\n", static_cast<int>(text.size()), text.c_str());
}
void perform_fuzzing_iteration(fuzzing_context& context, executer& executer)
{
++context.executions;
context.generator.access_input([&](const std::span<const uint8_t> input) {
uint64_t score{0};
const auto result = executer.execute(input, [&](uint64_t) { ++score; });
const auto result = executer.execute(input, [&](uint64_t) {
++score; //
});
if (result == execution_result::error)
{
printf("Found error!\n");
context.stop();
print_crash(input);
}
return score;
@@ -84,7 +140,9 @@ namespace fuzzer
for (size_t i = 0; i < concurrency; ++i)
{
this->workers_.emplace_back([&context] { worker(context); });
this->workers_.emplace_back([&context] {
worker(context); //
});
}
}
@@ -107,6 +165,7 @@ namespace fuzzer
void run(fuzzing_handler& handler, const size_t concurrency)
{
const utils::timer<> t{};
input_generator generator{};
fuzzing_context context{generator, handler};
worker_pool pool{context, concurrency};
@@ -121,5 +180,9 @@ namespace fuzzer
printf("Executions/s: %" PRIu64 " - Score: %" PRIx64 " - Avg: %.3f\n", executions, highest_scorer.score,
avg_score);
}
const auto duration = t.elapsed();
const int64_t seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
printf("Fuzzing stopped after %" PRIi64 "s\n", seconds);
}
}

View File

@@ -71,6 +71,11 @@ namespace fuzzer
{
std::unique_lock lock{this->mutex_};
if (this->top_scorer_.empty())
{
return 0.0;
}
double score{0.0};
for (const auto& e : this->top_scorer_)
{

View File

@@ -8,4 +8,8 @@ list(SORT SRC_FILES)
add_executable(test-sample ${SRC_FILES})
if(WIN)
target_link_libraries(test-sample PRIVATE ws2_32)
endif()
momo_assign_source_group(${SRC_FILES})

View File

@@ -0,0 +1,101 @@
// Microsoft Visual C++ generated resource script.
//
#pragma code_page(65001)
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "windows.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"#include ""windows.h""\r\n"
"\0"
END
2 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,0
PRODUCTVERSION 1,0,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE VFT_DLL
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "momo5502"
VALUE "FileDescription", "Windows Emulator Test Sample"
VALUE "FileVersion", "1.0.0.0"
VALUE "InternalName", "test-sample"
VALUE "LegalCopyright", "All rights reserved."
VALUE "OriginalFilename", "test-sample.exe"
VALUE "ProductName", "test-sample"
VALUE "ProductVersion", "1.0.0.0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
/////////////////////////////////////////////////////////////////////////////
//
// Binary Data
//
GLFW_ICON ICON "resources/icon.ico"
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

View File

@@ -9,13 +9,21 @@
#include <filesystem>
#include <string_view>
#ifndef NOMINMAX
#define NOMINMAX
#endif
#define WIN32_LEAN_AND_MEAN
#include <intrin.h>
#ifdef __MINGW64__
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <Windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#endif
using namespace std::literals;
@@ -273,16 +281,231 @@ namespace
return {std::string(data, std::min(static_cast<size_t>(length - 1), sizeof(data)))};
}
std::optional<std::vector<std::string>> get_all_registry_keys(const HKEY root, const char* path)
{
HKEY key{};
if (RegOpenKeyExA(root, path, 0, KEY_READ | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &key) != ERROR_SUCCESS)
{
return std::nullopt;
}
std::vector<std::string> keys;
std::vector<char> name_buffer(MAX_PATH + 1);
for (DWORD i = 0;; ++i)
{
auto name_buffer_len = static_cast<DWORD>(name_buffer.size());
const LSTATUS status =
RegEnumKeyExA(key, i, name_buffer.data(), &name_buffer_len, nullptr, nullptr, nullptr, nullptr);
if (status == ERROR_SUCCESS)
{
keys.emplace_back(name_buffer.data(), name_buffer_len);
}
else if (status == ERROR_NO_MORE_ITEMS)
{
break;
}
else
{
keys.clear();
break;
}
}
if (keys.empty())
{
RegCloseKey(key);
return std::nullopt;
}
if (RegCloseKey(key) != ERROR_SUCCESS)
{
return std::nullopt;
}
return keys;
}
std::optional<std::vector<std::string>> get_all_registry_values(const HKEY root, const char* path)
{
HKEY key{};
if (RegOpenKeyExA(root, path, 0, KEY_READ | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &key) != ERROR_SUCCESS)
{
return std::nullopt;
}
std::vector<std::string> values;
std::vector<char> name_buffer(MAX_PATH + 1);
for (DWORD i = 0;; ++i)
{
auto name_buffer_len = static_cast<DWORD>(name_buffer.size());
const auto status =
RegEnumValueA(key, i, name_buffer.data(), &name_buffer_len, nullptr, nullptr, nullptr, nullptr);
if (status == ERROR_SUCCESS)
{
values.emplace_back(name_buffer.data(), name_buffer_len);
}
else if (status == ERROR_NO_MORE_ITEMS)
{
break;
}
else
{
values.clear();
break;
}
}
if (values.empty())
{
RegCloseKey(key);
return std::nullopt;
}
if (RegCloseKey(key) != ERROR_SUCCESS)
{
return std::nullopt;
}
return values;
}
bool test_registry()
{
const auto val =
// Basic Reading Test
const auto prog_files_dir =
read_registry_string(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows\CurrentVersion)", "ProgramFilesDir");
if (!val)
if (!prog_files_dir || *prog_files_dir != "C:\\Program Files")
{
return false;
}
return *val == "C:\\Program Files";
// WOW64 Redirection Test
const auto pst_display = read_registry_string(
HKEY_LOCAL_MACHINE,
R"(SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Time Zones\Pacific Standard Time)", "Display");
if (!pst_display || pst_display->empty())
{
return false;
}
// Key Sub-keys Enumeration Test
const auto subkeys_opt =
get_all_registry_keys(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)");
if (!subkeys_opt)
{
return false;
}
bool found_fonts = false;
for (const auto& key_name : *subkeys_opt)
{
if (key_name == "Fonts")
{
found_fonts = true;
break;
}
}
if (!found_fonts)
{
return false;
}
// Key Values Enumeration Test
const auto values_opt =
get_all_registry_values(HKEY_LOCAL_MACHINE, R"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)");
if (!values_opt)
{
return false;
}
bool found_product_name = false;
for (const auto& val_name : *values_opt)
{
if (val_name == "ProductName")
{
found_product_name = true;
break;
}
}
if (!found_product_name)
{
return false;
}
return true;
}
bool test_system_info()
{
char sys_dir[MAX_PATH];
if (GetSystemDirectoryA(sys_dir, sizeof(sys_dir)) == 0)
{
return false;
}
if (strlen(sys_dir) != 19)
{
return false;
}
// TODO: This currently doesn't work.
/*
char username[256];
DWORD username_len = sizeof(username);
if (!GetUserNameA(username, &username_len))
{
return false;
}
if (username_len <= 1)
{
return false;
}
*/
return true;
}
bool test_time_zone()
{
DYNAMIC_TIME_ZONE_INFORMATION current_dtzi = {};
DWORD result = GetDynamicTimeZoneInformation(&current_dtzi);
if (result == TIME_ZONE_ID_INVALID)
{
return false;
}
if (current_dtzi.Bias != -60 || current_dtzi.StandardBias != 0 || current_dtzi.DaylightBias != -60 ||
current_dtzi.DynamicDaylightTimeDisabled != FALSE)
{
return false;
}
if (wcscmp(current_dtzi.StandardName, L"W. Europe Standard Time") != 0 ||
wcscmp(current_dtzi.DaylightName, L"W. Europe Daylight Time") != 0 ||
wcscmp(current_dtzi.TimeZoneKeyName, L"W. Europe Standard Time") != 0)
{
return false;
}
if (current_dtzi.StandardDate.wYear != 0 || current_dtzi.StandardDate.wMonth != 10 ||
current_dtzi.StandardDate.wDayOfWeek != 0 || current_dtzi.StandardDate.wDay != 5 ||
current_dtzi.StandardDate.wHour != 3 || current_dtzi.StandardDate.wMinute != 0 ||
current_dtzi.StandardDate.wSecond != 0 || current_dtzi.StandardDate.wMilliseconds != 0)
{
return false;
}
if (current_dtzi.DaylightDate.wYear != 0 || current_dtzi.DaylightDate.wMonth != 3 ||
current_dtzi.DaylightDate.wDayOfWeek != 0 || current_dtzi.DaylightDate.wDay != 5 ||
current_dtzi.DaylightDate.wHour != 2 || current_dtzi.DaylightDate.wMinute != 0 ||
current_dtzi.DaylightDate.wSecond != 0 || current_dtzi.DaylightDate.wMilliseconds != 0)
{
return false;
}
return true;
}
void throw_exception()
@@ -347,8 +570,10 @@ namespace
return false;
}
if (sendto(sender, send_data.data(), static_cast<int>(send_data.size()), 0,
reinterpret_cast<sockaddr*>(&destination), sizeof(destination)) != send_data.size())
const auto sent_bytes = sendto(sender, send_data.data(), static_cast<int>(send_data.size()), 0,
reinterpret_cast<sockaddr*>(&destination), sizeof(destination));
if (static_cast<size_t>(sent_bytes) != send_data.size())
{
puts("Failed to send data!");
return false;
@@ -370,6 +595,7 @@ namespace
return send_data == std::string_view(buffer, len);
}
#ifndef __MINGW64__
void throw_access_violation()
{
if (do_the_task)
@@ -444,6 +670,41 @@ namespace
&& test_illegal_instruction_exception() //
&& test_unhandled_exception();
}
#endif
bool trap_flag_cleared = false;
constexpr DWORD TRAP_FLAG_MASK = 0x100;
LONG NTAPI single_step_handler(PEXCEPTION_POINTERS exception_info)
{
if (exception_info->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP)
{
PCONTEXT context = exception_info->ContextRecord;
trap_flag_cleared = (context->EFlags & TRAP_FLAG_MASK) == 0;
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
bool test_interrupts()
{
PVOID veh_handle = AddVectoredExceptionHandler(1, single_step_handler);
if (!veh_handle)
return false;
__writeeflags(__readeflags() | TRAP_FLAG_MASK);
#ifdef __MINGW64__
asm("nop");
#else
__nop();
#endif
RemoveVectoredExceptionHandler(veh_handle);
return trap_flag_cleared;
}
void print_time()
{
@@ -451,11 +712,18 @@ namespace
printf("Time: %lld\n", std::chrono::duration_cast<std::chrono::nanoseconds>(epoch_time).count());
}
bool test_apis()
{
wchar_t buffer[0x100];
DWORD size = sizeof(buffer) / 2;
return GetComputerNameExW(ComputerNameNetBIOS, buffer, &size);
}
bool test_apc()
{
int executions = 0;
auto* apc_func = +[](ULONG_PTR param) {
auto* apc_func = +[](const ULONG_PTR param) {
*reinterpret_cast<int*>(param) += 1; //
};
@@ -494,12 +762,21 @@ int main(const int argc, const char* argv[])
RUN_TEST(test_io, "I/O")
RUN_TEST(test_dir_io, "Dir I/O")
RUN_TEST(test_apis, "APIs")
RUN_TEST(test_working_directory, "Working Directory")
RUN_TEST(test_registry, "Registry")
RUN_TEST(test_system_info, "System Info")
RUN_TEST(test_time_zone, "Time Zone")
RUN_TEST(test_threads, "Threads")
RUN_TEST(test_env, "Environment")
RUN_TEST(test_exceptions, "Exceptions")
#ifndef __MINGW64__
RUN_TEST(test_native_exceptions, "Native Exceptions")
#endif
if (!getenv("EMULATOR_ICICLE"))
{
RUN_TEST(test_interrupts, "Interrupts")
}
RUN_TEST(test_tls, "TLS")
RUN_TEST(test_socket, "Socket")
RUN_TEST(test_apc, "APC")

View File

@@ -111,8 +111,33 @@ CALL :collect shcore.dll
CALL :collect diagnosticdatasettings.dll
CALL :collect mswsock.dll
CALL :collect umpdc.dll
CALL :collect pdh.dll
CALL :collect dxva2.dll
CALL :collect propsys.dll
CALL :collect wintypes.dll
CALL :collect slwga.dll
CALL :collect sppc.dll
CALL :collect kernel.appcore.dll
CALL :collect windows.storage.dll
CALL :collect winnlsres.dll
CALL :collect nlsbres.dll
CALL :collect netutils.dll
CALL :collect dinput8.dll
CALL :collect d3d10.dll
CALL :collect d3d10core.dll
CALL :collect cabinet.dll
CALL :collect msacm32.dll
CALL :collect coloradapterclient.dll
CALL :collect netmsg.dll
CALL :collect rstrtmgr.dll
CALL :collect ncrypt.dll
CALL :collect ntasn1.dll
CALL :collect srvcli.dll
CALL :collect locale.nls
CALL :collect c_1252.nls
CALL :collect c_850.nls
CALL :collect c_437.nls
EXIT /B 0

View File

@@ -22,16 +22,16 @@ int main()
printf("------------\n\n");
const auto peb = static_cast<PPEB64>(GetCurrentProcessPeb());
const auto api_set_map = peb->ApiSetMap;
const auto api_set_map = reinterpret_cast<API_SET_NAMESPACE*>(peb->ApiSetMap);
printf("APISET: 0x%p\n", api_set_map);
printf("Version: %d\n", api_set_map->Version);
printf("Size: %08X\n", api_set_map->Size);
printf("Flags: %08X\n", api_set_map->Flags);
printf("Count: %d\n", api_set_map->Count);
printf("EntryOffset: %08X\n", api_set_map->EntryOffset);
printf("HashOffset: %08X\n", api_set_map->HashOffset);
printf("HashFactor: %08X\n", api_set_map->HashFactor);
printf("APISET: 0x%p\n", static_cast<void*>(api_set_map));
printf("Version: %lu\n", api_set_map->Version);
printf("Size: %08lX\n", api_set_map->Size);
printf("Flags: %08lX\n", api_set_map->Flags);
printf("Count: %lu\n", api_set_map->Count);
printf("EntryOffset: %08lX\n", api_set_map->EntryOffset);
printf("HashOffset: %08lX\n", api_set_map->HashOffset);
printf("HashFactor: %08lX\n", api_set_map->HashFactor);
// print_apiset(apiSetMap);
// Compress the API-SET binary blob
@@ -68,7 +68,7 @@ void print_apiset(PAPI_SET_NAMESPACE api_set_map)
std::wstring name(reinterpret_cast<wchar_t*>(reinterpret_cast<ULONG_PTR>(api_set_map) + entry->NameOffset),
entry->NameLength / sizeof(wchar_t));
printf("-----------\n[%05d]: Contract Name: %ls\n", i, name.data());
printf("-----------\n[%05lu]: Contract Name: %ls\n", i, name.data());
for (ULONG x = 0; x < entry->ValueCount; x++)
{

View File

@@ -14,9 +14,10 @@ target_link_libraries(windows-emulator-test PRIVATE
gtest
gtest_main
windows-emulator
backend-selection
)
if(WIN32)
if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8)
add_dependencies(windows-emulator-test test-sample)
endif()

View File

@@ -34,7 +34,7 @@ namespace test
constexpr auto offset = 1;
const auto instructionsToExecute = executedInstructions - offset;
new_emu.start(instructionsToExecute);
new_emu.start(static_cast<size_t>(instructionsToExecute));
ASSERT_EQ(new_emu.get_executed_instructions(), instructionsToExecute);
ASSERT_NOT_TERMINATED(new_emu);

View File

@@ -1,10 +1,15 @@
#pragma once
#ifdef __MINGW64__
#include <unistd.h>
#endif
#include <cstdlib>
#include <gtest/gtest.h>
#include <windows_emulator.hpp>
#include <backend_selection.hpp>
#include "static_socket_factory.hpp"
#include <network/static_socket_factory.hpp>
#define ASSERT_NOT_TERMINATED(win_emu) \
do \
@@ -63,7 +68,9 @@ namespace test
if (is_verbose)
{
settings.disable_logging = false;
callbacks.on_stdout = [](const std::string_view data) {
std::cout << data; //
};
}
settings.emulation_root = get_emulator_root();
@@ -72,6 +79,7 @@ namespace test
std::filesystem::temp_directory_path() / ("emulator-test-file-" + std::to_string(getpid()) + ".txt");
return windows_emulator{
create_x86_64_emulator(),
settings,
std::move(callbacks),
emulator_interfaces{
@@ -87,8 +95,9 @@ namespace test
if (is_verbose)
{
settings.disable_logging = false;
// settings.verbose_calls = true;
callbacks.on_stdout = [](const std::string_view data) {
std::cout << data; //
};
}
settings.emulation_root = get_emulator_root();
@@ -97,6 +106,7 @@ namespace test
std::filesystem::temp_directory_path() / ("emulator-test-file-" + std::to_string(getpid()) + ".txt");
return windows_emulator{
create_x86_64_emulator(),
get_sample_app_settings(config),
settings,
std::move(callbacks),
@@ -109,7 +119,6 @@ namespace test
inline windows_emulator create_sample_emulator(const sample_configuration& config = {})
{
emulator_settings settings{
.disable_logging = true,
.use_relative_time = true,
};
@@ -119,7 +128,6 @@ namespace test
inline windows_emulator create_empty_emulator()
{
emulator_settings settings{
.disable_logging = true,
.use_relative_time = true,
};
@@ -155,7 +163,7 @@ namespace test
return s1.get_diff(s2).has_value();
};
if (!has_diff_after_count(limit))
if (!has_diff_after_count(static_cast<size_t>(limit)))
{
puts("Emulation has no diff");
}
@@ -170,7 +178,7 @@ namespace test
const auto diff = (upper_bound - lower_bound);
const auto pivot = lower_bound + (diff / 2);
const auto has_diff = has_diff_after_count(pivot);
const auto has_diff = has_diff_after_count(static_cast<size_t>(pivot));
auto* bound = has_diff ? &upper_bound : &lower_bound;
*bound = pivot;
@@ -178,7 +186,7 @@ namespace test
printf("Bounds: %" PRIx64 " - %" PRIx64 "\n", lower_bound, upper_bound);
}
(void)get_state_for_count(lower_bound);
(void)get_state_for_count(static_cast<size_t>(lower_bound));
const auto rip = emu.emu().read_instruction_pointer();

View File

@@ -12,7 +12,6 @@ namespace test
};
const emulator_settings settings{
.disable_logging = true,
.use_relative_time = false,
};

View File

@@ -14,17 +14,7 @@ if(NOT MOMO_ENABLE_CLANG_TIDY)
target_precompile_headers(windows-emulator PRIVATE std_include.hpp)
endif()
target_link_libraries(windows-emulator PRIVATE
unicorn-emulator
)
if (MOMO_ENABLE_RUST_CODE)
target_link_libraries(windows-emulator PRIVATE
icicle-emulator
)
endif()
target_link_libraries(windows-emulator PUBLIC emulator)
target_link_libraries(windows-emulator PUBLIC emulator minidump)
target_include_directories(windows-emulator INTERFACE "${CMAKE_CURRENT_LIST_DIR}")

View File

@@ -5,7 +5,7 @@
#include "../emulator_utils.hpp"
#include <x64_emulator.hpp>
#include <arch_emulator.hpp>
#include <utils/io.hpp>
#include <utils/compression.hpp>
@@ -14,7 +14,7 @@ namespace apiset
{
namespace
{
uint64_t copy_string(x64_emulator& emu, emulator_allocator& allocator, const void* base_ptr,
uint64_t copy_string(x86_64_emulator& emu, emulator_allocator& allocator, const void* base_ptr,
const uint64_t offset, const size_t length)
{
if (!length)
@@ -29,7 +29,7 @@ namespace apiset
return str_obj;
}
ULONG copy_string_as_relative(x64_emulator& emu, emulator_allocator& allocator, const uint64_t result_base,
ULONG copy_string_as_relative(x86_64_emulator& emu, emulator_allocator& allocator, const uint64_t result_base,
const void* base_ptr, const uint64_t offset, const size_t length)
{
const auto address = copy_string(emu, allocator, base_ptr, offset, length);
@@ -57,12 +57,13 @@ namespace apiset
{
switch (location)
{
#ifdef OS_WINDOWS
#ifdef OS_WINDOWS_64
case location::host: {
const auto apiSetMap =
reinterpret_cast<const API_SET_NAMESPACE*>(NtCurrentTeb64()->ProcessEnvironmentBlock->ApiSetMap);
const auto* dataPtr = reinterpret_cast<const std::byte*>(apiSetMap);
return {dataPtr, dataPtr + apiSetMap->Size};
const auto* teb = NtCurrentTeb64();
const auto* peb = reinterpret_cast<PEB64*>(teb->ProcessEnvironmentBlock);
const auto* api_set_map = reinterpret_cast<const API_SET_NAMESPACE*>(peb->ApiSetMap);
const auto* data_ptr = reinterpret_cast<const std::byte*>(api_set_map);
return {data_ptr, data_ptr + api_set_map->Size};
}
#else
case location::host:
@@ -114,13 +115,13 @@ namespace apiset
return obtain(apiset_loc, root);
}
emulator_object<API_SET_NAMESPACE> clone(x64_emulator& emu, emulator_allocator& allocator,
emulator_object<API_SET_NAMESPACE> clone(x86_64_emulator& emu, emulator_allocator& allocator,
const container& container)
{
return clone(emu, allocator, container.get());
}
emulator_object<API_SET_NAMESPACE> clone(x64_emulator& emu, emulator_allocator& allocator,
emulator_object<API_SET_NAMESPACE> clone(x86_64_emulator& emu, emulator_allocator& allocator,
const API_SET_NAMESPACE& orig_api_set_map)
{
const auto api_set_map_obj = allocator.reserve<API_SET_NAMESPACE>();

View File

@@ -29,9 +29,9 @@ namespace apiset
container obtain(location location, const std::filesystem::path& root);
container obtain(const std::filesystem::path& root);
emulator_object<API_SET_NAMESPACE> clone(x64_emulator& emu, emulator_allocator& allocator,
emulator_object<API_SET_NAMESPACE> clone(x86_64_emulator& emu, emulator_allocator& allocator,
const API_SET_NAMESPACE& orig_api_set_map);
emulator_object<API_SET_NAMESPACE> clone(x64_emulator& emu, emulator_allocator& allocator,
emulator_object<API_SET_NAMESPACE> clone(x86_64_emulator& emu, emulator_allocator& allocator,
const container& container);
}

View File

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

View File

@@ -1,8 +1,8 @@
#pragma once
#include "x64_emulator.hpp"
#include "arch_emulator.hpp"
namespace cpu_context
{
void save(x64_emulator& emu, CONTEXT64& context);
void restore(x64_emulator& emu, const CONTEXT64& context);
void save(x86_64_emulator& emu, CONTEXT64& context);
void restore(x86_64_emulator& emu, const CONTEXT64& context);
}

View File

@@ -225,7 +225,7 @@ namespace
const io_device_context& c)
{
constexpr auto info_size = offsetof(AFD_POLL_INFO64, Handles);
if (!c.input_buffer || c.input_buffer_length < info_size)
if (!c.input_buffer || c.input_buffer_length < info_size || c.input_buffer != c.output_buffer)
{
throw std::runtime_error("Bad AFD poll data");
}
@@ -254,12 +254,7 @@ namespace
{
int16_t socket_events{};
if (poll_events & (AFD_POLL_ACCEPT | AFD_POLL_RECEIVE))
{
socket_events |= POLLRDNORM;
}
if (poll_events & AFD_POLL_RECEIVE_EXPEDITED)
if (poll_events & (AFD_POLL_DISCONNECT | AFD_POLL_ACCEPT | AFD_POLL_RECEIVE))
{
socket_events |= POLLRDNORM;
}
@@ -269,7 +264,7 @@ namespace
socket_events |= POLLRDBAND;
}
if (poll_events & (AFD_POLL_CONNECT_FAIL | AFD_POLL_SEND))
if (poll_events & (AFD_POLL_CONNECT | AFD_POLL_CONNECT_FAIL | AFD_POLL_SEND))
{
socket_events |= POLLWRNORM;
}
@@ -277,35 +272,51 @@ namespace
return socket_events;
}
ULONG map_socket_response_events_to_afd(const int16_t socket_events)
ULONG map_socket_response_events_to_afd(const int16_t socket_events, const ULONG afd_poll_events,
const bool is_listening, const bool is_connecting)
{
ULONG afd_events = 0;
if (socket_events & POLLRDNORM)
{
afd_events |= (AFD_POLL_ACCEPT | AFD_POLL_RECEIVE);
if (!is_listening && afd_poll_events & AFD_POLL_RECEIVE)
{
afd_events |= AFD_POLL_RECEIVE;
}
else if (is_listening && afd_poll_events & AFD_POLL_ACCEPT)
{
afd_events |= AFD_POLL_ACCEPT;
}
}
if (socket_events & POLLRDBAND)
if (socket_events & POLLRDBAND && afd_poll_events & AFD_POLL_RECEIVE_EXPEDITED)
{
afd_events |= AFD_POLL_RECEIVE_EXPEDITED;
}
if (socket_events & POLLWRNORM)
{
afd_events |= (AFD_POLL_CONNECT_FAIL | AFD_POLL_SEND);
if (!is_connecting && afd_poll_events & AFD_POLL_SEND)
{
afd_events |= AFD_POLL_SEND;
}
else if (is_connecting && afd_poll_events & AFD_POLL_CONNECT)
{
afd_events |= AFD_POLL_CONNECT;
}
}
if ((socket_events & (POLLHUP | POLLERR)) == (POLLHUP | POLLERR))
if ((socket_events & (POLLHUP | POLLERR)) == (POLLHUP | POLLERR) &&
afd_poll_events & (AFD_POLL_CONNECT_FAIL | AFD_POLL_ABORT))
{
afd_events |= (AFD_POLL_CONNECT_FAIL | AFD_POLL_ABORT);
}
else if (socket_events & POLLHUP)
else if (socket_events & POLLHUP && afd_poll_events & AFD_POLL_DISCONNECT)
{
afd_events |= AFD_POLL_DISCONNECT;
}
if (socket_events & POLLNVAL)
if (socket_events & POLLNVAL && afd_poll_events & AFD_POLL_LOCAL_CLOSE)
{
afd_events |= AFD_POLL_LOCAL_CLOSE;
}
@@ -313,62 +324,14 @@ namespace
return afd_events;
}
NTSTATUS perform_poll(windows_emulator& win_emu, const io_device_context& c,
const std::span<network::i_socket* const> endpoints,
const std::span<const AFD_POLL_HANDLE_INFO64> handles)
{
std::vector<network::poll_entry> poll_data{};
poll_data.resize(endpoints.size());
for (size_t i = 0; i < endpoints.size() && i < handles.size(); ++i)
{
auto& pfd = poll_data.at(i);
const auto& handle = handles[i];
pfd.s = endpoints[i];
pfd.events = map_afd_request_events_to_socket(handle.PollEvents);
pfd.revents = pfd.events;
}
const auto count = win_emu.socket_factory().poll_sockets(poll_data);
if (count <= 0)
{
return STATUS_PENDING;
}
constexpr auto info_size = offsetof(AFD_POLL_INFO64, Handles);
const emulator_object<AFD_POLL_HANDLE_INFO64> handle_info_obj{win_emu.emu(), c.input_buffer + info_size};
size_t current_index = 0;
for (size_t i = 0; i < endpoints.size(); ++i)
{
const auto& pfd = poll_data.at(i);
if (pfd.revents == 0)
{
continue;
}
auto entry = handle_info_obj.read(i);
entry.PollEvents = map_socket_response_events_to_afd(pfd.revents);
entry.Status = STATUS_SUCCESS;
handle_info_obj.write(entry, current_index++);
break;
}
assert(current_index == static_cast<size_t>(count));
const emulator_object<AFD_POLL_INFO64> info_obj{win_emu.emu(), c.input_buffer};
info_obj.access([&](AFD_POLL_INFO64& info) {
info.NumberOfHandles = static_cast<ULONG>(current_index); //
});
return STATUS_SUCCESS;
}
struct afd_endpoint : io_device
{
struct pending_connection
{
network::address remote_address;
std::unique_ptr<network::i_socket> accepted_socket;
};
std::unique_ptr<network::i_socket> s_{};
bool executing_delayed_ioctl_{};
@@ -376,6 +339,14 @@ namespace
std::optional<bool> require_poll_{};
std::optional<io_device_context> delayed_ioctl_{};
std::optional<std::chrono::steady_clock::time_point> timeout_{};
std::optional<std::function<void(windows_emulator&, const io_device_context&)>> timeout_callback_{};
std::unordered_map<LONG, pending_connection> pending_connections_{};
LONG next_sequence_{0};
std::optional<handle> event_select_event_{};
ULONG event_select_mask_{0};
ULONG triggered_events_{0};
afd_endpoint()
{
@@ -415,15 +386,17 @@ namespace
this->s_->set_blocking(false);
}
void delay_ioctrl(const io_device_context& c,
void delay_ioctrl(const io_device_context& c, const std::optional<bool> require_poll = {},
const std::optional<std::chrono::steady_clock::time_point> timeout = {},
const std::optional<bool> require_poll = {})
const std::optional<std::function<void(windows_emulator&, const io_device_context&)>>&
timeout_callback = {})
{
if (this->executing_delayed_ioctl_)
{
return;
}
this->timeout_callback_ = timeout_callback;
this->timeout_ = timeout;
this->require_poll_ = require_poll;
this->delayed_ioctl_ = c;
@@ -431,6 +404,7 @@ namespace
void clear_pending_state()
{
this->timeout_callback_ = {};
this->timeout_ = {};
this->require_poll_ = {};
this->delayed_ioctl_ = {};
@@ -438,41 +412,89 @@ namespace
void work(windows_emulator& win_emu) override
{
if (!this->delayed_ioctl_ || !this->s_)
if (!this->s_ || (!this->delayed_ioctl_ && !this->event_select_mask_))
{
return;
}
this->executing_delayed_ioctl_ = true;
const auto _ = utils::finally([&] { this->executing_delayed_ioctl_ = false; });
network::poll_entry pfd{};
pfd.s = this->s_.get();
if (this->require_poll_.has_value())
if (this->delayed_ioctl_ && this->require_poll_.has_value())
{
const auto is_ready = this->s_->is_ready(*this->require_poll_);
if (!is_ready)
pfd.events |= *this->require_poll_ ? POLLIN : POLLOUT;
}
if (this->event_select_mask_)
{
pfd.events =
static_cast<int16_t>(pfd.events | map_afd_request_events_to_socket(this->event_select_mask_));
}
pfd.revents = pfd.events;
if (pfd.events != 0)
{
win_emu.socket_factory().poll_sockets(std::span{&pfd, 1});
}
const auto socket_events = pfd.revents;
if (socket_events && this->event_select_mask_)
{
const bool is_connecting =
this->delayed_ioctl_ && _AFD_REQUEST(this->delayed_ioctl_->io_control_code) == AFD_CONNECT;
ULONG current_events = map_socket_response_events_to_afd(socket_events, this->event_select_mask_,
pfd.s->is_listening(), is_connecting);
if ((current_events & ~this->triggered_events_) != 0)
{
return;
this->triggered_events_ |= current_events;
if (auto* event = win_emu.process.events.get(*this->event_select_event_))
{
event->signaled = true;
}
}
}
const auto status = this->execute_ioctl(win_emu, *this->delayed_ioctl_);
if (status == STATUS_PENDING)
if (this->delayed_ioctl_)
{
if (!this->timeout_ || this->timeout_ > win_emu.clock().steady_now())
this->executing_delayed_ioctl_ = true;
const auto _ = utils::finally([&] { this->executing_delayed_ioctl_ = false; });
if (this->require_poll_.has_value())
{
return;
const auto is_ready =
socket_events & ((*this->require_poll_ ? POLLIN : POLLOUT) | POLLHUP | POLLERR);
if (!is_ready)
{
return;
}
}
write_io_status(this->delayed_ioctl_->io_status_block, STATUS_TIMEOUT);
}
const auto status = this->execute_ioctl(win_emu, *this->delayed_ioctl_);
if (status == STATUS_PENDING)
{
if (!this->timeout_ || this->timeout_ > win_emu.clock().steady_now())
{
return;
}
auto* e = win_emu.process.events.get(this->delayed_ioctl_->event);
if (e)
{
e->signaled = true;
}
write_io_status(this->delayed_ioctl_->io_status_block, STATUS_TIMEOUT);
this->clear_pending_state();
if (this->timeout_callback_)
{
(*this->timeout_callback_)(win_emu, *this->delayed_ioctl_);
}
}
auto* e = win_emu.process.events.get(this->delayed_ioctl_->event);
if (e)
{
e->signaled = true;
}
this->clear_pending_state();
}
}
void deserialize_object(utils::buffer_deserializer& buffer) override
@@ -497,33 +519,91 @@ namespace
{
if (_AFD_BASE(c.io_control_code) != FSCTL_AFD_BASE)
{
win_emu.log.print(color::cyan, "Bad AFD IOCTL: %X\n", c.io_control_code);
win_emu.log.error("Bad AFD IOCTL: 0x%X\n", static_cast<uint32_t>(c.io_control_code));
return STATUS_NOT_SUPPORTED;
}
win_emu.log.print(color::dark_gray, "--> AFD IOCTL: %X\n", c.io_control_code);
const auto request = _AFD_REQUEST(c.io_control_code);
switch (request)
{
case AFD_BIND:
return this->ioctl_bind(win_emu, c);
case AFD_CONNECT:
return this->ioctl_connect(win_emu, c);
case AFD_START_LISTEN:
return this->ioctl_listen(win_emu, c);
case AFD_WAIT_FOR_LISTEN:
return this->ioctl_wait_for_listen(win_emu, c);
case AFD_ACCEPT:
return this->ioctl_accept(win_emu, c);
case AFD_SEND:
return this->ioctl_send(win_emu, c);
case AFD_RECEIVE:
return this->ioctl_receive(win_emu, c);
case AFD_SEND_DATAGRAM:
return this->ioctl_send_datagram(win_emu, c);
case AFD_RECEIVE_DATAGRAM:
return this->ioctl_receive_datagram(win_emu, c);
case AFD_POLL:
return this->ioctl_poll(win_emu, c);
case AFD_GET_ADDRESS:
return this->ioctl_get_address(win_emu, c);
case AFD_EVENT_SELECT:
return this->ioctl_event_select(win_emu, c);
case AFD_ENUM_NETWORK_EVENTS:
return this->ioctl_enum_network_events(win_emu, c);
case AFD_SET_CONTEXT:
case AFD_GET_INFORMATION:
case AFD_SET_INFORMATION:
case AFD_QUERY_HANDLES:
case AFD_TRANSPORT_IOCTL:
return STATUS_SUCCESS;
default:
win_emu.log.print(color::gray, "Unsupported AFD IOCTL: %X\n", c.io_control_code);
win_emu.log.error("Unsupported AFD IOCTL: 0x%X (%u)\n", static_cast<uint32_t>(c.io_control_code),
static_cast<uint32_t>(request));
return STATUS_NOT_SUPPORTED;
}
}
NTSTATUS ioctl_connect(windows_emulator& win_emu, const io_device_context& c)
{
if (!this->s_)
{
throw std::runtime_error("Invalid AFD endpoint socket!");
}
auto data = win_emu.emu().read_memory(c.input_buffer, c.input_buffer_length);
constexpr auto address_offset = 24;
if (data.size() < address_offset)
{
return STATUS_BUFFER_TOO_SMALL;
}
const auto addr = convert_to_host_address(win_emu, std::span(data).subspan(address_offset));
if (!this->s_->connect(addr))
{
const auto error = this->s_->get_last_error();
if (error == SERR(EWOULDBLOCK))
{
this->delay_ioctrl(c, false);
return STATUS_PENDING;
}
if (this->executing_delayed_ioctl_ && error == SERR(EISCONN))
{
return STATUS_SUCCESS;
}
return STATUS_UNSUCCESSFUL;
}
return STATUS_SUCCESS;
}
NTSTATUS ioctl_bind(windows_emulator& win_emu, const io_device_context& c) const
{
if (!this->s_)
@@ -550,12 +630,266 @@ namespace
return STATUS_SUCCESS;
}
static std::vector<network::i_socket*> resolve_endpoints(windows_emulator& win_emu,
const std::span<const AFD_POLL_HANDLE_INFO64> handles)
NTSTATUS ioctl_listen(windows_emulator& win_emu, const io_device_context& c) const
{
if (!this->s_)
{
throw std::runtime_error("Invalid AFD endpoint socket!");
}
if (c.input_buffer_length < sizeof(AFD_LISTEN_INFO))
{
return STATUS_BUFFER_TOO_SMALL;
}
const auto listen_info = win_emu.emu().read_memory<AFD_LISTEN_INFO>(c.input_buffer);
if (!this->s_->listen(static_cast<int>(listen_info.MaximumConnectionQueue)))
{
return STATUS_INVALID_PARAMETER;
}
return STATUS_SUCCESS;
}
NTSTATUS ioctl_wait_for_listen(windows_emulator& win_emu, const io_device_context& c)
{
if (!this->s_)
{
throw std::runtime_error("Invalid AFD endpoint socket!");
}
if (c.output_buffer_length < sizeof(AFD_LISTEN_RESPONSE_INFO))
{
return STATUS_BUFFER_TOO_SMALL;
}
network::address remote_address{};
auto accepted_socket_ptr = this->s_->accept(remote_address);
if (!accepted_socket_ptr)
{
const auto error = this->s_->get_last_error();
if (error == SERR(EWOULDBLOCK))
{
this->delay_ioctrl(c, true);
return STATUS_PENDING;
}
return STATUS_UNSUCCESSFUL;
}
if (!remote_address.is_ipv4())
{
throw std::runtime_error("Unsupported address family");
}
pending_connection pending{};
pending.remote_address = remote_address;
pending.accepted_socket = std::move(accepted_socket_ptr);
LONG sequence = next_sequence_++;
pending_connections_.try_emplace(sequence, std::move(pending));
AFD_LISTEN_RESPONSE_INFO response{};
response.Sequence = sequence;
auto transport_buffer = convert_to_win_address(win_emu, remote_address);
memcpy(&response.RemoteAddress, transport_buffer.data(), sizeof(win_sockaddr));
win_emu.emu().write_memory<AFD_LISTEN_RESPONSE_INFO>(c.output_buffer, response);
if (c.io_status_block)
{
IO_STATUS_BLOCK<EmulatorTraits<Emu64>> block{};
block.Information = sizeof(AFD_LISTEN_RESPONSE_INFO);
c.io_status_block.write(block);
}
return STATUS_SUCCESS;
}
NTSTATUS ioctl_accept(windows_emulator& win_emu, const io_device_context& c)
{
if (!this->s_)
{
throw std::runtime_error("Invalid AFD endpoint socket!");
}
if (c.input_buffer_length < sizeof(AFD_ACCEPT_INFO))
{
return STATUS_BUFFER_TOO_SMALL;
}
const auto accept_info = win_emu.emu().read_memory<AFD_ACCEPT_INFO>(c.input_buffer);
const auto it = pending_connections_.find(accept_info.Sequence);
if (it == pending_connections_.end())
{
return STATUS_INVALID_PARAMETER;
}
auto& accepted_socket = it->second.accepted_socket;
auto* target_device = win_emu.process.devices.get(accept_info.AcceptHandle);
if (!target_device)
{
return STATUS_INVALID_HANDLE;
}
auto* target_endpoint = target_device->get_internal_device<afd_endpoint>();
if (!target_endpoint)
{
return STATUS_INVALID_HANDLE;
}
target_endpoint->s_ = std::move(accepted_socket);
pending_connections_.erase(it);
return STATUS_SUCCESS;
}
NTSTATUS ioctl_receive(windows_emulator& win_emu, const io_device_context& c)
{
if (!this->s_)
{
throw std::runtime_error("Invalid AFD endpoint socket!");
}
auto& emu = win_emu.emu();
if (c.input_buffer_length < sizeof(AFD_RECV_INFO<EmulatorTraits<Emu64>>))
{
return STATUS_BUFFER_TOO_SMALL;
}
const auto receive_info = emu.read_memory<AFD_RECV_INFO<EmulatorTraits<Emu64>>>(c.input_buffer);
if (!receive_info.BufferArray || receive_info.BufferCount == 0)
{
return STATUS_INVALID_PARAMETER;
}
if (receive_info.BufferCount > 1)
{
// TODO: Scatter/Gather
return STATUS_NOT_SUPPORTED;
}
const auto wsabuf = emu.read_memory<EMU_WSABUF<EmulatorTraits<Emu64>>>(receive_info.BufferArray);
if (!wsabuf.buf || wsabuf.len == 0)
{
return STATUS_INVALID_PARAMETER;
}
std::vector<std::byte> host_buffer;
host_buffer.resize(wsabuf.len);
const auto bytes_received = this->s_->recv(host_buffer);
if (bytes_received < 0)
{
const auto error = this->s_->get_last_error();
if (error == SERR(EWOULDBLOCK))
{
this->delay_ioctrl(c, true);
return STATUS_PENDING;
}
if (error == SERR(ECONNRESET))
{
return STATUS_CONNECTION_RESET;
}
return STATUS_UNSUCCESSFUL;
}
emu.write_memory(wsabuf.buf, host_buffer.data(), static_cast<size_t>(bytes_received));
if (c.io_status_block)
{
IO_STATUS_BLOCK<EmulatorTraits<Emu64>> block{};
block.Information = static_cast<uint32_t>(bytes_received);
c.io_status_block.write(block);
}
return STATUS_SUCCESS;
}
NTSTATUS ioctl_send(windows_emulator& win_emu, const io_device_context& c)
{
if (!this->s_)
{
throw std::runtime_error("Invalid AFD endpoint socket!");
}
auto& emu = win_emu.emu();
if (c.input_buffer_length < sizeof(AFD_SEND_INFO<EmulatorTraits<Emu64>>))
{
return STATUS_BUFFER_TOO_SMALL;
}
const auto send_info = emu.read_memory<AFD_SEND_INFO<EmulatorTraits<Emu64>>>(c.input_buffer);
if (!send_info.BufferArray || send_info.BufferCount == 0)
{
return STATUS_INVALID_PARAMETER;
}
if (send_info.BufferCount > 1)
{
// TODO: Scatter/Gather
return STATUS_NOT_SUPPORTED;
}
const auto wsabuf = emu.read_memory<EMU_WSABUF<EmulatorTraits<Emu64>>>(send_info.BufferArray);
if (!wsabuf.buf || wsabuf.len == 0)
{
return STATUS_INVALID_PARAMETER;
}
std::vector<std::byte> host_buffer;
host_buffer.resize(wsabuf.len);
emu.read_memory(wsabuf.buf, host_buffer.data(), host_buffer.size());
const auto bytes_sent = this->s_->send(host_buffer);
if (bytes_sent < 0)
{
const auto error = this->s_->get_last_error();
if (error == SERR(EWOULDBLOCK))
{
this->delay_ioctrl(c, false);
return STATUS_PENDING;
}
if (error == SERR(ECONNRESET))
{
return STATUS_CONNECTION_RESET;
}
return STATUS_UNSUCCESSFUL;
}
if (c.io_status_block)
{
IO_STATUS_BLOCK<EmulatorTraits<Emu64>> block{};
block.Information = static_cast<uint32_t>(bytes_sent);
c.io_status_block.write(block);
}
return STATUS_SUCCESS;
}
static std::vector<const afd_endpoint*> resolve_endpoints(windows_emulator& win_emu,
const std::span<const AFD_POLL_HANDLE_INFO64> handles)
{
auto& proc = win_emu.process;
std::vector<network::i_socket*> endpoints{};
std::vector<const afd_endpoint*> endpoints{};
endpoints.reserve(handles.size());
for (const auto& handle : handles)
@@ -572,12 +906,79 @@ namespace
throw std::runtime_error("Invalid AFD endpoint!");
}
endpoints.push_back(endpoint->s_.get());
endpoints.push_back(endpoint);
}
return endpoints;
}
static NTSTATUS perform_poll(windows_emulator& win_emu, const io_device_context& c,
const std::span<const afd_endpoint* const> endpoints,
const std::span<const AFD_POLL_HANDLE_INFO64> handles)
{
std::vector<network::poll_entry> poll_data{};
poll_data.resize(endpoints.size());
for (size_t i = 0; i < endpoints.size() && i < handles.size(); ++i)
{
auto& pfd = poll_data.at(i);
const auto& handle = handles[i];
pfd.s = endpoints[i]->s_.get();
pfd.events = map_afd_request_events_to_socket(handle.PollEvents);
pfd.revents = pfd.events;
}
const auto count = win_emu.socket_factory().poll_sockets(poll_data);
if (count <= 0)
{
return STATUS_PENDING;
}
constexpr auto info_size = offsetof(AFD_POLL_INFO64, Handles);
const emulator_object<AFD_POLL_HANDLE_INFO64> handle_info_obj{win_emu.emu(), c.input_buffer + info_size};
size_t current_index = 0;
for (size_t i = 0; i < endpoints.size(); ++i)
{
const auto& pfd = poll_data.at(i);
if (pfd.revents == 0)
{
continue;
}
const auto& handle = handles[i];
const auto& endpoint = endpoints[i];
const bool is_connecting =
endpoint->delayed_ioctl_ && _AFD_REQUEST(endpoint->delayed_ioctl_->io_control_code) == AFD_CONNECT;
auto entry = handle_info_obj.read(i);
entry.PollEvents = map_socket_response_events_to_afd(pfd.revents, handle.PollEvents,
pfd.s->is_listening(), is_connecting);
entry.Status = STATUS_SUCCESS;
handle_info_obj.write(entry, current_index++);
}
assert(current_index == static_cast<size_t>(count));
const emulator_object<AFD_POLL_INFO64> info_obj{win_emu.emu(), c.input_buffer};
info_obj.access([&](AFD_POLL_INFO64& info) {
info.NumberOfHandles = static_cast<ULONG>(current_index); //
});
if (c.io_status_block)
{
IO_STATUS_BLOCK<EmulatorTraits<Emu64>> block{};
block.Information = info_size + sizeof(AFD_POLL_HANDLE_INFO64) * current_index;
c.io_status_block.write(block);
}
return STATUS_SUCCESS;
}
NTSTATUS ioctl_poll(windows_emulator& win_emu, const io_device_context& c)
{
const auto [info, handles] = get_poll_info(win_emu, c);
@@ -591,9 +992,21 @@ namespace
if (!this->executing_delayed_ioctl_)
{
const auto timeout_callback = [](windows_emulator& win_emu, const io_device_context& c) {
const emulator_object<AFD_POLL_INFO64> info_obj{win_emu.emu(), c.input_buffer};
info_obj.access([&](AFD_POLL_INFO64& poll_info) {
poll_info.NumberOfHandles = 0; //
});
};
if (!info.Timeout.QuadPart)
{
return status;
if (status == STATUS_PENDING)
{
timeout_callback(win_emu, c);
return STATUS_TIMEOUT;
}
return STATUS_SUCCESS;
}
std::optional<std::chrono::steady_clock::time_point> timeout{};
@@ -602,7 +1015,7 @@ namespace
timeout = utils::convert_delay_interval_to_time_point(win_emu.clock(), info.Timeout);
}
this->delay_ioctrl(c, timeout);
this->delay_ioctrl(c, {}, timeout, timeout_callback);
}
return STATUS_PENDING;
@@ -641,7 +1054,7 @@ namespace
const auto error = this->s_->get_last_error();
if (error == SERR(EWOULDBLOCK))
{
this->delay_ioctrl(c, {}, true);
this->delay_ioctrl(c, true);
return STATUS_PENDING;
}
@@ -701,7 +1114,7 @@ namespace
const auto error = this->s_->get_last_error();
if (error == SERR(EWOULDBLOCK))
{
this->delay_ioctrl(c, {}, false);
this->delay_ioctrl(c, false);
return STATUS_PENDING;
}
@@ -717,6 +1130,139 @@ namespace
return STATUS_SUCCESS;
}
NTSTATUS ioctl_get_address(windows_emulator& win_emu, const io_device_context& c) const
{
if (!this->s_)
{
throw std::runtime_error("Invalid AFD endpoint socket!");
}
const auto local_address = this->s_->get_local_address();
if (!local_address)
{
return STATUS_INVALID_PARAMETER;
}
std::vector<std::byte> win_addr_bytes = convert_to_win_address(win_emu, *local_address);
if (c.output_buffer_length < win_addr_bytes.size())
{
return STATUS_BUFFER_TOO_SMALL;
}
win_emu.emu().write_memory(c.output_buffer, win_addr_bytes.data(), win_addr_bytes.size());
if (c.io_status_block)
{
IO_STATUS_BLOCK<EmulatorTraits<Emu64>> block{};
block.Information = static_cast<ULONG>(win_addr_bytes.size());
c.io_status_block.write(block);
}
return STATUS_SUCCESS;
}
NTSTATUS ioctl_event_select(windows_emulator& win_emu, const io_device_context& c)
{
if (!this->s_)
{
throw std::runtime_error("Invalid AFD endpoint socket!");
}
if (c.input_buffer_length < sizeof(AFD_EVENT_SELECT_INFO))
{
return STATUS_BUFFER_TOO_SMALL;
}
const auto select_info = win_emu.emu().read_memory<AFD_EVENT_SELECT_INFO>(c.input_buffer);
this->event_select_event_ = select_info.Event;
this->event_select_mask_ = select_info.PollEvents;
this->triggered_events_ = 0;
if (auto* event = win_emu.process.events.get(select_info.Event))
{
event->signaled = false;
}
return STATUS_SUCCESS;
}
NTSTATUS ioctl_enum_network_events(windows_emulator& win_emu, const io_device_context& c)
{
if (!this->s_)
{
throw std::runtime_error("Invalid AFD endpoint socket!");
}
if (c.output_buffer_length < 56)
{
return STATUS_BUFFER_TOO_SMALL;
}
if (c.input_buffer)
{
if (c.input_buffer_length == 0)
{
handle h{};
h.bits = c.input_buffer;
if (auto* event = win_emu.process.events.get(h))
{
event->signaled = false;
}
}
else
{
return STATUS_NOT_SUPPORTED;
}
}
win_emu.emu().write_memory(c.output_buffer, this->triggered_events_);
this->triggered_events_ = 0;
if (c.io_status_block)
{
IO_STATUS_BLOCK<EmulatorTraits<Emu64>> block{};
block.Information = 56;
c.io_status_block.write(block);
}
return STATUS_SUCCESS;
}
};
struct afd_async_connect_hlp : stateless_device
{
NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& c) override
{
if (c.io_control_code != 0x12007)
{
return STATUS_NOT_SUPPORTED;
}
if (c.input_buffer_length < 40)
{
return STATUS_BUFFER_TOO_SMALL;
}
const auto target_handle = win_emu.emu().read_memory<handle>(c.input_buffer + 16);
auto* target_device = win_emu.process.devices.get(target_handle);
if (!target_device)
{
return STATUS_INVALID_HANDLE;
}
auto* target_endpoint = target_device->get_internal_device<afd_endpoint>();
if (!target_endpoint)
{
return STATUS_INVALID_HANDLE;
}
return target_endpoint->execute_ioctl(win_emu, c);
}
};
}
@@ -724,3 +1270,8 @@ std::unique_ptr<io_device> create_afd_endpoint()
{
return std::make_unique<afd_endpoint>();
}
std::unique_ptr<io_device> create_afd_async_connect_hlp()
{
return std::make_unique<afd_async_connect_hlp>();
}

View File

@@ -2,3 +2,4 @@
#include "../io_device.hpp"
std::unique_ptr<io_device> create_afd_endpoint();
std::unique_ptr<io_device> create_afd_async_connect_hlp();

View File

@@ -6,6 +6,32 @@
typedef LONG TDI_STATUS;
struct win_sockaddr
{
USHORT sa_family;
CHAR sa_data[14];
};
struct AFD_LISTEN_INFO
{
BOOLEAN SanActive;
ULONG MaximumConnectionQueue;
BOOLEAN UseDelayedAcceptance;
};
struct AFD_LISTEN_RESPONSE_INFO
{
LONG Sequence;
win_sockaddr RemoteAddress;
};
struct AFD_ACCEPT_INFO
{
BOOLEAN SanActive;
LONG Sequence;
handle AcceptHandle;
};
template <typename Traits>
struct TDI_CONNECTION_INFORMATION
{
@@ -93,31 +119,37 @@ struct AFD_POLL_INFO64
AFD_POLL_HANDLE_INFO64 Handles[1];
};
#define AFD_POLL_RECEIVE_BIT 0
#define AFD_POLL_RECEIVE (1 << AFD_POLL_RECEIVE_BIT)
#define AFD_POLL_RECEIVE_EXPEDITED_BIT 1
#define AFD_POLL_RECEIVE_EXPEDITED (1 << AFD_POLL_RECEIVE_EXPEDITED_BIT)
#define AFD_POLL_SEND_BIT 2
#define AFD_POLL_SEND (1 << AFD_POLL_SEND_BIT)
#define AFD_POLL_DISCONNECT_BIT 3
#define AFD_POLL_DISCONNECT (1 << AFD_POLL_DISCONNECT_BIT)
#define AFD_POLL_ABORT_BIT 4
#define AFD_POLL_ABORT (1 << AFD_POLL_ABORT_BIT)
#define AFD_POLL_LOCAL_CLOSE_BIT 5
#define AFD_POLL_LOCAL_CLOSE (1 << AFD_POLL_LOCAL_CLOSE_BIT)
#define AFD_POLL_CONNECT_BIT 6
#define AFD_POLL_CONNECT (1 << AFD_POLL_CONNECT_BIT)
#define AFD_POLL_ACCEPT_BIT 7
#define AFD_POLL_ACCEPT (1 << AFD_POLL_ACCEPT_BIT)
#define AFD_POLL_CONNECT_FAIL_BIT 8
#define AFD_POLL_CONNECT_FAIL (1 << AFD_POLL_CONNECT_FAIL_BIT)
#define AFD_POLL_QOS_BIT 9
#define AFD_POLL_QOS (1 << AFD_POLL_QOS_BIT)
#define AFD_POLL_GROUP_QOS_BIT 10
#define AFD_POLL_GROUP_QOS (1 << AFD_POLL_GROUP_QOS_BIT)
#define AFD_POLL_RECEIVE_BIT 0
#define AFD_POLL_RECEIVE (1 << AFD_POLL_RECEIVE_BIT)
#define AFD_POLL_RECEIVE_EXPEDITED_BIT 1
#define AFD_POLL_RECEIVE_EXPEDITED (1 << AFD_POLL_RECEIVE_EXPEDITED_BIT)
#define AFD_POLL_SEND_BIT 2
#define AFD_POLL_SEND (1 << AFD_POLL_SEND_BIT)
#define AFD_POLL_DISCONNECT_BIT 3
#define AFD_POLL_DISCONNECT (1 << AFD_POLL_DISCONNECT_BIT)
#define AFD_POLL_ABORT_BIT 4
#define AFD_POLL_ABORT (1 << AFD_POLL_ABORT_BIT)
#define AFD_POLL_LOCAL_CLOSE_BIT 5
#define AFD_POLL_LOCAL_CLOSE (1 << AFD_POLL_LOCAL_CLOSE_BIT)
#define AFD_POLL_CONNECT_BIT 6
#define AFD_POLL_CONNECT (1 << AFD_POLL_CONNECT_BIT)
#define AFD_POLL_ACCEPT_BIT 7
#define AFD_POLL_ACCEPT (1 << AFD_POLL_ACCEPT_BIT)
#define AFD_POLL_CONNECT_FAIL_BIT 8
#define AFD_POLL_CONNECT_FAIL (1 << AFD_POLL_CONNECT_FAIL_BIT)
#define AFD_POLL_QOS_BIT 9
#define AFD_POLL_QOS (1 << AFD_POLL_QOS_BIT)
#define AFD_POLL_GROUP_QOS_BIT 10
#define AFD_POLL_GROUP_QOS (1 << AFD_POLL_GROUP_QOS_BIT)
#define AFD_NUM_POLL_EVENTS 11
#define AFD_POLL_ALL ((1 << AFD_NUM_POLL_EVENTS) - 1)
#define AFD_NUM_POLL_EVENTS 11
#define AFD_POLL_ALL ((1 << AFD_NUM_POLL_EVENTS) - 1)
struct AFD_EVENT_SELECT_INFO
{
handle Event;
ULONG PollEvents;
};
#define _AFD_REQUEST(ioctl) ((((ULONG)(ioctl)) >> 2) & 0x03FF)
#define _AFD_BASE(ioctl) ((((ULONG)(ioctl)) >> 12) & 0xFFFFF)
@@ -173,5 +205,11 @@ struct AFD_POLL_INFO64
#define AFD_NO_OPERATION 39
#define AFD_VALIDATE_GROUP 40
#define AFD_GET_UNACCEPTED_CONNECT_DATA 41
#define AFD_ROUTING_INTERFACE_QUERY 42
#define AFD_ROUTING_INTERFACE_CHANGE 43
#define AFD_ADDRESS_LIST_QUERY 44
#define AFD_ADDRESS_LIST_CHANGE 45
#define AFD_JOIN_LEAF 46
#define AFD_TRANSPORT_IOCTL 47
// NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)

View File

@@ -2,18 +2,119 @@
#include "mount_point_manager.hpp"
#include "../windows_emulator.hpp"
#include "mountmgr.hpp"
namespace
{
std::pair<ULONG, USHORT> write_data(std::vector<uint8_t>& buffer, const std::span<const uint8_t> data)
{
const auto offset = buffer.size();
buffer.insert(buffer.end(), data.begin(), data.end());
return std::make_pair(static_cast<ULONG>(offset), static_cast<USHORT>(data.size()));
}
template <typename Char>
std::pair<ULONG, USHORT> write_string(std::vector<uint8_t>& buffer, const std::basic_string_view<Char> str)
{
std::span data(reinterpret_cast<const uint8_t*>(str.data()), str.size() * sizeof(Char));
return write_data(buffer, data);
}
std::string make_drive_id(const uint64_t low = 0, const uint64_t high = 0)
{
std::string id = "DMIO:ID:";
id.append(reinterpret_cast<const char*>(&low), sizeof(low));
id.append(reinterpret_cast<const char*>(&high), sizeof(high));
return id;
}
std::u16string make_volume(const uint64_t low = 0, const uint64_t high = 0)
{
auto str = utils::string::to_hex_string(low) + utils::string::to_hex_string(high);
str.insert(str.begin() + 20, '-');
str.insert(str.begin() + 16, '-');
str.insert(str.begin() + 12, '-');
str.insert(str.begin() + 8, '-');
const std::string volume = utils::string::va("\\??\\Volume{%s}", str.c_str());
return u8_to_u16(volume);
}
struct mount_point_manager : stateless_device
{
NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& c) override
static NTSTATUS query_points(windows_emulator& win_emu, const io_device_context& c)
{
if (c.io_control_code != 0x6D0030)
const auto drives = win_emu.file_sys.list_drives();
const auto struct_size = sizeof(MOUNTMGR_MOUNT_POINTS) + sizeof(MOUNTMGR_MOUNT_POINT) * drives.size();
std::vector<MOUNTMGR_MOUNT_POINT> mount_points{};
std::vector<uint8_t> buffer{};
buffer.resize(struct_size);
{
return STATUS_NOT_SUPPORTED;
MOUNTMGR_MOUNT_POINT point{};
const auto symlink = write_string<char16_t>(buffer, u"\\DosDevices\\");
const auto id = write_string<char>(buffer, make_drive_id(0, 1));
const auto name = write_string<char16_t>(buffer, u"\\Device\\HarddiskVolume0");
point.SymbolicLinkNameOffset = symlink.first;
point.SymbolicLinkNameLength = symlink.second;
point.UniqueIdOffset = id.first;
point.UniqueIdLength = id.second;
point.DeviceNameOffset = name.first;
point.DeviceNameLength = name.second;
mount_points.push_back(point);
}
for (const auto drive : drives)
{
MOUNTMGR_MOUNT_POINT point{};
const auto symlink = write_string<char16_t>(buffer, make_volume(drive, 0));
const auto id = write_string<char>(buffer, make_drive_id(drive, 0));
const auto name = write_string<char16_t>(buffer, u"\\Device\\HarddiskVolume" +
u8_to_u16(std::to_string(drive - 'a' + 1)));
point.SymbolicLinkNameOffset = symlink.first;
point.SymbolicLinkNameLength = symlink.second;
point.UniqueIdOffset = id.first;
point.UniqueIdLength = id.second;
point.DeviceNameOffset = name.first;
point.DeviceNameLength = name.second;
mount_points.push_back(point);
}
MOUNTMGR_MOUNT_POINTS points{};
points.Size = static_cast<ULONG>(buffer.size());
points.NumberOfMountPoints = static_cast<ULONG>(mount_points.size());
memcpy(buffer.data(), &points, sizeof(points));
memcpy(buffer.data() + offsetof(MOUNTMGR_MOUNT_POINTS, MountPoints), mount_points.data(),
mount_points.size() * sizeof(MOUNTMGR_MOUNT_POINT));
const auto length = std::min(static_cast<size_t>(c.output_buffer_length), buffer.size());
win_emu.emu().write_memory(c.output_buffer, buffer.data(), length);
if (c.io_status_block)
{
IO_STATUS_BLOCK<EmulatorTraits<Emu64>> block{};
block.Information = buffer.size();
c.io_status_block.write(block);
}
return length < buffer.size() ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
}
static NTSTATUS get_drive_letter(windows_emulator& win_emu, const io_device_context& c)
{
if (c.input_buffer_length < 2)
{
return STATUS_NOT_SUPPORTED;
@@ -61,6 +162,27 @@ namespace
return STATUS_SUCCESS;
}
NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& c) override
{
if (c.io_control_code == IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH)
{
return get_drive_letter(win_emu, c);
}
if (c.io_control_code == IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS)
{
return get_drive_letter(win_emu, c);
}
if (c.io_control_code == IOCTL_MOUNTMGR_QUERY_POINTS)
{
return query_points(win_emu, c);
}
win_emu.log.error("Unsupported mount point IOCTL: %X\n", static_cast<uint32_t>(c.io_control_code));
return STATUS_NOT_SUPPORTED;
}
};
}

View File

@@ -0,0 +1,69 @@
#pragma once
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
#define MOUNTMGRCONTROLTYPE 0x0000006D // 'm'
#define MOUNTDEVCONTROLTYPE 0x0000004D // 'M'
#define IOCTL_MOUNTMGR_CREATE_POINT \
CTL_CODE(MOUNTMGRCONTROLTYPE, 0, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_MOUNTMGR_DELETE_POINTS \
CTL_CODE(MOUNTMGRCONTROLTYPE, 1, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_MOUNTMGR_QUERY_POINTS CTL_CODE(MOUNTMGRCONTROLTYPE, 2, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_MOUNTMGR_DELETE_POINTS_DBONLY \
CTL_CODE(MOUNTMGRCONTROLTYPE, 3, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER \
CTL_CODE(MOUNTMGRCONTROLTYPE, 4, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_MOUNTMGR_AUTO_DL_ASSIGNMENTS \
CTL_CODE(MOUNTMGRCONTROLTYPE, 5, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED \
CTL_CODE(MOUNTMGRCONTROLTYPE, 6, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED \
CTL_CODE(MOUNTMGRCONTROLTYPE, 7, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_MOUNTMGR_CHANGE_NOTIFY CTL_CODE(MOUNTMGRCONTROLTYPE, 8, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_MOUNTMGR_KEEP_LINKS_WHEN_OFFLINE \
CTL_CODE(MOUNTMGRCONTROLTYPE, 9, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_MOUNTMGR_CHECK_UNPROCESSED_VOLUMES CTL_CODE(MOUNTMGRCONTROLTYPE, 10, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION CTL_CODE(MOUNTMGRCONTROLTYPE, 11, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH CTL_CODE(MOUNTMGRCONTROLTYPE, 12, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS CTL_CODE(MOUNTMGRCONTROLTYPE, 13, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_MOUNTMGR_SCRUB_REGISTRY \
CTL_CODE(MOUNTMGRCONTROLTYPE, 14, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT CTL_CODE(MOUNTMGRCONTROLTYPE, 15, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_MOUNTMGR_SET_AUTO_MOUNT \
CTL_CODE(MOUNTMGRCONTROLTYPE, 16, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_MOUNTMGR_BOOT_DL_ASSIGNMENT \
CTL_CODE(MOUNTMGRCONTROLTYPE, 17, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) // since WIN7
#define IOCTL_MOUNTMGR_TRACELOG_CACHE CTL_CODE(MOUNTMGRCONTROLTYPE, 18, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_MOUNTMGR_PREPARE_VOLUME_DELETE \
CTL_CODE(MOUNTMGRCONTROLTYPE, 19, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_MOUNTMGR_CANCEL_VOLUME_DELETE \
CTL_CODE(MOUNTMGRCONTROLTYPE, 20, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) // since WIN8
#define IOCTL_MOUNTMGR_SILO_ARRIVAL \
CTL_CODE(MOUNTMGRCONTROLTYPE, 21, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) // since RS1
#define IOCTL_MOUNTDEV_QUERY_DEVICE_NAME CTL_CODE(MOUNTDEVCONTROLTYPE, 2, METHOD_BUFFERED, FILE_ANY_ACCESS)
typedef struct _MOUNTMGR_MOUNT_POINT
{
ULONG SymbolicLinkNameOffset;
USHORT SymbolicLinkNameLength;
ULONG UniqueIdOffset;
USHORT UniqueIdLength;
ULONG DeviceNameOffset;
USHORT DeviceNameLength;
} MOUNTMGR_MOUNT_POINT, *PMOUNTMGR_MOUNT_POINT;
//
// Output structure for IOCTL_MOUNTMGR_DELETE_POINTS,
// IOCTL_MOUNTMGR_QUERY_POINTS, and IOCTL_MOUNTMGR_DELETE_POINTS_DBONLY.
//
typedef struct _MOUNTMGR_MOUNT_POINTS
{
ULONG Size;
ULONG NumberOfMountPoints;
MOUNTMGR_MOUNT_POINT MountPoints[1];
} MOUNTMGR_MOUNT_POINTS, *PMOUNTMGR_MOUNT_POINTS;
// NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)

View File

@@ -0,0 +1,56 @@
#include "../std_include.hpp"
#include "security_support_provider.hpp"
#include "../windows_emulator.hpp"
namespace
{
struct security_support_provider : stateless_device
{
// RNG Microsoft Primitive Provider
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
std::uint8_t output_data[216] = //
{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x50, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x98, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x52, 0x00, 0x4E, 0x00, 0x47,
0x00, 0x00, 0x00, 0x4D, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x6F, 0x00,
0x66, 0x00, 0x74, 0x00, 0x20, 0x00, 0x50, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6D, 0x00, 0x69, 0x00, 0x74,
0x00, 0x69, 0x00, 0x76, 0x00, 0x65, 0x00, 0x20, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x76, 0x00,
0x69, 0x00, 0x64, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00,
0x63, 0x00, 0x72, 0x00, 0x79, 0x00, 0x70, 0x00, 0x74, 0x00, 0x70, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6D,
0x00, 0x69, 0x00, 0x74, 0x00, 0x69, 0x00, 0x76, 0x00, 0x65, 0x00, 0x73, 0x00, 0x2E, 0x00, 0x64, 0x00,
0x6C, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& c) override
{
if (c.io_control_code != 0x390400)
{
return STATUS_NOT_SUPPORTED;
}
const auto operation = win_emu.emu().read_memory<USHORT>(c.input_buffer + 6);
if (operation == 2)
{
win_emu.emu().write_memory(c.output_buffer, output_data);
if (c.io_status_block)
{
IO_STATUS_BLOCK<EmulatorTraits<Emu64>> block{};
block.Information = sizeof(output_data);
c.io_status_block.write(block);
}
}
return STATUS_SUCCESS;
}
};
}
std::unique_ptr<io_device> create_security_support_provider()
{
return std::make_unique<security_support_provider>();
}

View File

@@ -0,0 +1,4 @@
#pragma once
#include "../io_device.hpp"
std::unique_ptr<io_device> create_security_support_provider();

View File

@@ -7,25 +7,26 @@
namespace
{
template <typename T>
emulator_object<T> allocate_object_on_stack(x64_emulator& emu)
emulator_object<T> allocate_object_on_stack(x86_64_emulator& emu)
{
const auto old_sp = emu.reg(x64_register::rsp);
const auto new_sp = align_down(old_sp - sizeof(T), std::max(alignof(T), alignof(x64_emulator::pointer_type)));
emu.reg(x64_register::rsp, new_sp);
const auto old_sp = emu.reg(x86_register::rsp);
const auto new_sp =
align_down(old_sp - sizeof(T), std::max(alignof(T), alignof(x86_64_emulator::pointer_type)));
emu.reg(x86_register::rsp, new_sp);
return {emu, new_sp};
}
void unalign_stack(x64_emulator& emu)
void unalign_stack(x86_64_emulator& emu)
{
auto sp = emu.reg(x64_register::rsp);
auto sp = emu.reg(x86_register::rsp);
sp = align_down(sp - 0x10, 0x10) + 8;
emu.reg(x64_register::rsp, sp);
emu.reg(x86_register::rsp, sp);
}
void setup_stack(x64_emulator& emu, const uint64_t stack_base, const size_t stack_size)
void setup_stack(x86_64_emulator& emu, const uint64_t stack_base, const size_t stack_size)
{
const uint64_t stack_end = stack_base + stack_size;
emu.reg(x64_register::rsp, stack_end);
emu.reg(x86_register::rsp, stack_end);
}
bool is_object_signaled(process_context& c, const handle h, const uint32_t current_thread_id)
@@ -57,6 +58,10 @@ namespace
return !e || e->try_lock(current_thread_id);
}
case handle_types::timer: {
return true; // TODO
}
case handle_types::semaphore: {
auto* s = c.semaphores.get(h);
if (s)
@@ -93,7 +98,7 @@ emulator_thread::emulator_thread(memory_manager& memory, const process_context&
suspended(suspended),
last_registers(context.default_register_set)
{
this->stack_base = memory.allocate_memory(this->stack_size, memory_permission::read_write);
this->stack_base = memory.allocate_memory(static_cast<size_t>(this->stack_size), memory_permission::read_write);
this->gs_segment = emulator_allocator{
memory,
@@ -111,10 +116,10 @@ emulator_thread::emulator_thread(memory_manager& memory, const process_context&
teb_obj.ClientId.UniqueProcess = 1ul;
teb_obj.ClientId.UniqueThread = static_cast<uint64_t>(this->id);
teb_obj.NtTib.StackLimit = reinterpret_cast<std::uint64_t*>(this->stack_base);
teb_obj.NtTib.StackBase = reinterpret_cast<std::uint64_t*>(this->stack_base + this->stack_size);
teb_obj.NtTib.Self = &this->teb->ptr()->NtTib;
teb_obj.ProcessEnvironmentBlock = context.peb.ptr();
teb_obj.NtTib.StackLimit = this->stack_base;
teb_obj.NtTib.StackBase = this->stack_base + this->stack_size;
teb_obj.NtTib.Self = this->teb->value();
teb_obj.ProcessEnvironmentBlock = context.peb.value();
});
}
@@ -207,15 +212,15 @@ bool emulator_thread::is_thread_ready(process_context& process, utils::clock& cl
return true;
}
void emulator_thread::setup_registers(x64_emulator& emu, const process_context& context) const
void emulator_thread::setup_registers(x86_64_emulator& emu, const process_context& context) const
{
if (!this->gs_segment)
{
throw std::runtime_error("Missing GS segment");
}
setup_stack(emu, this->stack_base, this->stack_size);
emu.set_segment_base(x64_register::gs, this->gs_segment->get_base());
setup_stack(emu, this->stack_base, static_cast<size_t>(this->stack_size));
emu.set_segment_base(x86_register::gs, this->gs_segment->get_base());
CONTEXT64 ctx{};
ctx.ContextFlags = CONTEXT64_ALL;
@@ -232,7 +237,7 @@ void emulator_thread::setup_registers(x64_emulator& emu, const process_context&
unalign_stack(emu);
emu.reg(x64_register::rcx, ctx_obj.value());
emu.reg(x64_register::rdx, context.ntdll_image_base);
emu.reg(x64_register::rip, context.ldr_initialize_thunk);
emu.reg(x86_register::rcx, ctx_obj.value());
emu.reg(x86_register::rdx, context.ntdll_image_base);
emu.reg(x86_register::rip, context.ldr_initialize_thunk);
}

View File

@@ -105,17 +105,17 @@ class emulator_thread : public ref_counted_object
bool is_thread_ready(process_context& process, utils::clock& clock);
void save(x64_emulator& emu)
void save(x86_64_emulator& emu)
{
this->last_registers = emu.save_registers();
}
void restore(x64_emulator& emu) const
void restore(x86_64_emulator& emu) const
{
emu.restore_registers(this->last_registers);
}
void setup_if_necessary(x64_emulator& emu, const process_context& context)
void setup_if_necessary(x86_64_emulator& emu, const process_context& context)
{
if (!this->executed_instructions)
{
@@ -127,7 +127,7 @@ class emulator_thread : public ref_counted_object
const auto status = *this->pending_status;
this->pending_status = {};
emu.reg<uint64_t>(x64_register::rax, static_cast<uint64_t>(status));
emu.reg<uint64_t>(x86_register::rax, static_cast<uint64_t>(status));
}
}
@@ -210,8 +210,13 @@ class emulator_thread : public ref_counted_object
this->marker.mark_as_moved();
}
static bool deleter(emulator_thread& t)
{
return ref_counted_object::deleter(t) && t.is_terminated();
}
private:
void setup_registers(x64_emulator& emu, const process_context& context) const;
void setup_registers(x86_64_emulator& emu, const process_context& context) const;
void release()
{
@@ -227,7 +232,7 @@ class emulator_thread : public ref_counted_object
throw std::runtime_error("Emulator was never assigned!");
}
this->memory_ptr->release_memory(this->stack_base, this->stack_size);
this->memory_ptr->release_memory(this->stack_base, static_cast<size_t>(this->stack_size));
this->stack_base = 0;
}

View File

@@ -1,10 +1,11 @@
#pragma once
#include <x64_emulator.hpp>
#include <arch_emulator.hpp>
#include "memory_manager.hpp"
#include "memory_utils.hpp"
#include "address_utils.hpp"
#include "x86_register.hpp"
#include <utils/time.hpp>
@@ -51,7 +52,7 @@ class module_manager;
struct process_context;
using clock_wrapper = object_wrapper<utils::clock>;
using x64_emulator_wrapper = object_wrapper<x64_emulator>;
using x64_emulator_wrapper = object_wrapper<x86_64_emulator>;
using memory_manager_wrapper = object_wrapper<memory_manager>;
using module_manager_wrapper = object_wrapper<module_manager>;
using process_context_wrapper = object_wrapper<process_context>;
@@ -95,11 +96,6 @@ class emulator_object
return this->value() + this->size();
}
T* ptr() const
{
return reinterpret_cast<T*>(this->address_);
}
explicit operator bool() const
{
return this->address_ != 0;
@@ -166,6 +162,11 @@ class emulator_object
return emulator_object<T>(*this->memory_, this->address_ + offset);
}
memory_interface* get_memory_interface() const
{
return this->memory_;
}
private:
memory_interface* memory_{};
uint64_t address_{};
@@ -221,11 +222,11 @@ class emulator_allocator
return emulator_object<T>(*this->memory_, potential_start);
}
char16_t* copy_string(const std::u16string_view str)
uint64_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);
return uc_str.Buffer;
}
void make_unicode_string(UNICODE_STRING<EmulatorTraits<Emu64>>& result, const std::u16string_view str,
@@ -300,7 +301,8 @@ class emulator_allocator
{
if (this->address_ && this->size_)
{
manager.release_memory(this->address_, this->size_);
// TODO: Make all sizes uint64_t
manager.release_memory(this->address_, static_cast<size_t>(this->size_));
this->address_ = 0;
this->size_ = 0;
}
@@ -314,16 +316,22 @@ class emulator_allocator
};
template <typename Element>
std::basic_string<Element> read_string(memory_manager& mem, const uint64_t address)
std::basic_string<Element> read_string(memory_interface& mem, const uint64_t address,
const std::optional<size_t> size = {})
{
std::basic_string<Element> result{};
for (size_t i = 0;; ++i)
{
if (size && i >= *size)
{
break;
}
Element element{};
mem.read_memory(address + (i * sizeof(element)), &element, sizeof(element));
if (!element)
if (!size && !element)
{
break;
}
@@ -356,7 +364,24 @@ inline std::u16string read_unicode_string(const emulator& emu,
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 uint64_t uc_string)
{
return read_unicode_string(emu, emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>>{emu, uc_string});
}
inline uint64_t get_function_argument(x86_64_emulator& emu, const size_t index, bool is_syscall = false)
{
switch (index)
{
case 0:
return emu.reg(is_syscall ? x86_register::r10 : x86_register::rcx);
case 1:
return emu.reg(x86_register::rdx);
case 2:
return emu.reg(x86_register::r8);
case 3:
return emu.reg(x86_register::r9);
default:
return emu.read_stack(index + 1);
}
}

View File

@@ -33,7 +33,7 @@ namespace
}
record_obj.access([&](exception_record& r) {
r.ExceptionRecord = reinterpret_cast<EmulatorTraits<Emu64>::PVOID>(nested_record_obj.ptr());
r.ExceptionRecord = nested_record_obj.value(); //
});
}
@@ -89,7 +89,7 @@ namespace
uint64_t ss;
};
void dispatch_exception_pointers(x64_emulator& emu, const uint64_t dispatcher,
void dispatch_exception_pointers(x86_64_emulator& emu, const uint64_t dispatcher,
const EMU_EXCEPTION_POINTERS<EmulatorTraits<Emu64>> pointers)
{
constexpr auto mach_frame_size = 0x40;
@@ -102,19 +102,19 @@ namespace
const auto allocation_size = combined_size + mach_frame_size;
const auto initial_sp = emu.reg(x64_register::rsp);
const auto initial_sp = emu.reg(x86_register::rsp);
const auto new_sp = align_down(initial_sp - allocation_size, 0x100);
const auto total_size = initial_sp - new_sp;
assert(total_size >= allocation_size);
std::vector<uint8_t> zero_memory{};
zero_memory.resize(total_size, 0);
zero_memory.resize(static_cast<size_t>(total_size), 0);
emu.write_memory(new_sp, zero_memory.data(), zero_memory.size());
emu.reg(x64_register::rsp, new_sp);
emu.reg(x64_register::rip, dispatcher);
emu.reg(x86_register::rsp, new_sp);
emu.reg(x86_register::rip, dispatcher);
const emulator_object<CONTEXT64> context_record_obj{emu, new_sp};
context_record_obj.write(*reinterpret_cast<CONTEXT64*>(pointers.ContextRecord));
@@ -140,7 +140,7 @@ namespace
}
}
void dispatch_exception(x64_emulator& emu, const process_context& proc, const DWORD status,
void dispatch_exception(x86_64_emulator& emu, const process_context& proc, const DWORD status,
const std::vector<EmulatorTraits<Emu64>::ULONG_PTR>& parameters)
{
CONTEXT64 ctx{};
@@ -172,7 +172,7 @@ void dispatch_exception(x64_emulator& emu, const process_context& proc, const DW
dispatch_exception_pointers(emu, proc.ki_user_exception_dispatcher, pointers);
}
void dispatch_access_violation(x64_emulator& emu, const process_context& proc, const uint64_t address,
void dispatch_access_violation(x86_64_emulator& emu, const process_context& proc, const uint64_t address,
const memory_operation operation)
{
dispatch_exception(emu, proc, STATUS_ACCESS_VIOLATION,
@@ -182,22 +182,22 @@ void dispatch_access_violation(x64_emulator& emu, const process_context& proc, c
});
}
void dispatch_illegal_instruction_violation(x64_emulator& emu, const process_context& proc)
void dispatch_illegal_instruction_violation(x86_64_emulator& emu, const process_context& proc)
{
dispatch_exception(emu, proc, STATUS_ILLEGAL_INSTRUCTION, {});
}
void dispatch_integer_division_by_zero(x64_emulator& emu, const process_context& proc)
void dispatch_integer_division_by_zero(x86_64_emulator& emu, const process_context& proc)
{
dispatch_exception(emu, proc, STATUS_INTEGER_DIVIDE_BY_ZERO, {});
}
void dispatch_single_step(x64_emulator& emu, const process_context& proc)
void dispatch_single_step(x86_64_emulator& emu, const process_context& proc)
{
dispatch_exception(emu, proc, STATUS_SINGLE_STEP, {});
}
void dispatch_breakpoint(x64_emulator& emu, const process_context& proc)
void dispatch_breakpoint(x86_64_emulator& emu, const process_context& proc)
{
dispatch_exception(emu, proc, STATUS_BREAKPOINT, {});
}

View File

@@ -1,25 +1,25 @@
#pragma once
#include <x64_emulator.hpp>
#include <arch_emulator.hpp>
#include <platform/traits.hpp>
#include <platform/primitives.hpp>
struct process_context;
void dispatch_exception(x64_emulator& emu, const process_context& proc, DWORD status,
void dispatch_exception(x86_64_emulator& emu, const process_context& proc, DWORD status,
const std::vector<EmulatorTraits<Emu64>::ULONG_PTR>& parameters);
template <typename T>
requires(std::is_integral_v<T> && !std::is_same_v<T, DWORD>)
void dispatch_exception(x64_emulator& emu, const process_context& proc, const T status,
void dispatch_exception(x86_64_emulator& emu, const process_context& proc, const T status,
const std::vector<EmulatorTraits<Emu64>::ULONG_PTR>& parameters)
{
dispatch_exception(emu, proc, static_cast<DWORD>(status), parameters);
}
void dispatch_access_violation(x64_emulator& emu, const process_context& proc, uint64_t address,
void dispatch_access_violation(x86_64_emulator& emu, const process_context& proc, uint64_t address,
memory_operation operation);
void dispatch_illegal_instruction_violation(x64_emulator& emu, const process_context& proc);
void dispatch_integer_division_by_zero(x64_emulator& emu, const process_context& proc);
void dispatch_single_step(x64_emulator& emu, const process_context& proc);
void dispatch_breakpoint(x64_emulator& emu, const process_context& proc);
void dispatch_illegal_instruction_violation(x86_64_emulator& emu, const process_context& proc);
void dispatch_integer_division_by_zero(x86_64_emulator& emu, const process_context& proc);
void dispatch_single_step(x86_64_emulator& emu, const process_context& proc);
void dispatch_breakpoint(x86_64_emulator& emu, const process_context& proc);

View File

@@ -2,8 +2,6 @@
#include "std_include.hpp"
#include "windows_path.hpp"
#include <platform/compiler.hpp>
class file_system
{
public:
@@ -23,6 +21,41 @@ class file_system
return !is_escaping_relative_path(relative_path);
}
std::set<char> list_drives() const
{
std::set<char> drives{};
#ifdef OS_WINDOWS
if (this->root_.empty())
{
const auto drive_bits = GetLogicalDrives();
for (char drive = 'a'; drive <= 'z'; ++drive)
{
const auto drive_index = drive - 'a';
if (drive_bits & (1 << drive_index))
{
drives.insert(drive);
}
}
return drives;
}
#endif
std::error_code ec{};
for (const auto& file : std::filesystem::directory_iterator(this->root_, ec))
{
const auto filename = file.path().filename().string();
if (filename.size() == 1)
{
drives.insert(utils::string::char_to_lower(filename.front()));
}
}
return drives;
}
std::filesystem::path translate(const windows_path& win_path) const
{
if (!win_path.is_absolute())
@@ -56,6 +89,19 @@ class file_system
return root;
}
template <typename F>
void access_mapped_entries(const windows_path& win_path, const F& accessor) const
{
for (const auto& mapping : this->mappings_)
{
const auto& mapped_path = mapping.first;
if (!mapped_path.empty() && mapped_path.parent() == win_path)
{
accessor(mapping);
}
}
}
windows_path local_to_windows_path(const std::filesystem::path& local_path) const
{
const auto absolute_local_path = weakly_canonical(absolute(local_path));

View File

@@ -0,0 +1,28 @@
#pragma once
#include <utils/object.hpp>
#if (defined(__clang__) || defined(__GNUC__)) && !defined(__MINGW64__)
#define FORMAT_ATTRIBUTE(fmt_pos, var_pos) __attribute__((format(printf, fmt_pos, var_pos)))
#else
#define FORMAT_ATTRIBUTE(fmt_pos, var_pos)
#endif
enum class color
{
black,
red,
green,
yellow,
blue,
cyan,
pink,
white,
gray,
dark_gray,
};
struct generic_logger : utils::object
{
virtual void print(color c, std::string_view message) = 0;
virtual void print(color c, const char* message, ...) FORMAT_ATTRIBUTE(3, 4) = 0;
};

View File

@@ -19,6 +19,8 @@ struct handle_types
registry,
mutant,
token,
window,
timer,
};
};
@@ -134,6 +136,11 @@ class ref_counted_object
static bool deleter(ref_counted_object& e)
{
if (e.ref_count == 0)
{
return true;
}
return --e.ref_count == 0;
}
@@ -238,7 +245,7 @@ class handle_store : public generic_handle_store
return h;
}
bool erase(const typename value_map::iterator& entry)
std::pair<typename value_map::iterator, bool> erase(const typename value_map::iterator& entry)
{
if (this->block_mutation_)
{
@@ -247,25 +254,25 @@ class handle_store : public generic_handle_store
if (entry == this->store_.end())
{
return false;
return {entry, false};
}
if constexpr (handle_detail::has_deleter_function<T>())
{
if (!T::deleter(entry->second))
{
return true;
return {entry, true};
}
}
this->store_.erase(entry);
return true;
auto new_iter = this->store_.erase(entry);
return {new_iter, true};
}
bool erase(const handle_value h)
{
const auto entry = this->get_iterator(h);
return this->erase(entry);
return this->erase(entry).second;
}
bool erase(const handle h) override
@@ -401,6 +408,7 @@ constexpr auto NULL_HANDLE = make_handle(0ULL);
constexpr auto KNOWN_DLLS_DIRECTORY = make_pseudo_handle(0x1, handle_types::directory);
constexpr auto BASE_NAMED_OBJECTS_DIRECTORY = make_pseudo_handle(0x2, handle_types::directory);
constexpr auto RPC_CONTROL_DIRECTORY = make_pseudo_handle(0x3, handle_types::directory);
constexpr auto KNOWN_DLLS_SYMLINK = make_pseudo_handle(0x1, handle_types::symlink);
constexpr auto SHARED_SECTION = make_pseudo_handle(0x1, handle_types::section);
@@ -409,6 +417,8 @@ constexpr auto DBWIN_BUFFER = make_pseudo_handle(0x2, handle_types::section);
constexpr auto WER_PORT_READY = make_pseudo_handle(0x1, handle_types::event);
constexpr auto DBWIN_DATA_READY = make_pseudo_handle(0x2, handle_types::event);
constexpr auto DBWIN_BUFFER_READY = make_pseudo_handle(0x3, handle_types::event);
constexpr auto SVCCTRL_START_EVENT = make_pseudo_handle(0x4, handle_types::event);
constexpr auto LSA_AUTHENTICATION_INITIALIZED = make_pseudo_handle(0x5, handle_types::event);
constexpr auto CONSOLE_HANDLE = make_pseudo_handle(0x1, handle_types::file);
constexpr auto STDOUT_HANDLE = make_pseudo_handle(0x2, handle_types::file);

View File

@@ -1,7 +1,9 @@
#include "std_include.hpp"
#include "io_device.hpp"
#include "windows_emulator.hpp"
#include "devices/afd_endpoint.hpp"
#include "devices/mount_point_manager.hpp"
#include "devices/security_support_provider.hpp"
namespace
{
@@ -16,11 +18,12 @@ namespace
std::unique_ptr<io_device> create_device(const std::u16string_view device)
{
if (device == u"CNG" //
|| device == u"Nsi" //
|| device == u"KsecDD" //
|| device == u"PcwDrv" //
|| device == u"DeviceApi\\CMApi" //
if (device == u"CNG" //
|| device == u"Nsi" //
|| device == u"RasAcd" //
|| device == u"PcwDrv" //
|| device == u"DeviceApi\\CMApi" //
|| device == u"DeviceApi\\CMNotify" //
|| device == u"ConDrv\\Server")
{
return std::make_unique<dummy_device>();
@@ -31,10 +34,48 @@ std::unique_ptr<io_device> create_device(const std::u16string_view device)
return create_afd_endpoint();
}
if (device == u"Afd\\AsyncConnectHlp")
{
return create_afd_async_connect_hlp();
}
if (device == u"MountPointManager")
{
return create_mount_point_manager();
}
if (device == u"KsecDD")
{
return create_security_support_provider();
}
throw std::runtime_error("Unsupported device: " + u16_to_u8(device));
}
NTSTATUS io_device_container::io_control(windows_emulator& win_emu, const io_device_context& context)
{
this->assert_validity();
win_emu.callbacks.on_ioctrl(*this->device_, this->device_name_, context.io_control_code);
return this->device_->io_control(win_emu, context);
}
void io_device_container::work(windows_emulator& win_emu)
{
this->assert_validity();
this->device_->work(win_emu);
}
void io_device_container::serialize_object(utils::buffer_serializer& buffer) const
{
this->assert_validity();
buffer.write_string(this->device_name_);
this->device_->serialize(buffer);
}
void io_device_container::deserialize_object(utils::buffer_deserializer& buffer)
{
buffer.read_string(this->device_name_);
this->setup();
this->device_->deserialize(buffer);
}

View File

@@ -1,7 +1,7 @@
#pragma once
#include <memory>
#include <x64_emulator.hpp>
#include <arch_emulator.hpp>
#include <serialization.hpp>
#include "emulator_utils.hpp"
@@ -22,7 +22,7 @@ struct io_device_context
emulator_pointer output_buffer{};
ULONG output_buffer_length{};
io_device_context(x64_emulator& emu)
io_device_context(x86_64_emulator& emu)
: io_status_block(emu)
{
}
@@ -146,42 +146,33 @@ class io_device_container : public io_device
this->device_->create(win_emu, data);
}
NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& context) override
{
this->assert_validity();
return this->device_->io_control(win_emu, context);
}
void work(windows_emulator& win_emu) override;
NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& context) override;
void work(windows_emulator& win_emu) override
{
this->assert_validity();
this->device_->work(win_emu);
}
void serialize_object(utils::buffer_serializer& buffer) const override
{
this->assert_validity();
buffer.write_string(this->device_name_);
this->device_->serialize(buffer);
}
void deserialize_object(utils::buffer_deserializer& buffer) override
{
buffer.read_string(this->device_name_);
this->setup();
this->device_->deserialize(buffer);
}
void serialize_object(utils::buffer_serializer& buffer) const override;
void deserialize_object(utils::buffer_deserializer& buffer) override;
template <typename T = io_device>
requires(std::is_base_of_v<io_device, T> || std::is_same_v<io_device, T>)
T* get_internal_device()
T* get_internal_device() const
{
this->assert_validity();
auto* value = this->device_.get();
return dynamic_cast<T*>(value);
}
std::u16string_view get_device_name() const
{
this->assert_validity();
return this->device_name_;
}
std::u16string get_device_path() const
{
this->assert_validity();
return u"\\Device\\" + this->device_name_;
}
private:
std::u16string device_name_{};
std::unique_ptr<io_device> device_{};

View File

@@ -144,7 +144,7 @@ void kusd_mmio::read(const uint64_t addr, void* data, const size_t size)
const auto real_size = valid_end - addr;
const auto* kusd_buffer = reinterpret_cast<uint8_t*>(&this->kusd_);
memcpy(data, kusd_buffer + addr, real_size);
memcpy(data, kusd_buffer + addr, static_cast<size_t>(real_size));
}
uint64_t kusd_mmio::address()

View File

@@ -3,7 +3,7 @@
#include "std_include.hpp"
#include <serialization.hpp>
#include "x64_emulator.hpp"
#include "arch_emulator.hpp"
#include <utils/time.hpp>

View File

@@ -6,16 +6,19 @@
namespace
{
#ifdef _WIN32
#define COLOR(win, posix) win
#define COLOR(win, posix, web) win
using color_type = WORD;
#elif defined(__EMSCRIPTEN__)
#define COLOR(win, posix, web) web
using color_type = const char*;
#else
#define COLOR(win, posix) posix
#define COLOR(win, posix, web) posix
using color_type = const char*;
#endif
color_type get_reset_color()
{
return COLOR(7, "\033[0m");
return COLOR(7, "\033[0m", "</span>");
}
color_type get_color_type(const color c)
@@ -25,23 +28,23 @@ namespace
switch (c)
{
case black:
return COLOR(0x8, "\033[0;90m");
return COLOR(0x8, "\033[0;90m", "<span class=\"terminal-black\">");
case red:
return COLOR(0xC, "\033[0;91m");
return COLOR(0xC, "\033[0;91m", "<span class=\"terminal-red\">");
case green:
return COLOR(0xA, "\033[0;92m");
return COLOR(0xA, "\033[0;92m", "<span class=\"terminal-green\">");
case yellow:
return COLOR(0xE, "\033[0;93m");
return COLOR(0xE, "\033[0;93m", "<span class=\"terminal-yellow\">");
case blue:
return COLOR(0x9, "\033[0;94m");
return COLOR(0x9, "\033[0;94m", "<span class=\"terminal-blue\">");
case cyan:
return COLOR(0xB, "\033[0;96m");
return COLOR(0xB, "\033[0;96m", "<span class=\"terminal-cyan\">");
case pink:
return COLOR(0xD, "\033[0;95m");
return COLOR(0xD, "\033[0;95m", "<span class=\"terminal-pink\">");
case white:
return COLOR(0xF, "\033[0;97m");
return COLOR(0xF, "\033[0;97m", "<span class=\"terminal-white\">");
case dark_gray:
return COLOR(0x8, "\033[0;97m");
return COLOR(0x8, "\033[0;90m", "<span class=\"terminal-dark-gray\">");
case gray:
default:
return get_reset_color();
@@ -93,7 +96,7 @@ namespace
va_list ap; \
va_start(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)
{
@@ -103,9 +106,9 @@ namespace
}
}
void logger::print(const color c, const std::string_view message) const
void logger::print_message(const color c, const std::string_view message, const bool force) const
{
if (this->disable_output_)
if (!force && this->disable_output_)
{
return;
}
@@ -113,44 +116,49 @@ void logger::print(const color c, const std::string_view message) const
print_colored(message, get_color_type(c));
}
void logger::print(const color c, const std::string_view message)
{
this->print_message(c, message);
}
// NOLINTNEXTLINE(cert-dcl50-cpp)
void logger::print(const color c, const char* message, ...) const
void logger::print(const color c, const char* message, ...)
{
format_to_string(message, data);
this->print(c, data);
this->print_message(c, data);
}
// NOLINTNEXTLINE(cert-dcl50-cpp)
void logger::info(const char* message, ...) const
{
format_to_string(message, data);
this->print(color::cyan, data);
this->print_message(color::cyan, data);
}
// NOLINTNEXTLINE(cert-dcl50-cpp)
void logger::warn(const char* message, ...) const
{
format_to_string(message, data);
this->print(color::yellow, data);
this->print_message(color::yellow, data);
}
// NOLINTNEXTLINE(cert-dcl50-cpp)
void logger::error(const char* message, ...) const
{
format_to_string(message, data);
this->print(color::red, data);
this->print_message(color::red, data, true);
}
// NOLINTNEXTLINE(cert-dcl50-cpp)
void logger::success(const char* message, ...) const
{
format_to_string(message, data);
this->print(color::green, data);
this->print_message(color::green, data);
}
// NOLINTNEXTLINE(cert-dcl50-cpp)
void logger::log(const char* message, ...) const
{
format_to_string(message, data);
this->print(color::gray, data);
this->print_message(color::gray, data);
}

View File

@@ -1,30 +1,11 @@
#pragma once
#include "generic_logger.hpp"
#ifdef OS_WINDOWS
#define FORMAT_ATTRIBUTE(fmt_pos, var_pos)
#else
#define FORMAT_ATTRIBUTE(fmt_pos, var_pos) __attribute__((format(printf, fmt_pos, var_pos)))
#endif
enum class color
{
black,
red,
green,
yellow,
blue,
cyan,
pink,
white,
gray,
dark_gray,
};
class logger
class logger : public generic_logger
{
public:
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, std::string_view message) override;
void print(color c, const char* message, ...) override FORMAT_ATTRIBUTE(3, 4);
void info(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
void warn(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
void error(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3);
@@ -43,4 +24,5 @@ class logger
private:
bool disable_output_{false};
void print_message(color c, std::string_view message, bool force = false) const;
};

View File

@@ -22,9 +22,10 @@ namespace
const auto first_length = split_point - i->first;
const auto second_length = i->second.length - first_length;
i->second.length = first_length;
i->second.length = static_cast<size_t>(first_length);
regions[split_point] = memory_manager::committed_region{second_length, i->second.permissions};
regions[split_point] =
memory_manager::committed_region{static_cast<size_t>(second_length), i->second.permissions};
}
}
}
@@ -99,6 +100,25 @@ void memory_manager::update_layout_version()
#endif
}
memory_stats memory_manager::compute_memory_stats() const
{
memory_stats stats{};
stats.reserved_memory = 0;
stats.committed_memory = 0;
for (const auto& reserved_region : this->reserved_regions_ | std::views::values)
{
stats.reserved_memory += reserved_region.length;
for (const auto& committed_region : reserved_region.committed_regions | std::views::values)
{
stats.committed_memory += committed_region.length;
}
}
return stats;
}
void memory_manager::serialize_memory_state(utils::buffer_serializer& buffer, const bool is_snapshot) const
{
buffer.write_atomic(this->layout_version_);
@@ -312,8 +332,8 @@ bool memory_manager::commit_memory(const uint64_t address, const size_t size, co
if (map_length > 0)
{
this->map_memory(map_start, map_length, permissions);
committed_regions[map_start] = committed_region{map_length, permissions};
this->map_memory(map_start, static_cast<size_t>(map_length), permissions);
committed_regions[map_start] = committed_region{static_cast<size_t>(map_length), permissions};
}
last_region_start = sub_region.first;
@@ -326,8 +346,8 @@ bool memory_manager::commit_memory(const uint64_t address, const size_t size, co
const auto map_start = last_region ? (last_region_start + last_region->length) : address;
const auto map_length = end - map_start;
this->map_memory(map_start, map_length, permissions);
committed_regions[map_start] = committed_region{map_length, permissions};
this->map_memory(map_start, static_cast<size_t>(map_length), permissions);
committed_regions[map_start] = committed_region{static_cast<size_t>(map_length), permissions};
}
merge_regions(committed_regions);
@@ -398,7 +418,7 @@ bool memory_manager::release_memory(const uint64_t address, size_t size)
size = entry->second.length;
}
size = page_align_up(size);
size = static_cast<size_t>(page_align_up(size));
if (size > entry->second.length)
{
@@ -498,7 +518,7 @@ region_info memory_manager::get_region_info(const uint64_t address)
{
region_info result{};
result.start = MIN_ALLOCATION_ADDRESS;
result.length = MAX_ALLOCATION_ADDRESS - result.start;
result.length = static_cast<size_t>(MAX_ALLOCATION_ADDRESS - result.start);
result.permissions = memory_permission::none;
result.initial_permissions = memory_permission::none;
result.allocation_base = {};
@@ -514,7 +534,7 @@ region_info memory_manager::get_region_info(const uint64_t address)
auto upper_bound = this->reserved_regions_.upper_bound(address);
if (upper_bound == this->reserved_regions_.begin())
{
result.length = upper_bound->first - result.start;
result.length = static_cast<size_t>(upper_bound->first - result.start);
return result;
}
@@ -523,7 +543,7 @@ region_info memory_manager::get_region_info(const uint64_t address)
if (lower_end <= address)
{
result.start = lower_end;
result.length = MAX_ALLOCATION_ADDRESS - result.start;
result.length = static_cast<size_t>(MAX_ALLOCATION_ADDRESS - result.start);
return result;
}
@@ -546,7 +566,7 @@ region_info memory_manager::get_region_info(const uint64_t address)
auto committed_bound = committed_regions.upper_bound(address);
if (committed_bound == committed_regions.begin())
{
result.length = committed_bound->first - result.start;
result.length = static_cast<size_t>(committed_bound->first - result.start);
return result;
}
@@ -555,7 +575,7 @@ region_info memory_manager::get_region_info(const uint64_t address)
if (committed_lower_end <= address)
{
result.start = committed_lower_end;
result.length = lower_end - result.start;
result.length = static_cast<size_t>(lower_end - result.start);
return result;
}

View File

@@ -24,6 +24,12 @@ struct region_info : basic_memory_region
using mmio_read_callback = std::function<void(uint64_t addr, void* data, size_t size)>;
using mmio_write_callback = std::function<void(uint64_t addr, const void* data, size_t size)>;
struct memory_stats
{
uint64_t reserved_memory = 0;
uint64_t committed_memory = 0;
};
class memory_manager : public memory_interface
{
public:
@@ -90,6 +96,8 @@ class memory_manager : public memory_interface
void serialize_memory_state(utils::buffer_serializer& buffer, bool is_snapshot) const;
void deserialize_memory_state(utils::buffer_deserializer& buffer, bool is_snapshot);
memory_stats compute_memory_stats() const;
private:
memory_interface* memory_{};
reserved_region_map reserved_regions_{};

View File

@@ -0,0 +1,727 @@
#include "std_include.hpp"
#include "minidump_loader.hpp"
#include "windows_emulator.hpp"
#include "windows_objects.hpp"
#include "emulator_thread.hpp"
#include "common/platform/unicode.hpp"
#include "common/platform/kernel_mapped.hpp"
#include "memory_utils.hpp"
#include <minidump/minidump.hpp>
namespace minidump_loader
{
struct dump_statistics
{
size_t thread_count = 0;
size_t module_count = 0;
size_t memory_region_count = 0;
size_t memory_segment_count = 0;
size_t handle_count = 0;
uint64_t total_memory_size = 0;
bool has_exception = false;
bool has_system_info = false;
};
std::string get_architecture_string(const minidump::minidump_file* dump_file)
{
if (!dump_file)
{
return "Unknown";
}
const auto* sys_info = dump_file->get_system_info();
if (!sys_info)
{
return "Unknown";
}
const auto arch = static_cast<minidump::processor_architecture>(sys_info->processor_architecture);
switch (arch)
{
case minidump::processor_architecture::amd64:
return "x64 (AMD64)";
case minidump::processor_architecture::intel:
return "x86 (Intel)";
case minidump::processor_architecture::arm64:
return "ARM64";
default:
return "Unknown (" + std::to_string(static_cast<int>(arch)) + ")";
}
}
bool parse_minidump_file(windows_emulator& win_emu, const std::filesystem::path& minidump_path,
std::unique_ptr<minidump::minidump_file>& dump_file,
std::unique_ptr<minidump::minidump_reader>& dump_reader)
{
win_emu.log.info("Parsing minidump file\n");
if (!std::filesystem::exists(minidump_path))
{
win_emu.log.error("Minidump file does not exist: %s\n", minidump_path.string().c_str());
return false;
}
const auto file_size = std::filesystem::file_size(minidump_path);
win_emu.log.info("File size: %ju bytes\n", file_size);
auto parsed_file = minidump::minidump_file::parse(minidump_path.string());
if (!parsed_file)
{
win_emu.log.error("Failed to parse minidump file\n");
return false;
}
win_emu.log.info("Minidump header parsed successfully\n");
auto reader = parsed_file->get_reader();
if (!reader)
{
win_emu.log.error("Failed to create minidump reader\n");
return false;
}
dump_file = std::move(parsed_file);
dump_reader = std::move(reader);
win_emu.log.info("Minidump reader created successfully\n");
return true;
}
bool validate_dump_compatibility(windows_emulator& win_emu, const minidump::minidump_file* dump_file)
{
win_emu.log.info("Validating dump compatibility\n");
if (!dump_file)
{
win_emu.log.error("Dump file not loaded\n");
return false;
}
const auto& header = dump_file->header();
if (!header.is_valid())
{
win_emu.log.error("Invalid minidump signature or header\n");
return false;
}
win_emu.log.info("Minidump signature: 0x%08X (valid)\n", header.signature);
win_emu.log.info("Version: %u.%u\n", header.version, header.implementation_version);
win_emu.log.info("Number of streams: %u\n", header.number_of_streams);
win_emu.log.info("Flags: 0x%016" PRIx64 "\n", header.flags);
const auto* sys_info = dump_file->get_system_info();
if (sys_info)
{
const auto arch = static_cast<minidump::processor_architecture>(sys_info->processor_architecture);
const bool is_x64 = (arch == minidump::processor_architecture::amd64);
win_emu.log.info("Processor architecture: %s\n", get_architecture_string(dump_file).c_str());
if (!is_x64)
{
win_emu.log.error("Only x64 minidumps are currently supported\n");
return false;
}
win_emu.log.info("Architecture compatibility: OK (x64)\n");
}
else
{
win_emu.log.warn("No system info stream found - proceeding with caution\n");
}
return true;
}
void log_dump_summary(windows_emulator& win_emu, const minidump::minidump_file* dump_file, dump_statistics& stats)
{
win_emu.log.info("Generating dump summary\n");
stats = {};
if (!dump_file)
{
win_emu.log.error("Dump file not loaded\n");
return;
}
stats.thread_count = dump_file->threads().size();
stats.module_count = dump_file->modules().size();
stats.memory_region_count = dump_file->memory_regions().size();
stats.memory_segment_count = dump_file->memory_segments().size();
stats.handle_count = dump_file->handles().size();
stats.has_exception = (dump_file->get_exception_info() != nullptr);
stats.has_system_info = (dump_file->get_system_info() != nullptr);
for (const auto& segment : dump_file->memory_segments())
{
stats.total_memory_size += segment.size;
}
win_emu.log.info(
"Summary: %s, %zu threads, %zu modules, %zu regions, %zu segments, %zu handles, %" PRIu64 " bytes memory\n",
get_architecture_string(dump_file).c_str(), stats.thread_count, stats.module_count,
stats.memory_region_count, stats.memory_segment_count, stats.handle_count, stats.total_memory_size);
}
void process_streams(windows_emulator& win_emu, const minidump::minidump_file* dump_file)
{
if (!dump_file)
{
return;
}
// Process system info
const auto* sys_info = dump_file->get_system_info();
if (sys_info)
{
win_emu.log.info("System: OS %u.%u.%u, %u processors, type %u, platform %u\n", sys_info->major_version,
sys_info->minor_version, sys_info->build_number, sys_info->number_of_processors,
sys_info->product_type, sys_info->platform_id);
}
// Process memory info
const auto& memory_regions = dump_file->memory_regions();
uint64_t total_reserved = 0;
uint64_t total_committed = 0;
size_t guard_pages = 0;
for (const auto& region : memory_regions)
{
total_reserved += region.region_size;
if (region.state & MEM_COMMIT)
{
total_committed += region.region_size;
}
if (region.protect & PAGE_GUARD)
{
guard_pages++;
}
}
win_emu.log.info("Memory: %zu regions, %" PRIu64 " bytes reserved, %" PRIu64
" bytes committed, %zu guard pages\n",
memory_regions.size(), total_reserved, total_committed, guard_pages);
// Process memory content
const auto& memory_segments = dump_file->memory_segments();
uint64_t min_addr = UINT64_MAX;
uint64_t max_addr = 0;
for (const auto& segment : memory_segments)
{
min_addr = std::min(min_addr, segment.start_virtual_address);
max_addr = std::max(max_addr, segment.end_virtual_address());
}
if (!memory_segments.empty())
{
win_emu.log.info("Content: %zu segments, range 0x%" PRIx64 "-0x%" PRIx64 " (%" PRIu64 " bytes span)\n",
memory_segments.size(), min_addr, max_addr, max_addr - min_addr);
}
// Process modules
const auto& modules = dump_file->modules();
for (const auto& mod : modules)
{
win_emu.log.info("Module: %s at 0x%" PRIx64 " (%u bytes)\n", mod.module_name.c_str(), mod.base_of_image,
mod.size_of_image);
}
// Process threads
const auto& threads = dump_file->threads();
for (const auto& thread : threads)
{
win_emu.log.info("Thread %u: TEB 0x%" PRIx64 ", stack 0x%" PRIx64 " (%u bytes), context %u bytes\n",
thread.thread_id, thread.teb, thread.stack_start_of_memory_range, thread.stack_data_size,
thread.context_data_size);
}
// Process handles
const auto& handles = dump_file->handles();
if (!handles.empty())
{
std::map<std::string, size_t> handle_type_counts;
for (const auto& handle : handles)
{
handle_type_counts[handle.type_name]++;
}
win_emu.log.info("Handles: %zu total\n", handles.size());
for (const auto& [type, count] : handle_type_counts)
{
win_emu.log.info(" %s: %zu\n", type.c_str(), count);
}
}
// Process exception info
const auto* exception = dump_file->get_exception_info();
if (exception)
{
win_emu.log.info("Exception: thread %u, code 0x%08X at 0x%" PRIx64 "\n", exception->thread_id,
exception->exception_record.exception_code, exception->exception_record.exception_address);
}
}
void reconstruct_memory_state(windows_emulator& win_emu, const minidump::minidump_file* dump_file,
minidump::minidump_reader* dump_reader)
{
if (!dump_file || !dump_reader)
{
win_emu.log.error("Dump file or reader not loaded\n");
return;
}
const auto& memory_regions = dump_file->memory_regions();
const auto& memory_segments = dump_file->memory_segments();
win_emu.log.info("Reconstructing memory: %zu regions, %zu data segments\n", memory_regions.size(),
memory_segments.size());
size_t reserved_count = 0;
size_t committed_count = 0;
size_t failed_count = 0;
for (const auto& region : memory_regions)
{
// Log the memory region details
win_emu.log.info("Region: 0x%" PRIx64 ", size=%" PRIu64 ", state=0x%08X, protect=0x%08X\n",
region.base_address, region.region_size, region.state, region.protect);
const bool is_reserved = (region.state & MEM_RESERVE) != 0;
const bool is_committed = (region.state & MEM_COMMIT) != 0;
const bool is_free = (region.state & MEM_FREE) != 0;
if (is_free)
{
continue;
}
auto protect_value = region.protect;
if (protect_value == 0)
{
protect_value = PAGE_READONLY;
win_emu.log.warn(" Region 0x%" PRIx64 " has zero protection, using PAGE_READONLY\n",
region.base_address);
}
memory_permission perms = map_nt_to_emulator_protection(protect_value);
try
{
if (is_committed)
{
if (win_emu.memory.allocate_memory(region.base_address, static_cast<size_t>(region.region_size),
perms, false))
{
committed_count++;
win_emu.log.info(" Allocated committed 0x%" PRIx64 ": size=%" PRIu64
", state=0x%08X, protect=0x%08X\n",
region.base_address, region.region_size, region.state, region.protect);
}
else
{
failed_count++;
win_emu.log.warn(" Failed to allocate committed 0x%" PRIx64 ": size=%" PRIu64 "\n",
region.base_address, region.region_size);
}
}
else if (is_reserved)
{
if (win_emu.memory.allocate_memory(region.base_address, static_cast<size_t>(region.region_size),
perms, true))
{
reserved_count++;
win_emu.log.info(" Reserved 0x%" PRIx64 ": size=%" PRIu64 ", state=0x%08X, protect=0x%08X\n",
region.base_address, region.region_size, region.state, region.protect);
}
else
{
failed_count++;
win_emu.log.warn(" Failed to reserve 0x%" PRIx64 ": size=%" PRIu64 "\n", region.base_address,
region.region_size);
}
}
}
catch (const std::exception& e)
{
failed_count++;
win_emu.log.error(" Exception allocating 0x%" PRIx64 ": %s\n", region.base_address, e.what());
}
}
win_emu.log.info("Regions: %zu reserved, %zu committed, %zu failed\n", reserved_count, committed_count,
failed_count);
size_t written_count = 0;
size_t write_failed_count = 0;
uint64_t total_bytes_written = 0;
for (const auto& segment : memory_segments)
{
try
{
auto memory_data =
dump_reader->read_memory(segment.start_virtual_address, static_cast<size_t>(segment.size));
win_emu.memory.write_memory(segment.start_virtual_address, memory_data.data(),
static_cast<size_t>(memory_data.size()));
written_count++;
total_bytes_written += memory_data.size();
win_emu.log.info(" Written segment 0x%" PRIx64 ": %zu bytes\n", segment.start_virtual_address,
memory_data.size());
}
catch (const std::exception& e)
{
write_failed_count++;
win_emu.log.error(" Failed to write segment 0x%" PRIx64 ": %s\n", segment.start_virtual_address,
e.what());
}
}
win_emu.log.info("Content: %zu segments written (%" PRIu64 " bytes), %zu failed\n", written_count,
total_bytes_written, write_failed_count);
}
bool is_main_executable(const minidump::module_info& mod)
{
const auto name = mod.module_name;
return name.find(".exe") != std::string::npos;
}
bool is_ntdll(const minidump::module_info& mod)
{
const auto name = mod.module_name;
return name == "ntdll.dll" || name == "NTDLL.DLL";
}
bool is_win32u(const minidump::module_info& mod)
{
const auto name = mod.module_name;
return name == "win32u.dll" || name == "WIN32U.DLL";
}
void reconstruct_module_state(windows_emulator& win_emu, const minidump::minidump_file* dump_file)
{
if (!dump_file)
{
win_emu.log.error("Dump file not loaded\n");
return;
}
const auto& modules = dump_file->modules();
win_emu.log.info("Reconstructing module state: %zu modules\n", modules.size());
size_t mapped_count = 0;
size_t failed_count = 0;
size_t identified_count = 0;
for (const auto& mod : modules)
{
try
{
auto* mapped_module = win_emu.mod_manager.map_memory_module(mod.base_of_image, mod.size_of_image,
mod.module_name, win_emu.log);
if (mapped_module)
{
mapped_count++;
win_emu.log.info(" Mapped %s at 0x%" PRIx64 " (%u bytes, %zu sections, %zu exports)\n",
mod.module_name.c_str(), mod.base_of_image, mod.size_of_image,
mapped_module->sections.size(), mapped_module->exports.size());
if (is_main_executable(mod))
{
win_emu.mod_manager.executable = mapped_module;
identified_count++;
win_emu.log.info(" Identified as main executable\n");
}
else if (is_ntdll(mod))
{
win_emu.mod_manager.ntdll = mapped_module;
identified_count++;
win_emu.log.info(" Identified as ntdll\n");
}
else if (is_win32u(mod))
{
win_emu.mod_manager.win32u = mapped_module;
identified_count++;
win_emu.log.info(" Identified as win32u\n");
}
}
else
{
failed_count++;
win_emu.log.warn(" Failed to map %s at 0x%" PRIx64 "\n", mod.module_name.c_str(),
mod.base_of_image);
}
}
catch (const std::exception& e)
{
failed_count++;
win_emu.log.error(" Exception mapping %s: %s\n", mod.module_name.c_str(), e.what());
}
}
win_emu.log.info("Module reconstruction: %zu mapped, %zu failed, %zu system modules identified\n", mapped_count,
failed_count, identified_count);
}
void setup_kusd_from_dump(windows_emulator& win_emu, const minidump::minidump_file* dump_file)
{
const auto* sys_info = dump_file->get_system_info();
if (!sys_info)
{
win_emu.log.warn("No system info available - using default KUSD\n");
return;
}
win_emu.log.info("Setting up KUSER_SHARED_DATA from dump system info\n");
auto& kusd = win_emu.process.kusd.get();
kusd.NtMajorVersion = sys_info->major_version;
kusd.NtMinorVersion = sys_info->minor_version;
kusd.NtBuildNumber = sys_info->build_number;
kusd.NativeProcessorArchitecture = sys_info->processor_architecture;
kusd.ActiveProcessorCount = sys_info->number_of_processors;
kusd.UnparkedProcessorCount = sys_info->number_of_processors;
kusd.NtProductType = static_cast<NT_PRODUCT_TYPE>(sys_info->product_type);
kusd.ProductTypeIsValid = 1;
win_emu.log.info("KUSD updated: Windows %u.%u.%u, %u processors, product type %u\n", sys_info->major_version,
sys_info->minor_version, sys_info->build_number, sys_info->number_of_processors,
sys_info->product_type);
}
bool load_thread_context(const std::filesystem::path& minidump_path, const minidump::thread_info& thread_info,
std::vector<std::byte>& context_buffer)
{
if (thread_info.context_data_size == 0)
{
return false;
}
std::ifstream context_file(minidump_path, std::ios::binary);
if (!context_file.is_open())
{
return false;
}
context_file.seekg(thread_info.context_rva);
context_buffer.resize(thread_info.context_data_size);
context_file.read(reinterpret_cast<char*>(context_buffer.data()), thread_info.context_data_size);
return context_file.good();
}
void reconstruct_threads(windows_emulator& win_emu, const minidump::minidump_file* dump_file,
const std::filesystem::path& minidump_path)
{
const auto& threads = dump_file->threads();
if (threads.empty())
{
win_emu.log.warn("No threads found in minidump\n");
return;
}
win_emu.log.info("Reconstructing threads: %zu threads\n", threads.size());
size_t success_count = 0;
size_t context_loaded_count = 0;
for (const auto& thread_info : threads)
{
try
{
emulator_thread thread(win_emu.memory);
thread.id = thread_info.thread_id;
thread.stack_base = thread_info.stack_start_of_memory_range;
thread.stack_size = thread_info.stack_data_size;
// Load CPU context if available
const bool context_loaded = load_thread_context(minidump_path, thread_info, thread.last_registers);
if (context_loaded)
{
context_loaded_count++;
}
// Set TEB address if valid
if (thread_info.teb != 0)
{
thread.teb = emulator_object<TEB64>(win_emu.memory);
thread.teb->set_address(thread_info.teb);
}
win_emu.log.info(" Thread %u: TEB=0x%" PRIx64 ", stack=0x%" PRIx64 " (%u bytes), context=%s\n",
thread_info.thread_id, thread_info.teb, thread.stack_base, thread_info.stack_data_size,
context_loaded ? "loaded" : "unavailable");
win_emu.process.threads.store(std::move(thread));
success_count++;
}
catch (const std::exception& e)
{
win_emu.log.error(" Failed to reconstruct thread %u: %s\n", thread_info.thread_id, e.what());
}
}
// Set active thread to first available thread
if (success_count > 0)
{
auto& first_thread = win_emu.process.threads.begin()->second;
win_emu.process.active_thread = &first_thread;
}
win_emu.log.info("Thread reconstruction: %zu/%zu threads created, %zu with context\n", success_count,
threads.size(), context_loaded_count);
}
void setup_peb_from_teb(windows_emulator& win_emu, const minidump::minidump_file* dump_file)
{
const auto& threads = dump_file->threads();
if (threads.empty())
{
win_emu.log.warn("No threads available for PEB setup\n");
return;
}
const auto& first_thread = threads[0];
if (first_thread.teb == 0)
{
win_emu.log.warn("Thread %u has null TEB address\n", first_thread.thread_id);
return;
}
try
{
constexpr uint64_t teb_peb_offset = offsetof(TEB64, ProcessEnvironmentBlock);
uint64_t peb_address = 0;
win_emu.memory.read_memory(first_thread.teb + teb_peb_offset, &peb_address, sizeof(peb_address));
if (peb_address == 0)
{
win_emu.log.warn("PEB address is null in TEB at 0x%" PRIx64 "\n", first_thread.teb);
return;
}
win_emu.process.peb.set_address(peb_address);
win_emu.log.info("PEB address: 0x%" PRIx64 " (from TEB 0x%" PRIx64 ")\n", peb_address, first_thread.teb);
}
catch (const std::exception& e)
{
win_emu.log.error("Failed to read PEB from TEB: %s\n", e.what());
}
}
void reconstruct_handle_table(windows_emulator& win_emu, const minidump::minidump_file* dump_file)
{
const auto& handles = dump_file->handles();
if (handles.empty())
{
return;
}
win_emu.log.info("Reconstructing handle table: %zu handles\n", handles.size());
std::map<std::string, size_t> handle_type_counts;
size_t created_count = 0;
for (const auto& handle_info : handles)
{
handle_type_counts[handle_info.type_name]++;
try
{
if (handle_info.type_name == "Event")
{
event evt{};
evt.name = u8_to_u16(handle_info.object_name);
win_emu.process.events.store(std::move(evt));
created_count++;
}
else if (handle_info.type_name == "File")
{
file f{};
f.name = u8_to_u16(handle_info.object_name);
win_emu.process.files.store(std::move(f));
created_count++;
}
else if (handle_info.type_name == "Mutant")
{
mutant m{};
m.name = u8_to_u16(handle_info.object_name);
win_emu.process.mutants.store(std::move(m));
created_count++;
}
// Other handle types can be added here as needed
}
catch (const std::exception& e)
{
win_emu.log.error(" Failed to create %s handle '%s': %s\n", handle_info.type_name.c_str(),
handle_info.object_name.c_str(), e.what());
}
}
// Log summary by type
for (const auto& [type, count] : handle_type_counts)
{
win_emu.log.info(" %s: %zu handles\n", type.c_str(), count);
}
win_emu.log.info("Handle table: %zu/%zu handles reconstructed\n", created_count, handles.size());
}
void setup_exception_context(windows_emulator& win_emu, const minidump::minidump_file* dump_file)
{
const auto* exception_info = dump_file->get_exception_info();
if (!exception_info)
{
return;
}
win_emu.process.current_ip = exception_info->exception_record.exception_address;
win_emu.log.info("Exception context: address=0x%" PRIx64 ", code=0x%08X, thread=%u\n",
exception_info->exception_record.exception_address,
exception_info->exception_record.exception_code, exception_info->thread_id);
}
void load_minidump_into_emulator(windows_emulator& win_emu, const std::filesystem::path& minidump_path)
{
win_emu.log.info("Starting minidump loading process\n");
win_emu.log.info("Minidump file: %s\n", minidump_path.string().c_str());
try
{
std::unique_ptr<minidump::minidump_file> dump_file;
std::unique_ptr<minidump::minidump_reader> dump_reader;
if (!parse_minidump_file(win_emu, minidump_path, dump_file, dump_reader))
{
throw std::runtime_error("Failed to parse minidump file");
}
if (!validate_dump_compatibility(win_emu, dump_file.get()))
{
throw std::runtime_error("Minidump compatibility validation failed");
}
setup_kusd_from_dump(win_emu, dump_file.get());
dump_statistics stats;
log_dump_summary(win_emu, dump_file.get(), stats);
process_streams(win_emu, dump_file.get());
// Existing phases
reconstruct_memory_state(win_emu, dump_file.get(), dump_reader.get());
reconstruct_module_state(win_emu, dump_file.get());
// Process state reconstruction phases
setup_peb_from_teb(win_emu, dump_file.get());
reconstruct_threads(win_emu, dump_file.get(), minidump_path);
reconstruct_handle_table(win_emu, dump_file.get());
setup_exception_context(win_emu, dump_file.get());
win_emu.log.info("Process state reconstruction completed\n");
}
catch (const std::exception& e)
{
win_emu.log.error("Minidump loading failed: %s\n", e.what());
throw;
}
}
} // namespace minidump_loader

Some files were not shown because too many files have changed in this diff Show More