From c71c20458389d477ffd3916bfa3d4b180710fe80 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Mon, 18 Aug 2025 19:00:31 +0200 Subject: [PATCH 1/3] Print instruction summary --- page/src/components/settings-menu.tsx | 15 +++++++++++++ page/src/settings.ts | 6 +++++ src/analyzer/analysis.cpp | 32 ++++++++++++++++++++++++--- src/analyzer/analysis.hpp | 2 ++ src/analyzer/main.cpp | 29 ++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 3 deletions(-) diff --git a/page/src/components/settings-menu.tsx b/page/src/components/settings-menu.tsx index a92d08b3..fbc61839 100644 --- a/page/src/components/settings-menu.tsx +++ b/page/src/components/settings-menu.tsx @@ -172,6 +172,21 @@ export class SettingsMenu extends React.Component { /> +
+ { + this.setState({ instructionSummary: checked }); + }} + /> + +
+
{ switches.push("-i"); switches.push(f); diff --git a/src/analyzer/analysis.cpp b/src/analyzer/analysis.cpp index c872ea2a..29067a22 100644 --- a/src/analyzer/analysis.cpp +++ b/src/analyzer/analysis.cpp @@ -277,6 +277,25 @@ namespace return cs_insn_group(disasm.get_handle(), instructions.data(), CS_GRP_RET); } + void record_instruction(analysis_context& c, const uint64_t address) + { + std::array instruction_bytes{}; + const auto result = c.win_emu->emu().try_read_memory(address, instruction_bytes.data(), instruction_bytes.size()); + if (!result) + { + return; + } + + disassembler disasm{}; + const auto instructions = disasm.disassemble(instruction_bytes, 1); + if (instructions.empty()) + { + return; + } + + ++c.instructions[instructions[0].mnemonic]; + } + void handle_instruction(analysis_context& c, const uint64_t address) { auto& win_emu = *c.win_emu; @@ -312,18 +331,25 @@ namespace return win_emu.mod_manager.find_by_address(previous_ip); // }); + const auto is_current_binary_interesting = utils::make_lazy([&] { + return is_main_exe || (binary && c.settings->modules.contains(binary->name)); // + }); + const auto is_in_interesting_module = [&] { if (c.settings->modules.empty()) { return false; } - return (binary && c.settings->modules.contains(binary->name)) || - (previous_binary && c.settings->modules.contains(previous_binary->name)); + return is_current_binary_interesting || (previous_binary && c.settings->modules.contains(previous_binary->name)); }; + if (c.settings->instruction_summary && (is_current_binary_interesting || !binary)) + { + record_instruction(c, address); + } + const auto is_interesting_call = is_previous_main_exe // - || is_main_exe // || !previous_binary // || is_in_interesting_module(); diff --git a/src/analyzer/analysis.hpp b/src/analyzer/analysis.hpp index 08438603..b24e5355 100644 --- a/src/analyzer/analysis.hpp +++ b/src/analyzer/analysis.hpp @@ -15,6 +15,7 @@ struct analysis_settings bool verbose_logging{false}; bool silent{false}; bool buffer_stdout{false}; + bool instruction_summary{false}; string_set modules{}; string_set ignored_functions{}; @@ -39,6 +40,7 @@ struct analysis_context std::string output{}; bool has_reached_main{false}; + std::map instructions{}; std::vector accessed_imports{}; }; diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index f5a74ef8..81ae5e04 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -221,8 +221,33 @@ namespace } } + void print_instruction_summary(const analysis_context& c) + { + std::map> instruction_counts{}; + + for (const auto& [mnemonic, count] : c.instructions) + { + instruction_counts[count].push_back(mnemonic); + } + + c.win_emu->log.print(color::white, "Instruction summary:\n"); + + for (const auto& [count, mnemonics] : instruction_counts) + { + for (const auto& mnemonic : mnemonics) + { + c.win_emu->log.print(color::white, "%s: %" PRIx64 "\n", mnemonic.c_str(), count); + } + } + } + void do_post_emulation_work(const analysis_context& c) { + if (c.settings->instruction_summary) + { + print_instruction_summary(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"); @@ -647,6 +672,10 @@ namespace { options.tenet_trace = true; } + else if (arg == "-is" || arg == "--instruction-summary") + { + options.instruction_summary = true; + } else if (arg == "-m" || arg == "--module") { if (args.size() < 2) From 1db2c9716572b604112b3e7915540b7afb368273 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Mon, 18 Aug 2025 19:15:08 +0200 Subject: [PATCH 2/3] Fix help --- src/analyzer/main.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index 81ae5e04..a251c419 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -611,6 +611,7 @@ namespace printf(" -s, --silent Silent mode\n"); printf(" -v, --verbose Verbose logging\n"); printf(" -b, --buffer Buffer stdout\n"); + printf(" -f, --foreign Log read access to foreign modules\n"); printf(" -c, --concise Concise logging\n"); printf(" -x, --exec Log r/w access to executable memory\n"); printf(" -m, --module Specify module to track\n"); @@ -621,6 +622,7 @@ namespace printf(" -i, --ignore Comma-separated list of functions to ignore\n"); printf(" -p, --path Map Windows path to host path\n"); printf(" -r, --registry Set registry path (default: ./registry)\n\n"); + printf(" -is, --inst-summary Print a summary of executed instructions of the analyzed modules\n"); printf("Examples:\n"); printf(" analyzer -v -e path/to/root myapp.exe\n"); printf(" analyzer -e path/to/root -p c:/analysis-sample.exe /path/to/sample.exe c:/analysis-sample.exe\n"); @@ -640,7 +642,8 @@ namespace print_help(); std::exit(0); } - else if (arg == "-d" || arg == "--debug") + + if (arg == "-d" || arg == "--debug") { options.use_gdb = true; } @@ -672,7 +675,7 @@ namespace { options.tenet_trace = true; } - else if (arg == "-is" || arg == "--instruction-summary") + else if (arg == "-is" || arg == "--inst-summary") { options.instruction_summary = true; } From 80c8b88d234c0a41dd66de3f5ec35a2d7e051207 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Mon, 18 Aug 2025 19:29:25 +0200 Subject: [PATCH 3/3] Optimize instruction summary --- src/analyzer/analysis.cpp | 18 ++++++++---------- src/analyzer/analysis.hpp | 4 +++- src/analyzer/main.cpp | 13 +++++++------ 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/analyzer/analysis.cpp b/src/analyzer/analysis.cpp index 29067a22..4d518a6e 100644 --- a/src/analyzer/analysis.cpp +++ b/src/analyzer/analysis.cpp @@ -31,7 +31,7 @@ namespace }; } - std::string get_instruction_string(const emulator& emu, const uint64_t address) + std::string get_instruction_string(const disassembler& d, const emulator& emu, const uint64_t address) { std::array instruction_bytes{}; const auto result = emu.try_read_memory(address, instruction_bytes.data(), instruction_bytes.size()); @@ -40,8 +40,7 @@ namespace return {}; } - disassembler disasm{}; - const auto instructions = disasm.disassemble(instruction_bytes, 1); + const auto instructions = d.disassemble(instruction_bytes, 1); if (instructions.empty()) { return {}; @@ -59,7 +58,7 @@ namespace // TODO: Pass enum? if (details == "Illegal instruction") { - const auto inst = get_instruction_string(c.win_emu->emu(), rip); + const auto inst = get_instruction_string(c.d, c.win_emu->emu(), rip); if (!inst.empty()) { addition = " (" + inst + ")"; @@ -258,7 +257,7 @@ namespace } } - bool is_return(const emulator& emu, const uint64_t address) + bool is_return(const disassembler& d, const emulator& emu, const uint64_t address) { std::array instruction_bytes{}; const auto result = emu.try_read_memory(address, instruction_bytes.data(), instruction_bytes.size()); @@ -267,14 +266,13 @@ namespace return false; } - disassembler disasm{}; - const auto instructions = disasm.disassemble(instruction_bytes, 1); + const auto instructions = d.disassemble(instruction_bytes, 1); if (instructions.empty()) { return false; } - return cs_insn_group(disasm.get_handle(), instructions.data(), CS_GRP_RET); + return cs_insn_group(d.get_handle(), instructions.data(), CS_GRP_RET); } void record_instruction(analysis_context& c, const uint64_t address) @@ -293,7 +291,7 @@ namespace return; } - ++c.instructions[instructions[0].mnemonic]; + ++c.instructions[instructions[0].id]; } void handle_instruction(analysis_context& c, const uint64_t address) @@ -384,7 +382,7 @@ namespace win_emu.log.print(is_interesting_call ? color::yellow : color::gray, "Executing entry point: %s (0x%" PRIx64 ")\n", binary->name.c_str(), address); } - else if (is_previous_main_exe && binary != previous_binary && !is_return(c.win_emu->emu(), previous_ip)) + else if (is_previous_main_exe && binary != previous_binary && !is_return(c.d, c.win_emu->emu(), previous_ip)) { auto nearest_entry = binary->address_names.upper_bound(address); if (nearest_entry == binary->address_names.begin()) diff --git a/src/analyzer/analysis.hpp b/src/analyzer/analysis.hpp index b24e5355..e03eeab2 100644 --- a/src/analyzer/analysis.hpp +++ b/src/analyzer/analysis.hpp @@ -2,6 +2,7 @@ #include #include +#include "disassembler.hpp" struct mapped_module; class module_manager; @@ -40,7 +41,8 @@ struct analysis_context std::string output{}; bool has_reached_main{false}; - std::map instructions{}; + disassembler d{}; + std::unordered_map instructions{}; std::vector accessed_imports{}; }; diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index a251c419..fa79f325 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -223,20 +223,21 @@ namespace void print_instruction_summary(const analysis_context& c) { - std::map> instruction_counts{}; + std::map> instruction_counts{}; - for (const auto& [mnemonic, count] : c.instructions) + for (const auto& [instruction, count] : c.instructions) { - instruction_counts[count].push_back(mnemonic); + instruction_counts[count].push_back(instruction); } c.win_emu->log.print(color::white, "Instruction summary:\n"); - for (const auto& [count, mnemonics] : instruction_counts) + for (const auto& [count, instructions] : instruction_counts) { - for (const auto& mnemonic : mnemonics) + for (const auto& instruction : instructions) { - c.win_emu->log.print(color::white, "%s: %" PRIx64 "\n", mnemonic.c_str(), count); + const auto* mnemonic = cs_insn_name(c.d.get_handle(), instruction); + c.win_emu->log.print(color::white, "%s: %" PRIx64 "\n", mnemonic, count); } } }