Small callback optimization and unification

This commit is contained in:
momo5502
2025-02-14 08:58:27 +01:00
parent 6b4e86073e
commit 3218e26306
11 changed files with 62 additions and 69 deletions

View File

@@ -136,7 +136,7 @@ namespace
if (options.silent)
{
win_emu.buffer_stdout = false;
win_emu.callbacks.stdout_callback = [](const std::string_view data) {
win_emu.callbacks.on_stdout = [](const std::string_view data) {
(void)fwrite(data.data(), 1, data.size(), stdout);
};
}

View File

@@ -6,11 +6,9 @@ namespace test
{
std::string output_buffer{};
emulator_callbacks callbacks{
.stdout_callback =
[&output_buffer](const std::string_view data) {
emulator_callbacks callbacks{};
callbacks.on_stdout = [&output_buffer](const std::string_view data) {
output_buffer.append(data); //
},
};
const emulator_settings settings{

View File

@@ -163,7 +163,7 @@ class handle_store : public generic_handle_store
return blocked;
}
handle store(T value)
std::pair<handle, T*> store_and_get(T value)
{
if (this->block_mutation_)
{
@@ -171,9 +171,14 @@ class handle_store : public generic_handle_store
}
auto index = this->find_free_index();
this->store_.emplace(index, std::move(value));
const auto it = this->store_.emplace(index, std::move(value)).first;
return make_handle(index);
return {make_handle(index), &it->second};
}
handle store(T value)
{
return this->store_and_get(std::move(value)).first;
}
handle make_handle(const index_type index) const

View File

@@ -54,9 +54,10 @@ namespace utils
}
}
module_manager::module_manager(memory_manager& memory, file_system& file_sys)
module_manager::module_manager(memory_manager& memory, file_system& file_sys, callbacks& cb)
: memory_(&memory),
file_sys_(&file_sys)
file_sys_(&file_sys),
callbacks_(&cb)
{
}
@@ -95,7 +96,7 @@ mapped_module* module_manager::map_local_module(const std::filesystem::path& fil
const auto image_base = mod.image_base;
const auto entry = this->modules_.try_emplace(image_base, std::move(mod));
this->on_module_load(entry.first->second);
this->callbacks_->on_module_load(entry.first->second);
return &entry.first->second;
}
catch (const std::exception& e)
@@ -147,7 +148,7 @@ bool module_manager::unmap(const uint64_t address, const logger& logger)
logger.log("Unmapping %s (0x%" PRIx64 ")\n", mod->second.path.generic_string().c_str(), mod->second.image_base);
this->on_module_unload(mod->second);
this->callbacks_->on_module_unload(mod->second);
unmap_module(*this->memory_, mod->second);
this->modules_.erase(mod);

View File

@@ -10,11 +10,15 @@ class logger;
class module_manager
{
public:
using module_map = std::map<uint64_t, mapped_module>;
struct callbacks
{
utils::optional_function<void(mapped_module& mod)> on_module_load{};
utils::optional_function<void(mapped_module& mod)> on_module_unload{};
};
module_manager(memory_manager& memory, file_system& file_sys);
using module_map = std::map<uint64_t, mapped_module>;
module_manager(memory_manager& memory, file_system& file_sys, callbacks& cb);
void map_main_modules(const windows_path& executable_path, const windows_path& ntdll_path,
const windows_path& win32u_path, const logger& logger);
@@ -61,6 +65,7 @@ class module_manager
private:
memory_manager* memory_{};
file_system* file_sys_{};
callbacks* callbacks_{};
module_map modules_{};

View File

@@ -201,7 +201,7 @@ handle process_context::create_thread(memory_manager& memory, const uint64_t sta
const uint64_t stack_size)
{
emulator_thread t{memory, *this, start_address, argument, stack_size, ++this->spawned_thread_count};
auto h = this->threads.store(std::move(t));
on_create_thread(h, *this->threads.get(h));
auto [h, thr] = this->threads.store_and_get(std::move(t));
this->callbacks_->on_create_thread(h, *thr);
return h;
}

View File

@@ -32,8 +32,15 @@ struct application_settings;
struct process_context
{
process_context(x64_emulator& emu, memory_manager& memory)
: base_allocator(emu),
struct callbacks
{
utils::optional_function<void(handle h, emulator_thread& thr)> on_create_thread{};
utils::optional_function<void(handle h, emulator_thread& thr)> on_thread_terminated{};
};
process_context(x64_emulator& emu, memory_manager& memory, callbacks& cb)
: callbacks_(&cb),
base_allocator(emu),
peb(emu),
process_params(emu),
kusd(memory, *this)
@@ -44,14 +51,14 @@ struct process_context
const emulator_settings& emu_settings, const mapped_module& executable, const mapped_module& ntdll,
const apiset::container& apiset_container);
utils::optional_function<void(handle h, emulator_thread& thr)> on_create_thread{};
handle create_thread(memory_manager& memory, const uint64_t start_address, const uint64_t argument,
const uint64_t stack_size);
void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer);
callbacks* callbacks_{};
uint64_t executed_instructions{0};
uint64_t current_ip{0};
uint64_t previous_ip{0};

View File

@@ -71,6 +71,8 @@ void syscall_dispatcher::dispatch(windows_emulator& win_emu)
try
{
const auto* mod = win_emu.mod_manager.find_by_address(address);
const auto entry = this->handlers_.find(syscall_id);
if (entry == this->handlers_.end())
{
@@ -80,6 +82,13 @@ void syscall_dispatcher::dispatch(windows_emulator& win_emu)
return;
}
const std::string_view mod_name = mod ? mod->name : std::string_view{};
const auto res = win_emu.callbacks.on_syscall(syscall_id, address, mod_name, entry->second.name);
if (res == instruction_hook_continuation::skip_instruction)
{
return;
}
if (!entry->second.handler)
{
win_emu.log.error("Unimplemented syscall: %s - 0x%X\n", entry->second.name.c_str(), syscall_id);
@@ -88,12 +97,8 @@ void syscall_dispatcher::dispatch(windows_emulator& win_emu)
return;
}
const auto* mod = win_emu.mod_manager.find_by_address(address);
if (mod != win_emu.mod_manager.ntdll && mod != win_emu.mod_manager.win32u)
{
win_emu.callbacks.inline_syscall(syscall_id, address, mod ? mod->name.c_str() : "<N/A>",
entry->second.name);
win_emu.log.print(color::blue, "Executing inline syscall: %s (0x%X) at 0x%" PRIx64 " (%s)\n",
entry->second.name.c_str(), syscall_id, address, mod ? mod->name.c_str() : "<N/A>");
}
@@ -106,20 +111,16 @@ void syscall_dispatcher::dispatch(windows_emulator& win_emu)
uint64_t return_address{};
c.emu.try_read_memory(rsp, &return_address, sizeof(return_address));
const auto* mod_name = win_emu.mod_manager.find_name(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",
entry->second.name.c_str(), syscall_id, address, return_address, mod_name);
entry->second.name.c_str(), syscall_id, address, return_address, caller_mod_name);
}
else
{
const auto* previous_mod = win_emu.mod_manager.find_by_address(context.previous_ip);
win_emu.callbacks.outofline_syscall(syscall_id, address, mod ? mod->name.c_str() : "<N/A>",
entry->second.name, context.previous_ip,
previous_mod ? previous_mod->name.c_str() : "<N/A>");
win_emu.log.print(color::blue,
"Crafted out-of-line syscall: %s (0x%X) at 0x%" PRIx64 " (%s) via 0x%" PRIx64
" (%s)\n",

View File

@@ -2878,7 +2878,7 @@ namespace
io_status_block.write(block);
}
c.win_emu.callbacks.stdout_callback(temp_buffer);
c.win_emu.callbacks.on_stdout(temp_buffer);
if (!temp_buffer.ends_with("\n"))
{
@@ -3613,7 +3613,7 @@ namespace
}
thread->exit_status = exit_status;
c.win_emu.callbacks.thread_terminated(thread_handle, *thread);
c.win_emu.callbacks.on_thread_terminated(thread_handle, *thread);
if (thread == c.proc.active_thread)
{
c.win_emu.yield_thread();

View File

@@ -184,8 +184,8 @@ windows_emulator::windows_emulator(const emulator_settings& settings, std::uniqu
file_sys(emulation_root.empty() ? emulation_root : emulation_root / "filesys"),
memory(*this->emu_),
registry(emulation_root.empty() ? settings.registry_directory : emulation_root / "registry"),
mod_manager(memory, file_sys),
process(*this->emu_, memory)
mod_manager(memory, file_sys, callbacks),
process(*this->emu_, memory, callbacks)
{
#ifndef OS_WINDOWS
if (this->emulation_root.empty())
@@ -220,10 +220,6 @@ void windows_emulator::setup_process(const application_settings& app_settings, c
const auto& emu = this->emu();
auto& context = this->process;
mod_manager.on_module_load = std::move(callbacks.module_loaded);
mod_manager.on_module_unload = std::move(callbacks.module_unloaded);
context.on_create_thread = std::move(callbacks.thread_created);
this->mod_manager.map_main_modules(app_settings.application, R"(C:\Windows\System32\ntdll.dll)",
R"(C:\Windows\System32\win32u.dll)", this->log);
@@ -361,14 +357,6 @@ void windows_emulator::on_instruction_execution(const uint64_t address)
void windows_emulator::setup_hooks()
{
this->emu().hook_instruction(x64_hookable_instructions::syscall, [&] {
for (const auto& hook : this->syscall_hooks_)
{
if (hook() == instruction_hook_continuation::skip_instruction)
{
return instruction_hook_continuation::skip_instruction;
}
}
this->dispatcher.dispatch(*this);
return instruction_hook_continuation::skip_instruction;
});

View File

@@ -10,23 +10,17 @@
#include "logger.hpp"
#include "file_system.hpp"
#include "memory_manager.hpp"
#include "module/module_manager.hpp"
std::unique_ptr<x64_emulator> create_default_x64_emulator();
struct emulator_callbacks
struct emulator_callbacks : module_manager::callbacks, process_context::callbacks
{
utils::optional_function<void(std::string_view)> stdout_callback{};
utils::optional_function<void(uint32_t syscall_id, x64_emulator::pointer_type address, std::string_view mod_name,
std::string_view syscall_name)>
inline_syscall{};
utils::optional_function<void(uint32_t syscall_id, x64_emulator::pointer_type address, std::string_view mod_name,
std::string_view syscall_name, x64_emulator::pointer_type prev_address,
std::string_view prev_mod_name)>
outofline_syscall{};
utils::optional_function<void(mapped_module& mod)> module_loaded{};
utils::optional_function<void(mapped_module& mod)> module_unloaded{};
utils::optional_function<void(handle h, emulator_thread& thr)> thread_created{};
utils::optional_function<void(handle h, emulator_thread& thr)> thread_terminated{};
utils::optional_function<instruction_hook_continuation(uint32_t syscall_id, x64_emulator::pointer_type address,
std::string_view mod_name, std::string_view syscall_name)>
on_syscall{};
utils::optional_function<void(std::string_view)> on_stdout{};
};
struct application_settings
@@ -57,6 +51,7 @@ class windows_emulator
public:
std::filesystem::path emulation_root{};
emulator_callbacks callbacks{};
logger log{};
file_system file_sys;
memory_manager memory;
@@ -64,7 +59,6 @@ class windows_emulator
module_manager mod_manager;
process_context process;
syscall_dispatcher dispatcher;
emulator_callbacks callbacks{};
windows_emulator(const emulator_settings& settings = {},
std::unique_ptr<x64_emulator> emu = create_default_x64_emulator());
@@ -107,11 +101,6 @@ class windows_emulator
void save_snapshot();
void restore_snapshot();
void add_syscall_hook(instruction_hook_callback callback)
{
this->syscall_hooks_.push_back(std::move(callback));
}
uint16_t get_host_port(const uint16_t emulator_port) const
{
const auto entry = this->port_mappings_.find(emulator_port);
@@ -170,7 +159,6 @@ class windows_emulator
bool use_relative_time_{false};
bool silent_until_main_{false};
std::vector<instruction_hook_callback> syscall_hooks_{};
std::unordered_map<uint16_t, uint16_t> port_mappings_{};
std::set<std::string, std::less<>> modules_{};