#include #include "frida-gumjs.h" #include "ranges.h" #include "shm.h" #include "stats.h" #include "util.h" #define MICRO_TO_SEC 1000000 #if defined(__aarch64__) typedef struct { guint64 num_blocks; guint64 num_instructions; guint64 num_eob; guint64 num_reloc; guint64 num_adr; guint64 num_adrp; guint64 num_b; guint64 num_bcc; guint64 num_bl; guint64 num_br; guint64 num_cbz; guint64 num_cbnz; guint64 num_ldr; guint64 num_ldrsw; guint64 num_ret; guint64 num_tbz; guint64 num_tbnz; } stats_data_arch_t; static stats_data_arch_t *stats_data_arch = NULL; void starts_arch_init(void) { stats_data_arch = shm_create(sizeof(stats_data_arch_t)); } static void stats_write_arch_stat(char *label, guint64 value, guint64 total) { stats_print("%-30s ", label); stats_print("%10" G_GINT64_MODIFIER "u ", value); if (total == 0) { stats_print("(--.--%%), "); } else { stats_print("(%5.2f%%) ", ((float)value * 100) / total); } stats_print("\n"); } static void stats_write_arch_stat_delta(char *label, guint64 prev_value, guint64 curr_value, guint elapsed, guint64 prev_total, guint64 curr_total) { guint64 delta = curr_value - prev_value; guint64 delta_total = curr_total - prev_total; guint64 per_sec = delta / elapsed; stats_print("%-30s ", label); stats_print("%10" G_GINT64_MODIFIER "u ", curr_value); if (curr_total == 0) { stats_print("(--.--%%), "); } else { stats_print("(%5.2f%%) ", ((float)curr_value * 100) / curr_total); } stats_print("%10" G_GINT64_MODIFIER "u ", delta); if (delta_total == 0) { stats_print("(--.--%%), "); } else { stats_print("(%5.2f%%) ", ((float)delta * 100) / delta_total); } stats_print("[%10" G_GINT64_MODIFIER "u/s]", per_sec); stats_print("\n"); } void stats_write_arch(stats_data_t *data) { guint elapsed = (data->curr.stats_time - data->prev.stats_time) / MICRO_TO_SEC; stats_print("%-30s %10s %19s\n", "Transitions", "cumulative", "delta"); stats_print("%-30s %10s %19s\n", "-----------", "----------", "-----"); stats_print( "%-30s %10" G_GINT64_MODIFIER "u %-8s %10" G_GINT64_MODIFIER "u\n", "total", data->curr.total, "", data->curr.total - data->prev.total); stats_write_arch_stat_delta("call_imm", data->prev.call_imm, data->curr.call_imm, elapsed, data->prev.total, data->curr.total); stats_write_arch_stat_delta("call_reg", data->prev.call_reg, data->curr.call_reg, elapsed, data->prev.total, data->curr.total); stats_write_arch_stat_delta("excluded_call_reg", data->prev.excluded_call_reg, data->curr.excluded_call_reg, elapsed, data->prev.total, data->curr.total); stats_write_arch_stat_delta("ret", data->prev.ret, data->curr.ret, elapsed, data->prev.total, data->curr.total); stats_write_arch_stat_delta("post_call_invoke", data->prev.post_call_invoke, data->curr.post_call_invoke, elapsed, data->prev.total, data->curr.total); stats_write_arch_stat_delta("excluded_call_imm", data->prev.excluded_call_imm, data->curr.excluded_call_imm, elapsed, data->prev.total, data->curr.total); stats_write_arch_stat_delta("jmp_imm", data->prev.jmp_imm, data->curr.jmp_imm, elapsed, data->prev.total, data->curr.total); stats_write_arch_stat_delta("jmp_reg", data->prev.jmp_reg, data->curr.jmp_reg, elapsed, data->prev.total, data->curr.total); stats_write_arch_stat_delta("jmp_cond_cc", data->prev.jmp_cond_cc, data->curr.jmp_cond_cc, elapsed, data->prev.total, data->curr.total); stats_write_arch_stat_delta("jmp_cond_cbz", data->prev.jmp_cond_cbz, data->curr.jmp_cond_cbz, elapsed, data->prev.total, data->curr.total); stats_write_arch_stat_delta("jmp_cond_cbnz", data->prev.jmp_cond_cbnz, data->curr.jmp_cond_cbnz, elapsed, data->prev.total, data->curr.total); stats_write_arch_stat_delta("jmp_cond_tbz", data->prev.jmp_cond_tbz, data->curr.jmp_cond_tbz, elapsed, data->prev.total, data->curr.total); stats_write_arch_stat_delta("jmp_cond_tbnz", data->prev.jmp_cond_tbnz, data->curr.jmp_cond_tbnz, elapsed, data->prev.total, data->curr.total); stats_write_arch_stat_delta("jmp_continuation", data->prev.jmp_continuation, data->curr.jmp_continuation, elapsed, data->prev.total, data->curr.total); stats_print("\n"); stats_print("\n"); stats_print("Instrumentation\n"); stats_print("---------------\n"); stats_print("%-30s %10" G_GINT64_MODIFIER "u\n", "Instructions", stats_data_arch->num_instructions); stats_print("%-30s %10" G_GINT64_MODIFIER "u\n", "Blocks", stats_data_arch->num_blocks); if (stats_data_arch->num_blocks != 0) { stats_print( "%-30s %10" G_GINT64_MODIFIER "u\n", "Avg Instructions / Block ", stats_data_arch->num_instructions / stats_data_arch->num_blocks); } stats_print("\n"); stats_print("\n"); guint64 num_instructions = stats_data_arch->num_instructions; stats_print("EOB Instructions\n"); stats_print("----------------\n"); stats_write_arch_stat("Total", stats_data_arch->num_eob, num_instructions); stats_write_arch_stat("B", stats_data_arch->num_b, num_instructions); stats_write_arch_stat("Bcc", stats_data_arch->num_bcc, num_instructions); stats_write_arch_stat("BL", stats_data_arch->num_bl, num_instructions); stats_write_arch_stat("BR", stats_data_arch->num_br, num_instructions); stats_write_arch_stat("CBZ", stats_data_arch->num_cbz, num_instructions); stats_write_arch_stat("CBNZ", stats_data_arch->num_cbnz, num_instructions); stats_write_arch_stat("RET", stats_data_arch->num_ret, num_instructions); stats_write_arch_stat("TBZ", stats_data_arch->num_tbz, num_instructions); stats_write_arch_stat("TBNZ", stats_data_arch->num_tbnz, num_instructions); stats_print("\n"); stats_print("\n"); stats_print("Relocated Instructions\n"); stats_print("----------------------\n"); stats_write_arch_stat("Total", stats_data_arch->num_reloc, num_instructions); stats_write_arch_stat("ADR", stats_data_arch->num_adr, num_instructions); stats_write_arch_stat("ADRP", stats_data_arch->num_adrp, num_instructions); stats_write_arch_stat("LDR", stats_data_arch->num_ldr, num_instructions); stats_write_arch_stat("LDRSW", stats_data_arch->num_ldrsw, num_instructions); stats_print("\n"); stats_print("\n"); } void stats_collect_arch(const cs_insn *instr, gboolean begin) { if (stats_data_arch == NULL) { return; } if (begin) { stats_data_arch->num_blocks++; } stats_data_arch->num_instructions++; switch (instr->id) { case ARM64_INS_ADR: stats_data_arch->num_adr++; stats_data_arch->num_reloc++; break; case ARM64_INS_ADRP: stats_data_arch->num_adrp++; stats_data_arch->num_reloc++; break; case ARM64_INS_B: switch (instr->detail->arm64.cc) { case ARM64_CC_INVALID: case ARM64_CC_AL: case ARM64_CC_NV: stats_data_arch->num_b++; break; default: stats_data_arch->num_bcc++; break; } stats_data_arch->num_eob++; break; case ARM64_INS_BR: case ARM64_INS_BRAA: case ARM64_INS_BRAAZ: case ARM64_INS_BRAB: case ARM64_INS_BRABZ: stats_data_arch->num_br++; stats_data_arch->num_eob++; break; case ARM64_INS_BL: case ARM64_INS_BLR: case ARM64_INS_BLRAA: case ARM64_INS_BLRAAZ: case ARM64_INS_BLRAB: case ARM64_INS_BLRABZ: stats_data_arch->num_bl++; stats_data_arch->num_eob++; break; case ARM64_INS_CBZ: stats_data_arch->num_cbz++; stats_data_arch->num_eob++; break; case ARM64_INS_CBNZ: stats_data_arch->num_cbnz++; stats_data_arch->num_eob++; break; case ARM64_INS_LDR: stats_data_arch->num_ldr++; stats_data_arch->num_reloc++; break; case ARM64_INS_LDRSW: stats_data_arch->num_ldrsw++; stats_data_arch->num_reloc++; break; case ARM64_INS_RET: case ARM64_INS_RETAA: case ARM64_INS_RETAB: stats_data_arch->num_ret++; stats_data_arch->num_eob++; break; case ARM64_INS_TBZ: stats_data_arch->num_tbz++; stats_data_arch->num_eob++; break; case ARM64_INS_TBNZ: stats_data_arch->num_tbnz++; stats_data_arch->num_eob++; break; default: break; } } #endif