From 1c79b82ab85da06686bd00f65099d7553d05a802 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 1 Feb 2022 08:13:28 +0000 Subject: Add AARCH64 support for setting ic_entries --- frida_mode/src/stalker.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'frida_mode/src') diff --git a/frida_mode/src/stalker.c b/frida_mode/src/stalker.c index 3a421867..80e4e707 100644 --- a/frida_mode/src/stalker.c +++ b/frida_mode/src/stalker.c @@ -100,7 +100,7 @@ void stalker_init(void) { FOKF(cBLU "Stalker" cRST " - " cGRN "adjacent_blocks:" cYEL " [%u]", stalker_adjacent_blocks); -#if !(defined(__x86_64__) || defined(__i386__)) +#if !(defined(__x86_64__) || defined(__i386__) || defined(__aarch64__)) if (getenv("AFL_FRIDA_STALKER_IC_ENTRIES") != NULL) { FFATAL("AFL_FRIDA_STALKER_IC_ENTRIES not supported"); @@ -134,6 +134,9 @@ void stalker_init(void) { #if defined(__x86_64__) || defined(__i386__) stalker = g_object_new(GUM_TYPE_STALKER, "ic-entries", stalker_ic_entries, "adjacent-blocks", stalker_adjacent_blocks, NULL); +#elif defined(__aarch64__) + stalker = + g_object_new(GUM_TYPE_STALKER, "ic-entries", stalker_ic_entries, NULL); #else stalker = gum_stalker_new(); #endif -- cgit 1.4.1 From e2f76dd41e908d37caa6ab41bfe0f1c62f9b501f Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 1 Feb 2022 08:13:28 +0000 Subject: AARCH64 branch suppression --- frida_mode/src/instrument/instrument_arm64.c | 123 +++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) (limited to 'frida_mode/src') diff --git a/frida_mode/src/instrument/instrument_arm64.c b/frida_mode/src/instrument/instrument_arm64.c index 57b60317..e1dc2441 100644 --- a/frida_mode/src/instrument/instrument_arm64.c +++ b/frida_mode/src/instrument/instrument_arm64.c @@ -14,9 +14,12 @@ #define PAGE_MASK (~(GUM_ADDRESS(0xfff))) #define PAGE_ALIGNED(x) ((GUM_ADDRESS(x) & PAGE_MASK) == GUM_ADDRESS(x)) +#define GUM_RESTORATION_PROLOG_SIZE 4 #if defined(__aarch64__) +static GHashTable *coverage_blocks = NULL; + __attribute__((aligned(0x1000))) static guint8 area_ptr_dummy[MAP_SIZE]; #pragma pack(push, 1) @@ -120,6 +123,101 @@ gboolean instrument_is_coverage_optimize_supported(void) { } +static void instrument_coverage_switch(GumStalkerObserver *self, + gpointer start_address, + const cs_insn * from_insn, + gpointer * target) { + + UNUSED_PARAMETER(self); + UNUSED_PARAMETER(start_address); + + cs_arm64 * arm64; + arm64_cc cc; + gsize fixup_offset; + + if (from_insn == NULL) { return; } + + arm64 = &from_insn->detail->arm64; + cc = arm64->cc; + + if (!g_hash_table_contains(coverage_blocks, GSIZE_TO_POINTER(*target))) { + + return; + + } + + switch (from_insn->id) { + + case ARM64_INS_B: + case ARM64_INS_BL: + if (cc != ARM64_CC_INVALID) { return; } + break; + + case ARM64_INS_RET: + case ARM64_INS_RETAA: + case ARM64_INS_RETAB: + if (arm64->op_count != 0) { return; } + break; + default: + return; + + } + + /* + * Since each block is prefixed with a restoration prologue, we need to be + * able to begin execution at an offset into the block and execute both this + * restoration prologue and the instrumented block without the coverage code. + * We therefore layout our block as follows: + * + * +-----+--------------------------+------------------+-----+-------------+ + * | LDP | COVERAGE INSTRUMENTATION | BR | LDP | TARGET CODE | + * +-----+--------------------------+------------------+-----+-------------+ + * + * ^ ^ ^ ^ + * | | | | + * A B C D + * + * Without instrumentation suppression, the block is either executed at point + * (A) if it is reached by an indirect branch (and registers need to be + * restored) or point (B) if it is reached by an direct branch (and hence the + * registers don't need restoration). Similarly, we can start execution of the + * block at points (C) or (D) to achieve the same functionality, but without + * executing the coverage instrumentation. + * + * In either case, Stalker will call us back with the address of the target + * block to be executed as the destination. It is not until later that Stalker + * will determine which branch type is required given the location of its + * instrumented code and add the `GUM_RESTORATION_PROLOG_SIZE` to the target + * address. Therefore, we need to map the address of point (A) to that of + * point (C) and also ensure that the offset between (A)->(B) and (C)->(D) is + * identical so that if Stalker can use an immediate call, it still branches + * to a valid offset. + */ + fixup_offset = GUM_RESTORATION_PROLOG_SIZE + + G_STRUCT_OFFSET(afl_log_code_asm_t, restoration_prolog); + *target += fixup_offset; + +} + +static void instrument_coverage_suppress_init(void) { + + static gboolean initialized = false; + if (initialized) { return; } + initialized = true; + + GumStalkerObserver * observer = stalker_get_observer(); + GumStalkerObserverInterface *iface = GUM_STALKER_OBSERVER_GET_IFACE(observer); + iface->switch_callback = instrument_coverage_switch; + + coverage_blocks = g_hash_table_new(g_direct_hash, g_direct_equal); + if (coverage_blocks == NULL) { + + FATAL("Failed to g_hash_table_new, errno: %d", errno); + + } + +} + static gboolean instrument_coverage_in_range(gssize offset) { return (offset >= G_MININT33 && offset <= G_MAXINT33); @@ -151,6 +249,7 @@ void instrument_coverage_optimize(const cs_insn * instr, afl_log_code code = {0}; GumArm64Writer *cw = output->writer.arm64; + gpointer block_start; guint64 area_offset = instrument_get_offset_hash(GUM_ADDRESS(instr->address)); gsize map_size_pow2; gsize area_offset_ror; @@ -171,8 +270,32 @@ void instrument_coverage_optimize(const cs_insn * instr, // gum_arm64_writer_put_brk_imm(cw, 0x0); + instrument_coverage_suppress_init(); + code_addr = cw->pc; + /* + * On AARCH64, immediate branches can only be encoded with a 28-bit offset. To + * make a longer branch, it is necessary to load a register with the target + * address, this register must be saved beyond the red-zone before the branch + * is taken. To restore this register each block is prefixed by Stalker with + * an instruction to load x16,x17 from beyond the red-zone on the stack. A + * pair of registers are saved/restored because on AARCH64, the stack pointer + * must be 16 byte aligned. This instruction is emitted into the block before + * the tranformer (from which we are called) is executed. If is is possible + * for Stalker to make a direct branch (the target block is close enough), it + * can forego pushing the registers and instead branch at an offset into the + * block to skip this restoration prolog. + */ + block_start = + GSIZE_TO_POINTER(GUM_ADDRESS(cw->code) - GUM_RESTORATION_PROLOG_SIZE); + + if (!g_hash_table_add(coverage_blocks, block_start)) { + + FATAL("Failed - g_hash_table_add"); + + } + code.code = template; /* -- cgit 1.4.1 From 055af8202613e8dbaa288facdf159add10ffa593 Mon Sep 17 00:00:00 2001 From: jon Date: Tue, 1 Feb 2022 08:13:28 +0000 Subject: Make default coverage code branchless --- frida_mode/src/instrument/instrument_arm64.c | 95 +++++++++++++++------------- 1 file changed, 52 insertions(+), 43 deletions(-) (limited to 'frida_mode/src') diff --git a/frida_mode/src/instrument/instrument_arm64.c b/frida_mode/src/instrument/instrument_arm64.c index e1dc2441..1a704585 100644 --- a/frida_mode/src/instrument/instrument_arm64.c +++ b/frida_mode/src/instrument/instrument_arm64.c @@ -47,6 +47,9 @@ typedef struct { // b 0x7fb6f0dee4 // ldp x16, x17, [sp], #144 + uint32_t b_imm8; /* br #68 */ + uint32_t restoration_prolog; /* ldp x16, x17, [sp], #0x90 */ + uint32_t stp_x0_x1; /* stp x0, x1, [sp, #-0xa0] */ uint32_t adrp_x0_prev_loc1; /* adrp x0, #0xXXXX */ @@ -69,9 +72,6 @@ typedef struct { uint32_t ldp_x0_x1; /* ldp x0, x1, [sp, #-0xa0] */ - uint32_t b_imm8; /* br #8 */ - uint32_t restoration_prolog; /* ldp x16, x17, [sp], #0x90 */ - } afl_log_code_asm_t; #pragma pack(pop) @@ -86,6 +86,9 @@ typedef union { static const afl_log_code_asm_t template = { + .b_imm8 = 0x14000011, + + .restoration_prolog = 0xa8c947f0, .stp_x0_x1 = 0xa93607e0, .adrp_x0_prev_loc1 = 0x90000000, @@ -110,9 +113,6 @@ static const afl_log_code_asm_t template = .ldp_x0_x1 = 0xa97607e0, - .b_imm8 = 0x14000002, - .restoration_prolog = 0xa8c947f0, - } ; @@ -123,75 +123,84 @@ gboolean instrument_is_coverage_optimize_supported(void) { } -static void instrument_coverage_switch(GumStalkerObserver *self, - gpointer start_address, - const cs_insn * from_insn, - gpointer * target) { - - UNUSED_PARAMETER(self); - UNUSED_PARAMETER(start_address); +static gboolean instrument_is_deterministic(const cs_insn *from_insn) { - cs_arm64 * arm64; - arm64_cc cc; - gsize fixup_offset; + cs_arm64 *arm64; + arm64_cc cc; - if (from_insn == NULL) { return; } + if (from_insn == NULL) { return FALSE; } arm64 = &from_insn->detail->arm64; cc = arm64->cc; - if (!g_hash_table_contains(coverage_blocks, GSIZE_TO_POINTER(*target))) { - - return; - - } - switch (from_insn->id) { case ARM64_INS_B: case ARM64_INS_BL: - if (cc != ARM64_CC_INVALID) { return; } + if (cc == ARM64_CC_INVALID) { return TRUE; } break; case ARM64_INS_RET: case ARM64_INS_RETAA: case ARM64_INS_RETAB: - if (arm64->op_count != 0) { return; } + if (arm64->op_count == 0) { return TRUE; } break; default: - return; + return FALSE; } + return FALSE; + +} + +static void instrument_coverage_switch(GumStalkerObserver *self, + gpointer start_address, + const cs_insn * from_insn, + gpointer * target) { + + UNUSED_PARAMETER(self); + UNUSED_PARAMETER(start_address); + + gsize fixup_offset; + + if (!g_hash_table_contains(coverage_blocks, GSIZE_TO_POINTER(*target)) && + !g_hash_table_contains(coverage_blocks, GSIZE_TO_POINTER(*target + 4))) { + + return; + + } + + if (instrument_is_deterministic(from_insn)) { return; } + /* * Since each block is prefixed with a restoration prologue, we need to be * able to begin execution at an offset into the block and execute both this * restoration prologue and the instrumented block without the coverage code. * We therefore layout our block as follows: * - * +-----+--------------------------+------------------+-----+-------------+ - * | LDP | COVERAGE INSTRUMENTATION | BR | LDP | TARGET CODE | - * +-----+--------------------------+------------------+-----+-------------+ + * +-----+------------------+-----+--------------------------+-------------+ + * | LDP | BR | LDP | COVERAGE INSTRUMENTATION | TARGET CODE | + * +-----+------------------+-----+--------------------------+-------------+ * - * ^ ^ ^ ^ - * | | | | - * A B C D + * ^ ^ ^ ^ + * | | | | + * A B C D * * Without instrumentation suppression, the block is either executed at point - * (A) if it is reached by an indirect branch (and registers need to be - * restored) or point (B) if it is reached by an direct branch (and hence the + * (C) if it is reached by an indirect branch (and registers need to be + * restored) or point (D) if it is reached by an direct branch (and hence the * registers don't need restoration). Similarly, we can start execution of the - * block at points (C) or (D) to achieve the same functionality, but without + * block at points (A) or (B) to achieve the same functionality, but without * executing the coverage instrumentation. * * In either case, Stalker will call us back with the address of the target - * block to be executed as the destination. It is not until later that Stalker - * will determine which branch type is required given the location of its - * instrumented code and add the `GUM_RESTORATION_PROLOG_SIZE` to the target - * address. Therefore, we need to map the address of point (A) to that of - * point (C) and also ensure that the offset between (A)->(B) and (C)->(D) is - * identical so that if Stalker can use an immediate call, it still branches - * to a valid offset. + * block to be executed as the destination. We can then check if the branch is + * a deterministic one and if so branch to point (C) or (D) rather than (A) + * or (B). We lay the code out in this fashion so that in the event we can't + * suppress coverage (the most likely), we can vector directly to the coverage + * instrumentation code and execute entirely without any branches. If we + * suppress the coverage, we simply branch beyond it instead. */ fixup_offset = GUM_RESTORATION_PROLOG_SIZE + G_STRUCT_OFFSET(afl_log_code_asm_t, restoration_prolog); @@ -249,7 +258,7 @@ void instrument_coverage_optimize(const cs_insn * instr, afl_log_code code = {0}; GumArm64Writer *cw = output->writer.arm64; - gpointer block_start; + gpointer block_start; guint64 area_offset = instrument_get_offset_hash(GUM_ADDRESS(instr->address)); gsize map_size_pow2; gsize area_offset_ror; -- cgit 1.4.1 From 452eb9f75bd37954f3944e4391c3b11663668583 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 8 Feb 2022 08:58:35 +0000 Subject: Fix initialization in non-persistent mode --- frida_mode/src/entry.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'frida_mode/src') diff --git a/frida_mode/src/entry.c b/frida_mode/src/entry.c index 995f765f..5d9bcaaf 100644 --- a/frida_mode/src/entry.c +++ b/frida_mode/src/entry.c @@ -78,6 +78,12 @@ void entry_init(void) { void entry_start(void) { + if (persistent_start == 0) { + + ranges_exclude(); + stalker_trust(); + + } if (entry_point == 0) { entry_launch(); } } -- cgit 1.4.1 From cf853fb2494912a1c4b531ffcf302843266639b7 Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 8 Feb 2022 20:15:48 +0100 Subject: reintroduce AFL_PERSISTENT and AFL_DEFER_FORKSRV --- docs/Changelog.md | 3 +++ docs/env_variables.md | 10 +++++++--- frida_mode/src/entry.c | 1 + src/afl-common.c | 22 ++++++++-------------- src/afl-fuzz-init.c | 10 ++++++++-- src/afl-fuzz-state.c | 8 ++++---- src/afl-fuzz.c | 24 ++---------------------- 7 files changed, 33 insertions(+), 45 deletions(-) (limited to 'frida_mode/src') diff --git a/docs/Changelog.md b/docs/Changelog.md index bdb2dda3..142b85b3 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -14,6 +14,9 @@ sending a mail to . - afl-fuzz: - new commandline options -g/G to set min/max length of generated fuzz inputs + - reintroduced AFL_PERSISTENT and AFL_DEFER_FORKSRV to allow + persistent mode and manual forkserver support if these are not + in the target binary (e.g. are in a shared library) - frida_mode: - update to new frida release, handles now c++ throw/catch diff --git a/docs/env_variables.md b/docs/env_variables.md index f7ad4ff9..06c08f31 100644 --- a/docs/env_variables.md +++ b/docs/env_variables.md @@ -532,9 +532,13 @@ checks or alter some of the more exotic semantics of the tool: - Setting `AFL_TRY_AFFINITY` tries to attempt binding to a specific CPU core on Linux systems, but will not terminate if that fails. - - Outdated environment variables that are not supported anymore: - - `AFL_DEFER_FORKSRV` - - `AFL_PERSISTENT` + - The following environment variables are only needed if you implemented + your own forkserver or persistent mode, or if __AFL_LOOP or __AFL_INIT + are in a shared library and not the main binary: + - `AFL_DEFER_FORKSRV` enforces a deferred forkserver even if none was + detected in the target binary + - `AFL_PERSISTENT` enforces persistent mode even if none was detected + in the target binary ## 5) Settings for afl-qemu-trace diff --git a/frida_mode/src/entry.c b/frida_mode/src/entry.c index 5d9bcaaf..05af7ebb 100644 --- a/frida_mode/src/entry.c +++ b/frida_mode/src/entry.c @@ -84,6 +84,7 @@ void entry_start(void) { stalker_trust(); } + if (entry_point == 0) { entry_launch(); } } diff --git a/src/afl-common.c b/src/afl-common.c index e684302a..7c074acc 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -63,8 +63,7 @@ u32 check_binary_signatures(u8 *fn) { if (f_data == MAP_FAILED) { PFATAL("Unable to mmap file '%s'", fn); } close(fd); - if (memmem(f_data, f_len, PERSIST_SIG, strlen(PERSIST_SIG) + 1) || - getenv(PERSIST_ENV_VAR)) { + if (memmem(f_data, f_len, PERSIST_SIG, strlen(PERSIST_SIG) + 1)) { if (!be_quiet) { OKF(cPIN "Persistent mode binary detected."); } setenv(PERSIST_ENV_VAR, "1", 1); @@ -72,11 +71,9 @@ u32 check_binary_signatures(u8 *fn) { } else if (getenv("AFL_PERSISTENT")) { - if (!be_quiet) { - - WARNF("AFL_PERSISTENT is no longer supported and may misbehave!"); - - } + if (!be_quiet) { OKF(cPIN "Persistent mode enforced."); } + setenv(PERSIST_ENV_VAR, "1", 1); + ret = 1; } else if (getenv("AFL_FRIDA_PERSISTENT_ADDR")) { @@ -91,8 +88,7 @@ u32 check_binary_signatures(u8 *fn) { } - if (memmem(f_data, f_len, DEFER_SIG, strlen(DEFER_SIG) + 1) || - getenv(DEFER_ENV_VAR)) { + if (memmem(f_data, f_len, DEFER_SIG, strlen(DEFER_SIG) + 1)) { if (!be_quiet) { OKF(cPIN "Deferred forkserver binary detected."); } setenv(DEFER_ENV_VAR, "1", 1); @@ -100,11 +96,9 @@ u32 check_binary_signatures(u8 *fn) { } else if (getenv("AFL_DEFER_FORKSRV")) { - if (!be_quiet) { - - WARNF("AFL_DEFER_FORKSRV is no longer supported and may misbehave!"); - - } + if (!be_quiet) { OKF(cPIN "Deferred forkserver enforced."); } + setenv(DEFER_ENV_VAR, "1", 1); + ret += 2; } diff --git a/src/afl-fuzz-init.c b/src/afl-fuzz-init.c index 45f28d4b..05a654c8 100644 --- a/src/afl-fuzz-init.c +++ b/src/afl-fuzz-init.c @@ -2822,7 +2822,11 @@ void check_binary(afl_state_t *afl, u8 *fname) { } else if (getenv("AFL_PERSISTENT")) { - WARNF("AFL_PERSISTENT is no longer supported and may misbehave!"); + OKF(cPIN "Persistent mode enforced."); + setenv(PERSIST_ENV_VAR, "1", 1); + afl->persistent_mode = 1; + afl->fsrv.persistent_mode = 1; + afl->shmem_testcase_mode = 1; } else if (getenv("AFL_FRIDA_PERSISTENT_ADDR")) { @@ -2843,7 +2847,9 @@ void check_binary(afl_state_t *afl, u8 *fname) { } else if (getenv("AFL_DEFER_FORKSRV")) { - WARNF("AFL_DEFER_FORKSRV is no longer supported and may misbehave!"); + OKF(cPIN "Deferred forkserver enforced."); + setenv(DEFER_ENV_VAR, "1", 1); + afl->deferred_mode = 1; } diff --git a/src/afl-fuzz-state.c b/src/afl-fuzz-state.c index 115e62de..129e4c8b 100644 --- a/src/afl-fuzz-state.c +++ b/src/afl-fuzz-state.c @@ -486,15 +486,15 @@ void read_afl_environment(afl_state_t *afl, char **envp) { afl_environment_variable_len)) { - afl->min_length = atoi( - (u8 *)get_afl_env(afl_environment_variables[i])); + afl->min_length = + atoi((u8 *)get_afl_env(afl_environment_variables[i])); } else if (!strncmp(env, "AFL_INPUT_LEN_MAX", afl_environment_variable_len)) { - afl->max_length = atoi( - (u8 *)get_afl_env(afl_environment_variables[i])); + afl->max_length = + atoi((u8 *)get_afl_env(afl_environment_variables[i])); } diff --git a/src/afl-fuzz.c b/src/afl-fuzz.c index 7e3b3c94..c923cc9d 100644 --- a/src/afl-fuzz.c +++ b/src/afl-fuzz.c @@ -294,8 +294,8 @@ static void usage(u8 *argv0, int more_help) { " 'signalfx' and 'influxdb'\n" "AFL_TESTCACHE_SIZE: use a cache for testcases, improves performance (in MB)\n" "AFL_TMPDIR: directory to use for input file generation (ramdisk recommended)\n" - //"AFL_PERSISTENT: not supported anymore -> no effect, just a warning\n" - //"AFL_DEFER_FORKSRV: not supported anymore -> no effect, just a warning\n" + "AFL_PERSISTENT: enforce persistent mode (if __AFL_LOOP is in a shared lib\n" + "AFL_DEFER_FORKSRV: enforced deferred forkserver (__AFL_INIT is in a .so\n" "\n" ); @@ -1920,26 +1920,6 @@ int main(int argc, char **argv_orig, char **envp) { check_binary(afl, argv[optind]); - if (getenv(PERSIST_ENV_VAR) && !afl->persistent_mode) { - - WARNF( - "Persistent mode environment variable detected, forcing persistent " - "mode!"); - afl->persistent_mode = 1; - afl->fsrv.persistent_mode = 1; - afl->shmem_testcase_mode = 1; - - } - - if (getenv(DEFER_ENV_VAR) && !afl->deferred_mode) { - - WARNF( - "Deferred forkserver mode environment variable detected, forcing " - "deferred forkserver!"); - afl->deferred_mode = 1; - - } - #ifdef AFL_PERSISTENT_RECORD if (unlikely(afl->fsrv.persistent_record)) { -- cgit 1.4.1