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..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,32 @@ 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) + { + 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].id]; } void handle_instruction(analysis_context& c, const uint64_t address) @@ -312,18 +329,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(); @@ -358,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 08438603..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; @@ -15,6 +16,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 +41,8 @@ struct analysis_context std::string output{}; bool has_reached_main{false}; + disassembler d{}; + std::unordered_map instructions{}; std::vector accessed_imports{}; }; diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index f5a74ef8..fa79f325 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -221,8 +221,34 @@ namespace } } + void print_instruction_summary(const analysis_context& c) + { + std::map> instruction_counts{}; + + for (const auto& [instruction, count] : c.instructions) + { + instruction_counts[count].push_back(instruction); + } + + c.win_emu->log.print(color::white, "Instruction summary:\n"); + + for (const auto& [count, instructions] : instruction_counts) + { + for (const auto& instruction : instructions) + { + const auto* mnemonic = cs_insn_name(c.d.get_handle(), instruction); + c.win_emu->log.print(color::white, "%s: %" PRIx64 "\n", mnemonic, 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"); @@ -586,6 +612,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"); @@ -596,6 +623,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"); @@ -615,7 +643,8 @@ namespace print_help(); std::exit(0); } - else if (arg == "-d" || arg == "--debug") + + if (arg == "-d" || arg == "--debug") { options.use_gdb = true; } @@ -647,6 +676,10 @@ namespace { options.tenet_trace = true; } + else if (arg == "-is" || arg == "--inst-summary") + { + options.instruction_summary = true; + } else if (arg == "-m" || arg == "--module") { if (args.size() < 2)