mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-23 21:51:02 +00:00
Merge branch 'unicorn-upgrade-2' into unhandled-exceptions
This commit is contained in:
@@ -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
|
||||
...
|
||||
@@ -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})
|
||||
|
||||
|
||||
@@ -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
341
src/analyzer/analysis.cpp
Normal 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
33
src/analyzer/analysis.hpp
Normal 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);
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
23
src/backend-selection/CMakeLists.txt
Normal file
23
src/backend-selection/CMakeLists.txt
Normal 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()
|
||||
23
src/backend-selection/backend_selection.cpp
Normal file
23
src/backend-selection/backend_selection.cpp
Normal 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();
|
||||
}
|
||||
6
src/backend-selection/backend_selection.hpp
Normal file
6
src/backend-selection/backend_selection.hpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <arch_emulator.hpp>
|
||||
|
||||
std::unique_ptr<x86_64_emulator> create_x86_64_emulator();
|
||||
@@ -1,5 +1,5 @@
|
||||
add_subdirectory(unicorn-emulator)
|
||||
|
||||
if (MOMO_ENABLE_RUST_CODE)
|
||||
if (MOMO_ENABLE_RUST)
|
||||
add_subdirectory(icicle-emulator)
|
||||
endif()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -10,7 +10,9 @@
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <unicorn/unicorn.h>
|
||||
|
||||
#ifdef __clang__
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -8,6 +8,7 @@ struct UNICODE_STRING
|
||||
{
|
||||
USHORT Length;
|
||||
USHORT MaximumLength;
|
||||
uint32_t _Padding;
|
||||
EMULATOR_CAST(typename Traits::PVOID, char16_t*) Buffer;
|
||||
};
|
||||
|
||||
|
||||
55
src/common/platform/window.hpp
Normal file
55
src/common/platform/window.hpp
Normal 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)
|
||||
@@ -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<>>;
|
||||
}
|
||||
|
||||
52
src/common/utils/string.cpp
Normal file
52
src/common/utils/string.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
99
src/common/utils/wildcard.hpp
Normal file
99
src/common/utils/wildcard.hpp
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,10 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#ifdef __MINGW64__
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
25
src/debugger/CMakeLists.txt
Normal file
25
src/debugger/CMakeLists.txt
Normal 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"
|
||||
)
|
||||
257
src/debugger/event_handler.cpp
Normal file
257
src/debugger/event_handler.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
23
src/debugger/event_handler.hpp
Normal file
23
src/debugger/event_handler.hpp
Normal 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
94
src/debugger/events.fbs
Normal 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;
|
||||
2385
src/debugger/events_generated.hxx
Normal file
2385
src/debugger/events_generated.hxx
Normal file
File diff suppressed because it is too large
Load Diff
76
src/debugger/message_transmitter.cpp
Normal file
76
src/debugger/message_transmitter.cpp
Normal 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
|
||||
}
|
||||
}
|
||||
11
src/debugger/message_transmitter.hpp
Normal file
11
src/debugger/message_transmitter.hpp
Normal 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();
|
||||
}
|
||||
66
src/emulator/arch_emulator.hpp
Normal file
66
src/emulator/arch_emulator.hpp
Normal 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>;
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
enum class x64_register
|
||||
// x86_64 and x86_32 register definitions
|
||||
enum class x86_register
|
||||
{
|
||||
invalid = 0,
|
||||
ah,
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_)
|
||||
{
|
||||
|
||||
@@ -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})
|
||||
|
||||
101
src/samples/test-sample/resource.rc
Normal file
101
src/samples/test-sample/resource.rc
Normal 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
|
||||
|
||||
BIN
src/samples/test-sample/resources/icon.ico
Normal file
BIN
src/samples/test-sample/resources/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 107 KiB |
@@ -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(¤t_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")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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++)
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ namespace test
|
||||
};
|
||||
|
||||
const emulator_settings settings{
|
||||
.disable_logging = true,
|
||||
.use_relative_time = false,
|
||||
};
|
||||
|
||||
|
||||
@@ -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}")
|
||||
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
69
src/windows-emulator/devices/mountmgr.hpp
Normal file
69
src/windows-emulator/devices/mountmgr.hpp
Normal 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)
|
||||
56
src/windows-emulator/devices/security_support_provider.cpp
Normal file
56
src/windows-emulator/devices/security_support_provider.cpp
Normal 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>();
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include "../io_device.hpp"
|
||||
|
||||
std::unique_ptr<io_device> create_security_support_provider();
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, {});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
28
src/windows-emulator/generic_logger.hpp
Normal file
28
src/windows-emulator/generic_logger.hpp
Normal 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;
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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_{};
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "std_include.hpp"
|
||||
#include <serialization.hpp>
|
||||
|
||||
#include "x64_emulator.hpp"
|
||||
#include "arch_emulator.hpp"
|
||||
|
||||
#include <utils/time.hpp>
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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_{};
|
||||
|
||||
727
src/windows-emulator/minidump_loader.cpp
Normal file
727
src/windows-emulator/minidump_loader.cpp
Normal 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
Reference in New Issue
Block a user