mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-19 11:43:56 +00:00
Print summary of executed instructions (#452)
This commit is contained in:
@@ -172,6 +172,21 @@ export class SettingsMenu extends React.Component<SettingsMenuProps, Settings> {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-6">
|
||||
<Checkbox
|
||||
id="settings-summary"
|
||||
checked={this.state.instructionSummary}
|
||||
onCheckedChange={(checked: boolean) => {
|
||||
this.setState({ instructionSummary: checked });
|
||||
}}
|
||||
/>
|
||||
<SettingsLabel
|
||||
htmlFor="settings-summary"
|
||||
text={"Print Instruction Summary"}
|
||||
tooltip={"Print summary of executed instructions"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-6">
|
||||
<Checkbox
|
||||
id="settings-persist"
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface Settings {
|
||||
execAccess: boolean;
|
||||
foreignAccess: boolean;
|
||||
wasm64: boolean;
|
||||
instructionSummary: boolean;
|
||||
ignoredFunctions: string[];
|
||||
interestingModules: string[];
|
||||
commandLine: string;
|
||||
@@ -24,6 +25,7 @@ export function createDefaultSettings(): Settings {
|
||||
execAccess: true,
|
||||
foreignAccess: false,
|
||||
wasm64: false,
|
||||
instructionSummary: false,
|
||||
ignoredFunctions: [],
|
||||
interestingModules: [],
|
||||
commandLine: "",
|
||||
@@ -83,6 +85,10 @@ export function translateSettings(settings: Settings): string[] {
|
||||
switches.push("-f");
|
||||
}
|
||||
|
||||
if (settings.instructionSummary) {
|
||||
switches.push("-is");
|
||||
}
|
||||
|
||||
settings.ignoredFunctions.forEach((f) => {
|
||||
switches.push("-i");
|
||||
switches.push(f);
|
||||
|
||||
@@ -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<uint8_t, MAX_INSTRUCTION_BYTES> 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<uint8_t, MAX_INSTRUCTION_BYTES> 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<uint8_t, MAX_INSTRUCTION_BYTES> 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())
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#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<uint32_t, uint64_t> instructions{};
|
||||
std::vector<accessed_import> accessed_imports{};
|
||||
};
|
||||
|
||||
|
||||
@@ -221,8 +221,34 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
void print_instruction_summary(const analysis_context& c)
|
||||
{
|
||||
std::map<uint64_t, std::vector<uint32_t>> 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<int>(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 <module> Specify module to track\n");
|
||||
@@ -596,6 +623,7 @@ namespace
|
||||
printf(" -i, --ignore <funcs> Comma-separated list of functions to ignore\n");
|
||||
printf(" -p, --path <src> <dst> Map Windows path to host path\n");
|
||||
printf(" -r, --registry <path> 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)
|
||||
|
||||
Reference in New Issue
Block a user