From 32fcbf3ded7cd3c14b6659b94108b6130aa3582b Mon Sep 17 00:00:00 2001 From: momo5502 Date: Tue, 3 Jun 2025 20:29:58 +0200 Subject: [PATCH 01/20] Prepare event manager The event manager forms the basis for semantic logging. The emulator transmits events and the manager can handle them. This means to either print information to stdout, do nothing, etc... --- src/windows-emulator/event_manager.cpp | 2 + src/windows-emulator/event_manager.hpp | 98 +++++++++++++++++++++ src/windows-emulator/generic_logger.hpp | 28 ++++++ src/windows-emulator/logger.cpp | 7 +- src/windows-emulator/logger.hpp | 26 +----- src/windows-emulator/syscall_dispatcher.cpp | 10 ++- 6 files changed, 144 insertions(+), 27 deletions(-) create mode 100644 src/windows-emulator/event_manager.cpp create mode 100644 src/windows-emulator/event_manager.hpp create mode 100644 src/windows-emulator/generic_logger.hpp diff --git a/src/windows-emulator/event_manager.cpp b/src/windows-emulator/event_manager.cpp new file mode 100644 index 00000000..e20376ce --- /dev/null +++ b/src/windows-emulator/event_manager.cpp @@ -0,0 +1,2 @@ +#include "std_include.hpp" +#include "event_manager.hpp" diff --git a/src/windows-emulator/event_manager.hpp b/src/windows-emulator/event_manager.hpp new file mode 100644 index 00000000..26a2c98b --- /dev/null +++ b/src/windows-emulator/event_manager.hpp @@ -0,0 +1,98 @@ +#pragma once +#include "logger.hpp" + +struct mapped_module; +struct windows_emulator; + +enum class event_type +{ + syscall, + function_call, +}; + +struct event : utils::object +{ + event() = default; + + event(event&&) = delete; + event& operator=(event&&) = delete; + event(const event&) = delete; + event& operator=(const event&) = delete; + + virtual event_type get_type() const = 0; + + virtual void print(const generic_logger& log) const + { + (void)log; + } +}; + +template +struct typed_event : event +{ + using event::event; + + event_type get_type() const override + { + return Type; + } +}; + +template +class rich_event : typed_event +{ + public: + rich_event(windows_emulator& win_emu, Data data) + : win_emu(&win_emu), + data(std::move(data)) + { + } + + const Data& get_data() const + { + return this->data; + } + + protected: + windows_emulator* win_emu{}; + Data data{}; +}; + +struct syscall_data +{ + uint32_t id{}; + std::string_view name{}; +}; + +struct syscall_event : rich_event +{ + struct extended_info + { + uint64_t address{}; + mapped_module* origin{}; + }; + + extended_info get_extended_info() const; +}; + +struct event_manager : utils::object +{ + virtual void handle(const event& e); +}; + +class printing_event_manager : public event_manager +{ + public: + printing_event_manager(generic_logger& log) + : logger_(&log) + { + } + + void handle(const event& e) override + { + e.print(*this->logger_); + } + + private: + generic_logger* logger_{}; +}; diff --git a/src/windows-emulator/generic_logger.hpp b/src/windows-emulator/generic_logger.hpp new file mode 100644 index 00000000..54c48a50 --- /dev/null +++ b/src/windows-emulator/generic_logger.hpp @@ -0,0 +1,28 @@ +#pragma once +#include + +#if defined(__clang__) || defined(__GNUC__) +#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; +}; diff --git a/src/windows-emulator/logger.cpp b/src/windows-emulator/logger.cpp index 292bdfb6..3ad989cb 100644 --- a/src/windows-emulator/logger.cpp +++ b/src/windows-emulator/logger.cpp @@ -116,8 +116,13 @@ void logger::print_message(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_message(c, data); diff --git a/src/windows-emulator/logger.hpp b/src/windows-emulator/logger.hpp index aafcbb9c..f91be95e 100644 --- a/src/windows-emulator/logger.hpp +++ b/src/windows-emulator/logger.hpp @@ -1,29 +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, 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); diff --git a/src/windows-emulator/syscall_dispatcher.cpp b/src/windows-emulator/syscall_dispatcher.cpp index 164da74b..9495f630 100644 --- a/src/windows-emulator/syscall_dispatcher.cpp +++ b/src/windows-emulator/syscall_dispatcher.cpp @@ -24,8 +24,8 @@ void syscall_dispatcher::deserialize(utils::buffer_deserializer& buffer) this->add_handlers(); } -void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, std::span ntdll_data, - const exported_symbols& win32u_exports, std::span win32u_data) +void syscall_dispatcher::setup(const exported_symbols& ntdll_exports, const std::span ntdll_data, + const exported_symbols& win32u_exports, const std::span win32u_data) { this->handlers_ = {}; @@ -150,8 +150,10 @@ void syscall_dispatcher::dispatch(windows_emulator& win_emu) } } -syscall_dispatcher::syscall_dispatcher(const exported_symbols& ntdll_exports, std::span ntdll_data, - const exported_symbols& win32u_exports, std::span win32u_data) +syscall_dispatcher::syscall_dispatcher(const exported_symbols& ntdll_exports, + const std::span ntdll_data, + const exported_symbols& win32u_exports, + const std::span win32u_data) { this->setup(ntdll_exports, ntdll_data, win32u_exports, win32u_data); } From 25295707ece2d9a926fb8af3e45eceab4bd9169a Mon Sep 17 00:00:00 2001 From: momo5502 Date: Wed, 4 Jun 2025 18:52:59 +0200 Subject: [PATCH 02/20] Event manager progress --- src/windows-emulator/event_manager.cpp | 43 ++++++++++++ src/windows-emulator/event_manager.hpp | 78 +++++++++++++-------- src/windows-emulator/syscall_dispatcher.cpp | 43 +++--------- src/windows-emulator/windows_emulator.hpp | 16 +++-- 4 files changed, 110 insertions(+), 70 deletions(-) diff --git a/src/windows-emulator/event_manager.cpp b/src/windows-emulator/event_manager.cpp index e20376ce..805d3ff4 100644 --- a/src/windows-emulator/event_manager.cpp +++ b/src/windows-emulator/event_manager.cpp @@ -1,2 +1,45 @@ #include "std_include.hpp" #include "event_manager.hpp" +#include "windows_emulator.hpp" + +void syscall_event::print(generic_logger& log) const +{ + auto& win = *this->win_emu; + auto& emu = win.emu(); + + const auto address = emu.read_instruction_pointer(); + const auto* mod = win.mod_manager.find_by_address(address); + + if (mod != win.mod_manager.ntdll && mod != win.mod_manager.win32u) + { + log.print(color::blue, "Executing inline syscall: %.*s (0x%X) at 0x%" PRIx64 " (%s)\n", + static_cast(this->data.name.size()), this->data.name.data(), this->data.id, address, + mod ? mod->name.c_str() : ""); + } + 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.mod_manager.find_name(return_address); + + log.print(color::dark_gray, "Executing syscall: %.*s (0x%X) at 0x%" PRIx64 " via 0x%" PRIx64 " (%s)\n", + static_cast(this->data.name.size()), this->data.name.data(), this->data.id, address, + return_address, caller_mod_name); + } + else + { + const auto* previous_mod = win.mod_manager.find_by_address(win.process.previous_ip); + + log.print(color::blue, + "Crafted out-of-line syscall: %.*s (0x%X) at 0x%" PRIx64 " (%s) via 0x%" PRIx64 " (%s)\n", + static_cast(this->data.name.size()), this->data.name.data(), this->data.id, address, + mod ? mod->name.c_str() : "", win.process.previous_ip, + previous_mod ? previous_mod->name.c_str() : ""); + } + } +} diff --git a/src/windows-emulator/event_manager.hpp b/src/windows-emulator/event_manager.hpp index 26a2c98b..91987688 100644 --- a/src/windows-emulator/event_manager.hpp +++ b/src/windows-emulator/event_manager.hpp @@ -2,82 +2,98 @@ #include "logger.hpp" struct mapped_module; -struct windows_emulator; +class windows_emulator; -enum class event_type +enum class emulation_event_type { syscall, function_call, }; -struct event : utils::object +struct emulation_event : utils::object { - event() = default; + emulation_event() = default; - event(event&&) = delete; - event& operator=(event&&) = delete; - event(const event&) = delete; - event& operator=(const event&) = delete; + emulation_event(emulation_event&&) = delete; + emulation_event& operator=(emulation_event&&) = delete; + emulation_event(const emulation_event&) = delete; + emulation_event& operator=(const emulation_event&) = delete; - virtual event_type get_type() const = 0; + virtual emulation_event_type get_type() const = 0; - virtual void print(const generic_logger& log) const + virtual void print(generic_logger& log) const { (void)log; } }; -template -struct typed_event : event +template +struct typed_event : emulation_event { - using event::event; + using emulation_event::emulation_event; - event_type get_type() const override + emulation_event_type get_type() const override { return Type; } }; -template -class rich_event : typed_event +struct empty_data +{ +}; + +template +class rich_event : public typed_event { public: - rich_event(windows_emulator& win_emu, Data data) + rich_event(windows_emulator& win_emu, Input input = {}, Output output = {}) : win_emu(&win_emu), - data(std::move(data)) + in(std::move(input)) { } - const Data& get_data() const + const Input& get_input() const { - return this->data; + return this->in; + } + + Output& get_output() + { + return this->out; + } + + const Output& get_output() const + { + return this->out; } protected: windows_emulator* win_emu{}; - Data data{}; + Input in{}; + Output out{}; }; -struct syscall_data +struct syscall_input { uint32_t id{}; std::string_view name{}; }; -struct syscall_event : rich_event +struct syscall_output { - struct extended_info - { - uint64_t address{}; - mapped_module* origin{}; - }; + bool skip{false}; +}; - extended_info get_extended_info() const; +struct syscall_event : rich_event +{ + using rich_event::rich_event; + + void print(generic_logger& log) const override; }; struct event_manager : utils::object { - virtual void handle(const event& e); + virtual void handle(emulation_event& e); }; class printing_event_manager : public event_manager @@ -88,7 +104,7 @@ class printing_event_manager : public event_manager { } - void handle(const event& e) override + void handle(emulation_event& e) override { e.print(*this->logger_); } diff --git a/src/windows-emulator/syscall_dispatcher.cpp b/src/windows-emulator/syscall_dispatcher.cpp index 9495f630..b5e6232b 100644 --- a/src/windows-emulator/syscall_dispatcher.cpp +++ b/src/windows-emulator/syscall_dispatcher.cpp @@ -87,9 +87,14 @@ 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) + syscall_event e(c.win_emu, { + .id = syscall_id, + .name = entry->second.name, + }); + + c.win_emu.manager().handle(e); + + if (e.get_output().skip) { return; } @@ -102,38 +107,6 @@ void syscall_dispatcher::dispatch(windows_emulator& win_emu) return; } - if (mod != win_emu.mod_manager.ntdll && mod != win_emu.mod_manager.win32u) - { - win_emu.log.print(color::blue, "Executing inline syscall: %s (0x%X) at 0x%" PRIx64 " (%s)\n", - entry->second.name.c_str(), syscall_id, address, mod ? mod->name.c_str() : ""); - } - else - { - if (mod->is_within(context.previous_ip)) - { - const auto rsp = c.emu.read_stack_pointer(); - - uint64_t return_address{}; - c.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", - 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.log.print(color::blue, - "Crafted out-of-line syscall: %s (0x%X) at 0x%" PRIx64 " (%s) via 0x%" PRIx64 - " (%s)\n", - entry->second.name.c_str(), syscall_id, address, mod ? mod->name.c_str() : "", - context.previous_ip, previous_mod ? previous_mod->name.c_str() : ""); - } - } - entry->second.handler(c); } catch (std::exception& e) diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index a1fae00e..cd2a7ed8 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -5,6 +5,7 @@ #include +#include "event_manager.hpp" #include "syscall_dispatcher.hpp" #include "process_context.hpp" #include "logger.hpp" @@ -15,10 +16,6 @@ struct emulator_callbacks : module_manager::callbacks, process_context::callbacks { - utils::optional_function - on_syscall{}; - utils::optional_function on_stdout{}; }; @@ -58,6 +55,7 @@ class windows_emulator std::unique_ptr emu_{}; std::unique_ptr clock_{}; std::unique_ptr socket_factory_{}; + std::unique_ptr event_manager_{}; public: std::filesystem::path emulation_root{}; @@ -93,6 +91,16 @@ class windows_emulator return *this->emu_; } + event_manager& manager() + { + return *this->event_manager_; + } + + event_manager& manager() const + { + return *this->event_manager_; + } + utils::clock& clock() { return *this->clock_; From 5230909c239e89d0361c1317b8d6d2f90836d05a Mon Sep 17 00:00:00 2001 From: momo5502 Date: Wed, 4 Jun 2025 19:11:16 +0200 Subject: [PATCH 03/20] Switch back to using callbacks --- src/analyzer/analysis.cpp | 64 +++++++++++ src/analyzer/analysis.hpp | 5 + src/windows-emulator/event_manager.cpp | 45 -------- src/windows-emulator/event_manager.hpp | 114 -------------------- src/windows-emulator/syscall_dispatcher.cpp | 12 +-- src/windows-emulator/windows_emulator.hpp | 17 +-- 6 files changed, 75 insertions(+), 182 deletions(-) create mode 100644 src/analyzer/analysis.cpp create mode 100644 src/analyzer/analysis.hpp delete mode 100644 src/windows-emulator/event_manager.cpp delete mode 100644 src/windows-emulator/event_manager.hpp diff --git a/src/analyzer/analysis.cpp b/src/analyzer/analysis.cpp new file mode 100644 index 00000000..de307274 --- /dev/null +++ b/src/analyzer/analysis.cpp @@ -0,0 +1,64 @@ +#include "analysis.hpp" +#include "windows_emulator.hpp" + +namespace +{ + emulator_callbacks::continuation handle_syscall(windows_emulator& win_emu, const uint32_t syscall_id, + const std::string_view syscall_name) + { + 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", + static_cast(syscall_name.size()), syscall_name.data(), syscall_id, address, + mod ? mod->name.c_str() : ""); + } + 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", + static_cast(syscall_name.size()), syscall_name.data(), 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", + static_cast(syscall_name.size()), syscall_name.data(), syscall_id, address, + mod ? mod->name.c_str() : "", win_emu.process.previous_ip, + previous_mod ? previous_mod->name.c_str() : ""); + } + + return instruction_hook_continuation::run_instruction; + } + + template + std::function make_callback(windows_emulator& win_emu, + Return (*callback)(windows_emulator&, Args...)) + { + return [&win_emu, callback](Args... args) { + return callback(win_emu, std::forward(args)...); // + }; + } +} + +void register_analysis_callbacks(windows_emulator& win_emu) +{ + auto& cb = win_emu.callbacks; + + cb.on_syscall = make_callback(win_emu, handle_syscall); +} diff --git a/src/analyzer/analysis.hpp b/src/analyzer/analysis.hpp new file mode 100644 index 00000000..e5cc8050 --- /dev/null +++ b/src/analyzer/analysis.hpp @@ -0,0 +1,5 @@ +#pragma once + +class windows_emulator; + +void register_analysis_callbacks(windows_emulator& win_emu); diff --git a/src/windows-emulator/event_manager.cpp b/src/windows-emulator/event_manager.cpp deleted file mode 100644 index 805d3ff4..00000000 --- a/src/windows-emulator/event_manager.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "std_include.hpp" -#include "event_manager.hpp" -#include "windows_emulator.hpp" - -void syscall_event::print(generic_logger& log) const -{ - auto& win = *this->win_emu; - auto& emu = win.emu(); - - const auto address = emu.read_instruction_pointer(); - const auto* mod = win.mod_manager.find_by_address(address); - - if (mod != win.mod_manager.ntdll && mod != win.mod_manager.win32u) - { - log.print(color::blue, "Executing inline syscall: %.*s (0x%X) at 0x%" PRIx64 " (%s)\n", - static_cast(this->data.name.size()), this->data.name.data(), this->data.id, address, - mod ? mod->name.c_str() : ""); - } - 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.mod_manager.find_name(return_address); - - log.print(color::dark_gray, "Executing syscall: %.*s (0x%X) at 0x%" PRIx64 " via 0x%" PRIx64 " (%s)\n", - static_cast(this->data.name.size()), this->data.name.data(), this->data.id, address, - return_address, caller_mod_name); - } - else - { - const auto* previous_mod = win.mod_manager.find_by_address(win.process.previous_ip); - - log.print(color::blue, - "Crafted out-of-line syscall: %.*s (0x%X) at 0x%" PRIx64 " (%s) via 0x%" PRIx64 " (%s)\n", - static_cast(this->data.name.size()), this->data.name.data(), this->data.id, address, - mod ? mod->name.c_str() : "", win.process.previous_ip, - previous_mod ? previous_mod->name.c_str() : ""); - } - } -} diff --git a/src/windows-emulator/event_manager.hpp b/src/windows-emulator/event_manager.hpp deleted file mode 100644 index 91987688..00000000 --- a/src/windows-emulator/event_manager.hpp +++ /dev/null @@ -1,114 +0,0 @@ -#pragma once -#include "logger.hpp" - -struct mapped_module; -class windows_emulator; - -enum class emulation_event_type -{ - syscall, - function_call, -}; - -struct emulation_event : utils::object -{ - emulation_event() = default; - - emulation_event(emulation_event&&) = delete; - emulation_event& operator=(emulation_event&&) = delete; - emulation_event(const emulation_event&) = delete; - emulation_event& operator=(const emulation_event&) = delete; - - virtual emulation_event_type get_type() const = 0; - - virtual void print(generic_logger& log) const - { - (void)log; - } -}; - -template -struct typed_event : emulation_event -{ - using emulation_event::emulation_event; - - emulation_event_type get_type() const override - { - return Type; - } -}; - -struct empty_data -{ -}; - -template -class rich_event : public typed_event -{ - public: - rich_event(windows_emulator& win_emu, Input input = {}, Output output = {}) - : win_emu(&win_emu), - in(std::move(input)) - { - } - - const Input& get_input() const - { - return this->in; - } - - Output& get_output() - { - return this->out; - } - - const Output& get_output() const - { - return this->out; - } - - protected: - windows_emulator* win_emu{}; - Input in{}; - Output out{}; -}; - -struct syscall_input -{ - uint32_t id{}; - std::string_view name{}; -}; - -struct syscall_output -{ - bool skip{false}; -}; - -struct syscall_event : rich_event -{ - using rich_event::rich_event; - - void print(generic_logger& log) const override; -}; - -struct event_manager : utils::object -{ - virtual void handle(emulation_event& e); -}; - -class printing_event_manager : public event_manager -{ - public: - printing_event_manager(generic_logger& log) - : logger_(&log) - { - } - - void handle(emulation_event& e) override - { - e.print(*this->logger_); - } - - private: - generic_logger* logger_{}; -}; diff --git a/src/windows-emulator/syscall_dispatcher.cpp b/src/windows-emulator/syscall_dispatcher.cpp index b5e6232b..c25ad547 100644 --- a/src/windows-emulator/syscall_dispatcher.cpp +++ b/src/windows-emulator/syscall_dispatcher.cpp @@ -76,8 +76,6 @@ 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()) { @@ -87,14 +85,8 @@ void syscall_dispatcher::dispatch(windows_emulator& win_emu) return; } - syscall_event e(c.win_emu, { - .id = syscall_id, - .name = entry->second.name, - }); - - c.win_emu.manager().handle(e); - - if (e.get_output().skip) + const auto res = win_emu.callbacks.on_syscall(syscall_id, entry->second.name); + if (res == instruction_hook_continuation::skip_instruction) { return; } diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index cd2a7ed8..34df864a 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -5,7 +5,6 @@ #include -#include "event_manager.hpp" #include "syscall_dispatcher.hpp" #include "process_context.hpp" #include "logger.hpp" @@ -16,7 +15,10 @@ struct emulator_callbacks : module_manager::callbacks, process_context::callbacks { - utils::optional_function on_stdout{}; + using continuation = instruction_hook_continuation; + + utils::optional_function on_syscall{}; + utils::optional_function on_stdout{}; }; struct application_settings @@ -55,7 +57,6 @@ class windows_emulator std::unique_ptr emu_{}; std::unique_ptr clock_{}; std::unique_ptr socket_factory_{}; - std::unique_ptr event_manager_{}; public: std::filesystem::path emulation_root{}; @@ -91,16 +92,6 @@ class windows_emulator return *this->emu_; } - event_manager& manager() - { - return *this->event_manager_; - } - - event_manager& manager() const - { - return *this->event_manager_; - } - utils::clock& clock() { return *this->clock_; From 5609de9dde5171d0f7c8473dd74530d0e14061a1 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Wed, 4 Jun 2025 19:17:09 +0200 Subject: [PATCH 04/20] Small fixes and prepare for more semantic logging --- src/windows-emulator/devices/afd_endpoint.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/windows-emulator/devices/afd_endpoint.cpp b/src/windows-emulator/devices/afd_endpoint.cpp index bc1ab997..d067eb1a 100644 --- a/src/windows-emulator/devices/afd_endpoint.cpp +++ b/src/windows-emulator/devices/afd_endpoint.cpp @@ -519,13 +519,14 @@ namespace { if (_AFD_BASE(c.io_control_code) != FSCTL_AFD_BASE) { - win_emu.log.print(color::cyan, "Bad AFD IOCTL: 0x%X\n", c.io_control_code); + win_emu.log.error("Bad AFD IOCTL: 0x%X\n", static_cast(c.io_control_code)); return STATUS_NOT_SUPPORTED; } const auto request = _AFD_REQUEST(c.io_control_code); - win_emu.log.print(color::dark_gray, "--> AFD IOCTL: 0x%X (%d)\n", c.io_control_code, request); + win_emu.log.print(color::dark_gray, "--> AFD IOCTL: 0x%X (%u)\n", static_cast(c.io_control_code), + static_cast(request)); switch (request) { @@ -562,7 +563,8 @@ namespace case AFD_TRANSPORT_IOCTL: return STATUS_SUCCESS; default: - win_emu.log.print(color::gray, "Unsupported AFD IOCTL: 0x%X (%d)\n", c.io_control_code, request); + win_emu.log.error("Unsupported AFD IOCTL: 0x%X (%u)\n", static_cast(c.io_control_code), + static_cast(request)); return STATUS_NOT_SUPPORTED; } } @@ -995,8 +997,8 @@ namespace { const auto timeout_callback = [](windows_emulator& win_emu, const io_device_context& c) { const emulator_object info_obj{win_emu.emu(), c.input_buffer}; - info_obj.access([&](AFD_POLL_INFO64& info) { - info.NumberOfHandles = 0; // + info_obj.access([&](AFD_POLL_INFO64& poll_info) { + poll_info.NumberOfHandles = 0; // }); }; @@ -1275,4 +1277,4 @@ std::unique_ptr create_afd_endpoint() std::unique_ptr create_afd_async_connect_hlp() { return std::make_unique(); -} \ No newline at end of file +} From 84e8e86b9495ef646b80313caa1af3b598396cfd Mon Sep 17 00:00:00 2001 From: momo5502 Date: Wed, 4 Jun 2025 19:28:50 +0200 Subject: [PATCH 05/20] Extract sus activity logging --- src/analyzer/analysis.cpp | 38 +++++++++++++---------- src/windows-emulator/syscalls/system.cpp | 2 +- src/windows-emulator/syscalls/thread.cpp | 6 ++-- src/windows-emulator/windows_emulator.cpp | 8 ++--- src/windows-emulator/windows_emulator.hpp | 1 + 5 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/analyzer/analysis.cpp b/src/analyzer/analysis.cpp index de307274..b663f456 100644 --- a/src/analyzer/analysis.cpp +++ b/src/analyzer/analysis.cpp @@ -1,8 +1,25 @@ #include "analysis.hpp" #include "windows_emulator.hpp" +#define STR_VIEW_VA(str) static_cast((str).size()), (str).data() + namespace { + template + std::function make_callback(windows_emulator& win_emu, + Return (*callback)(windows_emulator&, Args...)) + { + return [&win_emu, callback](Args... args) { + return callback(win_emu, std::forward(args)...); // + }; + } + + void handle_suspicious_activity(windows_emulator& win_emu, const std::string_view details) + { + const auto rip = win_emu.emu().read_instruction_pointer(); + win_emu.log.print(color::pink, "Suspicious: %.*s (0x" PRIX64 ")\n", STR_VIEW_VA(details), rip); + } + emulator_callbacks::continuation handle_syscall(windows_emulator& win_emu, const uint32_t syscall_id, const std::string_view syscall_name) { @@ -15,8 +32,7 @@ namespace if (is_sus_module) { win_emu.log.print(color::blue, "Executing inline syscall: %.*s (0x%X) at 0x%" PRIx64 " (%s)\n", - static_cast(syscall_name.size()), syscall_name.data(), syscall_id, address, - mod ? mod->name.c_str() : ""); + STR_VIEW_VA(syscall_name), syscall_id, address, mod ? mod->name.c_str() : ""); } else if (mod->is_within(win_emu.process.previous_ip)) { @@ -29,8 +45,7 @@ namespace win_emu.log.print(color::dark_gray, "Executing syscall: %.*s (0x%X) at 0x%" PRIx64 " via 0x%" PRIx64 " (%s)\n", - static_cast(syscall_name.size()), syscall_name.data(), syscall_id, address, - return_address, caller_mod_name); + STR_VIEW_VA(syscall_name), syscall_id, address, return_address, caller_mod_name); } else { @@ -38,22 +53,12 @@ namespace win_emu.log.print(color::blue, "Crafted out-of-line syscall: %.*s (0x%X) at 0x%" PRIx64 " (%s) via 0x%" PRIx64 " (%s)\n", - static_cast(syscall_name.size()), syscall_name.data(), syscall_id, address, - mod ? mod->name.c_str() : "", win_emu.process.previous_ip, - previous_mod ? previous_mod->name.c_str() : ""); + STR_VIEW_VA(syscall_name), syscall_id, address, mod ? mod->name.c_str() : "", + win_emu.process.previous_ip, previous_mod ? previous_mod->name.c_str() : ""); } return instruction_hook_continuation::run_instruction; } - - template - std::function make_callback(windows_emulator& win_emu, - Return (*callback)(windows_emulator&, Args...)) - { - return [&win_emu, callback](Args... args) { - return callback(win_emu, std::forward(args)...); // - }; - } } void register_analysis_callbacks(windows_emulator& win_emu) @@ -61,4 +66,5 @@ void register_analysis_callbacks(windows_emulator& win_emu) auto& cb = win_emu.callbacks; cb.on_syscall = make_callback(win_emu, handle_syscall); + cb.on_suspicious_activity = make_callback(win_emu, handle_suspicious_activity); } diff --git a/src/windows-emulator/syscalls/system.cpp b/src/windows-emulator/syscalls/system.cpp index 2cee38e8..02b73402 100644 --- a/src/windows-emulator/syscalls/system.cpp +++ b/src/windows-emulator/syscalls/system.cpp @@ -109,7 +109,7 @@ namespace syscalls return STATUS_NOT_SUPPORTED; case SystemControlFlowTransition: - c.win_emu.log.print(color::pink, "Warbird control flow transition!\n"); + c.win_emu.callbacks.on_suspicious_activity("Warbird control flow transition"); return STATUS_NOT_SUPPORTED; case SystemTimeOfDayInformation: diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index d8c24a66..ebac1908 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -26,7 +26,7 @@ namespace syscalls if (info_class == ThreadHideFromDebugger) { - c.win_emu.log.print(color::pink, "--> Hiding thread %X from debugger!\n", thread->id); + c.win_emu.callbacks.on_suspicious_activity("Hiding thread from debugger"); return STATUS_SUCCESS; } @@ -470,7 +470,7 @@ namespace syscalls thread_context.access([&](CONTEXT64& context) { if ((context.ContextFlags & CONTEXT_DEBUG_REGISTERS_64) == CONTEXT_DEBUG_REGISTERS_64) { - c.win_emu.log.print(color::pink, "--> Reading debug registers!\n"); + c.win_emu.callbacks.on_suspicious_activity("Reading debug registers"); } cpu_context::save(c.emu, context); @@ -509,7 +509,7 @@ namespace syscalls if ((context.ContextFlags & CONTEXT_DEBUG_REGISTERS_64) == CONTEXT_DEBUG_REGISTERS_64) { - c.win_emu.log.print(color::pink, "--> Setting debug registers!\n"); + c.win_emu.callbacks.on_suspicious_activity("Setting debug registers"); } return STATUS_SUCCESS; diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 76cf20ba..d52896e3 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -550,24 +550,24 @@ void windows_emulator::setup_hooks() case 1: if ((eflags & 0x100) != 0) { - this->log.print(color::pink, "Singlestep (Trap Flag): 0x%" PRIx64 "\n", rip); + this->callbacks.on_suspicious_activity("Singlestep (Trap Flag)"); this->emu().reg(x86_register::eflags, eflags & ~0x100); } else { - this->log.print(color::pink, "Singlestep: 0x%" PRIx64 "\n", rip); + this->callbacks.on_suspicious_activity("Singlestep"); } dispatch_single_step(this->emu(), this->process); return; case 3: - this->log.print(color::pink, "Breakpoint: 0x%" PRIx64 "\n", rip); + this->callbacks.on_suspicious_activity("Breakpoint"); dispatch_breakpoint(this->emu(), this->process); return; case 6: dispatch_illegal_instruction_violation(this->emu(), this->process); return; case 45: - this->log.print(color::pink, "DbgPrint: 0x%" PRIx64 "\n", rip); + this->callbacks.on_suspicious_activity("DbgPrint"); dispatch_breakpoint(this->emu(), this->process); return; default: diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index 34df864a..3f8eba65 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -19,6 +19,7 @@ struct emulator_callbacks : module_manager::callbacks, process_context::callback utils::optional_function on_syscall{}; utils::optional_function on_stdout{}; + utils::optional_function on_suspicious_activity{}; }; struct application_settings From de0d9a17a5315e53cfe46d73f31c3010f9b8da26 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Wed, 4 Jun 2025 20:19:47 +0200 Subject: [PATCH 06/20] Small fixes --- src/analyzer/analysis.cpp | 2 +- src/windows-emulator/syscalls/locale.cpp | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/analyzer/analysis.cpp b/src/analyzer/analysis.cpp index b663f456..1316a29c 100644 --- a/src/analyzer/analysis.cpp +++ b/src/analyzer/analysis.cpp @@ -17,7 +17,7 @@ namespace void handle_suspicious_activity(windows_emulator& win_emu, const std::string_view details) { const auto rip = win_emu.emu().read_instruction_pointer(); - win_emu.log.print(color::pink, "Suspicious: %.*s (0x" PRIX64 ")\n", STR_VIEW_VA(details), rip); + win_emu.log.print(color::pink, "Suspicious: %.*s (0x%" PRIX64 ")\n", STR_VIEW_VA(details), rip); } emulator_callbacks::continuation handle_syscall(windows_emulator& win_emu, const uint32_t syscall_id, diff --git a/src/windows-emulator/syscalls/locale.cpp b/src/windows-emulator/syscalls/locale.cpp index 0cf09022..504f543c 100644 --- a/src/windows-emulator/syscalls/locale.cpp +++ b/src/windows-emulator/syscalls/locale.cpp @@ -34,14 +34,13 @@ namespace syscalls return STATUS_SUCCESS; } - NTSTATUS handle_NtGetNlsSectionPtr(const syscall_context& c, ULONG section_type, ULONG section_data, - emulator_pointer /*context_data*/, emulator_object section_pointer, - emulator_object section_size) + NTSTATUS handle_NtGetNlsSectionPtr(const syscall_context& c, const ULONG section_type, const ULONG section_data, + emulator_pointer /*context_data*/, + const emulator_object section_pointer, + const emulator_object section_size) { if (section_type == 11) { - c.win_emu.log.print(color::dark_gray, "--> Code Page: %d\n", section_data); - const auto file_path = R"(C:\Windows\System32\C_)" + std::to_string(section_data) + ".NLS"; const auto locale_file = utils::io::read_file(c.win_emu.file_sys.translate(file_path)); if (locale_file.empty()) @@ -59,7 +58,7 @@ namespace syscalls return STATUS_SUCCESS; } - c.win_emu.log.print(color::gray, "Unsupported section type: %X\n", section_type); + c.win_emu.log.warn("Unsupported section type: %X\n", static_cast(section_type)); return STATUS_NOT_SUPPORTED; } @@ -78,13 +77,13 @@ namespace syscalls return STATUS_NOT_SUPPORTED; } - NTSTATUS handle_NtQueryDefaultUILanguage(const syscall_context&, emulator_object language_id) + NTSTATUS handle_NtQueryDefaultUILanguage(const syscall_context&, const emulator_object language_id) { language_id.write(0x407); return STATUS_SUCCESS; } - NTSTATUS handle_NtQueryInstallUILanguage(const syscall_context&, emulator_object language_id) + NTSTATUS handle_NtQueryInstallUILanguage(const syscall_context&, const emulator_object language_id) { language_id.write(0x407); return STATUS_SUCCESS; From 1f829463c1ec9d86696cde190ef4bcee9d39e9bf Mon Sep 17 00:00:00 2001 From: momo5502 Date: Wed, 4 Jun 2025 20:22:28 +0200 Subject: [PATCH 07/20] Use more semantic logging --- src/windows-emulator/syscalls/object.cpp | 5 ++--- src/windows-emulator/syscalls/registry.cpp | 9 ++++----- src/windows-emulator/syscalls/thread.cpp | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/windows-emulator/syscalls/object.cpp b/src/windows-emulator/syscalls/object.cpp index 71ab7b28..5d17be4a 100644 --- a/src/windows-emulator/syscalls/object.cpp +++ b/src/windows-emulator/syscalls/object.cpp @@ -158,8 +158,7 @@ namespace syscalls if (!is_awaitable_object_type(h)) { - c.win_emu.log.print(color::gray, "Unsupported handle type for NtWaitForMultipleObjects: %d!\n", - h.value.type); + c.win_emu.log.warn("Unsupported handle type for NtWaitForMultipleObjects: %d!\n", h.value.type); return STATUS_NOT_SUPPORTED; } @@ -180,7 +179,7 @@ namespace syscalls { if (!is_awaitable_object_type(h)) { - c.win_emu.log.print(color::gray, "Unsupported handle type for NtWaitForSingleObject: %d!\n", h.value.type); + c.win_emu.log.warn("Unsupported handle type for NtWaitForSingleObject: %d!\n", h.value.type); return STATUS_NOT_SUPPORTED; } diff --git a/src/windows-emulator/syscalls/registry.cpp b/src/windows-emulator/syscalls/registry.cpp index 969a0d70..96017a30 100644 --- a/src/windows-emulator/syscalls/registry.cpp +++ b/src/windows-emulator/syscalls/registry.cpp @@ -111,7 +111,7 @@ namespace syscalls return STATUS_SUCCESS; } - c.win_emu.log.print(color::gray, "Unsupported registry class: %X\n", key_information_class); + c.win_emu.log.warn("Unsupported registry class: %X\n", key_information_class); c.emu.stop(); return STATUS_NOT_SUPPORTED; } @@ -224,7 +224,7 @@ namespace syscalls return STATUS_SUCCESS; } - c.win_emu.log.print(color::gray, "Unsupported registry value class: %X\n", key_value_information_class); + c.win_emu.log.warn("Unsupported registry value class: %X\n", key_value_information_class); c.emu.stop(); return STATUS_NOT_SUPPORTED; } @@ -332,7 +332,7 @@ namespace syscalls return STATUS_SUCCESS; } - c.win_emu.log.print(color::gray, "Unsupported registry enumeration class: %X\n", key_information_class); + c.win_emu.log.warn("Unsupported registry enumeration class: %X\n", key_information_class); return STATUS_NOT_SUPPORTED; } @@ -443,8 +443,7 @@ namespace syscalls return STATUS_SUCCESS; } - c.win_emu.log.print(color::gray, "Unsupported registry value enumeration class: %X\n", - key_value_information_class); + c.win_emu.log.warn("Unsupported registry value enumeration class: %X\n", key_value_information_class); return STATUS_NOT_SUPPORTED; } } diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index ebac1908..83a615fb 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -329,7 +329,7 @@ namespace syscalls { if (lock.value()) { - c.win_emu.log.print(color::gray, "NtAlertThreadByThreadIdEx with lock not supported yet!"); + c.win_emu.log.warn("NtAlertThreadByThreadIdEx with lock not supported yet!"); // c.emu.stop(); // return STATUS_NOT_SUPPORTED; } From d18a60561c92feba7869cd80873f4e28a7119ef8 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Wed, 4 Jun 2025 20:26:33 +0200 Subject: [PATCH 08/20] Fix warning --- src/windows-emulator/devices/security_support_provider.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/windows-emulator/devices/security_support_provider.cpp b/src/windows-emulator/devices/security_support_provider.cpp index 9796246f..f3de5cb5 100644 --- a/src/windows-emulator/devices/security_support_provider.cpp +++ b/src/windows-emulator/devices/security_support_provider.cpp @@ -26,7 +26,7 @@ namespace NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& c) override { - win_emu.log.print(color::dark_gray, "--> KSEC IOCTL: 0x%X\n", c.io_control_code); + win_emu.log.print(color::dark_gray, "--> KSEC IOCTL: 0x%X\n", static_cast(c.io_control_code)); if (c.io_control_code != 0x390400) { From 956e73d839e0607bee18163b4472b2ff0076bdf8 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Wed, 4 Jun 2025 20:45:48 +0200 Subject: [PATCH 09/20] Some fixes --- src/analyzer/analysis.cpp | 2 +- src/common/platform/primitives.hpp | 4 ++++ src/windows-emulator/generic_logger.hpp | 2 +- src/windows-emulator/syscalls/thread.cpp | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/analyzer/analysis.cpp b/src/analyzer/analysis.cpp index 1316a29c..9d449519 100644 --- a/src/analyzer/analysis.cpp +++ b/src/analyzer/analysis.cpp @@ -17,7 +17,7 @@ namespace void handle_suspicious_activity(windows_emulator& win_emu, const std::string_view details) { const auto rip = win_emu.emu().read_instruction_pointer(); - win_emu.log.print(color::pink, "Suspicious: %.*s (0x%" PRIX64 ")\n", STR_VIEW_VA(details), rip); + win_emu.log.print(color::pink, "Suspicious: %.*s (0x%" PRIx64 ")\n", STR_VIEW_VA(details), rip); } emulator_callbacks::continuation handle_syscall(windows_emulator& win_emu, const uint32_t syscall_id, diff --git a/src/common/platform/primitives.hpp b/src/common/platform/primitives.hpp index ad5fa657..419e61d9 100644 --- a/src/common/platform/primitives.hpp +++ b/src/common/platform/primitives.hpp @@ -66,4 +66,8 @@ using USHORT = WORD; #define FALSE 0 #endif +static_assert(sizeof(DWORD) == 4); +static_assert(sizeof(ULONG) == 4); +static_assert(sizeof(int) == 4); + // NOLINTEND(modernize-use-using) diff --git a/src/windows-emulator/generic_logger.hpp b/src/windows-emulator/generic_logger.hpp index 54c48a50..d212dc4c 100644 --- a/src/windows-emulator/generic_logger.hpp +++ b/src/windows-emulator/generic_logger.hpp @@ -1,7 +1,7 @@ #pragma once #include -#if defined(__clang__) || defined(__GNUC__) +#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) diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index 83a615fb..322220e3 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -425,7 +425,7 @@ namespace syscalls if (flags != 0) { - c.win_emu.log.error("NtGetNextThread flags %X not supported\n", flags); + c.win_emu.log.error("NtGetNextThread flags %X not supported\n", static_cast(flags)); c.emu.stop(); return STATUS_NOT_SUPPORTED; } From f046246740f8d13f59e9bc048fd5329725ef189d Mon Sep 17 00:00:00 2001 From: momo5502 Date: Wed, 4 Jun 2025 21:21:48 +0200 Subject: [PATCH 10/20] Extract more analysis logic --- src/analyzer/analysis.cpp | 92 +++++++++++++++++++++++ src/windows-emulator/windows_emulator.cpp | 84 +-------------------- src/windows-emulator/windows_emulator.hpp | 16 ++-- 3 files changed, 103 insertions(+), 89 deletions(-) diff --git a/src/analyzer/analysis.cpp b/src/analyzer/analysis.cpp index 9d449519..583876ed 100644 --- a/src/analyzer/analysis.cpp +++ b/src/analyzer/analysis.cpp @@ -1,5 +1,6 @@ #include "analysis.hpp" #include "windows_emulator.hpp" +#include "utils/lazy_object.hpp" #define STR_VIEW_VA(str) static_cast((str).size()), (str).data() @@ -20,6 +21,96 @@ namespace win_emu.log.print(color::pink, "Suspicious: %.*s (0x%" PRIx64 ")\n", STR_VIEW_VA(details), rip); } + void handle_instruction(windows_emulator& win_emu, const uint64_t address) + { + 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(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 (win_emu.modules_.empty()) + { + return false; + } + + return (binary && win_emu.modules_.contains(binary->name)) || + (previous_binary && win_emu.modules_.contains(previous_binary->name)); + }; + + const auto is_interesting_call = is_previous_main_exe // + || is_main_exe // + || is_in_interesting_module(); + + if (win_emu.silent_until_main_ && is_main_exe) + { + win_emu.silent_until_main_ = false; + win_emu.log.disable_output(false); + } + + if (!win_emu.verbose && !win_emu.verbose_calls && !is_interesting_call) + { + return; + } + + if (binary) + { + const auto export_entry = binary->address_names.find(address); + if (export_entry != binary->address_names.end() && + !win_emu.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); + } + 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); + } + } + + if (!win_emu.verbose) + { + return; + } + + auto& emu = win_emu.emu(); + + // TODO: Remove or cleanup + win_emu.log.print( + color::gray, + "Inst: %16" PRIx64 " - RAX: %16" PRIx64 " - RBX: %16" PRIx64 " - RCX: %16" PRIx64 " - RDX: %16" PRIx64 + " - R8: %16" PRIx64 " - R9: %16" PRIx64 " - RDI: %16" PRIx64 " - RSI: %16" PRIx64 " - %s\n", + address, emu.reg(x86_register::rax), emu.reg(x86_register::rbx), emu.reg(x86_register::rcx), + emu.reg(x86_register::rdx), emu.reg(x86_register::r8), emu.reg(x86_register::r9), + emu.reg(x86_register::rdi), emu.reg(x86_register::rsi), binary ? binary->name.c_str() : ""); + } + emulator_callbacks::continuation handle_syscall(windows_emulator& win_emu, const uint32_t syscall_id, const std::string_view syscall_name) { @@ -66,5 +157,6 @@ void register_analysis_callbacks(windows_emulator& win_emu) auto& cb = win_emu.callbacks; cb.on_syscall = make_callback(win_emu, handle_syscall); + cb.on_instruction = make_callback(win_emu, handle_instruction); cb.on_suspicious_activity = make_callback(win_emu, handle_suspicious_activity); } diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index d52896e3..af53e96d 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -418,89 +418,7 @@ void windows_emulator::on_instruction_execution(const uint64_t address) this->process.previous_ip = this->process.current_ip; this->process.current_ip = this->emu().read_instruction_pointer(); - const auto is_main_exe = this->mod_manager.executable->is_within(address); - const auto is_previous_main_exe = this->mod_manager.executable->is_within(this->process.previous_ip); - - const auto binary = utils::make_lazy([&] { - if (is_main_exe) - { - return this->mod_manager.executable; - } - - return this->mod_manager.find_by_address(address); // - }); - - const auto previous_binary = utils::make_lazy([&] { - if (is_previous_main_exe) - { - return this->mod_manager.executable; - } - - return this->mod_manager.find_by_address(this->process.previous_ip); // - }); - - const auto is_in_interesting_module = [&] { - if (this->modules_.empty()) - { - return false; - } - - return (binary && this->modules_.contains(binary->name)) || - (previous_binary && this->modules_.contains(previous_binary->name)); - }; - - const auto is_interesting_call = is_previous_main_exe // - || is_main_exe // - || is_in_interesting_module(); - - if (this->silent_until_main_ && is_main_exe) - { - this->silent_until_main_ = false; - this->log.disable_output(false); - } - - if (!this->verbose && !this->verbose_calls && !is_interesting_call) - { - return; - } - - if (binary) - { - const auto export_entry = binary->address_names.find(address); - if (export_entry != binary->address_names.end() && !this->ignored_functions_.contains(export_entry->second)) - { - const auto rsp = this->emu().read_stack_pointer(); - - uint64_t return_address{}; - this->emu().try_read_memory(rsp, &return_address, sizeof(return_address)); - - const auto* mod_name = this->mod_manager.find_name(return_address); - - 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); - } - else if (address == binary->entry_point) - { - log.print(is_interesting_call ? color::yellow : color::gray, "Executing entry point: %s (0x%" PRIx64 ")\n", - binary->name.c_str(), address); - } - } - - if (!this->verbose) - { - return; - } - - auto& emu = this->emu(); - - // TODO: Remove or cleanup - log.print(color::gray, - "Inst: %16" PRIx64 " - RAX: %16" PRIx64 " - RBX: %16" PRIx64 " - RCX: %16" PRIx64 " - RDX: %16" PRIx64 - " - R8: %16" PRIx64 " - R9: %16" PRIx64 " - RDI: %16" PRIx64 " - RSI: %16" PRIx64 " - %s\n", - address, emu.reg(x86_register::rax), emu.reg(x86_register::rbx), emu.reg(x86_register::rcx), - emu.reg(x86_register::rdx), emu.reg(x86_register::r8), emu.reg(x86_register::r9), - emu.reg(x86_register::rdi), emu.reg(x86_register::rsi), binary ? binary->name.c_str() : ""); + this->callbacks.on_instruction(address); } void windows_emulator::setup_hooks() diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index 3f8eba65..391bfe59 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -20,6 +20,7 @@ struct emulator_callbacks : module_manager::callbacks, process_context::callback utils::optional_function on_syscall{}; utils::optional_function on_stdout{}; utils::optional_function on_suspicious_activity{}; + utils::optional_function on_instruction{}; }; struct application_settings @@ -176,25 +177,28 @@ class windows_emulator } } - bool verbose{false}; - bool verbose_calls{false}; - bool buffer_stdout{false}; + // TODO: Remove bool fuzzing{false}; void yield_thread(bool alertable = false); bool perform_thread_switch(); bool activate_thread(uint32_t id); + // TODO: Move to analyzer + bool verbose{false}; + bool verbose_calls{false}; + bool buffer_stdout{false}; + bool silent_until_main_{false}; + std::set> modules_{}; + std::set> ignored_functions_{}; + private: bool switch_thread_{false}; bool use_relative_time_{false}; - bool silent_until_main_{false}; std::atomic_bool should_stop{false}; std::unordered_map port_mappings_{}; - std::set> modules_{}; - std::set> ignored_functions_{}; std::vector process_snapshot_{}; // std::optional process_snapshot_{}; From f2e0e91630bab620572aab64baed9e27e1c977aa Mon Sep 17 00:00:00 2001 From: momo5502 Date: Thu, 5 Jun 2025 18:59:27 +0200 Subject: [PATCH 11/20] Isolate more analysis into analyzer --- src/analyzer/analysis.cpp | 116 ++++++++++-------- src/analyzer/analysis.hpp | 22 +++- src/analyzer/main.cpp | 51 ++++---- src/analyzer/object_watching.hpp | 8 +- .../emulation_test_utils.hpp | 11 +- src/windows-emulator-test/time_test.cpp | 1 - src/windows-emulator/syscalls/file.cpp | 33 ++--- src/windows-emulator/windows_emulator.cpp | 8 +- src/windows-emulator/windows_emulator.hpp | 17 +-- 9 files changed, 130 insertions(+), 137 deletions(-) diff --git a/src/analyzer/analysis.cpp b/src/analyzer/analysis.cpp index 583876ed..1fc652a3 100644 --- a/src/analyzer/analysis.cpp +++ b/src/analyzer/analysis.cpp @@ -7,24 +7,34 @@ namespace { template - std::function make_callback(windows_emulator& win_emu, - Return (*callback)(windows_emulator&, Args...)) + std::function make_callback(analysis_context& c, Return (*callback)(analysis_context&, Args...)) { - return [&win_emu, callback](Args... args) { - return callback(win_emu, std::forward(args)...); // + return [&c, callback](Args... args) { + return callback(c, std::forward(args)...); // }; } - void handle_suspicious_activity(windows_emulator& win_emu, const std::string_view details) + template + std::function make_callback(analysis_context& c, + Return (*callback)(const analysis_context&, Args...)) { - const auto rip = win_emu.emu().read_instruction_pointer(); - win_emu.log.print(color::pink, "Suspicious: %.*s (0x%" PRIx64 ")\n", STR_VIEW_VA(details), rip); + return [&c, callback](Args... args) { + return callback(c, std::forward(args)...); // + }; } - void handle_instruction(windows_emulator& win_emu, const uint64_t address) + 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 (0x%" PRIx64 ")\n", STR_VIEW_VA(details), rip); + } + + void handle_instruction(analysis_context& c, const uint64_t address) + { + auto& win_emu = *c.win_emu; + 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(win_emu.process.previous_ip); + 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) @@ -45,75 +55,56 @@ namespace }); const auto is_in_interesting_module = [&] { - if (win_emu.modules_.empty()) + if (c.settings->modules.empty()) { return false; } - return (binary && win_emu.modules_.contains(binary->name)) || - (previous_binary && win_emu.modules_.contains(previous_binary->name)); + 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 (win_emu.silent_until_main_ && is_main_exe) + if (!c.has_reached_main && c.settings->concise_logging && !c.settings->silent && is_main_exe) { - win_emu.silent_until_main_ = false; + c.has_reached_main = true; win_emu.log.disable_output(false); } - if (!win_emu.verbose && !win_emu.verbose_calls && !is_interesting_call) + if ((!c.settings->verbose_logging && !is_interesting_call) || !binary) { return; } - if (binary) + 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 export_entry = binary->address_names.find(address); - if (export_entry != binary->address_names.end() && - !win_emu.ignored_functions_.contains(export_entry->second)) - { - const auto rsp = win_emu.emu().read_stack_pointer(); + 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)); + 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); + 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); - } - 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); - } + 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 (!win_emu.verbose) + else if (address == binary->entry_point) { - return; + win_emu.log.print(is_interesting_call ? color::yellow : color::gray, + "Executing entry point: %s (0x%" PRIx64 ")\n", binary->name.c_str(), address); } - - auto& emu = win_emu.emu(); - - // TODO: Remove or cleanup - win_emu.log.print( - color::gray, - "Inst: %16" PRIx64 " - RAX: %16" PRIx64 " - RBX: %16" PRIx64 " - RCX: %16" PRIx64 " - RDX: %16" PRIx64 - " - R8: %16" PRIx64 " - R9: %16" PRIx64 " - RDI: %16" PRIx64 " - RSI: %16" PRIx64 " - %s\n", - address, emu.reg(x86_register::rax), emu.reg(x86_register::rbx), emu.reg(x86_register::rcx), - emu.reg(x86_register::rdx), emu.reg(x86_register::r8), emu.reg(x86_register::r9), - emu.reg(x86_register::rdi), emu.reg(x86_register::rsi), binary ? binary->name.c_str() : ""); } - emulator_callbacks::continuation handle_syscall(windows_emulator& win_emu, const uint32_t syscall_id, + 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(); @@ -150,13 +141,30 @@ namespace 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(data.size()), data.data(), data.ends_with("\n") ? "" : "\n"); + } + } } -void register_analysis_callbacks(windows_emulator& win_emu) +void register_analysis_callbacks(analysis_context& c) { - auto& cb = win_emu.callbacks; + auto& cb = c.win_emu->callbacks; - cb.on_syscall = make_callback(win_emu, handle_syscall); - cb.on_instruction = make_callback(win_emu, handle_instruction); - cb.on_suspicious_activity = make_callback(win_emu, handle_suspicious_activity); + cb.on_stdout = make_callback(c, handle_stdout); + cb.on_syscall = make_callback(c, handle_syscall); + cb.on_instruction = make_callback(c, handle_instruction); + cb.on_suspicious_activity = make_callback(c, handle_suspicious_activity); } diff --git a/src/analyzer/analysis.hpp b/src/analyzer/analysis.hpp index e5cc8050..17e1b40d 100644 --- a/src/analyzer/analysis.hpp +++ b/src/analyzer/analysis.hpp @@ -2,4 +2,24 @@ class windows_emulator; -void register_analysis_callbacks(windows_emulator& win_emu); +struct analysis_settings +{ + bool concise_logging{false}; + bool verbose_logging{false}; + bool silent{false}; + bool buffer_stdout{false}; + + std::set> modules{}; + std::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); diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index 9a6a23f1..9636578a 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -6,6 +6,7 @@ #include "object_watching.hpp" #include "snapshot.hpp" +#include "analysis.hpp" #ifdef OS_EMSCRIPTEN #include @@ -17,18 +18,12 @@ 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}; std::filesystem::path dump{}; std::string registry_path{"./registry"}; std::string emulation_root{}; - std::set> modules{}; - std::set> ignored_functions{}; std::unordered_map path_mappings{}; }; @@ -59,23 +54,23 @@ namespace } void watch_system_objects(windows_emulator& win_emu, const std::set>& modules, - const bool cache_logging) + const bool verbose) { (void)win_emu; (void)modules; - (void)cache_logging; + (void)verbose; #if !defined(__GNUC__) || defined(__clang__) - watch_object(win_emu, modules, *win_emu.current_thread().teb, cache_logging); - watch_object(win_emu, modules, win_emu.process.peb, cache_logging); + 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{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) @@ -86,7 +81,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 @@ -206,12 +201,7 @@ 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, }; } @@ -253,8 +243,16 @@ namespace bool run(const analysis_options& options, const std::span args) { - const auto win_emu = setup_emulator(options, 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(); + + // TODO: Move to analysis #ifdef OS_EMSCRIPTEN win_emu->callbacks.on_thread_switch = [&] { debugger::event_context c{.win_emu = *win_emu}; @@ -265,16 +263,9 @@ namespace 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.verbose_logging); - win_emu->buffer_stdout = options.buffer_stdout; + watch_system_objects(*win_emu, options.modules, options.verbose_logging); - 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); const auto& exe = *win_emu->mod_manager.executable; diff --git a/src/analyzer/object_watching.hpp b/src/analyzer/object_watching.hpp index dd77c4b3..3ba81167 100644 --- a/src/analyzer/object_watching.hpp +++ b/src/analyzer/object_watching.hpp @@ -6,23 +6,23 @@ template emulator_hook* watch_object(windows_emulator& emu, const std::set>& modules, - emulator_object object, const bool cache_logging = false) + emulator_object object, const auto verbose) { const reflect_type_info info{}; return emu.emu().hook_memory_read( object.value(), static_cast(object.size()), - [i = std::move(info), object, &emu, cache_logging, modules](const uint64_t address, const void*, size_t) { + [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 logged_addresses{}; if (is_main_access && !logged_addresses.insert(address).second) diff --git a/src/windows-emulator-test/emulation_test_utils.hpp b/src/windows-emulator-test/emulation_test_utils.hpp index d7d2cdd2..8285600f 100644 --- a/src/windows-emulator-test/emulation_test_utils.hpp +++ b/src/windows-emulator-test/emulation_test_utils.hpp @@ -68,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(); @@ -93,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(); @@ -116,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, }; @@ -126,7 +128,6 @@ namespace test inline windows_emulator create_empty_emulator() { emulator_settings settings{ - .disable_logging = true, .use_relative_time = true, }; diff --git a/src/windows-emulator-test/time_test.cpp b/src/windows-emulator-test/time_test.cpp index d82b2520..b71f111a 100644 --- a/src/windows-emulator-test/time_test.cpp +++ b/src/windows-emulator-test/time_test.cpp @@ -12,7 +12,6 @@ namespace test }; const emulator_settings settings{ - .disable_logging = true, .use_relative_time = false, }; diff --git a/src/windows-emulator/syscalls/file.cpp b/src/windows-emulator/syscalls/file.cpp index e79bd581..a44fdff7 100644 --- a/src/windows-emulator/syscalls/file.cpp +++ b/src/windows-emulator/syscalls/file.cpp @@ -141,19 +141,19 @@ namespace syscalls switch (fs_information_class) { case FileFsDeviceInformation: - return handle_query( - c.emu, fs_information, length, io_status_block, [&](FILE_FS_DEVICE_INFORMATION& info) { - if (file_handle == STDOUT_HANDLE && !c.win_emu.buffer_stdout) - { - info.DeviceType = FILE_DEVICE_CONSOLE; - info.Characteristics = 0x20000; - } - else - { - info.DeviceType = FILE_DEVICE_DISK; - info.Characteristics = 0x20020; - } - }); + return handle_query(c.emu, fs_information, length, io_status_block, + [&](FILE_FS_DEVICE_INFORMATION& info) { + if (file_handle == STDOUT_HANDLE) + { + info.DeviceType = FILE_DEVICE_CONSOLE; + info.Characteristics = 0x20000; + } + else + { + info.DeviceType = FILE_DEVICE_DISK; + info.Characteristics = 0x20020; + } + }); case FileFsSizeInformation: return handle_query(c.emu, fs_information, length, io_status_block, @@ -689,13 +689,6 @@ namespace syscalls c.win_emu.callbacks.on_stdout(temp_buffer); - if (!temp_buffer.ends_with("\n")) - { - temp_buffer.push_back('\n'); - } - - c.win_emu.log.info("%.*s", static_cast(temp_buffer.size()), temp_buffer.data()); - return STATUS_SUCCESS; } diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index af53e96d..7961c5f3 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -304,8 +304,7 @@ windows_emulator::windows_emulator(std::unique_ptr emu, const e registry(emulation_root.empty() ? settings.registry_directory : emulation_root / "registry"), mod_manager(memory, file_sys, this->callbacks), process(*this->emu_, memory, *this->clock_, this->callbacks), - modules_(settings.modules), - ignored_functions_(settings.ignored_functions) + use_relative_time_(settings.use_relative_time) { #ifndef OS_WINDOWS if (this->emulation_root.empty()) @@ -324,11 +323,6 @@ windows_emulator::windows_emulator(std::unique_ptr emu, const e this->map_port(mapping.first, mapping.second); } - this->verbose_calls = settings.verbose_calls; - this->silent_until_main_ = settings.silent_until_main && !settings.disable_logging; - this->use_relative_time_ = settings.use_relative_time; - this->log.disable_output(settings.disable_logging || this->silent_until_main_); - this->setup_hooks(); } diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index 391bfe59..1131efe9 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -32,18 +32,13 @@ struct application_settings struct emulator_settings { + bool use_relative_time{false}; + std::filesystem::path emulation_root{}; std::filesystem::path registry_directory{"./registry"}; - bool verbose_calls{false}; - bool disable_logging{false}; - bool silent_until_main{false}; - bool use_relative_time{false}; - std::unordered_map port_mappings{}; std::unordered_map path_mappings{}; - std::set> modules{}; - std::set> ignored_functions{}; }; struct emulator_interfaces @@ -184,14 +179,6 @@ class windows_emulator bool perform_thread_switch(); bool activate_thread(uint32_t id); - // TODO: Move to analyzer - bool verbose{false}; - bool verbose_calls{false}; - bool buffer_stdout{false}; - bool silent_until_main_{false}; - std::set> modules_{}; - std::set> ignored_functions_{}; - private: bool switch_thread_{false}; bool use_relative_time_{false}; From 3cfb29c5c190b36d0382f7434f3808a7f398c0b6 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Thu, 5 Jun 2025 19:06:39 +0200 Subject: [PATCH 12/20] Print buffered stdout --- src/analyzer/main.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index 9636578a..8aeb172b 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -104,8 +104,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(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++; @@ -153,12 +164,14 @@ namespace } catch (const std::exception& e) { + 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 (...) { + do_post_emulation_work(c); win_emu.log.error("Emulation failed at: 0x%" PRIx64 "\n", win_emu.emu().read_instruction_pointer()); throw; } @@ -166,6 +179,7 @@ namespace const auto exit_status = win_emu.process.exit_status; if (!exit_status.has_value()) { + do_post_emulation_work(c); win_emu.log.error("Emulation terminated without status!\n"); return false; } @@ -174,6 +188,7 @@ namespace 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); @@ -325,7 +340,7 @@ namespace 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 bundle_arguments(const int argc, char** argv) From f4282f44d745c0411d6dc9969d4246b8d6225398 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Thu, 5 Jun 2025 19:20:45 +0200 Subject: [PATCH 13/20] Fix compilation --- src/analyzer/analysis.cpp | 2 ++ src/analyzer/analysis.hpp | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/analyzer/analysis.cpp b/src/analyzer/analysis.cpp index 1fc652a3..7c00b0ad 100644 --- a/src/analyzer/analysis.cpp +++ b/src/analyzer/analysis.cpp @@ -1,3 +1,5 @@ +#include "std_include.hpp" + #include "analysis.hpp" #include "windows_emulator.hpp" #include "utils/lazy_object.hpp" diff --git a/src/analyzer/analysis.hpp b/src/analyzer/analysis.hpp index 17e1b40d..072e8503 100644 --- a/src/analyzer/analysis.hpp +++ b/src/analyzer/analysis.hpp @@ -1,5 +1,8 @@ #pragma once +#include +#include + class windows_emulator; struct analysis_settings From 9b8ea27a294de47af103f94386f768f966e087ed Mon Sep 17 00:00:00 2001 From: momo5502 Date: Thu, 5 Jun 2025 20:53:38 +0200 Subject: [PATCH 14/20] Delay process setup --- src/analyzer/main.cpp | 7 ++- src/windows-emulator/windows_emulator.cpp | 52 +++++++++++++++++------ src/windows-emulator/windows_emulator.hpp | 20 ++++++++- src/windows-emulator/windows_path.hpp | 3 +- 4 files changed, 63 insertions(+), 19 deletions(-) diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index 8aeb172b..3e8de362 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -56,6 +56,8 @@ namespace void watch_system_objects(windows_emulator& win_emu, const std::set>& modules, const bool verbose) { + win_emu.setup_process_if_necessary(); + (void)win_emu; (void)modules; (void)verbose; @@ -264,7 +266,6 @@ namespace const auto win_emu = setup_emulator(options, args); win_emu->log.disable_output(options.concise_logging || options.silent); - context.win_emu = win_emu.get(); // TODO: Move to analysis @@ -277,10 +278,8 @@ namespace 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.verbose_logging); - register_analysis_callbacks(context); + watch_system_objects(*win_emu, options.modules, options.verbose_logging); const auto& exe = *win_emu->mod_manager.executable; diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 7961c5f3..79cc0664 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -289,7 +289,7 @@ windows_emulator::windows_emulator(std::unique_ptr emu, applica : windows_emulator(std::move(emu), settings, std::move(callbacks), std::move(interfaces)) { fixup_application_settings(app_settings); - this->setup_process(app_settings); + this->application_settings_ = std::move(app_settings); } windows_emulator::windows_emulator(std::unique_ptr emu, const emulator_settings& settings, @@ -328,6 +328,19 @@ windows_emulator::windows_emulator(std::unique_ptr emu, const e windows_emulator::~windows_emulator() = default; +void windows_emulator::setup_process_if_necessary() +{ + if (!this->application_settings_) + { + return; + } + + auto app_settings = std::move(*this->application_settings_); + this->application_settings_ = {}; + + this->setup_process(app_settings); +} + void windows_emulator::setup_process(const application_settings& app_settings) { const auto& emu = this->emu(); @@ -531,6 +544,7 @@ void windows_emulator::setup_hooks() void windows_emulator::start(size_t count) { this->should_stop = false; + this->setup_process_if_necessary(); const auto use_count = count > 0; const auto start_instructions = this->executed_instructions_; @@ -602,9 +616,11 @@ void windows_emulator::register_factories(utils::buffer_deserializer& buffer) void windows_emulator::serialize(utils::buffer_serializer& buffer) const { + buffer.write_optional(this->application_settings_); buffer.write(this->executed_instructions_); buffer.write(this->switch_thread_); buffer.write(this->use_relative_time_); + this->emu().serialize_state(buffer, false); this->memory.serialize_memory_state(buffer, false); this->mod_manager.serialize(buffer); @@ -616,6 +632,7 @@ void windows_emulator::deserialize(utils::buffer_deserializer& buffer) { this->register_factories(buffer); + buffer.read_optional(this->application_settings_); buffer.read(this->executed_instructions_); buffer.read(this->switch_thread_); @@ -638,13 +655,18 @@ void windows_emulator::deserialize(utils::buffer_deserializer& buffer) void windows_emulator::save_snapshot() { - utils::buffer_serializer serializer{}; - this->emu().serialize_state(serializer, true); - this->memory.serialize_memory_state(serializer, true); - this->mod_manager.serialize(serializer); - this->process.serialize(serializer); + utils::buffer_serializer buffer{}; - this->process_snapshot_ = serializer.move_buffer(); + buffer.write_optional(this->application_settings_); + buffer.write(this->executed_instructions_); + buffer.write(this->switch_thread_); + + this->emu().serialize_state(buffer, true); + this->memory.serialize_memory_state(buffer, true); + this->mod_manager.serialize(buffer); + this->process.serialize(buffer); + + this->process_snapshot_ = buffer.move_buffer(); // TODO: Make process copyable // this->process_snapshot_ = this->process; @@ -658,13 +680,17 @@ void windows_emulator::restore_snapshot() return; } - utils::buffer_deserializer deserializer{this->process_snapshot_}; + utils::buffer_deserializer buffer{this->process_snapshot_}; - this->register_factories(deserializer); + this->register_factories(buffer); - this->emu().deserialize_state(deserializer, true); - this->memory.deserialize_memory_state(deserializer, true); - this->mod_manager.deserialize(deserializer); - this->process.deserialize(deserializer); + buffer.read_optional(this->application_settings_); + buffer.read(this->executed_instructions_); + buffer.read(this->switch_thread_); + + this->emu().deserialize_state(buffer, true); + this->memory.deserialize_memory_state(buffer, true); + this->mod_manager.deserialize(buffer); + this->process.deserialize(buffer); // this->process = *this->process_snapshot_; } diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index 1131efe9..50141ad7 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -28,10 +28,25 @@ struct application_settings windows_path application{}; windows_path working_directory{}; std::vector arguments{}; + + void serialize(utils::buffer_serializer& buffer) const + { + buffer.write(this->application); + buffer.write(this->working_directory); + buffer.write_vector(this->arguments); + } + + void deserialize(utils::buffer_deserializer& buffer) + { + buffer.read(this->application); + buffer.read(this->working_directory); + buffer.read_vector(this->arguments); + } }; struct emulator_settings { + bool disable_logging{false}; bool use_relative_time{false}; std::filesystem::path emulation_root{}; @@ -50,6 +65,7 @@ struct emulator_interfaces class windows_emulator { uint64_t executed_instructions_{0}; + std::optional application_settings_{}; std::unique_ptr emu_{}; std::unique_ptr clock_{}; @@ -124,6 +140,8 @@ class windows_emulator return this->executed_instructions_; } + void setup_process_if_necessary(); + void start(size_t count = 0); void stop(); @@ -181,7 +199,7 @@ class windows_emulator private: bool switch_thread_{false}; - bool use_relative_time_{false}; + bool use_relative_time_{false}; // TODO: Get rid of that std::atomic_bool should_stop{false}; std::unordered_map port_mappings_{}; diff --git a/src/windows-emulator/windows_path.hpp b/src/windows-emulator/windows_path.hpp index e2b10ba0..82315d91 100644 --- a/src/windows-emulator/windows_path.hpp +++ b/src/windows-emulator/windows_path.hpp @@ -75,7 +75,8 @@ class windows_path template requires(!std::is_same_v, windows_path> && - !std::is_same_v, std::filesystem::path>) + !std::is_same_v, std::filesystem::path> && + !std::is_same_v, utils::buffer_deserializer>) windows_path(T&& path_like) : windows_path(std::filesystem::path(std::forward(path_like))) { From 24939583c44bacc8843a5e726b5f459bbfed81ba Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 6 Jun 2025 16:59:45 +0200 Subject: [PATCH 15/20] Fix serialization --- src/windows-emulator/module/module_manager.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/windows-emulator/module/module_manager.cpp b/src/windows-emulator/module/module_manager.cpp index 6e20e135..bd5428ed 100644 --- a/src/windows-emulator/module/module_manager.cpp +++ b/src/windows-emulator/module/module_manager.cpp @@ -145,9 +145,9 @@ void module_manager::serialize(utils::buffer_serializer& buffer) const { buffer.write_map(this->modules_); - buffer.write(this->executable->image_base); - buffer.write(this->ntdll->image_base); - buffer.write(this->win32u->image_base); + buffer.write(this->executable ? this->executable->image_base : 0); + buffer.write(this->ntdll ? this->ntdll->image_base : 0); + buffer.write(this->win32u ? this->win32u->image_base : 0); } void module_manager::deserialize(utils::buffer_deserializer& buffer) @@ -158,9 +158,9 @@ void module_manager::deserialize(utils::buffer_deserializer& buffer) const auto ntdll_base = buffer.read(); const auto win32u_base = buffer.read(); - this->executable = this->find_by_address(executable_base); - this->ntdll = this->find_by_address(ntdll_base); - this->win32u = this->find_by_address(win32u_base); + this->executable = executable_base ? this->find_by_address(executable_base) : nullptr; + this->ntdll = ntdll_base ? this->find_by_address(ntdll_base) : nullptr; + this->win32u = win32u_base ? this->find_by_address(win32u_base) : nullptr; } bool module_manager::unmap(const uint64_t address, const logger& logger) From bc77faec3d6e6e2381f73d1fd16229a368b4e89e Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 6 Jun 2025 19:27:50 +0200 Subject: [PATCH 16/20] Move more logging into callbacks --- src/analyzer/analysis.cpp | 38 ++++++++++++++++++- src/analyzer/main.cpp | 12 ------ src/windows-emulator/devices/afd_endpoint.cpp | 3 -- .../devices/security_support_provider.cpp | 2 - src/windows-emulator/io_device.cpp | 29 ++++++++++++++ src/windows-emulator/io_device.hpp | 29 ++------------ src/windows-emulator/process_context.hpp | 2 +- .../registry/registry_manager.hpp | 5 +++ src/windows-emulator/syscalls/event.cpp | 4 +- src/windows-emulator/syscalls/file.cpp | 23 ++++------- src/windows-emulator/syscalls/memory.cpp | 2 - src/windows-emulator/syscalls/mutant.cpp | 4 +- src/windows-emulator/syscalls/port.cpp | 2 +- src/windows-emulator/syscalls/registry.cpp | 10 +++-- src/windows-emulator/syscalls/section.cpp | 6 +-- src/windows-emulator/syscalls/timer.cpp | 2 +- src/windows-emulator/windows_emulator.cpp | 6 +-- src/windows-emulator/windows_emulator.hpp | 5 +++ 18 files changed, 106 insertions(+), 78 deletions(-) diff --git a/src/analyzer/analysis.cpp b/src/analyzer/analysis.cpp index 7c00b0ad..0e2b8f33 100644 --- a/src/analyzer/analysis.cpp +++ b/src/analyzer/analysis.cpp @@ -2,7 +2,11 @@ #include "analysis.hpp" #include "windows_emulator.hpp" -#include "utils/lazy_object.hpp" +#include + +#ifdef OS_EMSCRIPTEN +#include +#endif #define STR_VIEW_VA(str) static_cast((str).size()), (str).data() @@ -31,6 +35,34 @@ namespace c.win_emu->log.print(color::pink, "Suspicious: %.*s (0x%" PRIx64 ")\n", STR_VIEW_VA(details), rip); } + 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_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(code)); + } + + 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); + +#ifdef OS_EMSCRIPTEN + debugger::event_context ec{.win_emu = *c.win_emu}; + debugger::handle_events(ec); +#endif + } void handle_instruction(analysis_context& c, const uint64_t address) { auto& win_emu = *c.win_emu; @@ -167,6 +199,10 @@ void register_analysis_callbacks(analysis_context& c) 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_instruction = make_callback(c, handle_instruction); + cb.on_thread_switch = make_callback(c, handle_thread_switch); + 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); } diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index 3e8de362..88b3b08d 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -8,10 +8,6 @@ #include "snapshot.hpp" #include "analysis.hpp" -#ifdef OS_EMSCRIPTEN -#include -#endif - #include #include @@ -268,14 +264,6 @@ namespace win_emu->log.disable_output(options.concise_logging || options.silent); context.win_emu = win_emu.get(); - // TODO: Move to analysis -#ifdef OS_EMSCRIPTEN - win_emu->callbacks.on_thread_switch = [&] { - debugger::event_context c{.win_emu = *win_emu}; - debugger::handle_events(c); // - }; -#endif - win_emu->log.log("Using emulator: %s\n", win_emu->emu().get_name().c_str()); register_analysis_callbacks(context); diff --git a/src/windows-emulator/devices/afd_endpoint.cpp b/src/windows-emulator/devices/afd_endpoint.cpp index d067eb1a..72adae17 100644 --- a/src/windows-emulator/devices/afd_endpoint.cpp +++ b/src/windows-emulator/devices/afd_endpoint.cpp @@ -525,9 +525,6 @@ namespace const auto request = _AFD_REQUEST(c.io_control_code); - win_emu.log.print(color::dark_gray, "--> AFD IOCTL: 0x%X (%u)\n", static_cast(c.io_control_code), - static_cast(request)); - switch (request) { case AFD_BIND: diff --git a/src/windows-emulator/devices/security_support_provider.cpp b/src/windows-emulator/devices/security_support_provider.cpp index f3de5cb5..58b2a9f6 100644 --- a/src/windows-emulator/devices/security_support_provider.cpp +++ b/src/windows-emulator/devices/security_support_provider.cpp @@ -26,8 +26,6 @@ namespace NTSTATUS io_control(windows_emulator& win_emu, const io_device_context& c) override { - win_emu.log.print(color::dark_gray, "--> KSEC IOCTL: 0x%X\n", static_cast(c.io_control_code)); - if (c.io_control_code != 0x390400) { return STATUS_NOT_SUPPORTED; diff --git a/src/windows-emulator/io_device.cpp b/src/windows-emulator/io_device.cpp index b1803824..cefc0a15 100644 --- a/src/windows-emulator/io_device.cpp +++ b/src/windows-emulator/io_device.cpp @@ -1,5 +1,6 @@ #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" @@ -49,3 +50,31 @@ std::unique_ptr create_device(const std::u16string_view device) 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); +} diff --git a/src/windows-emulator/io_device.hpp b/src/windows-emulator/io_device.hpp index 970b5053..c55ca84d 100644 --- a/src/windows-emulator/io_device.hpp +++ b/src/windows-emulator/io_device.hpp @@ -146,32 +146,11 @@ 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 requires(std::is_base_of_v || std::is_same_v) diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index 6fad5508..2a718257 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -34,7 +34,7 @@ struct process_context { utils::optional_function on_create_thread{}; utils::optional_function on_thread_terminated{}; - utils::optional_function on_thread_switch{}; + utils::optional_function on_thread_switch{}; }; struct atom_entry diff --git a/src/windows-emulator/registry/registry_manager.hpp b/src/windows-emulator/registry/registry_manager.hpp index 809713aa..81e0cb79 100644 --- a/src/windows-emulator/registry/registry_manager.hpp +++ b/src/windows-emulator/registry/registry_manager.hpp @@ -40,6 +40,11 @@ struct registry_key : ref_counted_object buffer.read(this->hive); buffer.read(this->path); } + + std::u16string to_string() const + { + return this->hive.get().u16string() + u"\\" + this->path.get().u16string(); + } }; struct registry_value diff --git a/src/windows-emulator/syscalls/event.cpp b/src/windows-emulator/syscalls/event.cpp index b53c0910..40b7a8a7 100644 --- a/src/windows-emulator/syscalls/event.cpp +++ b/src/windows-emulator/syscalls/event.cpp @@ -63,7 +63,7 @@ namespace syscalls if (attributes.ObjectName) { name = read_unicode_string(c.emu, attributes.ObjectName); - c.win_emu.log.print(color::dark_gray, "--> Event name: %s\n", u16_to_u8(name).c_str()); + c.win_emu.callbacks.on_generic_access("Opening event", name); } } @@ -100,7 +100,7 @@ namespace syscalls { const auto attributes = object_attributes.read(); const auto name = read_unicode_string(c.emu, attributes.ObjectName); - c.win_emu.log.print(color::dark_gray, "--> Event name: %s\n", u16_to_u8(name).c_str()); + c.win_emu.callbacks.on_generic_access("Opening event", name); if (name == u"\\KernelObjects\\SystemErrorPortReady") { diff --git a/src/windows-emulator/syscalls/file.cpp b/src/windows-emulator/syscalls/file.cpp index a44fdff7..0d9abfd2 100644 --- a/src/windows-emulator/syscalls/file.cpp +++ b/src/windows-emulator/syscalls/file.cpp @@ -236,16 +236,7 @@ namespace syscalls if (!f->enumeration_state || query_flags & SL_RESTART_SCAN) { const auto mask = file_mask ? read_unicode_string(c.emu, file_mask) : u""; - - if (!mask.empty()) - { - c.win_emu.log.print(color::dark_gray, "--> Enumerating directory: %s (Mask: \"%s\")\n", - u16_to_u8(f->name).c_str(), u16_to_u8(mask).c_str()); - } - else - { - c.win_emu.log.print(color::dark_gray, "--> Enumerating directory: %s\n", u16_to_u8(f->name).c_str()); - } + c.win_emu.callbacks.on_generic_access("Enumerating directory", f->name); f->enumeration_state.emplace(file_enumeration_state{}); f->enumeration_state->files = scan_directory(c.win_emu.file_sys, f->name, mask); @@ -565,7 +556,7 @@ namespace syscalls const auto attributes = object_attributes.read(); auto filename = read_unicode_string(c.emu, attributes.ObjectName); - c.win_emu.log.print(color::dark_gray, "--> Query file info: %s\n", u16_to_u8(filename).c_str()); // + c.win_emu.callbacks.on_generic_access("Query file info", filename); const auto ret = [&](const NTSTATUS status) { block.Status = status; @@ -798,7 +789,7 @@ namespace syscalls auto filename = read_unicode_string(c.emu, attributes.ObjectName); auto printer = utils::finally([&] { - c.win_emu.log.print(color::dark_gray, "--> Opening file: %s\n", u16_to_u8(filename).c_str()); // + c.win_emu.callbacks.on_generic_access("Opening file", filename); // }); const auto io_device_name = get_io_device_name(filename); @@ -849,7 +840,7 @@ namespace syscalls if (is_directory || create_options & FILE_DIRECTORY_FILE) { - c.win_emu.log.print(color::dark_gray, "--> Opening folder: %s\n", u16_to_u8(f.name).c_str()); + c.win_emu.callbacks.on_generic_access("Opening folder", f.name); if (create_disposition & FILE_CREATE) { @@ -871,7 +862,7 @@ namespace syscalls return STATUS_SUCCESS; } - c.win_emu.log.print(color::dark_gray, "--> Opening file: %s\n", u16_to_u8(f.name).c_str()); + c.win_emu.callbacks.on_generic_access("Opening file", f.name); std::u16string mode = map_mode(desired_access, create_disposition); @@ -924,7 +915,7 @@ namespace syscalls filename = root->name + (has_separator ? u"" : u"\\") + filename; } - c.win_emu.log.print(color::dark_gray, "--> Querying file attributes: %s\n", u16_to_u8(filename).c_str()); + c.win_emu.callbacks.on_generic_access("Querying file attributes", filename); const auto local_filename = c.win_emu.file_sys.translate(filename).u8string(); @@ -965,7 +956,7 @@ namespace syscalls const auto filename = read_unicode_string( c.emu, emulator_object>>{c.emu, attributes.ObjectName}); - c.win_emu.log.print(color::dark_gray, "--> Querying file attributes: %s\n", u16_to_u8(filename).c_str()); + c.win_emu.callbacks.on_generic_access("Querying file attributes", filename); const auto local_filename = c.win_emu.file_sys.translate(filename).u8string(); diff --git a/src/windows-emulator/syscalls/memory.cpp b/src/windows-emulator/syscalls/memory.cpp index be5af2fb..5df89aef 100644 --- a/src/windows-emulator/syscalls/memory.cpp +++ b/src/windows-emulator/syscalls/memory.cpp @@ -192,8 +192,6 @@ namespace syscalls if (!potential_base) { - c.win_emu.log.print(color::dark_gray, "--> Not allocated\n"); - return STATUS_MEMORY_NOT_ALLOCATED; } diff --git a/src/windows-emulator/syscalls/mutant.cpp b/src/windows-emulator/syscalls/mutant.cpp index 1adb21d2..306c3474 100644 --- a/src/windows-emulator/syscalls/mutant.cpp +++ b/src/windows-emulator/syscalls/mutant.cpp @@ -44,7 +44,7 @@ namespace syscalls { name = read_unicode_string( c.emu, emulator_object>>{c.emu, attributes.ObjectName}); - c.win_emu.log.print(color::dark_gray, "--> Mutant name: %s\n", u16_to_u8(name).c_str()); + c.win_emu.callbacks.on_generic_access("Opening mutant", name); } } @@ -78,7 +78,7 @@ namespace syscalls if (attributes.ObjectName) { name = read_unicode_string(c.emu, attributes.ObjectName); - c.win_emu.log.print(color::dark_gray, "--> Mutant name: %s\n", u16_to_u8(name).c_str()); + c.win_emu.callbacks.on_generic_access("Opening mutant", name); } } diff --git a/src/windows-emulator/syscalls/port.cpp b/src/windows-emulator/syscalls/port.cpp index 50072d50..46fa7a03 100644 --- a/src/windows-emulator/syscalls/port.cpp +++ b/src/windows-emulator/syscalls/port.cpp @@ -14,7 +14,7 @@ namespace syscalls const emulator_object connection_info_length) { auto port_name = read_unicode_string(c.emu, server_port_name); - c.win_emu.log.print(color::dark_gray, "NtConnectPort: %s\n", u16_to_u8(port_name).c_str()); + c.win_emu.callbacks.on_generic_access("Connecting port", port_name); port p{}; p.name = std::move(port_name); diff --git a/src/windows-emulator/syscalls/registry.cpp b/src/windows-emulator/syscalls/registry.cpp index 96017a30..7ec1c034 100644 --- a/src/windows-emulator/syscalls/registry.cpp +++ b/src/windows-emulator/syscalls/registry.cpp @@ -25,7 +25,7 @@ namespace syscalls key = full_path.u16string(); } - c.win_emu.log.print(color::dark_gray, "--> Registry key: %s\n", u16_to_u8(key).c_str()); + c.win_emu.callbacks.on_generic_access("Registry key", key); auto entry = c.win_emu.registry.get_key({key}); if (!entry.has_value()) @@ -129,8 +129,12 @@ namespace syscalls } const auto query_name = read_unicode_string(c.emu, value_name); - c.win_emu.log.print(color::dark_gray, "--> Query value key: %s (%s\\%s)\n", u16_to_u8(query_name).c_str(), - key->hive.get().string().c_str(), key->path.get().string().c_str()); + + if (c.win_emu.callbacks.on_generic_access) + { + // TODO: Find a better way to log this + c.win_emu.callbacks.on_generic_access("Querying value key", query_name + u" (" + key->to_string() + u")"); + } const auto value = c.win_emu.registry.get_value(*key, u16_to_u8(query_name)); if (!value) diff --git a/src/windows-emulator/syscalls/section.cpp b/src/windows-emulator/syscalls/section.cpp index 27f3cb2e..47ba9633 100644 --- a/src/windows-emulator/syscalls/section.cpp +++ b/src/windows-emulator/syscalls/section.cpp @@ -20,7 +20,7 @@ namespace syscalls const auto* file = c.proc.files.get(file_handle); if (file) { - c.win_emu.log.print(color::dark_gray, "--> Section for file %s\n", u16_to_u8(file->name).c_str()); + c.win_emu.callbacks.on_generic_access("Section for file", file->name); s.file_name = file->name; } @@ -30,7 +30,7 @@ namespace syscalls if (attributes.ObjectName) { auto name = read_unicode_string(c.emu, attributes.ObjectName); - c.win_emu.log.print(color::dark_gray, "--> Section with name %s\n", u16_to_u8(name).c_str()); + c.win_emu.callbacks.on_generic_access("Section with name", name); s.name = std::move(name); } } @@ -60,7 +60,7 @@ namespace syscalls const auto attributes = object_attributes.read(); auto filename = read_unicode_string(c.emu, attributes.ObjectName); - c.win_emu.log.print(color::dark_gray, "--> Opening section: %s\n", u16_to_u8(filename).c_str()); + c.win_emu.callbacks.on_generic_access("Opening section", filename); if (filename == u"\\Windows\\SharedSection") { diff --git a/src/windows-emulator/syscalls/timer.cpp b/src/windows-emulator/syscalls/timer.cpp index a154e27f..cc320fa1 100644 --- a/src/windows-emulator/syscalls/timer.cpp +++ b/src/windows-emulator/syscalls/timer.cpp @@ -43,7 +43,7 @@ namespace syscalls if (attributes.ObjectName) { name = read_unicode_string(c.emu, attributes.ObjectName); - c.win_emu.log.print(color::dark_gray, "--> Timer name: %s\n", u16_to_u8(name).c_str()); + c.win_emu.callbacks.on_generic_access("Opening timer", name); } } diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 79cc0664..9c43e9ce 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -107,7 +107,7 @@ namespace return; } - win_emu.log.print(color::dark_gray, "Dispatching APC...\n"); + win_emu.callbacks.on_generic_activity("APC Dispatch"); const auto next_apx = apcs.front(); apcs.erase(apcs.begin()); @@ -165,8 +165,7 @@ namespace { if (active_thread) { - win_emu.log.print(color::dark_gray, "Performing thread switch: %X -> %X\n", active_thread->id, - thread.id); + win_emu.callbacks.on_thread_switch(*active_thread, thread); active_thread->save(emu); } @@ -184,7 +183,6 @@ namespace } thread.apc_alertable = false; - win_emu.callbacks.on_thread_switch(); return true; } diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index 50141ad7..d7373d40 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -13,14 +13,19 @@ #include "module/module_manager.hpp" #include "network/socket_factory.hpp" +struct io_device; + struct emulator_callbacks : module_manager::callbacks, process_context::callbacks { using continuation = instruction_hook_continuation; utils::optional_function on_syscall{}; utils::optional_function on_stdout{}; + utils::optional_function on_generic_access{}; + utils::optional_function on_generic_activity{}; utils::optional_function on_suspicious_activity{}; utils::optional_function on_instruction{}; + utils::optional_function on_ioctrl{}; }; struct application_settings From 9372e274530e30ff40c7ba71858a1152c9539f11 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 7 Jun 2025 07:11:27 +0200 Subject: [PATCH 17/20] Fix module logging --- src/analyzer/analysis.cpp | 13 +++++++++++++ src/windows-emulator/module/module_manager.cpp | 6 +----- src/windows-emulator/module/module_manager.hpp | 2 +- src/windows-emulator/syscalls/section.cpp | 2 +- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/analyzer/analysis.cpp b/src/analyzer/analysis.cpp index 0e2b8f33..eb9ecc06 100644 --- a/src/analyzer/analysis.cpp +++ b/src/analyzer/analysis.cpp @@ -63,6 +63,17 @@ namespace debugger::handle_events(ec); #endif } + + 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 handle_instruction(analysis_context& c, const uint64_t address) { auto& win_emu = *c.win_emu; @@ -200,6 +211,8 @@ void register_analysis_callbacks(analysis_context& c) 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_module_load = make_callback(c, handle_module_load); + cb.on_module_unload = make_callback(c, handle_module_unload); cb.on_instruction = make_callback(c, handle_instruction); cb.on_thread_switch = make_callback(c, handle_thread_switch); cb.on_generic_access = make_callback(c, handle_generic_access); diff --git a/src/windows-emulator/module/module_manager.cpp b/src/windows-emulator/module/module_manager.cpp index bd5428ed..00eb45ab 100644 --- a/src/windows-emulator/module/module_manager.cpp +++ b/src/windows-emulator/module/module_manager.cpp @@ -122,8 +122,6 @@ mapped_module* module_manager::map_local_module(const std::filesystem::path& fil auto mod = map_module_from_file(*this->memory_, std::move(local_file)); mod.is_static = is_static; - logger.log("Mapped %s at 0x%" PRIx64 "\n", mod.path.generic_string().c_str(), mod.image_base); - const auto image_base = mod.image_base; const auto entry = this->modules_.try_emplace(image_base, std::move(mod)); this->callbacks_->on_module_load(entry.first->second); @@ -163,7 +161,7 @@ void module_manager::deserialize(utils::buffer_deserializer& buffer) this->win32u = win32u_base ? this->find_by_address(win32u_base) : nullptr; } -bool module_manager::unmap(const uint64_t address, const logger& logger) +bool module_manager::unmap(const uint64_t address) { const auto mod = this->modules_.find(address); if (mod == this->modules_.end()) @@ -176,8 +174,6 @@ bool module_manager::unmap(const uint64_t address, const logger& logger) return true; } - logger.log("Unmapping %s (0x%" PRIx64 ")\n", mod->second.path.generic_string().c_str(), mod->second.image_base); - this->callbacks_->on_module_unload(mod->second); unmap_module(*this->memory_, mod->second); this->modules_.erase(mod); diff --git a/src/windows-emulator/module/module_manager.hpp b/src/windows-emulator/module/module_manager.hpp index 7a789d9d..4123f733 100644 --- a/src/windows-emulator/module/module_manager.hpp +++ b/src/windows-emulator/module/module_manager.hpp @@ -51,7 +51,7 @@ class module_manager void serialize(utils::buffer_serializer& buffer) const; void deserialize(utils::buffer_deserializer& buffer); - bool unmap(uint64_t address, const logger& logger); + bool unmap(uint64_t address); const module_map& modules() const { return modules_; diff --git a/src/windows-emulator/syscalls/section.cpp b/src/windows-emulator/syscalls/section.cpp index 47ba9633..753db3c6 100644 --- a/src/windows-emulator/syscalls/section.cpp +++ b/src/windows-emulator/syscalls/section.cpp @@ -287,7 +287,7 @@ namespace syscalls const auto* mod = c.win_emu.mod_manager.find_by_address(base_address); if (mod != nullptr) { - if (c.win_emu.mod_manager.unmap(base_address, c.win_emu.log)) + if (c.win_emu.mod_manager.unmap(base_address)) { return STATUS_SUCCESS; } From 802e295bccc51748247a9cd5c6c8e5abf7c4eec5 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 7 Jun 2025 07:29:30 +0200 Subject: [PATCH 18/20] Adapt more printing --- src/analyzer/analysis.cpp | 31 ++++++++++++++++++++++- src/windows-emulator/process_context.cpp | 2 +- src/windows-emulator/process_context.hpp | 3 ++- src/windows-emulator/syscalls/memory.cpp | 13 +++------- src/windows-emulator/syscalls/thread.cpp | 3 +-- src/windows-emulator/windows_emulator.hpp | 19 +++++++++----- 6 files changed, 49 insertions(+), 22 deletions(-) diff --git a/src/analyzer/analysis.cpp b/src/analyzer/analysis.cpp index eb9ecc06..cfcf1fcf 100644 --- a/src/analyzer/analysis.cpp +++ b/src/analyzer/analysis.cpp @@ -45,6 +45,23 @@ namespace 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_ioctrl(const analysis_context& c, const io_device&, const std::u16string_view device_name, const ULONG code) { @@ -52,6 +69,11 @@ namespace static_cast(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) { @@ -211,10 +233,17 @@ void register_analysis_callbacks(analysis_context& c) 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_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_instruction = make_callback(c, handle_instruction); + 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); diff --git a/src/windows-emulator/process_context.cpp b/src/windows-emulator/process_context.cpp index 936ec373..c479ce8e 100644 --- a/src/windows-emulator/process_context.cpp +++ b/src/windows-emulator/process_context.cpp @@ -361,7 +361,7 @@ handle process_context::create_thread(memory_manager& memory, const uint64_t sta { emulator_thread t{memory, *this, start_address, argument, stack_size, suspended, ++this->spawned_thread_count}; auto [h, thr] = this->threads.store_and_get(std::move(t)); - this->callbacks_->on_create_thread(h, *thr); + this->callbacks_->on_thread_create(h, *thr); return h; } diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index 2a718257..04e604d6 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -32,9 +32,10 @@ struct process_context { struct callbacks { - utils::optional_function on_create_thread{}; + utils::optional_function on_thread_create{}; utils::optional_function on_thread_terminated{}; utils::optional_function on_thread_switch{}; + utils::optional_function on_thread_set_name{}; }; struct atom_entry diff --git a/src/windows-emulator/syscalls/memory.cpp b/src/windows-emulator/syscalls/memory.cpp index 5df89aef..efd5e2f2 100644 --- a/src/windows-emulator/syscalls/memory.cpp +++ b/src/windows-emulator/syscalls/memory.cpp @@ -146,9 +146,7 @@ namespace syscalls const auto requested_protection = map_nt_to_emulator_protection(protection); - c.win_emu.log.print(color::dark_gray, "--> Changing protection at 0x%" PRIx64 "-0x%" PRIx64 " to %s\n", - aligned_start, aligned_start + aligned_length, - get_permission_string(requested_protection).c_str()); + c.win_emu.callbacks.on_memory_protect(aligned_start, aligned_length, requested_protection); memory_permission old_protection_value{}; @@ -208,16 +206,11 @@ namespace syscalls if (commit && !reserve && c.win_emu.memory.commit_memory(potential_base, static_cast(allocation_bytes), protection)) { - c.win_emu.log.print(is_executable(protection) ? color::gray : color::dark_gray, - "--> Committed 0x%" PRIx64 " - 0x%" PRIx64 " (%s)\n", potential_base, - potential_base + allocation_bytes, get_permission_string(protection).c_str()); - + c.win_emu.callbacks.on_memory_allocate(potential_base, allocation_bytes, protection, true); return STATUS_SUCCESS; } - c.win_emu.log.print(is_executable(protection) ? color::gray : color::dark_gray, - "--> Allocated 0x%" PRIx64 " - 0x%" PRIx64 " (%s)\n", potential_base, - potential_base + allocation_bytes, get_permission_string(protection).c_str()); + c.win_emu.callbacks.on_memory_allocate(potential_base, allocation_bytes, protection, false); return c.win_emu.memory.allocate_memory(potential_base, static_cast(allocation_bytes), protection, !commit) diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index 322220e3..0210ec3e 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -41,8 +41,7 @@ namespace syscalls const auto i = info.read(); thread->name = read_unicode_string(c.emu, i.ThreadName); - c.win_emu.log.print(color::blue, "Setting thread (%d) name: %s\n", thread->id, - u16_to_u8(thread->name).c_str()); + c.win_emu.callbacks.on_thread_set_name(*thread); return STATUS_SUCCESS; } diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index d7373d40..12ab5a54 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -15,17 +15,22 @@ struct io_device; +#define opt_func utils::optional_function + struct emulator_callbacks : module_manager::callbacks, process_context::callbacks { using continuation = instruction_hook_continuation; - utils::optional_function on_syscall{}; - utils::optional_function on_stdout{}; - utils::optional_function on_generic_access{}; - utils::optional_function on_generic_activity{}; - utils::optional_function on_suspicious_activity{}; - utils::optional_function on_instruction{}; - utils::optional_function on_ioctrl{}; + opt_func on_memory_protect{}; + opt_func on_memory_allocate{}; + + opt_func on_syscall{}; + opt_func on_stdout{}; + opt_func on_generic_access{}; + opt_func on_generic_activity{}; + opt_func on_suspicious_activity{}; + opt_func on_instruction{}; + opt_func on_ioctrl{}; }; struct application_settings From da4a4f90c96cad96981b1da66b69829dafd6af80 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 7 Jun 2025 07:52:42 +0200 Subject: [PATCH 19/20] Cleanup exception callbacks --- src/analyzer/analysis.cpp | 3 +- src/emulator/arch_emulator.hpp | 2 +- src/fuzzer/main.cpp | 13 +++++-- src/windows-emulator/process_context.cpp | 2 -- src/windows-emulator/process_context.hpp | 1 - src/windows-emulator/syscalls/exception.cpp | 8 ++--- src/windows-emulator/syscalls/thread.cpp | 2 +- src/windows-emulator/windows_emulator.cpp | 38 ++++++--------------- src/windows-emulator/windows_emulator.hpp | 5 ++- 9 files changed, 32 insertions(+), 42 deletions(-) diff --git a/src/analyzer/analysis.cpp b/src/analyzer/analysis.cpp index cfcf1fcf..005a914b 100644 --- a/src/analyzer/analysis.cpp +++ b/src/analyzer/analysis.cpp @@ -32,7 +32,8 @@ namespace 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 (0x%" PRIx64 ")\n", STR_VIEW_VA(details), rip); + 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) diff --git a/src/emulator/arch_emulator.hpp b/src/emulator/arch_emulator.hpp index ef218374..f20283a1 100644 --- a/src/emulator/arch_emulator.hpp +++ b/src/emulator/arch_emulator.hpp @@ -45,7 +45,7 @@ struct arm_emulator : arch_emulator enum class x86_hookable_instructions { - invalid, + invalid, // TODO: Get rid of that syscall, cpuid, rdtsc, diff --git a/src/fuzzer/main.cpp b/src/fuzzer/main.cpp index 7af4ba2d..78033fc7 100644 --- a/src/fuzzer/main.cpp +++ b/src/fuzzer/main.cpp @@ -28,12 +28,22 @@ namespace 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!"); } @@ -68,7 +78,6 @@ namespace fuzzer_executer(const std::span 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) { diff --git a/src/windows-emulator/process_context.cpp b/src/windows-emulator/process_context.cpp index c479ce8e..8a07e0ba 100644 --- a/src/windows-emulator/process_context.cpp +++ b/src/windows-emulator/process_context.cpp @@ -251,7 +251,6 @@ void process_context::serialize(utils::buffer_serializer& buffer) const buffer.write(this->shared_section_size); buffer.write(this->dbwin_buffer); buffer.write(this->dbwin_buffer_size); - buffer.write_optional(this->exception_rip); buffer.write_optional(this->exit_status); buffer.write(this->base_allocator); buffer.write(this->peb); @@ -291,7 +290,6 @@ void process_context::deserialize(utils::buffer_deserializer& buffer) buffer.read(this->shared_section_size); buffer.read(this->dbwin_buffer); buffer.read(this->dbwin_buffer_size); - buffer.read_optional(this->exception_rip); buffer.read_optional(this->exit_status); buffer.read(this->base_allocator); buffer.read(this->peb); diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index 04e604d6..b25c3e8b 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -93,7 +93,6 @@ struct process_context uint64_t dbwin_buffer{0}; uint64_t dbwin_buffer_size{0}; - std::optional exception_rip{}; std::optional exit_status{}; emulator_allocator base_allocator; diff --git a/src/windows-emulator/syscalls/exception.cpp b/src/windows-emulator/syscalls/exception.cpp index 42362bc3..4705dbf2 100644 --- a/src/windows-emulator/syscalls/exception.cpp +++ b/src/windows-emulator/syscalls/exception.cpp @@ -18,7 +18,7 @@ namespace syscalls } c.proc.exit_status = error_status; - c.proc.exception_rip = c.emu.read_instruction_pointer(); + c.win_emu.callbacks.on_exception(); c.emu.stop(); return STATUS_SUCCESS; @@ -27,7 +27,7 @@ namespace syscalls NTSTATUS handle_NtRaiseException( const syscall_context& c, const emulator_object>> /*exception_record*/, - const emulator_object thread_context, const BOOLEAN handle_exception) + const emulator_object /*thread_context*/, const BOOLEAN handle_exception) { if (handle_exception) { @@ -36,9 +36,9 @@ namespace syscalls return STATUS_NOT_SUPPORTED; } - c.proc.exception_rip = thread_context.read().Rip; + c.win_emu.callbacks.on_exception(); c.emu.stop(); return STATUS_SUCCESS; } -} \ No newline at end of file +} diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index 0210ec3e..d11b40c1 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -328,7 +328,7 @@ namespace syscalls { if (lock.value()) { - c.win_emu.log.warn("NtAlertThreadByThreadIdEx with lock not supported yet!"); + c.win_emu.log.warn("NtAlertThreadByThreadIdEx with lock not supported yet!\n"); // c.emu.stop(); // return STATUS_NOT_SUPPORTED; } diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 9c43e9ce..34e9394d 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -452,17 +452,13 @@ void windows_emulator::setup_hooks() return instruction_hook_continuation::skip_instruction; }); + // TODO: Unicorn needs this - This should be handled in the backend this->emu().hook_instruction(x86_hookable_instructions::invalid, [&] { - const auto ip = this->emu().read_instruction_pointer(); - - this->log.print(color::gray, "Invalid instruction at: 0x%" PRIx64 " (via 0x%" PRIx64 ")\n", ip, - this->process.previous_ip); - - return instruction_hook_continuation::skip_instruction; + return instruction_hook_continuation::skip_instruction; // }); this->emu().hook_interrupt([&](const int interrupt) { - const auto rip = this->emu().read_instruction_pointer(); + this->callbacks.on_exception(); const auto eflags = this->emu().reg(x86_register::eflags); switch (interrupt) @@ -473,13 +469,10 @@ void windows_emulator::setup_hooks() case 1: if ((eflags & 0x100) != 0) { - this->callbacks.on_suspicious_activity("Singlestep (Trap Flag)"); this->emu().reg(x86_register::eflags, eflags & ~0x100); } - else - { - this->callbacks.on_suspicious_activity("Singlestep"); - } + + this->callbacks.on_suspicious_activity("Singlestep"); dispatch_single_step(this->emu(), this->process); return; case 3: @@ -487,6 +480,7 @@ void windows_emulator::setup_hooks() dispatch_breakpoint(this->emu(), this->process); return; case 6: + this->callbacks.on_suspicious_activity("Illegal instruction"); dispatch_illegal_instruction_violation(this->emu(), this->process); return; case 45: @@ -494,16 +488,13 @@ void windows_emulator::setup_hooks() dispatch_breakpoint(this->emu(), this->process); return; default: + if (this->callbacks.on_generic_activity) + { + this->callbacks.on_generic_activity("Interrupt " + std::to_string(interrupt)); + } + break; } - - this->log.print(color::gray, "Interrupt: %i 0x%" PRIx64 "\n", interrupt, rip); - - if (this->fuzzing || true) // TODO: Fix - { - this->process.exception_rip = rip; - this->emu().stop(); - } }); this->emu().hook_memory_violation([&](const uint64_t address, const size_t size, const memory_operation operation, @@ -523,13 +514,6 @@ void windows_emulator::setup_hooks() size, permission.c_str(), ip, name); } - if (this->fuzzing) - { - this->process.exception_rip = ip; - this->emu().stop(); - return memory_violation_continuation::stop; - } - dispatch_access_violation(this->emu(), this->process, address, operation); return memory_violation_continuation::resume; }); diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index 12ab5a54..1ca10dcd 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -21,6 +21,8 @@ struct emulator_callbacks : module_manager::callbacks, process_context::callback { using continuation = instruction_hook_continuation; + opt_func on_exception{}; + opt_func on_memory_protect{}; opt_func on_memory_allocate{}; @@ -200,9 +202,6 @@ class windows_emulator } } - // TODO: Remove - bool fuzzing{false}; - void yield_thread(bool alertable = false); bool perform_thread_switch(); bool activate_thread(uint32_t id); From 05c5f0a085395ba6b88eae4973e1f547da3e09f4 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 7 Jun 2025 08:00:27 +0200 Subject: [PATCH 20/20] Final cleanup --- src/analyzer/analysis.cpp | 22 ++++++++++++++++++++++ src/windows-emulator/syscalls/thread.cpp | 13 +++++++------ src/windows-emulator/windows_emulator.cpp | 16 +--------------- src/windows-emulator/windows_emulator.hpp | 1 + 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/analyzer/analysis.cpp b/src/analyzer/analysis.cpp index 005a914b..aaf4d9d9 100644 --- a/src/analyzer/analysis.cpp +++ b/src/analyzer/analysis.cpp @@ -63,6 +63,27 @@ namespace 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) { @@ -236,6 +257,7 @@ void register_analysis_callbacks(analysis_context& c) 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); diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index d11b40c1..39098480 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -324,14 +324,15 @@ namespace syscalls } NTSTATUS handle_NtAlertThreadByThreadIdEx(const syscall_context& c, const uint64_t thread_id, - const emulator_object>> lock) + const emulator_object>> /*lock*/) { - if (lock.value()) + // TODO: Support lock + /*if (lock.value()) { - c.win_emu.log.warn("NtAlertThreadByThreadIdEx with lock not supported yet!\n"); - // c.emu.stop(); - // return STATUS_NOT_SUPPORTED; - } + c.win_emu.log.warn("NtAlertThreadByThreadIdEx with lock not supported yet!\n"); + // c.emu.stop(); + // return STATUS_NOT_SUPPORTED; + }*/ return handle_NtAlertThreadByThreadId(c, thread_id); } diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 34e9394d..9433e08a 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -499,21 +499,7 @@ void windows_emulator::setup_hooks() this->emu().hook_memory_violation([&](const uint64_t address, const size_t size, const memory_operation operation, const memory_violation_type type) { - const auto permission = get_permission_string(operation); - const auto ip = this->emu().read_instruction_pointer(); - const char* name = this->mod_manager.find_name(ip); - - if (type == memory_violation_type::protection) - { - this->log.print(color::gray, "Protection violation: 0x%" PRIx64 " (%zX) - %s at 0x%" PRIx64 " (%s)\n", - address, size, permission.c_str(), ip, name); - } - else if (type == memory_violation_type::unmapped) - { - this->log.print(color::gray, "Mapping violation: 0x%" PRIx64 " (%zX) - %s at 0x%" PRIx64 " (%s)\n", address, - size, permission.c_str(), ip, name); - } - + this->callbacks.on_memory_violate(address, size, operation, type); dispatch_access_violation(this->emu(), this->process, address, operation); return memory_violation_continuation::resume; }); diff --git a/src/windows-emulator/windows_emulator.hpp b/src/windows-emulator/windows_emulator.hpp index 1ca10dcd..c6732a30 100644 --- a/src/windows-emulator/windows_emulator.hpp +++ b/src/windows-emulator/windows_emulator.hpp @@ -25,6 +25,7 @@ struct emulator_callbacks : module_manager::callbacks, process_context::callback opt_func on_memory_protect{}; opt_func on_memory_allocate{}; + opt_func on_memory_violate{}; opt_func on_syscall{}; opt_func on_stdout{};