mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-11 16:46:16 +00:00
Handle concise logging via options (#44)
This commit is contained in:
@@ -3,23 +3,25 @@
|
||||
#include <windows_emulator.hpp>
|
||||
#include <debugging/win_x64_gdb_stub_handler.hpp>
|
||||
|
||||
#define CONCISE_EMULATOR_OUTPUT
|
||||
|
||||
#include "object_watching.hpp"
|
||||
|
||||
bool use_gdb = false;
|
||||
|
||||
namespace
|
||||
{
|
||||
void watch_system_objects(windows_emulator& win_emu)
|
||||
struct analysis_options
|
||||
{
|
||||
watch_object(win_emu, *win_emu.current_thread().teb);
|
||||
watch_object(win_emu, win_emu.process().peb);
|
||||
watch_object(win_emu, emulator_object<KUSER_SHARED_DATA>{win_emu.emu(), kusd_mmio::address()});
|
||||
auto* params_hook = watch_object(win_emu, win_emu.process().process_params);
|
||||
bool use_gdb{false};
|
||||
bool concise_logging{false};
|
||||
};
|
||||
|
||||
void watch_system_objects(windows_emulator& win_emu, const bool cache_logging)
|
||||
{
|
||||
watch_object(win_emu, *win_emu.current_thread().teb, cache_logging);
|
||||
watch_object(win_emu, win_emu.process().peb, cache_logging);
|
||||
watch_object(win_emu, emulator_object<KUSER_SHARED_DATA>{win_emu.emu(), kusd_mmio::address()}, cache_logging);
|
||||
auto* params_hook = watch_object(win_emu, win_emu.process().process_params, cache_logging);
|
||||
|
||||
win_emu.emu().hook_memory_write(win_emu.process().peb.value() + offsetof(PEB, ProcessParameters), 0x8,
|
||||
[&](const uint64_t address, size_t, const uint64_t value)
|
||||
[&, cache_logging](const uint64_t address, size_t, const uint64_t value)
|
||||
{
|
||||
const auto target_address = win_emu.process().peb.value() + offsetof(
|
||||
PEB, ProcessParameters);
|
||||
@@ -31,16 +33,16 @@ namespace
|
||||
};
|
||||
|
||||
win_emu.emu().delete_hook(params_hook);
|
||||
params_hook = watch_object(win_emu, obj);
|
||||
params_hook = watch_object(win_emu, obj, cache_logging);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void run_emulation(windows_emulator& win_emu)
|
||||
void run_emulation(windows_emulator& win_emu, const analysis_options& options)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (use_gdb)
|
||||
if (options.use_gdb)
|
||||
{
|
||||
const auto* address = "127.0.0.1:28960";
|
||||
win_emu.logger.print(color::pink, "Waiting for GDB connection on %s...\n", address);
|
||||
@@ -76,44 +78,44 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::wstring> parse_arguments(char* argv[], const size_t argc)
|
||||
std::vector<std::wstring> parse_arguments(const std::span<const std::string_view> args)
|
||||
{
|
||||
std::vector<std::wstring> args{};
|
||||
args.reserve(argc - 1);
|
||||
std::vector<std::wstring> wide_args{};
|
||||
wide_args.reserve(args.size() - 1);
|
||||
|
||||
for (size_t i = 1; i < argc; ++i)
|
||||
for (size_t i = 1; i < args.size(); ++i)
|
||||
{
|
||||
std::string_view arg(argv[i]);
|
||||
args.emplace_back(arg.begin(), arg.end());
|
||||
const auto& arg = args[i];
|
||||
wide_args.emplace_back(arg.begin(), arg.end());
|
||||
}
|
||||
|
||||
return args;
|
||||
return wide_args;
|
||||
}
|
||||
|
||||
void run(char* argv[], const size_t argc)
|
||||
void run(const analysis_options& options, const std::span<const std::string_view> args)
|
||||
{
|
||||
if (argc < 1)
|
||||
if (args.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
emulator_settings settings{
|
||||
.application = argv[0],
|
||||
.arguments = parse_arguments(argv, argc),
|
||||
#ifdef CONCISE_EMULATOR_OUTPUT
|
||||
.silent_until_main = true,
|
||||
#endif
|
||||
.application = args[0],
|
||||
.arguments = parse_arguments(args),
|
||||
.silent_until_main = options.concise_logging,
|
||||
};
|
||||
|
||||
windows_emulator win_emu{std::move(settings)};
|
||||
|
||||
(void)&watch_system_objects;
|
||||
watch_system_objects(win_emu);
|
||||
watch_system_objects(win_emu, options.concise_logging);
|
||||
win_emu.buffer_stdout = true;
|
||||
//win_emu.verbose_calls = true;
|
||||
|
||||
const auto& exe = *win_emu.process().executable;
|
||||
|
||||
const auto concise_logging = options.concise_logging;
|
||||
|
||||
for (const auto& section : exe.sections)
|
||||
{
|
||||
if ((section.region.permissions & memory_permission::exec) != memory_permission::exec)
|
||||
@@ -121,7 +123,7 @@ namespace
|
||||
continue;
|
||||
}
|
||||
|
||||
auto read_handler = [&, section](const uint64_t address, size_t, uint64_t)
|
||||
auto read_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t)
|
||||
{
|
||||
const auto rip = win_emu.emu().read_instruction_pointer();
|
||||
if (win_emu.process().module_manager.find_by_address(rip) != win_emu.process().executable)
|
||||
@@ -129,11 +131,12 @@ namespace
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONCISE_EMULATOR_OUTPUT
|
||||
static uint64_t count{0};
|
||||
++count;
|
||||
if (count > 100 && count % 10000 != 0) return;
|
||||
#endif
|
||||
if (concise_logging)
|
||||
{
|
||||
static uint64_t count{0};
|
||||
++count;
|
||||
if (count > 100 && count % 10000 != 0) return;
|
||||
}
|
||||
|
||||
win_emu.logger.print(
|
||||
color::green,
|
||||
@@ -141,7 +144,7 @@ namespace
|
||||
section.name.c_str(), address, rip);
|
||||
};
|
||||
|
||||
const auto write_handler = [&, section](const uint64_t address, size_t, uint64_t)
|
||||
const auto write_handler = [&, section, concise_logging](const uint64_t address, size_t, uint64_t)
|
||||
{
|
||||
const auto rip = win_emu.emu().read_instruction_pointer();
|
||||
if (win_emu.process().module_manager.find_by_address(rip) != win_emu.process().executable)
|
||||
@@ -149,11 +152,12 @@ namespace
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONCISE_EMULATOR_OUTPUT
|
||||
static uint64_t count{0};
|
||||
++count;
|
||||
if (count > 100 && count % 10000 != 0) return;
|
||||
#endif
|
||||
if (concise_logging)
|
||||
{
|
||||
static uint64_t count{0};
|
||||
++count;
|
||||
if (count > 100 && count % 10000 != 0) return;
|
||||
}
|
||||
|
||||
win_emu.logger.print(
|
||||
color::blue,
|
||||
@@ -165,32 +169,67 @@ namespace
|
||||
win_emu.emu().hook_memory_write(section.region.start, section.region.length, std::move(write_handler));
|
||||
}
|
||||
|
||||
run_emulation(win_emu);
|
||||
run_emulation(win_emu, options);
|
||||
}
|
||||
|
||||
std::vector<std::string_view> bundle_arguments(const int argc, char** argv)
|
||||
{
|
||||
std::vector<std::string_view> args{};
|
||||
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
args.push_back(argv[i]);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
analysis_options parse_options(std::vector<std::string_view>& args)
|
||||
{
|
||||
analysis_options options{};
|
||||
|
||||
while (!args.empty())
|
||||
{
|
||||
auto arg_it = args.begin();
|
||||
const auto& arg = *arg_it;
|
||||
|
||||
if (arg == "-d")
|
||||
{
|
||||
options.use_gdb = true;
|
||||
}
|
||||
else if (arg == "-c")
|
||||
{
|
||||
options.concise_logging = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
args.erase(arg_it);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
int main(const int argc, char** argv)
|
||||
{
|
||||
if (argc <= 1)
|
||||
{
|
||||
puts("Application not specified!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
//setvbuf(stdout, nullptr, _IOFBF, 0x10000);
|
||||
if (argc > 2 && argv[1] == "-d"sv)
|
||||
{
|
||||
use_gdb = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto args = bundle_arguments(argc, argv);
|
||||
const auto options = parse_options(args);
|
||||
|
||||
if (args.empty())
|
||||
{
|
||||
throw std::runtime_error("Application not specified!");
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
const auto offset = use_gdb ? 2 : 1;
|
||||
run(argv + offset, static_cast<size_t>(argc - offset));
|
||||
run(options, args);
|
||||
}
|
||||
while (use_gdb);
|
||||
while (options.use_gdb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
|
||||
#include "reflect_type_info.hpp"
|
||||
|
||||
//#define CACHE_OBJECT_ADDRESSES
|
||||
|
||||
template <typename T>
|
||||
emulator_hook* watch_object(windows_emulator& emu, emulator_object<T> object)
|
||||
emulator_hook* watch_object(windows_emulator& emu, emulator_object<T> object, const bool cache_logging = false)
|
||||
{
|
||||
const reflect_type_info<T> info{};
|
||||
|
||||
return emu.emu().hook_memory_read(object.value(), object.size(),
|
||||
[i = std::move(info), object, &emu](
|
||||
[i = std::move(info), object, &emu, cache_logging](
|
||||
const uint64_t address, size_t, uint64_t)
|
||||
{
|
||||
const auto rip = emu.emu().read_instruction_pointer();
|
||||
@@ -22,13 +20,14 @@ emulator_hook* watch_object(windows_emulator& emu, emulator_object<T> object)
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(CACHE_OBJECT_ADDRESSES) || defined(CONCISE_EMULATOR_OUTPUT)
|
||||
static std::unordered_set<uint64_t> logged_addresses{};
|
||||
if (is_main_access && !logged_addresses.insert(address).second)
|
||||
if (cache_logging)
|
||||
{
|
||||
return;
|
||||
static std::unordered_set<uint64_t> logged_addresses{};
|
||||
if (is_main_access && !logged_addresses.insert(address).second)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const auto offset = address - object.value();
|
||||
emu.logger.print(is_main_access ? color::green : color::dark_gray,
|
||||
|
||||
@@ -100,8 +100,7 @@ void syscall_dispatcher::dispatch(windows_emulator& win_emu)
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto* previous_mod = context.module_manager.find_by_address(context.previous_ip);
|
||||
if (previous_mod == mod)
|
||||
if (mod->is_within(context.previous_ip))
|
||||
{
|
||||
const auto rsp = c.emu.read_stack_pointer();
|
||||
const auto return_address = c.emu.read_memory<uint64_t>(rsp);
|
||||
@@ -113,6 +112,7 @@ void syscall_dispatcher::dispatch(windows_emulator& win_emu)
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto* previous_mod = context.module_manager.find_by_address(context.previous_ip);
|
||||
win_emu.logger.print(color::blue,
|
||||
"Crafted out-of-line syscall: %s (0x%X) at 0x%llX (%s) via 0x%llX (%s)\n",
|
||||
entry->second.name.c_str(),
|
||||
|
||||
@@ -805,6 +805,75 @@ void windows_emulator::perform_thread_switch()
|
||||
}
|
||||
}
|
||||
|
||||
void windows_emulator::on_instruction_execution(uint64_t address)
|
||||
{
|
||||
auto& process = this->process();
|
||||
auto& thread = this->current_thread();
|
||||
|
||||
++process.executed_instructions;
|
||||
const auto thread_insts = ++thread.executed_instructions;
|
||||
if (thread_insts % MAX_INSTRUCTIONS_PER_TIME_SLICE == 0)
|
||||
{
|
||||
this->switch_thread = true;
|
||||
this->emu().stop();
|
||||
}
|
||||
|
||||
process.previous_ip = process.current_ip;
|
||||
process.current_ip = this->emu().read_instruction_pointer();
|
||||
|
||||
const auto is_main_exe = process.executable->is_within(address);
|
||||
const auto is_interesting_call = process.executable->is_within(process.previous_ip) || is_main_exe;
|
||||
|
||||
if (this->silent_until_main_ && is_main_exe)
|
||||
{
|
||||
this->silent_until_main_ = false;
|
||||
this->logger.disable_output(false);
|
||||
}
|
||||
|
||||
if (!this->verbose && !this->verbose_calls && !is_interesting_call)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* binary = this->process().module_manager.find_by_address(address);
|
||||
|
||||
if (binary)
|
||||
{
|
||||
const auto export_entry = binary->address_names.find(address);
|
||||
if (export_entry != binary->address_names.end())
|
||||
{
|
||||
logger.print(is_interesting_call ? color::yellow : color::dark_gray,
|
||||
"Executing function: %s - %s (0x%llX)\n",
|
||||
binary->name.c_str(),
|
||||
export_entry->second.c_str(), address);
|
||||
}
|
||||
else if (address == binary->entry_point)
|
||||
{
|
||||
logger.print(is_interesting_call ? color::yellow : color::gray,
|
||||
"Executing entry point: %s (0x%llX)\n",
|
||||
binary->name.c_str(),
|
||||
address);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->verbose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto& emu = this->emu();
|
||||
|
||||
printf(
|
||||
"Inst: %16llX - RAX: %16llX - RBX: %16llX - RCX: %16llX - RDX: %16llX - R8: %16llX - R9: %16llX - RDI: %16llX - RSI: %16llX - %s\n",
|
||||
address,
|
||||
emu.reg(x64_register::rax), emu.reg(x64_register::rbx),
|
||||
emu.reg(x64_register::rcx),
|
||||
emu.reg(x64_register::rdx), emu.reg(x64_register::r8),
|
||||
emu.reg(x64_register::r9),
|
||||
emu.reg(x64_register::rdi), emu.reg(x64_register::rsi),
|
||||
binary ? binary->name.c_str() : "<N/A>");
|
||||
}
|
||||
|
||||
void windows_emulator::setup_hooks()
|
||||
{
|
||||
this->emu().hook_instruction(x64_hookable_instructions::syscall, [&]
|
||||
@@ -886,76 +955,12 @@ void windows_emulator::setup_hooks()
|
||||
return memory_violation_continuation::resume;
|
||||
});
|
||||
|
||||
this->emu().hook_memory_execution(0, std::numeric_limits<size_t>::max(),
|
||||
[&](const uint64_t address, const size_t, const uint64_t)
|
||||
{
|
||||
auto& process = this->process();
|
||||
auto& thread = this->current_thread();
|
||||
|
||||
++process.executed_instructions;
|
||||
const auto thread_insts = ++thread.executed_instructions;
|
||||
if (thread_insts % MAX_INSTRUCTIONS_PER_TIME_SLICE == 0)
|
||||
{
|
||||
this->switch_thread = true;
|
||||
this->emu().stop();
|
||||
}
|
||||
|
||||
process.previous_ip = process.current_ip;
|
||||
process.current_ip = this->emu().read_instruction_pointer();
|
||||
|
||||
const auto is_main_exe = process.executable->is_within(address);
|
||||
const auto is_interesting_call = process.executable->is_within(
|
||||
process.previous_ip) || is_main_exe;
|
||||
|
||||
if (this->silent_until_main_ && is_main_exe)
|
||||
{
|
||||
this->silent_until_main_ = false;
|
||||
this->logger.disable_output(false);
|
||||
}
|
||||
|
||||
if (!this->verbose && !this->verbose_calls && !is_interesting_call)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* binary = this->process().module_manager.find_by_address(address);
|
||||
|
||||
if (binary)
|
||||
{
|
||||
const auto export_entry = binary->address_names.find(address);
|
||||
if (export_entry != binary->address_names.end())
|
||||
{
|
||||
logger.print(is_interesting_call ? color::yellow : color::dark_gray,
|
||||
"Executing function: %s - %s (0x%llX)\n",
|
||||
binary->name.c_str(),
|
||||
export_entry->second.c_str(), address);
|
||||
}
|
||||
else if (address == binary->entry_point)
|
||||
{
|
||||
logger.print(is_interesting_call ? color::yellow : color::gray,
|
||||
"Executing entry point: %s (0x%llX)\n",
|
||||
binary->name.c_str(),
|
||||
address);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->verbose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto& emu = this->emu();
|
||||
|
||||
printf(
|
||||
"Inst: %16llX - RAX: %16llX - RBX: %16llX - RCX: %16llX - RDX: %16llX - R8: %16llX - R9: %16llX - RDI: %16llX - RSI: %16llX - %s\n",
|
||||
address,
|
||||
emu.reg(x64_register::rax), emu.reg(x64_register::rbx),
|
||||
emu.reg(x64_register::rcx),
|
||||
emu.reg(x64_register::rdx), emu.reg(x64_register::r8),
|
||||
emu.reg(x64_register::r9),
|
||||
emu.reg(x64_register::rdi), emu.reg(x64_register::rsi),
|
||||
binary ? binary->name.c_str() : "<N/A>");
|
||||
});
|
||||
this->emu().hook_memory_execution(
|
||||
0, std::numeric_limits<size_t>::max(),
|
||||
[&](const uint64_t address, const size_t, const uint64_t)
|
||||
{
|
||||
this->on_instruction_execution(address);
|
||||
});
|
||||
}
|
||||
|
||||
void windows_emulator::start(std::chrono::nanoseconds timeout, size_t count)
|
||||
|
||||
@@ -127,4 +127,5 @@ private:
|
||||
|
||||
void setup_hooks();
|
||||
void setup_process(const emulator_settings& settings);
|
||||
void on_instruction_execution(uint64_t address);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user