From f2e0e91630bab620572aab64baed9e27e1c977aa Mon Sep 17 00:00:00 2001 From: momo5502 Date: Thu, 5 Jun 2025 18:59:27 +0200 Subject: [PATCH] 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};