diff options
Diffstat (limited to 'frida_mode/src/stats')
-rw-r--r-- | frida_mode/src/stats/stats.c | 423 | ||||
-rw-r--r-- | frida_mode/src/stats/stats_arm32.c | 13 | ||||
-rw-r--r-- | frida_mode/src/stats/stats_arm64.c | 307 | ||||
-rw-r--r-- | frida_mode/src/stats/stats_x64.c | 325 | ||||
-rw-r--r-- | frida_mode/src/stats/stats_x86.c | 36 | ||||
-rw-r--r-- | frida_mode/src/stats/stats_x86_64.c | 420 |
6 files changed, 1034 insertions, 490 deletions
diff --git a/frida_mode/src/stats/stats.c b/frida_mode/src/stats/stats.c index 91a58741..7972b881 100644 --- a/frida_mode/src/stats/stats.c +++ b/frida_mode/src/stats/stats.c @@ -11,204 +11,405 @@ #include "debug.h" #include "util.h" +#include "entry.h" +#include "stalker.h" #include "stats.h" #define MICRO_TO_SEC 1000000 -stats_data_header_t *stats_data = NULL; +char * stats_filename = NULL; +guint64 stats_interval = 0; +static guint64 stats_interval_us = 0; +static int stats_fd = -1; +static stats_data_t *stats_data = MAP_FAILED; -static int stats_parent_pid = -1; -static int stats_fd = -1; +void stats_write(void) { -char * stats_filename = NULL; -guint64 stats_interval = 0; -gboolean stats_transitions = FALSE; + if (stats_filename == NULL) { return; } -void stats_config(void) { + if (stats_interval == 0) { return; } - stats_filename = getenv("AFL_FRIDA_STATS_FILE"); - stats_interval = util_read_num("AFL_FRIDA_STATS_INTERVAL"); - if (getenv("AFL_FRIDA_STATS_TRANSITIONS") != NULL) { + guint64 current_time = g_get_monotonic_time(); + if ((current_time - stats_data->prev.stats_time) < stats_interval_us) { - stats_transitions = TRUE; + return; } + IGNORED_RETURN(ftruncate(stats_fd, 0)); + IGNORED_RETURN(lseek(stats_fd, 0, SEEK_SET)); + + stats_data->curr.stats_time = current_time; + + GDateTime *date_time = g_date_time_new_now_local(); + char * date_string = g_date_time_format(date_time, "%Y-%m-%d"); + char * time_string = g_date_time_format(date_time, "%H:%M:%S"); + guint elapsed = (stats_data->curr.stats_time - stats_data->prev.stats_time) / + MICRO_TO_SEC; + + stats_print("stats\n"); + stats_print("-----\n"); + + stats_print("%-21s %s %s\n", "Time", date_string, time_string); + stats_print("%-30s %10u seconds \n", "Elapsed", elapsed); + + stats_print("\n"); + stats_print("\n"); + + g_free(time_string); + g_free(date_string); + g_date_time_unref(date_time); + + stats_write_arch(stats_data); + + memcpy(&stats_data->prev, &stats_data->curr, sizeof(stats_t)); + } -void stats_init(void) { +static void gum_afl_stalker_stats_increment_total( + GumStalkerObserver *observer) { - stats_parent_pid = getpid(); + UNUSED_PARAMETER(observer); - OKF("Stats - file [%s]", stats_filename); - OKF("Stats - interval [%" G_GINT64_MODIFIER "u]", stats_interval); + if (!entry_compiled) { return; } + stats_data->curr.total++; - if (stats_interval != 0 && stats_filename == NULL) { +} - FATAL( - "AFL_FRIDA_STATS_FILE must be specified if " - "AFL_FRIDA_STATS_INTERVAL is"); +static void gum_afl_stalker_stats_increment_call_imm( + GumStalkerObserver *observer) { - } + UNUSED_PARAMETER(observer); - if (stats_interval == 0) { stats_interval = 10; } + if (!entry_compiled) { return; } + stats_data->curr.call_imm++; - if (stats_filename == NULL) { return; } +} - if (!stats_is_supported_arch()) { +static void gum_afl_stalker_stats_increment_call_reg( + GumStalkerObserver *observer) { - FATAL("Stats is not supported on this architecture"); + UNUSED_PARAMETER(observer); - } + if (!entry_compiled) { return; } + stats_data->curr.call_reg++; - char *path = NULL; +} - if (stats_filename == NULL) { return; } +static void gum_afl_stalker_stats_increment_call_mem( + GumStalkerObserver *observer) { - if (stats_transitions) { gum_stalker_set_counters_enabled(TRUE); } + UNUSED_PARAMETER(observer); - path = g_canonicalize_filename(stats_filename, g_get_current_dir()); + if (!entry_compiled) { return; } + stats_data->curr.call_mem++; - OKF("Stats - path [%s]", path); +} - stats_fd = open(path, O_RDWR | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); +static void gum_afl_stalker_stats_increment_excluded_call_reg( + GumStalkerObserver *observer) { - if (stats_fd < 0) { FATAL("Failed to open stats file '%s'", path); } + UNUSED_PARAMETER(observer); - g_free(path); + if (!entry_compiled) { return; } + stats_data->curr.excluded_call_reg++; - size_t data_size = stats_data_size_arch(); +} - int shm_id = shmget(IPC_PRIVATE, data_size, IPC_CREAT | IPC_EXCL | 0600); - if (shm_id < 0) { FATAL("shm_id < 0 - errno: %d\n", errno); } +static void gum_afl_stalker_stats_increment_ret_slow_path( + GumStalkerObserver *observer) { - stats_data = shmat(shm_id, NULL, 0); - g_assert(stats_data != MAP_FAILED); + UNUSED_PARAMETER(observer); - /* - * Configure the shared memory region to be removed once the process dies. - */ - if (shmctl(shm_id, IPC_RMID, NULL) < 0) { + if (!entry_compiled) { return; } + stats_data->curr.ret_slow_path++; - FATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno); +} - } +static void gum_afl_stalker_stats_increment_ret(GumStalkerObserver *observer) { - /* Clear it, not sure it's necessary, just seems like good practice */ - memset(stats_data, '\0', data_size); + UNUSED_PARAMETER(observer); + + if (!entry_compiled) { return; } + stats_data->curr.ret++; } -void stats_vprint(int fd, char *format, va_list ap) { +static void gum_afl_stalker_stats_increment_post_call_invoke( + GumStalkerObserver *observer) { - char buffer[4096] = {0}; - int len; + UNUSED_PARAMETER(observer); - if (vsnprintf(buffer, sizeof(buffer) - 1, format, ap) < 0) { return; } + if (!entry_compiled) { return; } + stats_data->curr.post_call_invoke++; - len = strnlen(buffer, sizeof(buffer)); - IGNORED_RETURN(write(fd, buffer, len)); +} + +static void gum_afl_stalker_stats_increment_excluded_call_imm( + GumStalkerObserver *observer) { + + UNUSED_PARAMETER(observer); + + if (!entry_compiled) { return; } + stats_data->curr.excluded_call_imm++; } -void stats_print_fd(int fd, char *format, ...) { +static void gum_afl_stalker_stats_increment_jmp_imm( + GumStalkerObserver *observer) { - va_list ap; - va_start(ap, format); - stats_vprint(fd, format, ap); - va_end(ap); + UNUSED_PARAMETER(observer); + + if (!entry_compiled) { return; } + stats_data->curr.jmp_imm++; } -void stats_print(char *format, ...) { +static void gum_afl_stalker_stats_increment_jmp_reg( + GumStalkerObserver *observer) { - va_list ap; - va_start(ap, format); - stats_vprint(stats_fd, format, ap); - va_end(ap); + UNUSED_PARAMETER(observer); + + if (!entry_compiled) { return; } + stats_data->curr.jmp_reg++; } -void stats_write(void) { +static void gum_afl_stalker_stats_increment_jmp_mem( + GumStalkerObserver *observer) { - if (stats_parent_pid == getpid()) { return; } + UNUSED_PARAMETER(observer); - GDateTime *date_time = g_date_time_new_now_local(); - char *date_time_string = g_date_time_format(date_time, "%Y-%m-%e %H:%M:%S"); + if (!entry_compiled) { return; } + stats_data->curr.jmp_mem++; - stats_print("stats\n"); - stats_print("-----\n"); +} - stats_print("Index: %" G_GINT64_MODIFIER "u\n", - stats_data->stats_idx++); - stats_print("Pid: %d\n", getpid()); - stats_print("Time: %s\n", date_time_string); - stats_print("Blocks: %" G_GINT64_MODIFIER "u\n", - stats_data->num_blocks); - stats_print("Instructions: %" G_GINT64_MODIFIER "u\n", - stats_data->num_instructions); - stats_print("Avg Instructions / Block: %" G_GINT64_MODIFIER "u\n", - stats_data->num_instructions / stats_data->num_blocks); +static void gum_afl_stalker_stats_increment_jmp_cond_imm( + GumStalkerObserver *observer) { - stats_print("\n"); + UNUSED_PARAMETER(observer); - g_free(date_time_string); - g_date_time_unref(date_time); + if (!entry_compiled) { return; } + stats_data->curr.jmp_cond_imm++; - stats_write_arch(); +} - if (stats_transitions) { +static void gum_afl_stalker_stats_increment_jmp_cond_mem( + GumStalkerObserver *observer) { - GDateTime *date_time = g_date_time_new_now_local(); - char *date_time_string = g_date_time_format(date_time, "%Y-%m-%e %H:%M:%S"); + UNUSED_PARAMETER(observer); - stats_print_fd(STDERR_FILENO, "stats\n"); - stats_print_fd(STDERR_FILENO, "-----\n"); - stats_print_fd(STDERR_FILENO, "Index: %" G_GINT64_MODIFIER "u\n", - stats_data->transitions_idx++); - stats_print_fd(STDERR_FILENO, "Pid: %d\n", getpid()); - stats_print_fd(STDERR_FILENO, "Time: %s\n", date_time_string); + if (!entry_compiled) { return; } + stats_data->curr.jmp_cond_mem++; - g_free(date_time_string); - g_date_time_unref(date_time); - gum_stalker_dump_counters(); +} - } +static void gum_afl_stalker_stats_increment_jmp_cond_reg( + GumStalkerObserver *observer) { + + UNUSED_PARAMETER(observer); + + if (!entry_compiled) { return; } + stats_data->curr.jmp_cond_reg++; } -void stats_on_fork(void) { +static void gum_afl_stalker_stats_increment_jmp_cond_jcxz( + GumStalkerObserver *observer) { + + UNUSED_PARAMETER(observer); + + if (!entry_compiled) { return; } + stats_data->curr.jmp_cond_jcxz++; + +} + +static void gum_afl_stalker_stats_increment_jmp_cond_cc( + GumStalkerObserver *observer) { + + UNUSED_PARAMETER(observer); + + if (!entry_compiled) { return; } + stats_data->curr.jmp_cond_cc++; + +} + +static void gum_afl_stalker_stats_increment_jmp_cond_cbz( + GumStalkerObserver *observer) { + + UNUSED_PARAMETER(observer); + + if (!entry_compiled) { return; } + stats_data->curr.jmp_cond_cbz++; + +} + +static void gum_afl_stalker_stats_increment_jmp_cond_cbnz( + GumStalkerObserver *observer) { + + UNUSED_PARAMETER(observer); + + if (!entry_compiled) { return; } + stats_data->curr.jmp_cond_cbnz++; + +} + +static void gum_afl_stalker_stats_increment_jmp_cond_tbz( + GumStalkerObserver *observer) { - guint64 current_time; + UNUSED_PARAMETER(observer); + + if (!entry_compiled) { return; } + stats_data->curr.jmp_cond_tbz++; + +} + +static void gum_afl_stalker_stats_increment_jmp_cond_tbnz( + GumStalkerObserver *observer) { + + UNUSED_PARAMETER(observer); + + if (!entry_compiled) { return; } + stats_data->curr.jmp_cond_tbnz++; + +} + +static void gum_afl_stalker_stats_increment_jmp_continuation( + GumStalkerObserver *observer) { + + UNUSED_PARAMETER(observer); + + if (!entry_compiled) { return; } + stats_data->curr.jmp_continuation++; + +} + +static void stats_observer_init(GumStalkerObserver *observer) { + + GumStalkerObserverInterface *iface = GUM_STALKER_OBSERVER_GET_IFACE(observer); + iface->increment_total = gum_afl_stalker_stats_increment_total; + iface->increment_call_imm = gum_afl_stalker_stats_increment_call_imm; + iface->increment_call_reg = gum_afl_stalker_stats_increment_call_reg; + iface->increment_call_mem = gum_afl_stalker_stats_increment_call_mem; + iface->increment_excluded_call_reg = + gum_afl_stalker_stats_increment_excluded_call_reg; + iface->increment_ret_slow_path = + gum_afl_stalker_stats_increment_ret_slow_path; + iface->increment_ret = gum_afl_stalker_stats_increment_ret; + iface->increment_post_call_invoke = + gum_afl_stalker_stats_increment_post_call_invoke; + iface->increment_excluded_call_imm = + gum_afl_stalker_stats_increment_excluded_call_imm; + iface->increment_jmp_imm = gum_afl_stalker_stats_increment_jmp_imm; + iface->increment_jmp_reg = gum_afl_stalker_stats_increment_jmp_reg; + iface->increment_jmp_mem = gum_afl_stalker_stats_increment_jmp_mem; + iface->increment_jmp_cond_imm = gum_afl_stalker_stats_increment_jmp_cond_imm; + iface->increment_jmp_cond_mem = gum_afl_stalker_stats_increment_jmp_cond_mem; + iface->increment_jmp_cond_reg = gum_afl_stalker_stats_increment_jmp_cond_reg; + iface->increment_jmp_cond_jcxz = + gum_afl_stalker_stats_increment_jmp_cond_jcxz; + iface->increment_jmp_cond_cc = gum_afl_stalker_stats_increment_jmp_cond_cc; + iface->increment_jmp_cond_cbz = gum_afl_stalker_stats_increment_jmp_cond_cbz; + iface->increment_jmp_cond_cbnz = + gum_afl_stalker_stats_increment_jmp_cond_cbnz; + iface->increment_jmp_cond_tbz = gum_afl_stalker_stats_increment_jmp_cond_tbz; + iface->increment_jmp_cond_tbnz = + gum_afl_stalker_stats_increment_jmp_cond_tbnz; + iface->increment_jmp_continuation = + gum_afl_stalker_stats_increment_jmp_continuation; + +} + +void stats_config(void) { + + stats_filename = getenv("AFL_FRIDA_STATS_FILE"); + stats_interval = util_read_num("AFL_FRIDA_STATS_INTERVAL"); + +} + +void stats_init(void) { + + OKF("Stats - file [%s]", stats_filename); + OKF("Stats - interval [%" G_GINT64_MODIFIER "u]", stats_interval); + + if (stats_interval != 0 && stats_filename == NULL) { + + FATAL( + "AFL_FRIDA_STATS_FILE must be specified if " + "AFL_FRIDA_STATS_INTERVAL is"); + + } + + if (stats_interval == 0) { stats_interval = 10; } + stats_interval_us = stats_interval * MICRO_TO_SEC; if (stats_filename == NULL) { return; } - if (stats_interval == 0) { return; } + char *path = g_canonicalize_filename(stats_filename, g_get_current_dir()); + + OKF("Stats - path [%s]", path); - current_time = g_get_monotonic_time(); + stats_fd = open(path, O_RDWR | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + + if (stats_fd < 0) { FATAL("Failed to open stats file '%s'", path); } - if ((current_time - stats_data->stats_last_time) > - (stats_interval * MICRO_TO_SEC)) { + g_free(path); - stats_write(); - stats_data->stats_last_time = current_time; + int shm_id = + shmget(IPC_PRIVATE, sizeof(stats_data_t), IPC_CREAT | IPC_EXCL | 0600); + if (shm_id < 0) { FATAL("shm_id < 0 - errno: %d\n", errno); } + + stats_data = shmat(shm_id, NULL, 0); + g_assert(stats_data != MAP_FAILED); + + GumStalkerObserver *observer = stalker_get_observer(); + stats_observer_init(observer); + + /* + * Configure the shared memory region to be removed once the process dies. + */ + if (shmctl(shm_id, IPC_RMID, NULL) < 0) { + + FATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno); } + /* Clear it, not sure it's necessary, just seems like good practice */ + memset(stats_data, '\0', sizeof(stats_data_t)); + + starts_arch_init(); + } -void stats_collect(const cs_insn *instr, gboolean begin) { +void stats_print(char *format, ...) { - UNUSED_PARAMETER(instr); - UNUSED_PARAMETER(begin); + char buffer[4096] = {0}; + int len; - if (stats_fd < 0) { return; } + va_list ap; + va_start(ap, format); - if (begin) { stats_data->num_blocks++; } - stats_data->num_instructions++; + if (vsnprintf(buffer, sizeof(buffer) - 1, format, ap) < 0) { return; } - stats_collect_arch(instr); + len = strnlen(buffer, sizeof(buffer)); + IGNORED_RETURN(write(stats_fd, buffer, len)); + va_end(ap); + +} + +void stats_on_fork(void) { + + stats_write(); + +} + +void stats_collect(const cs_insn *instr, gboolean begin) { + + if (!entry_compiled) { return; } + if (stats_filename == NULL) { return; } + stats_collect_arch(instr, begin); } diff --git a/frida_mode/src/stats/stats_arm32.c b/frida_mode/src/stats/stats_arm32.c index 71953af3..5860d33b 100644 --- a/frida_mode/src/stats/stats_arm32.c +++ b/frida_mode/src/stats/stats_arm32.c @@ -7,27 +7,22 @@ #if defined(__arm__) -gboolean stats_is_supported_arch(void) { - - return FALSE; - -} - -size_t stats_data_size_arch(void) { +void starts_arch_init(void) { FATAL("Stats not supported on this architecture"); } -void stats_write_arch(void) { +void stats_write_arch(stats_data_t *data) { FATAL("Stats not supported on this architecture"); } -void stats_collect_arch(const cs_insn *instr) { +void stats_collect_arch(const cs_insn *instr, gboolean begin) { UNUSED_PARAMETER(instr); + UNUSED_PARAMETER(begin); FATAL("Stats not supported on this architecture"); } diff --git a/frida_mode/src/stats/stats_arm64.c b/frida_mode/src/stats/stats_arm64.c index d9d374a4..54b3faf1 100644 --- a/frida_mode/src/stats/stats_arm64.c +++ b/frida_mode/src/stats/stats_arm64.c @@ -1,34 +1,323 @@ +#include <sys/shm.h> +#include <sys/mman.h> + #include "frida-gumjs.h" #include "debug.h" +#include "ranges.h" #include "stats.h" #include "util.h" +#define MICRO_TO_SEC 1000000 + #if defined(__aarch64__) -gboolean stats_is_supported_arch(void) { +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) { + + int shm_id = shmget(IPC_PRIVATE, sizeof(stats_data_arch_t), + IPC_CREAT | IPC_EXCL | 0600); + if (shm_id < 0) { FATAL("shm_id < 0 - errno: %d\n", errno); } + + stats_data_arch = shmat(shm_id, NULL, 0); + g_assert(stats_data_arch != MAP_FAILED); + + /* + * Configure the shared memory region to be removed once the process dies. + */ + if (shmctl(shm_id, IPC_RMID, NULL) < 0) { + + FATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno); - return FALSE; + } + + /* Clear it, not sure it's necessary, just seems like good practice */ + memset(stats_data_arch, '\0', sizeof(stats_data_arch_t)); } -size_t stats_data_size_arch(void) { +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); - FATAL("Stats not supported on this architecture"); + } + + stats_print("\n"); } -void stats_write_arch(void) { +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); - FATAL("Stats not supported on this architecture"); + } + + stats_print("[%10" G_GINT64_MODIFIER "u/s]", per_sec); + stats_print("\n"); } -void stats_collect_arch(const cs_insn *instr) { +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; - UNUSED_PARAMETER(instr); - FATAL("Stats not supported on this architecture"); + } } diff --git a/frida_mode/src/stats/stats_x64.c b/frida_mode/src/stats/stats_x64.c deleted file mode 100644 index 11464a2a..00000000 --- a/frida_mode/src/stats/stats_x64.c +++ /dev/null @@ -1,325 +0,0 @@ -#include "frida-gumjs.h" - -#include "debug.h" - -#include "ranges.h" -#include "stats.h" -#include "util.h" - -#if defined(__x86_64__) - -typedef struct { - - stats_data_header_t header; - - guint64 num_call_imm; - guint64 num_call_imm_excluded; - guint64 num_call_reg; - guint64 num_call_mem; - - guint64 num_jmp_imm; - guint64 num_jmp_reg; - guint64 num_jmp_mem; - - guint64 num_jmp_cond_imm; - guint64 num_jmp_cond_reg; - guint64 num_jmp_cond_mem; - - guint64 num_jmp_cond_jcxz; - - guint64 num_ret; - - guint64 num_rip_relative; - - guint64 num_rip_relative_type[X86_INS_ENDING]; - char name_rip_relative_type[X86_INS_ENDING][CS_MNEMONIC_SIZE]; - -} stats_data_arch_t; - -gboolean stats_is_supported_arch(void) { - - return TRUE; - -} - -size_t stats_data_size_arch(void) { - - return sizeof(stats_data_arch_t); - -} - -void stats_write_arch(void) { - - stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data; - guint64 num_instructions = stats_data_arch->header.num_instructions; - - stats_print( - "Call Immediates: %" G_GINT64_MODIFIER - "u " - "(%3.2f%%)\n", - stats_data_arch->num_call_imm, - ((float)(stats_data_arch->num_call_imm * 100) / num_instructions)); - stats_print("Call Immediates Excluded: %" G_GINT64_MODIFIER - "u " - "(%3.2f%%)\n", - stats_data_arch->num_call_imm_excluded, - ((float)(stats_data_arch->num_call_imm_excluded * 100) / - num_instructions)); - stats_print( - "Call Register: %" G_GINT64_MODIFIER - "u " - "(%3.2f%%)\n", - stats_data_arch->num_call_reg, - ((float)(stats_data_arch->num_call_reg * 100) / num_instructions)); - stats_print( - "Call Memory: %" G_GINT64_MODIFIER - "u " - "(%3.2f%%)\n", - stats_data_arch->num_call_mem, - ((float)(stats_data_arch->num_call_mem * 100) / num_instructions)); - - stats_print("\n"); - - stats_print("Jump Immediates: %" G_GINT64_MODIFIER - "u " - "(%3.2f%%)\n", - stats_data_arch->num_jmp_imm, - ((float)(stats_data_arch->num_jmp_imm * 100) / num_instructions)); - stats_print("Jump Register: %" G_GINT64_MODIFIER - "u " - "(%3.2f%%)\n", - stats_data_arch->num_jmp_reg, - ((float)(stats_data_arch->num_jmp_reg * 100) / num_instructions)); - stats_print("Jump Memory: %" G_GINT64_MODIFIER - "u " - "(%3.2f%%)\n", - stats_data_arch->num_jmp_mem, - ((float)(stats_data_arch->num_jmp_mem * 100) / num_instructions)); - - stats_print("\n"); - - stats_print( - "Conditional Jump Immediates: %" G_GINT64_MODIFIER - "u " - "(%3.2f%%)\n", - stats_data_arch->num_jmp_cond_imm, - ((float)(stats_data_arch->num_jmp_cond_imm * 100) / num_instructions)); - stats_print( - "Conditional Jump CX Immediate: %" G_GINT64_MODIFIER - "u " - "(%3.2f%%)\n", - stats_data_arch->num_jmp_cond_jcxz, - ((float)(stats_data_arch->num_jmp_cond_jcxz * 100) / num_instructions)); - stats_print( - "Conditional Jump Register: %" G_GINT64_MODIFIER - "u " - "(%3.2f%%)\n", - stats_data_arch->num_jmp_cond_reg, - ((float)(stats_data_arch->num_jmp_cond_reg * 100) / num_instructions)); - stats_print( - "Conditional Jump Memory: %" G_GINT64_MODIFIER - "u " - "(%3.2f%%)\n", - stats_data_arch->num_jmp_cond_mem, - ((float)(stats_data_arch->num_jmp_cond_mem * 100) / num_instructions)); - - stats_print("\n"); - - stats_print("Returns: %" G_GINT64_MODIFIER - "u " - "(%3.2f%%)\n", - stats_data_arch->num_ret, - (stats_data_arch->num_ret * 100 / num_instructions)); - - stats_print("\n"); - - stats_print("Rip Relative: %" G_GINT64_MODIFIER - "u " - "(%3.2f%%)\n", - stats_data_arch->num_rip_relative, - (stats_data_arch->num_rip_relative * 100 / num_instructions)); - - for (size_t i = 0; i < X86_INS_ENDING; i++) { - - if (stats_data_arch->num_rip_relative_type[i] != 0) { - - stats_print(" %10d %s\n", - stats_data_arch->num_rip_relative_type[i], - stats_data_arch->name_rip_relative_type[i]); - - } - - } - - stats_print("\n"); - stats_print("\n"); - -} - -static x86_op_type stats_get_operand_type(const cs_insn *instr) { - - cs_x86 * x86 = &instr->detail->x86; - cs_x86_op *operand; - - if (x86->op_count != 1) { - - FATAL("Unexpected operand count (%d): %s %s\n", x86->op_count, - instr->mnemonic, instr->op_str); - - } - - operand = &x86->operands[0]; - - return operand->type; - -} - -static void stats_collect_call_imm_excluded_arch(const cs_insn *instr) { - - stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data; - cs_x86 * x86 = &instr->detail->x86; - cs_x86_op * operand = &x86->operands[0]; - - if (range_is_excluded((gpointer)operand->imm)) { - - stats_data_arch->num_call_imm_excluded++; - - } - -} - -static void stats_collect_call_arch(const cs_insn *instr) { - - stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data; - x86_op_type type = stats_get_operand_type(instr); - switch (type) { - - case X86_OP_IMM: - stats_data_arch->num_call_imm++; - stats_collect_call_imm_excluded_arch(instr); - break; - case X86_OP_REG: - stats_data_arch->num_call_reg++; - break; - case X86_OP_MEM: - stats_data_arch->num_call_mem++; - break; - default: - FATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str); - - } - -} - -static void stats_collect_jump_arch(const cs_insn *instr) { - - stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data; - x86_op_type type = stats_get_operand_type(instr); - switch (type) { - - case X86_OP_IMM: - stats_data_arch->num_jmp_imm++; - break; - case X86_OP_REG: - stats_data_arch->num_jmp_reg++; - break; - case X86_OP_MEM: - stats_data_arch->num_jmp_mem++; - break; - default: - FATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str); - - } - -} - -static void stats_collect_jump_cond_arch(const cs_insn *instr) { - - stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data; - x86_op_type type = stats_get_operand_type(instr); - switch (type) { - - case X86_OP_IMM: - stats_data_arch->num_jmp_cond_imm++; - break; - case X86_OP_REG: - stats_data_arch->num_jmp_cond_reg++; - break; - case X86_OP_MEM: - stats_data_arch->num_jmp_cond_mem++; - break; - default: - FATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str); - - } - -} - -static void stats_collect_rip_relative_arch(const cs_insn *instr) { - - stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data; - cs_x86 * x86 = &instr->detail->x86; - guint mod; - guint rm; - - if (x86->encoding.modrm_offset == 0) { return; } - - mod = (x86->modrm & 0xc0) >> 6; - if (mod != 0) { return; } - - rm = (x86->modrm & 0x07) >> 0; - if (rm != 5) { return; } - - stats_data_arch->num_rip_relative++; - stats_data_arch->num_rip_relative_type[instr->id]++; - memcpy(stats_data_arch->name_rip_relative_type[instr->id], instr->mnemonic, - CS_MNEMONIC_SIZE); - -} - -void stats_collect_arch(const cs_insn *instr) { - - stats_data_arch_t *stats_data_arch = (stats_data_arch_t *)stats_data; - switch (instr->id) { - - case X86_INS_CALL: - stats_collect_call_arch(instr); - break; - case X86_INS_JMP: - stats_collect_jump_arch(instr); - break; - case X86_INS_JA: - case X86_INS_JAE: - case X86_INS_JB: - case X86_INS_JBE: - case X86_INS_JE: - case X86_INS_JG: - case X86_INS_JGE: - case X86_INS_JL: - case X86_INS_JLE: - case X86_INS_JNE: - case X86_INS_JNO: - case X86_INS_JNP: - case X86_INS_JNS: - case X86_INS_JO: - case X86_INS_JP: - case X86_INS_JS: - stats_collect_jump_cond_arch(instr); - break; - case X86_INS_JECXZ: - case X86_INS_JRCXZ: - stats_data_arch->num_jmp_cond_jcxz++; - break; - case X86_INS_RET: - stats_data_arch->num_ret++; - break; - default: - stats_collect_rip_relative_arch(instr); - break; - - } - -} - -#endif - diff --git a/frida_mode/src/stats/stats_x86.c b/frida_mode/src/stats/stats_x86.c deleted file mode 100644 index d9c4f652..00000000 --- a/frida_mode/src/stats/stats_x86.c +++ /dev/null @@ -1,36 +0,0 @@ -#include "frida-gumjs.h" - -#include "debug.h" - -#include "stats.h" -#include "util.h" - -#if defined(__i386__) - -gboolean stats_is_supported_arch(void) { - - return FALSE; - -} - -size_t stats_data_size_arch(void) { - - FATAL("Stats not supported on this architecture"); - -} - -void stats_write_arch(void) { - - FATAL("Stats not supported on this architecture"); - -} - -void stats_collect_arch(const cs_insn *instr) { - - UNUSED_PARAMETER(instr); - FATAL("Stats not supported on this architecture"); - -} - -#endif - diff --git a/frida_mode/src/stats/stats_x86_64.c b/frida_mode/src/stats/stats_x86_64.c new file mode 100644 index 00000000..ab914951 --- /dev/null +++ b/frida_mode/src/stats/stats_x86_64.c @@ -0,0 +1,420 @@ +#include <sys/shm.h> +#include <sys/mman.h> + +#include "frida-gumjs.h" + +#include "debug.h" + +#include "ranges.h" +#include "stats.h" +#include "util.h" + +#define MICRO_TO_SEC 1000000 + +#if defined(__x86_64__) || defined(__i386__) + +typedef struct { + + guint64 num_blocks; + guint64 num_instructions; + + guint64 num_eob; + + guint64 num_call_imm; + guint64 num_call_imm_excluded; + guint64 num_call_reg; + guint64 num_call_mem; + + guint64 num_jmp_imm; + guint64 num_jmp_reg; + guint64 num_jmp_mem; + + guint64 num_jmp_cond_imm; + guint64 num_jmp_cond_reg; + guint64 num_jmp_cond_mem; + + guint64 num_jmp_cond_jcxz; + + guint64 num_ret; + + guint64 num_rip_relative; + + guint64 num_rip_relative_type[X86_INS_ENDING]; + char name_rip_relative_type[X86_INS_ENDING][CS_MNEMONIC_SIZE]; + +} stats_data_arch_t; + +static stats_data_arch_t *stats_data_arch = NULL; + +void starts_arch_init(void) { + + int shm_id = shmget(IPC_PRIVATE, sizeof(stats_data_arch_t), + IPC_CREAT | IPC_EXCL | 0600); + if (shm_id < 0) { FATAL("shm_id < 0 - errno: %d\n", errno); } + + stats_data_arch = shmat(shm_id, NULL, 0); + g_assert(stats_data_arch != MAP_FAILED); + + /* + * Configure the shared memory region to be removed once the process dies. + */ + if (shmctl(shm_id, IPC_RMID, NULL) < 0) { + + FATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno); + + } + + /* Clear it, not sure it's necessary, just seems like good practice */ + memset(stats_data_arch, '\0', 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("call_mem", data->prev.call_mem, + data->curr.call_mem, elapsed, data->prev.total, + data->curr.total); + stats_write_arch_stat_delta("ret_slow_path", data->prev.ret_slow_path, + data->curr.ret_slow_path, 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_mem", data->prev.jmp_mem, data->curr.jmp_mem, + elapsed, data->prev.total, data->curr.total); + stats_write_arch_stat_delta("jmp_cond_imm", data->prev.jmp_cond_imm, + data->curr.jmp_cond_imm, elapsed, + data->prev.total, data->curr.total); + stats_write_arch_stat_delta("jmp_cond_mem", data->prev.jmp_cond_mem, + data->curr.jmp_cond_mem, elapsed, + data->prev.total, data->curr.total); + stats_write_arch_stat_delta("jmp_cond_reg", data->prev.jmp_cond_reg, + data->curr.jmp_cond_reg, elapsed, + data->prev.total, data->curr.total); + stats_write_arch_stat_delta("jmp_cond_jcxz", data->prev.jmp_cond_jcxz, + data->curr.jmp_cond_jcxz, 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("Call Immediates", stats_data_arch->num_call_imm, + num_instructions); + stats_write_arch_stat("Call Immediates Excluded", + stats_data_arch->num_call_imm_excluded, + num_instructions); + stats_write_arch_stat("Call Register", stats_data_arch->num_call_reg, + num_instructions); + stats_write_arch_stat("Call Memory", stats_data_arch->num_call_mem, + num_instructions); + stats_write_arch_stat("Jump Immediates", stats_data_arch->num_jmp_imm, + num_instructions); + stats_write_arch_stat("Jump Register", stats_data_arch->num_jmp_reg, + num_instructions); + stats_write_arch_stat("Jump Memory", stats_data_arch->num_jmp_mem, + num_instructions); + stats_write_arch_stat("Conditional Jump Immediates", + stats_data_arch->num_jmp_cond_imm, num_instructions); + stats_write_arch_stat("Conditional Jump CX Immediate", + stats_data_arch->num_jmp_cond_jcxz, num_instructions); + stats_write_arch_stat("Conditional Jump Register", + stats_data_arch->num_jmp_cond_reg, num_instructions); + stats_write_arch_stat("Conditional Jump Memory", + stats_data_arch->num_jmp_cond_mem, num_instructions); + stats_write_arch_stat("Returns", stats_data_arch->num_ret, 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_rip_relative, + num_instructions); + + for (size_t i = 0; i < X86_INS_ENDING; i++) { + + if (stats_data_arch->num_rip_relative_type[i] != 0) { + + stats_write_arch_stat(stats_data_arch->name_rip_relative_type[i], + stats_data_arch->num_rip_relative_type[i], + stats_data_arch->num_rip_relative); + + } + + } + + stats_print("\n"); + stats_print("\n"); + +} + +static x86_op_type stats_get_operand_type(const cs_insn *instr) { + + cs_x86 * x86 = &instr->detail->x86; + cs_x86_op *operand; + + if (x86->op_count != 1) { + + FATAL("Unexpected operand count (%d): %s %s\n", x86->op_count, + instr->mnemonic, instr->op_str); + + } + + operand = &x86->operands[0]; + + return operand->type; + +} + +static void stats_collect_call_imm_excluded_arch(const cs_insn *instr) { + + cs_x86 * x86 = &instr->detail->x86; + cs_x86_op *operand = &x86->operands[0]; + + if (range_is_excluded(GUM_ADDRESS(operand->imm))) { + + stats_data_arch->num_call_imm_excluded++; + + } + +} + +static void stats_collect_call_arch(const cs_insn *instr) { + + x86_op_type type = stats_get_operand_type(instr); + switch (type) { + + case X86_OP_IMM: + stats_data_arch->num_call_imm++; + stats_collect_call_imm_excluded_arch(instr); + break; + case X86_OP_REG: + stats_data_arch->num_call_reg++; + break; + case X86_OP_MEM: + stats_data_arch->num_call_mem++; + break; + default: + FATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str); + + } + +} + +static void stats_collect_jump_arch(const cs_insn *instr) { + + x86_op_type type = stats_get_operand_type(instr); + switch (type) { + + case X86_OP_IMM: + stats_data_arch->num_jmp_imm++; + break; + case X86_OP_REG: + stats_data_arch->num_jmp_reg++; + break; + case X86_OP_MEM: + stats_data_arch->num_jmp_mem++; + break; + default: + FATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str); + + } + +} + +static void stats_collect_jump_cond_arch(const cs_insn *instr) { + + x86_op_type type = stats_get_operand_type(instr); + switch (type) { + + case X86_OP_IMM: + stats_data_arch->num_jmp_cond_imm++; + break; + case X86_OP_REG: + stats_data_arch->num_jmp_cond_reg++; + break; + case X86_OP_MEM: + stats_data_arch->num_jmp_cond_mem++; + break; + default: + FATAL("Invalid operand type: %s %s\n", instr->mnemonic, instr->op_str); + + } + +} + +static void stats_collect_rip_relative_arch(const cs_insn *instr) { + + cs_x86 *x86 = &instr->detail->x86; + guint mod; + guint rm; + + if (x86->encoding.modrm_offset == 0) { return; } + + mod = (x86->modrm & 0xc0) >> 6; + if (mod != 0) { return; } + + rm = (x86->modrm & 0x07) >> 0; + if (rm != 5) { return; } + + stats_data_arch->num_rip_relative++; + stats_data_arch->num_rip_relative_type[instr->id]++; + memcpy(stats_data_arch->name_rip_relative_type[instr->id], instr->mnemonic, + CS_MNEMONIC_SIZE); + +} + +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 X86_INS_CALL: + stats_collect_call_arch(instr); + stats_data_arch->num_eob++; + break; + case X86_INS_JMP: + stats_collect_jump_arch(instr); + stats_data_arch->num_eob++; + break; + case X86_INS_JA: + case X86_INS_JAE: + case X86_INS_JB: + case X86_INS_JBE: + case X86_INS_JE: + case X86_INS_JG: + case X86_INS_JGE: + case X86_INS_JL: + case X86_INS_JLE: + case X86_INS_JNE: + case X86_INS_JNO: + case X86_INS_JNP: + case X86_INS_JNS: + case X86_INS_JO: + case X86_INS_JP: + case X86_INS_JS: + stats_collect_jump_cond_arch(instr); + stats_data_arch->num_eob++; + break; + case X86_INS_JECXZ: + case X86_INS_JRCXZ: + stats_data_arch->num_jmp_cond_jcxz++; + stats_data_arch->num_eob++; + break; + case X86_INS_RET: + stats_data_arch->num_ret++; + stats_data_arch->num_eob++; + break; + default: + stats_collect_rip_relative_arch(instr); + break; + + } + +} + +#endif + |