mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-20 20:23:57 +00:00
Isolate more analysis into analyzer
This commit is contained in:
@@ -7,24 +7,34 @@
|
||||
namespace
|
||||
{
|
||||
template <typename Return, typename... Args>
|
||||
std::function<Return(Args...)> make_callback(windows_emulator& win_emu,
|
||||
Return (*callback)(windows_emulator&, Args...))
|
||||
std::function<Return(Args...)> make_callback(analysis_context& c, Return (*callback)(analysis_context&, Args...))
|
||||
{
|
||||
return [&win_emu, callback](Args... args) {
|
||||
return callback(win_emu, std::forward<Args>(args)...); //
|
||||
return [&c, callback](Args... args) {
|
||||
return callback(c, std::forward<Args>(args)...); //
|
||||
};
|
||||
}
|
||||
|
||||
void handle_suspicious_activity(windows_emulator& win_emu, const std::string_view details)
|
||||
template <typename Return, typename... Args>
|
||||
std::function<Return(Args...)> 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>(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() : "<N/A>");
|
||||
}
|
||||
|
||||
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<int>(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);
|
||||
}
|
||||
|
||||
@@ -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<std::string, std::less<>> modules{};
|
||||
std::set<std::string, std::less<>> 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);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "object_watching.hpp"
|
||||
#include "snapshot.hpp"
|
||||
#include "analysis.hpp"
|
||||
|
||||
#ifdef OS_EMSCRIPTEN
|
||||
#include <event_handler.hpp>
|
||||
@@ -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<std::string, std::less<>> modules{};
|
||||
std::set<std::string, std::less<>> ignored_functions{};
|
||||
std::unordered_map<windows_path, std::filesystem::path> path_mappings{};
|
||||
};
|
||||
|
||||
@@ -59,23 +54,23 @@ namespace
|
||||
}
|
||||
|
||||
void watch_system_objects(windows_emulator& win_emu, const std::set<std::string, std::less<>>& 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<KUSER_SHARED_DATA64>{win_emu.emu(), kusd_mmio::address()},
|
||||
cache_logging);
|
||||
verbose);
|
||||
|
||||
auto* params_hook = watch_object(win_emu, modules, win_emu.process.process_params, cache_logging);
|
||||
auto* params_hook = watch_object(win_emu, modules, win_emu.process.process_params, verbose);
|
||||
|
||||
win_emu.emu().hook_memory_write(
|
||||
win_emu.process.peb.value() + offsetof(PEB64, ProcessParameters), 0x8,
|
||||
[&win_emu, cache_logging, params_hook, modules](const uint64_t address, const void*, size_t) mutable {
|
||||
[&win_emu, verbose, params_hook, modules](const uint64_t address, const void*, size_t) mutable {
|
||||
const auto target_address = win_emu.process.peb.value() + offsetof(PEB64, ProcessParameters);
|
||||
|
||||
if (address == target_address)
|
||||
@@ -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<const std::string_view> 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;
|
||||
|
||||
|
||||
@@ -6,23 +6,23 @@
|
||||
|
||||
template <typename T>
|
||||
emulator_hook* watch_object(windows_emulator& emu, const std::set<std::string, std::less<>>& modules,
|
||||
emulator_object<T> object, const bool cache_logging = false)
|
||||
emulator_object<T> object, const auto verbose)
|
||||
{
|
||||
const reflect_type_info<T> info{};
|
||||
|
||||
return emu.emu().hook_memory_read(
|
||||
object.value(), static_cast<size_t>(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<uint64_t> logged_addresses{};
|
||||
if (is_main_access && !logged_addresses.insert(address).second)
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ namespace test
|
||||
};
|
||||
|
||||
const emulator_settings settings{
|
||||
.disable_logging = true,
|
||||
.use_relative_time = false,
|
||||
};
|
||||
|
||||
|
||||
@@ -141,19 +141,19 @@ namespace syscalls
|
||||
switch (fs_information_class)
|
||||
{
|
||||
case FileFsDeviceInformation:
|
||||
return handle_query<FILE_FS_DEVICE_INFORMATION>(
|
||||
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<FILE_FS_DEVICE_INFORMATION>(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<FILE_FS_SIZE_INFORMATION>(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<int>(temp_buffer.size()), temp_buffer.data());
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -304,8 +304,7 @@ windows_emulator::windows_emulator(std::unique_ptr<x86_64_emulator> 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<x86_64_emulator> 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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<uint16_t, uint16_t> port_mappings{};
|
||||
std::unordered_map<windows_path, std::filesystem::path> path_mappings{};
|
||||
std::set<std::string, std::less<>> modules{};
|
||||
std::set<std::string, std::less<>> 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<std::string, std::less<>> modules_{};
|
||||
std::set<std::string, std::less<>> ignored_functions_{};
|
||||
|
||||
private:
|
||||
bool switch_thread_{false};
|
||||
bool use_relative_time_{false};
|
||||
|
||||
Reference in New Issue
Block a user