diff options
Diffstat (limited to 'frida_mode')
65 files changed, 4279 insertions, 689 deletions
diff --git a/frida_mode/DEBUGGING.md b/frida_mode/DEBUGGING.md new file mode 100644 index 00000000..69663510 --- /dev/null +++ b/frida_mode/DEBUGGING.md @@ -0,0 +1,160 @@ +If you are using FRIDA mode and have hit some problems, then this guide may help +you to diagnose any problems you are encountering. This assumes you have +followed the [osx-lib](#test/osx-lib) example to start fuzzing your target. + +It should be noted that attempting to debug code using gdb which has been +instrumented in FRIDA is unlikely to be successful since the debugger will be +inserting breakpoints by patching the code in memory. FRIDA works by reading +this code and generating an instrumented copy to execute. In any case, unless +you are very familiar with the implementation of Stalker, the instrumented code +generated by FRIDA is likely to be very difficult to follow. For this reason, +the following debugging strategies are outlined below. + +By convention below all files should be provided with their path (they are +omitted for readability) and all items in `<braces>` are placeholders and should +be replaced accordingly. + +# Select your version +Test with both the `dev` and `stable` branches of AFL++. The `dev` branch should +have the very latest version containing any fixes for identified issues. The +`stable` branch is updated less frequently, but equally might avoid a problem if +a regression has been introduced into the `dev` branch. + +# Enable Diagnostic Information +- Run your target specifying the `AFL_DEBUG_CHILD=1` environment variable. This + will print a lot more diagnostic information to the screen when the target + starts up. If you have a simple configuration issue then you will likely see a + warning or error message in the output. + +# Check your Test Harness +If any of the following steps fail, then there is a problem with your test +harness, or your target library. Since this is running without FRIDA mode or +`afl-fuzz` that greatly reduces the search area for your defect. This is why it +is *VERY* important to carry out these basic steps first before taking on the +additional complexity of debugging with FRIDA mode or `afl-fuzz`. + +- Run your harness outside of the fuzzer, passing it a representative seed as + it's input `./harness <input>`. +- Pass you harness multiple seeds to check that it is stable when running + multiple tests as it will when running in fork server mode `./harness <input1> + <intput2>`. +- Build your test harness with `CFLAGS=-fsanitize=address` and + `LDFLAGS=-fsanitize=address`. Then run it again with multiple inputs to check + for errors (note that when fuzzing your harness should not be built with any + sanitizer options). + +# Check the Samples +FRIDA mode contains a number of different sample targets in the `test` folder. +Have a look throught these and find one which is similar to your real target. +Check whether you have any issues running the sample target and make sure you +compare the command line used to launch the sample with that you are using to +launch your real target very carefully to check for any differences. If possible +start with one of these samples and gradually make changes one at a time +re-testing as you go until you have migrated it to run your own target. + +# FRIDA Mode +## Basic +First just try running your target with `LD_PRELOAD=afl-frida-trace.so ./harness + <input>`. An error here means that your defect occurs when running with just + FRIDA mode and isn't related to `afl-fuzz`. + +Now you can try commenting out the implementation of `LLVMFuzzerTestOneInput` so +that the harness doesn't actually run your target library. This may also aid in +narrowing down the problem. +```c +int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size){ + // fpn_crashme(data, size); + return 0; +} + +``` + +## Persistent Mode +If your target is ok running in basic mode, you can try running it in persistent +mode (if that is the configuration you are having issues with) as follows (again +outside of afl-fuzz). This time you will want to run it inside a debugger so +that you can use the debugger to send the `SIGCONT` signals (by continuing) +usually sent by `afl-fuzz` on each iteration. + +```bash +gdb \ + --ex 'set environment __AFL_PERSISTENT=1' \ + --ex 'set environment AFL_FRIDA_PERSISTENT_CNT=3' \ + --ex 'set environment LD_PRELOAD=afl-frida-trace.so' \ + --ex 'set environment AFL_FRIDA_PERSISTENT_ADDR=<entry_address>' \ + --args ./harness <input> +``` +Note we have to manually set the `__AFL_PERSISTENT` environment variable which +is usually passed by `afl-fuzz`. + +Note that setting breakpoints etc is likely to interfere with FRIDA and cause +spurious errors. + +If this is successful, you can try additionally loading the hook library: +```bash +gdb \ + --ex 'set environment __AFL_PERSISTENT=1' \ + --ex 'set environment AFL_FRIDA_PERSISTENT_CNT=3' \ + --ex 'set environment LD_PRELOAD=afl-frida-trace.so' \ + --ex 'set environment AFL_FRIDA_PERSISTENT_ADDR=<entry_address>' \ + --ex 'set environment AFL_FRIDA_PERSISTENT_HOOK=frida_hook.so' + --args ./harness <input> +``` +Note that the format of the hook used for FRIDA mode is subtly different to that +used when running in QEMU mode as shown below. Thus the DSO used for the hook is +not interchangeable. + +```c +void afl_persistent_hook(GumCpuContext *regs, uint8_t *input_buf, + uint32_t input_buf_len); + +void afl_persistent_hook(struct x86_64_regs *regs, uint64_t guest_base, + uint8_t *input_buf, uint32_t input_buf_len); +``` + +## ASAN +It is also possible to enable ASAN (if that is the configuration you are having +issues with) without having to use `afl-fuzz`. This can be done as follows (note +that the name of the asan DSO may need to be changed depending on your +platform). Note that the asan DSO must appear first in the `LD_PRELOAD` +environment variable: + +```bash +LD_PRELOAD=libclang_rt.asan-x86_64.so:afl-frida-trace.so \ +ASAN_OPTIONS=detect_leaks=false,halt_on_error=0 \ +AFL_USE_FASAN=1 \ + ./harness <input> +``` + +Note that care should be taken to ensure that if you set `AFL_INST_LIBS=1`, you +use `AFL_FRIDA_INST_RANGES` or `AFL_FRIDA_EXCLUDE_RANGES` to exclude the ASAN +DSO from coverage. Failure to do so will result in ASAN attempting to sanitize +itself and as a result detecting failures when it attempts to update the shadow +maps. + +# Printf +If you have an idea of where things are going wrong for you, then don't be +scared to add `printf` statements to either AFL++ or FRIDA mode itself to show +more diagnostic information. Just be sure to set `AFL_DEBUG=1` and +`AFL_DEBUG_CHILD=1` when you are testing it. + +# Core Dumps +Lastly, if your defect only occurs when using `afl-fuzz` (e.g. when using +`CMPLOG` which cannot be tested outside of `afl-fuzz` due to it's need for a +shared memory mapping being created for it to record its data), it is possible +to enable the creation of a core dump for post-mortem analysis. + +Firstly check your `/proc/sys/kernel/core_pattern` configuration is simply set +to a filename (AFL++ encourages you to set it to the value 'core' in any case +since it doesn't want any handler applications getting in the way). Next set +`ulimit -c unlimited` to remove any size limitations for core files. Lastly, +when you `afl-fuzz` set the environment variable `AFL_DEBUG=1` to enable the +creation of the `core` file. The file should be created in the working directory +of the target application. If there is an existing `core` file aleady there, +then it may not be overwritten. + +# Reach out +Get in touch on discord and ask for help. The groups are pretty active so +someone may well be able to offer some advice. Better still, if you are able to +create a minimal reproducer for your problem it will make it easier to diagnose +the issue. diff --git a/frida_mode/GNUmakefile b/frida_mode/GNUmakefile index 6b17982d..3e35e2f6 100644 --- a/frida_mode/GNUmakefile +++ b/frida_mode/GNUmakefile @@ -83,16 +83,7 @@ ifndef OS $(error "Operating system unsupported") endif -ifeq "$(ARCH)" "arm64" -# 15.0.0 Not released for aarch64 yet -GUM_DEVKIT_VERSION=14.2.18 -else -ifeq "$(ARCH)" "armhf" -GUM_DEVKIT_VERSION=14.2.18 -else -GUM_DEVKIT_VERSION=15.0.0 -endif -endif +GUM_DEVKIT_VERSION=15.0.16 GUM_DEVKIT_FILENAME=frida-gumjs-devkit-$(GUM_DEVKIT_VERSION)-$(OS)-$(ARCH).tar.xz GUM_DEVKIT_URL="https://github.com/frida/frida/releases/download/$(GUM_DEVKIT_VERSION)/$(GUM_DEVKIT_FILENAME)" @@ -166,7 +157,7 @@ $(GUM_DEVKIT_TARBALL): $(FRIDA_GUM_DEVKIT_COMPRESSED_TARBALL)| $(FRIDA_BUILD_DIR cp -v $< $@ else $(GUM_DEVKIT_TARBALL): | $(FRIDA_BUILD_DIR) - wget -O $@ $(GUM_DEVKIT_URL) + wget -O $@ $(GUM_DEVKIT_URL) || curl -L -o $@ $(GUM_DEVKIT_URL) endif $(GUM_DEVIT_LIBRARY): $(GUM_DEVKIT_TARBALL) diff --git a/frida_mode/README.md b/frida_mode/README.md index 63959af6..165f8089 100644 --- a/frida_mode/README.md +++ b/frida_mode/README.md @@ -131,7 +131,8 @@ instances run CMPLOG mode and instrumentation of the binary is less frequent (only on CMP, SUB and CALL instructions) performance is not quite so critical. ## Advanced configuration options - +* `AFL_FRIDA_INST_COVERAGE_FILE` - File to write DynamoRio format coverage +information (e.g. to be loaded within IDA lighthouse). * `AFL_FRIDA_INST_DEBUG_FILE` - File to write raw assembly of original blocks and their instrumented counterparts during block compilation. ``` @@ -161,7 +162,12 @@ instrumentation (the default where available). Required to use `AFL_FRIDA_INST_TRACE`. * `AFL_FRIDA_INST_NO_PREFETCH` - Disable prefetching. By default the child will report instrumented blocks back to the parent so that it can also instrument -them and they be inherited by the next child on fork. +them and they be inherited by the next child on fork, implies +`AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH`. +* `AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH` - Disable prefetching of stalker +backpatching information. By default the child will report applied backpatches +to the parent so that they can be applied and then be inherited by the next +child on fork. * `AFL_FRIDA_INST_SEED` - Sets the initial seed for the hash function used to generate block (and hence edge) IDs. Setting this to a constant value may be useful for debugging purposes, e.g. investigating unstable edges. @@ -169,6 +175,9 @@ useful for debugging purposes, e.g. investigating unstable edges. implies `AFL_FRIDA_INST_NO_OPTIMIZE`. * `AFL_FRIDA_INST_TRACE_UNIQUE` - As per `AFL_FRIDA_INST_TRACE`, but each edge is logged only once, requires `AFL_FRIDA_INST_NO_OPTIMIZE`. +* `AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE` - File to write DynamoRio format +coverage information for unstable edges (e.g. to be loaded within IDA +lighthouse). * `AFL_FRIDA_OUTPUT_STDOUT` - Redirect the standard output of the target application to the named file (supersedes the setting of `AFL_DEBUG_CHILD`) * `AFL_FRIDA_OUTPUT_STDERR` - Redirect the standard error of the target @@ -188,6 +197,11 @@ gdb \ --args <my-executable> [my arguments] ``` +* `AFL_FRIDA_SECCOMP_FILE` - Write a log of any syscalls made by the target to +the specified file. +* `AFL_FRIDA_STALKER_IC_ENTRIES` - Configure the number of inline cache entries +stored along-side branch instructions which provide a cache to avoid having to +call back into FRIDA to find the next block. Default is 32. * `AFL_FRIDA_STATS_FILE` - Write statistics information about the code being instrumented to the given file name. The statistics are written only for the child process when new block is instrumented (when the @@ -197,67 +211,70 @@ the existing blocks instrumented have been executed in a different order. ``` stats ----- -Index: 2 -Pid: 1815944 -Time: 2021-05-28 15:26:41 -Blocks: 1985 -Instructions: 9192 -Avg Instructions / Block: 4 - -Call Immediates: 391 (4.25%) -Call Immediates Excluded: 65 (0.71%) -Call Register: 0 (0.00%) -Call Memory: 0 (0.00%) - -Jump Immediates: 202 (2.20%) -Jump Register: 10 (0.11%) -Jump Memory: 12 (0.13%) - -Conditional Jump Immediates: 1210 (13.16%) -Conditional Jump CX Immediate: 0 (0.00%) -Conditional Jump Register: 0 (0.00%) -Conditional Jump Memory: 0 (0.00%) - -Returns: 159 (0.00%) - -Rip Relative: 247 (0.00%) - +Time 2021-07-21 11:45:49 +Elapsed 1 seconds + + +Transitions cumulative delta +----------- ---------- ----- +total 753619 17645 +call_imm 9193 ( 1.22%) 344 ( 1.95%) [ 344/s] +call_reg 0 ( 0.00%) 0 ( 0.00%) [ 0/s] +call_mem 0 ( 0.00%) 0 ( 0.00%) [ 0/s] +ret_slow_path 67974 ( 9.02%) 2988 (16.93%) [ 2988/s] +post_call_invoke 7996 ( 1.06%) 299 ( 1.69%) [ 299/s] +excluded_call_imm 3804 ( 0.50%) 200 ( 1.13%) [ 200/s] +jmp_imm 5445 ( 0.72%) 255 ( 1.45%) [ 255/s] +jmp_reg 42081 ( 5.58%) 1021 ( 5.79%) [ 1021/s] +jmp_mem 578092 (76.71%) 10956 (62.09%) [ 10956/s] +jmp_cond_imm 38951 ( 5.17%) 1579 ( 8.95%) [ 1579/s] +jmp_cond_mem 0 ( 0.00%) 0 ( 0.00%) [ 0/s] +jmp_cond_reg 0 ( 0.00%) 0 ( 0.00%) [ 0/s] +jmp_cond_jcxz 0 ( 0.00%) 0 ( 0.00%) [ 0/s] +jmp_continuation 84 ( 0.01%) 3 ( 0.02%) [ 3/s] + + +Instrumentation +--------------- +Instructions 7907 +Blocks 1764 +Avg Instructions / Block 4 + + +EOB Instructions +---------------- +Total 1763 (22.30%) +Call Immediates 358 ( 4.53%) +Call Immediates Excluded 74 ( 0.94%) +Call Register 0 ( 0.00%) +Call Memory 0 ( 0.00%) +Jump Immediates 176 ( 2.23%) +Jump Register 8 ( 0.10%) +Jump Memory 10 ( 0.13%) +Conditional Jump Immediates 1051 (13.29%) +Conditional Jump CX Immediate 0 ( 0.00%) +Conditional Jump Register 0 ( 0.00%) +Conditional Jump Memory 0 ( 0.00%) +Returns 160 ( 2.02%) + + +Relocated Instructions +---------------------- +Total 232 ( 2.93%) +addsd 2 ( 0.86%) +cmp 46 (19.83%) +comisd 2 ( 0.86%) +divsd 2 ( 0.86%) +divss 2 ( 0.86%) +lea 142 (61.21%) +mov 32 (13.79%) +movsd 2 ( 0.86%) +ucomisd 2 ( 0.86%) ``` * `AFL_FRIDA_STATS_INTERVAL` - The maximum frequency to output statistics information. Stats will be written whenever they are updated if the given interval has elapsed since last time they were written. -* `AFL_FRIDA_STATS_TRANSITIONS` - Also dump the internal stalker counters to -stderr when the regular stats are written. Note that these stats are reset in -the child each time a new fork occurs since they are not stored in shared -memory. Unfortunately, these stats are internal to stalker, so this is the best -we can do for now. -``` -stats ------ -Index: 2 -Pid: 1816794 -Time: 2021-05-28 15:26:41 - - -total_transitions: 786 - call_imms: 97 - call_regs: 0 - call_mems: 0 - post_call_invokes: 86 - excluded_call_imms: 29 - ret_slow_paths: 23 - jmp_imms: 58 - jmp_mems: 7 - jmp_regs: 26 - - jmp_cond_imms: 460 - jmp_cond_mems: 0 - jmp_cond_regs: 0 - jmp_cond_jcxzs: 0 - - jmp_continuations: 0 -``` ## FASAN - Frida Address Sanitizer Mode Frida mode also supports FASAN. The design of this is actually quite simple and very similar to that used when instrumenting applications compiled from source. @@ -307,10 +324,13 @@ library and call a target function within it. The dependent library can either be loaded in using `dlopen` and `dlsym` in a function marked `__attribute__((constructor()))` or the test harness can simply be linked against it. It is important that the target library is loaded before execution -of `main`, since this is the point that FRIDA mode is initialized. Otherwise, it +of `main`, since this is the point where FRIDA mode is initialized. Otherwise, it will not be possible to configure coverage for the test library using `AFL_FRIDA_INST_RANGES` or similar. +# Debugging +Please refer to the [debugging](#debugging) guide for assistant should you +encounter problems with FRIDA mode. ## TODO diff --git a/frida_mode/Scripting.md b/frida_mode/Scripting.md index 5467db99..f6017fad 100644 --- a/frida_mode/Scripting.md +++ b/frida_mode/Scripting.md @@ -80,7 +80,6 @@ Afl.setInstrumentEnableTracing(); Afl.setInstrumentTracingUnique(); Afl.setStatsFile("/tmp/stats.txt"); Afl.setStatsInterval(1); -Afl.setStatsTransitions(); /* *ALWAYS* call this when you have finished all your configuration */ Afl.done(); @@ -834,13 +833,6 @@ class Afl { } /** - * See `AFL_FRIDA_STATS_TRANSITIONS` - */ - public static setStatsTransitions(): void { - Afl.jsApiSetStatsTransitions(); - } - - /** * See `AFL_FRIDA_OUTPUT_STDERR`. This function takes a single `string` as * an argument. */ diff --git a/frida_mode/frida.map b/frida_mode/frida.map index 7223d50e..0fc48aa6 100644 --- a/frida_mode/frida.map +++ b/frida_mode/frida.map @@ -10,6 +10,7 @@ js_api_error; js_api_set_debug_maps; js_api_set_entrypoint; + js_api_set_instrument_coverage_file; js_api_set_instrument_debug_file; js_api_set_instrument_jit; js_api_set_instrument_libraries; @@ -17,16 +18,19 @@ js_api_set_instrument_seed; js_api_set_instrument_trace; js_api_set_instrument_trace_unique; + js_api_set_instrument_unstable_coverage_file; js_api_set_persistent_address; js_api_set_persistent_count; js_api_set_persistent_debug; js_api_set_persistent_hook; js_api_set_persistent_return; + js_api_set_prefetch_backpatch_disable; js_api_set_prefetch_disable; + js_api_set_seccomp_file; js_api_set_stalker_callback; + js_api_set_stalker_ic_entries; js_api_set_stats_file; js_api_set_stats_interval; - js_api_set_stats_transitions; js_api_set_stderr; js_api_set_stdout; diff --git a/frida_mode/include/entry.h b/frida_mode/include/entry.h index cbc5c8c7..3f0a4ecc 100644 --- a/frida_mode/include/entry.h +++ b/frida_mode/include/entry.h @@ -4,7 +4,8 @@ #include "frida-gumjs.h" extern guint64 entry_point; -extern gboolean entry_reached; +extern gboolean entry_compiled; +extern gboolean entry_run; void entry_config(void); diff --git a/frida_mode/include/instrument.h b/frida_mode/include/instrument.h index 29f14da9..909b2a2c 100644 --- a/frida_mode/include/instrument.h +++ b/frida_mode/include/instrument.h @@ -6,11 +6,13 @@ #include "config.h" extern char * instrument_debug_filename; +extern char * instrument_coverage_filename; extern gboolean instrument_tracing; extern gboolean instrument_optimize; extern gboolean instrument_unique; extern __thread guint64 instrument_previous_pc; extern guint64 instrument_hash_zero; +extern char * instrument_coverage_unstable_filename; extern gboolean instrument_use_fixed_seed; extern guint64 instrument_fixed_seed; @@ -38,6 +40,15 @@ void instrument_debug_end(GumStalkerOutput *output); void instrument_flush(GumStalkerOutput *output); gpointer instrument_cur(GumStalkerOutput *output); +void instrument_coverage_config(void); +void instrument_coverage_init(void); +void instrument_coverage_start(uint64_t address); +void instrument_coverage_end(uint64_t address); + +void instrument_coverage_unstable(guint64 edge, guint64 previous_rip, + guint64 previous_end, guint64 current_rip, + guint64 current_end); + void instrument_on_fork(); guint64 instrument_get_offset_hash(GumAddress current_rip); diff --git a/frida_mode/include/prefetch.h b/frida_mode/include/prefetch.h index 835d5e8a..d1ea5a31 100644 --- a/frida_mode/include/prefetch.h +++ b/frida_mode/include/prefetch.h @@ -4,6 +4,7 @@ #include "frida-gumjs.h" extern gboolean prefetch_enable; +extern gboolean prefetch_backpatch; void prefetch_config(void); void prefetch_init(void); diff --git a/frida_mode/include/ranges.h b/frida_mode/include/ranges.h index 2eb9b355..0220a59d 100644 --- a/frida_mode/include/ranges.h +++ b/frida_mode/include/ranges.h @@ -10,7 +10,7 @@ extern gboolean ranges_inst_jit; void ranges_config(void); void ranges_init(void); -gboolean range_is_excluded(gpointer address); +gboolean range_is_excluded(GumAddress address); void ranges_exclude(); diff --git a/frida_mode/include/seccomp.h b/frida_mode/include/seccomp.h new file mode 100644 index 00000000..7e8a7d25 --- /dev/null +++ b/frida_mode/include/seccomp.h @@ -0,0 +1,444 @@ +#ifndef _SECCOMP_H +#define _SECCOMP_H + +#ifndef __APPLE__ + + #include <stdint.h> + #include <linux/filter.h> + + #include "frida-gumjs.h" + + /******************************************************************************/ + #define PR_SET_NO_NEW_PRIVS 38 + + #define SECCOMP_SET_MODE_STRICT 0 + #define SECCOMP_SET_MODE_FILTER 1 + #define SECCOMP_GET_ACTION_AVAIL 2 + #define SECCOMP_GET_NOTIF_SIZES 3 + + #define SECCOMP_IOC_MAGIC '!' + #define SECCOMP_IO(nr) _IO(SECCOMP_IOC_MAGIC, nr) + #define SECCOMP_IOR(nr, type) _IOR(SECCOMP_IOC_MAGIC, nr, type) + #define SECCOMP_IOW(nr, type) _IOW(SECCOMP_IOC_MAGIC, nr, type) + #define SECCOMP_IOWR(nr, type) _IOWR(SECCOMP_IOC_MAGIC, nr, type) + + /* Flags for seccomp notification fd ioctl. */ + #define SECCOMP_IOCTL_NOTIF_RECV SECCOMP_IOWR(0, struct seccomp_notif) + #define SECCOMP_IOCTL_NOTIF_SEND SECCOMP_IOWR(1, struct seccomp_notif_resp) + #define SECCOMP_IOCTL_NOTIF_ID_VALID SECCOMP_IOW(2, __u64) + + #define SECCOMP_FILTER_FLAG_NEW_LISTENER (1UL << 3) + #define SECCOMP_RET_ALLOW 0x7fff0000U + #define SECCOMP_RET_USER_NOTIF 0x7fc00000U + + #define SYS_seccomp __NR_seccomp + #ifndef __NR_seccomp + #if defined(__arm__) + #define __NR_seccomp 383 + #elif defined(__aarch64__) + #define __NR_seccomp 277 + #elif defined(__x86_64__) + #define __NR_seccomp 317 + #elif defined(__i386__) + #define __NR_seccomp 354 + #else + #pragma error "Unsupported architecture" + #endif + #endif + + #define SECCOMP_USER_NOTIF_FLAG_CONTINUE (1UL << 0) + +struct seccomp_notif_resp { + + __u64 id; + __s64 val; + __s32 error; + __u32 flags; + +}; + +struct seccomp_data { + + int nr; + __u32 arch; + __u64 instruction_pointer; + __u64 args[6]; + +}; + +struct seccomp_notif { + + __u64 id; + __u32 pid; + __u32 flags; + struct seccomp_data data; + +}; + +struct seccomp_notif_sizes { + + __u16 seccomp_notif; + __u16 seccomp_notif_resp; + __u16 seccomp_data; + +}; + + /******************************************************************************/ + + #define SECCOMP_SOCKET_SEND_FD 0x1D3 + #define SECCOMP_SOCKET_RECV_FD 0x1D4 + + #define SECCOMP_OUTPUT_FILE_FD 0x1D5 + #define SECCOMP_PARENT_EVENT_FD 0x1D6 + +enum { + + SYS_READ = 0, + SYS_WRITE = 1, + SYS_OPEN = 2, + SYS_CLOSE = 3, + SYS_STAT = 4, + SYS_FSTAT = 5, + SYS_LSTAT = 6, + SYS_POLL = 7, + SYS_LSEEK = 8, + SYS_MMAP = 9, + SYS_MPROTECT = 10, + SYS_MUNMAP = 11, + SYS_BRK = 12, + SYS_RT_SIGACTION = 13, + SYS_RT_SIGPROCMASK = 14, + SYS_RT_SIGRETURN = 15, + SYS_IOCTL = 16, + SYS_PREAD64 = 17, + SYS_PWRITE64 = 18, + SYS_READV = 19, + SYS_WRITEV = 20, + SYS_ACCESS = 21, + SYS_PIPE = 22, + SYS_SELECT = 23, + SYS_SCHED_YIELD = 24, + SYS_MREMAP = 25, + SYS_MSYNC = 26, + SYS_MINCORE = 27, + SYS_MADVISE = 28, + SYS_SHMGET = 29, + SYS_SHMAT = 30, + SYS_SHMCTL = 31, + SYS_DUP = 32, + SYS_DUP2 = 33, + SYS_PAUSE = 34, + SYS_NANOSLEEP = 35, + SYS_GETITIMER = 36, + SYS_ALARM = 37, + SYS_SETITIMER = 38, + SYS_GETPID = 39, + SYS_SENDFILE = 40, + SYS_SOCKET = 41, + SYS_CONNECT = 42, + SYS_ACCEPT = 43, + SYS_SENDTO = 44, + SYS_RECVFROM = 45, + SYS_SENDMSG = 46, + SYS_RECVMSG = 47, + SYS_SHUTDOWN = 48, + SYS_BIND = 49, + SYS_LISTEN = 50, + SYS_GETSOCKNAME = 51, + SYS_GETPEERNAME = 52, + SYS_SOCKETPAIR = 53, + SYS_SETSOCKOPT = 54, + SYS_GETSOCKOPT = 55, + SYS_CLONE = 56, + SYS_FORK = 57, + SYS_VFORK = 58, + SYS_EXECVE = 59, + SYS_EXIT = 60, + SYS_WAIT4 = 61, + SYS_KILL = 62, + SYS_UNAME = 63, + SYS_SEMGET = 64, + SYS_SEMOP = 65, + SYS_SEMCTL = 66, + SYS_SHMDT = 67, + SYS_MSGGET = 68, + SYS_MSGSND = 69, + SYS_MSGRCV = 70, + SYS_MSGCTL = 71, + SYS_FCNTL = 72, + SYS_FLOCK = 73, + SYS_FSYNC = 74, + SYS_FDATASYNC = 75, + SYS_TRUNCATE = 76, + SYS_FTRUNCATE = 77, + SYS_GETDENTS = 78, + SYS_GETCWD = 79, + SYS_CHDIR = 80, + SYS_FCHDIR = 81, + SYS_RENAME = 82, + SYS_MKDIR = 83, + SYS_RMDIR = 84, + SYS_CREAT = 85, + SYS_LINK = 86, + SYS_UNLINK = 87, + SYS_SYMLINK = 88, + SYS_READLINK = 89, + SYS_CHMOD = 90, + SYS_FCHMOD = 91, + SYS_CHOWN = 92, + SYS_FCHOWN = 93, + SYS_LCHOWN = 94, + SYS_UMASK = 95, + SYS_GETTIMEOFDAY = 96, + SYS_GETRLIMIT = 97, + SYS_GETRUSAGE = 98, + SYS_SYSINFO = 99, + SYS_TIMES = 100, + SYS_PTRACE = 101, + SYS_GETUID = 102, + SYS_SYSLOG = 103, + SYS_GETGID = 104, + SYS_SETUID = 105, + SYS_SETGID = 106, + SYS_GETEUID = 107, + SYS_GETEGID = 108, + SYS_SETPGID = 109, + SYS_GETPPID = 110, + SYS_GETPGRP = 111, + SYS_SETSID = 112, + SYS_SETREUID = 113, + SYS_SETREGID = 114, + SYS_GETGROUPS = 115, + SYS_SETGROUPS = 116, + SYS_SETRESUID = 117, + SYS_GETRESUID = 118, + SYS_SETRESGID = 119, + SYS_GETRESGID = 120, + SYS_GETPGID = 121, + SYS_SETFSUID = 122, + SYS_SETFSGID = 123, + SYS_GETSID = 124, + SYS_CAPGET = 125, + SYS_CAPSET = 126, + SYS_RT_SIGPENDING = 127, + SYS_RT_SIGTIMEDWAIT = 128, + SYS_RT_SIGQUEUEINFO = 129, + SYS_RT_SIGSUSPEND = 130, + SYS_SIGALTSTACK = 131, + SYS_UTIME = 132, + SYS_MKNOD = 133, + SYS_USELIB = 134, + SYS_PERSONALITY = 135, + SYS_USTAT = 136, + SYS_STATFS = 137, + SYS_FSTATFS = 138, + SYS_SYSFS = 139, + SYS_GETPRIORITY = 140, + SYS_SETPRIORITY = 141, + SYS_SCHED_SETPARAM = 142, + SYS_SCHED_GETPARAM = 143, + SYS_SCHED_SETSCHEDULER = 144, + SYS_SCHED_GETSCHEDULER = 145, + SYS_SCHED_GET_PRIORITY_MAX = 146, + SYS_SCHED_GET_PRIORITY_MIN = 147, + SYS_SCHED_RR_GET_INTERVAL = 148, + SYS_MLOCK = 149, + SYS_MUNLOCK = 150, + SYS_MLOCKALL = 151, + SYS_MUNLOCKALL = 152, + SYS_VHANGUP = 153, + SYS_MODIFY_LDT = 154, + SYS_PIVOT_ROOT = 155, + SYS__SYSCTL = 156, + SYS_PRCTL = 157, + SYS_ARCH_PRCTL = 158, + SYS_ADJTIMEX = 159, + SYS_SETRLIMIT = 160, + SYS_CHROOT = 161, + SYS_SYNC = 162, + SYS_ACCT = 163, + SYS_SETTIMEOFDAY = 164, + SYS_MOUNT = 165, + SYS_UMOUNT2 = 166, + SYS_SWAPON = 167, + SYS_SWAPOFF = 168, + SYS_REBOOT = 169, + SYS_SETHOSTNAME = 170, + SYS_SETDOMAINNAME = 171, + SYS_IOPL = 172, + SYS_IOPERM = 173, + SYS_CREATE_MODULE = 174, + SYS_INIT_MODULE = 175, + SYS_DELETE_MODULE = 176, + SYS_GET_KERNEL_SYMS = 177, + SYS_QUERY_MODULE = 178, + SYS_QUOTACTL = 179, + SYS_NFSSERVCTL = 180, + SYS_GETPMSG = 181, + SYS_PUTPMSG = 182, + SYS_AFS_SYSCALL = 183, + SYS_TUXCALL = 184, + SYS_SECURITY = 185, + SYS_GETTID = 186, + SYS_READAHEAD = 187, + SYS_SETXATTR = 188, + SYS_LSETXATTR = 189, + SYS_FSETXATTR = 190, + SYS_GETXATTR = 191, + SYS_LGETXATTR = 192, + SYS_FGETXATTR = 193, + SYS_LISTXATTR = 194, + SYS_LLISTXATTR = 195, + SYS_FLISTXATTR = 196, + SYS_REMOVEXATTR = 197, + SYS_LREMOVEXATTR = 198, + SYS_FREMOVEXATTR = 199, + SYS_TKILL = 200, + SYS_TIME = 201, + SYS_FUTEX = 202, + SYS_SCHED_SETAFFINITY = 203, + SYS_SCHED_GETAFFINITY = 204, + SYS_SET_THREAD_AREA = 205, + SYS_IO_SETUP = 206, + SYS_IO_DESTROY = 207, + SYS_IO_GETEVENTS = 208, + SYS_IO_SUBMIT = 209, + SYS_IO_CANCEL = 210, + SYS_GET_THREAD_AREA = 211, + SYS_LOOKUP_DCOOKIE = 212, + SYS_EPOLL_CREATE = 213, + SYS_EPOLL_CTL_OLD = 214, + SYS_EPOLL_WAIT_OLD = 215, + SYS_REMAP_FILE_PAGES = 216, + SYS_GETDENTS64 = 217, + SYS_SET_TID_ADDRESS = 218, + SYS_RESTART_SYSCALL = 219, + SYS_SEMTIMEDOP = 220, + SYS_FADVISE64 = 221, + SYS_TIMER_CREATE = 222, + SYS_TIMER_SETTIME = 223, + SYS_TIMER_GETTIME = 224, + SYS_TIMER_GETOVERRUN = 225, + SYS_TIMER_DELETE = 226, + SYS_CLOCK_SETTIME = 227, + SYS_CLOCK_GETTIME = 228, + SYS_CLOCK_GETRES = 229, + SYS_CLOCK_NANOSLEEP = 230, + SYS_EXIT_GROUP = 231, + SYS_EPOLL_WAIT = 232, + SYS_EPOLL_CTL = 233, + SYS_TGKILL = 234, + SYS_UTIMES = 235, + SYS_VSERVER = 236, + SYS_MBIND = 237, + SYS_SET_MEMPOLICY = 238, + SYS_GET_MEMPOLICY = 239, + SYS_MQ_OPEN = 240, + SYS_MQ_UNLINK = 241, + SYS_MQ_TIMEDSEND = 242, + SYS_MQ_TIMEDRECEIVE = 243, + SYS_MQ_NOTIFY = 244, + SYS_MQ_GETSETATTR = 245, + SYS_KEXEC_LOAD = 246, + SYS_WAITID = 247, + SYS_ADD_KEY = 248, + SYS_REQUEST_KEY = 249, + SYS_KEYCTL = 250, + SYS_IOPRIO_SET = 251, + SYS_IOPRIO_GET = 252, + SYS_INOTIFY_INIT = 253, + SYS_INOTIFY_ADD_WATCH = 254, + SYS_INOTIFY_RM_WATCH = 255, + SYS_MIGRATE_PAGES = 256, + SYS_OPENAT = 257, + SYS_MKDIRAT = 258, + SYS_MKNODAT = 259, + SYS_FCHOWNAT = 260, + SYS_FUTIMESAT = 261, + SYS_NEWFSTATAT = 262, + SYS_UNLINKAT = 263, + SYS_RENAMEAT = 264, + SYS_LINKAT = 265, + SYS_SYMLINKAT = 266, + SYS_READLINKAT = 267, + SYS_FCHMODAT = 268, + SYS_FACCESSAT = 269, + SYS_PSELECT6 = 270, + SYS_PPOLL = 271, + SYS_UNSHARE = 272, + SYS_SET_ROBUST_LIST = 273, + SYS_GET_ROBUST_LIST = 274, + SYS_SPLICE = 275, + SYS_TEE = 276, + SYS_SYNC_FILE_RANGE = 277, + SYS_VMSPLICE = 278, + SYS_MOVE_PAGES = 279, + SYS_UTIMENSAT = 280, + SYS_EPOLL_PWAIT = 281, + SYS_SIGNALFD = 282, + SYS_TIMERFD_CREATE = 283, + SYS_EVENTFD = 284, + SYS_FALLOCATE = 285, + SYS_TIMERFD_SETTIME = 286, + SYS_TIMERFD_GETTIME = 287, + SYS_ACCEPT4 = 288, + SYS_SIGNALFD4 = 289, + SYS_EVENTFD2 = 290, + SYS_EPOLL_CREATE1 = 291, + SYS_DUP3 = 292, + SYS_PIPE2 = 293, + SYS_INOTIFY_INIT1 = 294, + SYS_PREADV = 295, + SYS_PWRITEV = 296, + SYS_RT_TGSIGQUEUEINFO = 297, + SYS_PERF_EVENT_OPEN = 298, + SYS_RECVMMSG = 299, + SYS_FANOTIFY_INIT = 300, + SYS_FANOTIFY_MARK = 301, + SYS_PRLIMIT64 = 302 + +}; + +typedef void (*seccomp_child_func_t)(int event_fd, void *ctx); + +typedef void (*seccomp_filter_callback_t)(struct seccomp_notif * req, + struct seccomp_notif_resp *resp, + GumReturnAddressArray * frames); + +void seccomp_atomic_set(volatile bool *ptr, bool val); +bool seccomp_atomic_try_set(volatile bool *ptr, bool val); +void seccomp_atomic_wait(volatile bool *ptr, bool val); + +void seccomp_callback_parent(void); +void seccomp_callback_initialize(void); + +void seccomp_child_run(seccomp_child_func_t child_func, void *ctx, pid_t *child, + int *event_fd); +void seccomp_child_wait(int event_fd); + +int seccomp_event_create(void); +void seccomp_event_signal(int fd); +void seccomp_event_wait(int fd); +void seccomp_event_destroy(int fd); + +int seccomp_filter_install(pid_t child); +void seccomp_filter_child_install(void); +void seccomp_filter_run(int fd, seccomp_filter_callback_t callback); + +void seccomp_print(char *format, ...); + +void seccomp_socket_create(int *sock); +void seccomp_socket_send(int sockfd, int fd); +int seccomp_socket_recv(int sockfd); + +char *seccomp_syscall_lookup(int id); + +#endif +extern char *seccomp_filename; + +void seccomp_config(void); +void seccomp_init(void); +void seccomp_on_fork(void); + +#endif + diff --git a/frida_mode/include/stalker.h b/frida_mode/include/stalker.h index b5e05d5a..955f3913 100644 --- a/frida_mode/include/stalker.h +++ b/frida_mode/include/stalker.h @@ -3,11 +3,15 @@ #include "frida-gumjs.h" +extern guint stalker_ic_entries; + void stalker_config(void); void stalker_init(void); GumStalker *stalker_get(void); void stalker_start(void); void stalker_trust(void); +GumStalkerObserver *stalker_get_observer(void); + #endif diff --git a/frida_mode/include/stats.h b/frida_mode/include/stats.h index cd2350ea..0ad227c3 100644 --- a/frida_mode/include/stats.h +++ b/frida_mode/include/stats.h @@ -5,30 +5,56 @@ typedef struct { - guint64 num_blocks; - guint64 num_instructions; - guint64 stats_last_time; - guint64 stats_idx; - guint64 transitions_idx; + guint64 stats_time; + guint64 total; + guint64 call_imm; + guint64 call_reg; + guint64 call_mem; + guint64 excluded_call_reg; + guint64 ret_slow_path; + guint64 ret; + guint64 post_call_invoke; + guint64 excluded_call_imm; + guint64 jmp_imm; + guint64 jmp_reg; + guint64 jmp_mem; + guint64 jmp_cond_imm; + guint64 jmp_cond_mem; + guint64 jmp_cond_reg; + guint64 jmp_cond_jcxz; + guint64 jmp_cond_cc; + guint64 jmp_cond_cbz; + guint64 jmp_cond_cbnz; + guint64 jmp_cond_tbz; + guint64 jmp_cond_tbnz; + guint64 jmp_continuation; + +} stats_t; -} stats_data_header_t; +typedef struct { + + /* transitions */ + stats_t curr; + stats_t prev; + +} stats_data_t; -extern stats_data_header_t *stats_data; +#define GUM_TYPE_AFL_STALKER_STATS (gum_afl_stalker_stats_get_type()) +G_DECLARE_FINAL_TYPE(GumAflStalkerStats, gum_afl_stalker_stats, GUM, + AFL_STALKER_STATS, GObject) -extern char * stats_filename; -extern guint64 stats_interval; -extern gboolean stats_transitions; +extern char * stats_filename; +extern guint64 stats_interval; void stats_config(void); void stats_init(void); void stats_collect(const cs_insn *instr, gboolean begin); void stats_print(char *format, ...); -gboolean stats_is_supported_arch(void); -size_t stats_data_size_arch(void); -void stats_collect_arch(const cs_insn *instr); -void stats_write_arch(void); -void stats_on_fork(void); +void starts_arch_init(void); +void stats_collect_arch(const cs_insn *instr, gboolean begin); +void stats_write_arch(stats_data_t *data); +void stats_on_fork(void); #endif diff --git a/frida_mode/many-linux/Dockerfile b/frida_mode/many-linux/Dockerfile index 2cd56bc8..6d077dad 100644 --- a/frida_mode/many-linux/Dockerfile +++ b/frida_mode/many-linux/Dockerfile @@ -1,24 +1,10 @@ FROM fridadotre/manylinux-x86_64 -COPY realpath /bin/realpath -RUN chmod +x /bin/realpath - RUN yum -y install xz -RUN yum -y install vim-common - -WORKDIR / -RUN git clone https://github.com/AFLplusplus/AFLplusplus.git - -WORKDIR /AFLplusplus -RUN mkdir -p /AFLplusplus/frida_mode/build/frida/ -RUN curl -L -o /AFLplusplus/frida_mode/build/frida/frida-gumjs-devkit-15.0.0-linux-x86_64.tar.xz "https://github.com/frida/frida/releases/download/15.0.0/frida-gumjs-devkit-15.0.0-linux-x86_64.tar.xz" WORKDIR /AFLplusplus -RUN git checkout dev -WORKDIR /AFLplusplus/frida_mode ENV CFLAGS="\ -DADDR_NO_RANDOMIZE=0x0040000 \ -Wno-implicit-function-declaration \ " ENV CXX=$CC -RUN make diff --git a/frida_mode/many-linux/GNUmakefile b/frida_mode/many-linux/GNUmakefile index 2860f20c..9992d4fe 100644 --- a/frida_mode/many-linux/GNUmakefile +++ b/frida_mode/many-linux/GNUmakefile @@ -1,20 +1,25 @@ PWD:=$(shell pwd)/ +ROOT:=$(PWD)../../ BUILD_DIR:=$(PWD)build/ -.PHONY: all clean shell +.PHONY: all build docker clean shell -all: | $(BUILD_DIR) - docker build --tag many-afl-frida . +all: docker docker run --rm \ - -v $(PWD)build/:/export \ + -v $(ROOT):/AFLplusplus \ many-afl-frida \ - cp /AFLplusplus/afl-frida-trace.so /export + make -C /AFLplusplus/frida_mode clean all -$(BUILD_DIR): - mkdir -p $@ +build: + docker run --rm \ + -v $(ROOT):/AFLplusplus \ + many-afl-frida \ + make -C /AFLplusplus/frida_mode + +docker: + docker build --tag many-afl-frida . clean: - rm -rf $(BUILD_DIR) docker images --filter 'dangling=true' -q --no-trunc | xargs -L1 docker rmi --force shell: diff --git a/frida_mode/many-linux/README.md b/frida_mode/many-linux/README.md index 2c7b6823..4bd7a6c1 100644 --- a/frida_mode/many-linux/README.md +++ b/frida_mode/many-linux/README.md @@ -5,4 +5,5 @@ This folder contains a Docker image to allow the building of based on CentOS Linux 5. By building `afl-frida-trace.so` for such an old version of Linux, given the strong backward compatibility of Linux, this should work on the majority of Linux environments. This may be useful for targetting -Linux distributions other than your development environment. \ No newline at end of file +Linux distributions other than your development environment. `many-local` builds +`AFLplusplus` from the local working copy in the `many-linux` environment. diff --git a/frida_mode/many-linux/realpath b/frida_mode/many-linux/realpath deleted file mode 100755 index 1fdc49a7..00000000 --- a/frida_mode/many-linux/realpath +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -readlink -f -- "$@" diff --git a/frida_mode/src/cmplog/cmplog.c b/frida_mode/src/cmplog/cmplog.c index a2609c8e..ae3116eb 100644 --- a/frida_mode/src/cmplog/cmplog.c +++ b/frida_mode/src/cmplog/cmplog.c @@ -56,7 +56,9 @@ void cmplog_config(void) { void cmplog_init(void) { - if (__afl_cmp_map != NULL) { OKF("CMPLOG mode enabled"); } + OKF("CMPLOG - Enabled [%c]", __afl_cmp_map == NULL ? ' ' : 'X'); + + if (__afl_cmp_map == NULL) { return; } cmplog_get_ranges(); diff --git a/frida_mode/src/entry.c b/frida_mode/src/entry.c index a0ffd028..186ddd3a 100644 --- a/frida_mode/src/entry.c +++ b/frida_mode/src/entry.c @@ -1,3 +1,5 @@ +#include <dlfcn.h> + #include "frida-gumjs.h" #include "debug.h" @@ -6,6 +8,7 @@ #include "instrument.h" #include "persistent.h" #include "ranges.h" +#include "seccomp.h" #include "stalker.h" #include "stats.h" #include "util.h" @@ -13,7 +16,8 @@ extern void __afl_manual_init(); guint64 entry_point = 0; -gboolean entry_reached = FALSE; +gboolean entry_compiled = FALSE; +gboolean entry_run = FALSE; static void entry_launch(void) { @@ -21,7 +25,9 @@ static void entry_launch(void) { __afl_manual_init(); /* Child here */ + entry_run = TRUE; instrument_on_fork(); + seccomp_on_fork(); stats_on_fork(); } @@ -36,6 +42,8 @@ void entry_init(void) { OKF("entry_point: 0x%016" G_GINT64_MODIFIER "X", entry_point); + if (dlopen(NULL, RTLD_NOW) == NULL) { FATAL("Failed to dlopen: %d", errno); } + } void entry_start(void) { @@ -48,6 +56,7 @@ static void entry_callout(GumCpuContext *cpu_context, gpointer user_data) { UNUSED_PARAMETER(cpu_context); UNUSED_PARAMETER(user_data); + entry_compiled = TRUE; entry_launch(); } @@ -59,7 +68,6 @@ void entry_prologue(GumStalkerIterator *iterator, GumStalkerOutput *output) { if (persistent_start == 0) { - entry_reached = TRUE; ranges_exclude(); stalker_trust(); diff --git a/frida_mode/src/instrument/instrument.c b/frida_mode/src/instrument/instrument.c index 67aafa5a..fd0982f8 100644 --- a/frida_mode/src/instrument/instrument.c +++ b/frida_mode/src/instrument/instrument.c @@ -29,14 +29,23 @@ guint64 instrument_hash_seed = 0; gboolean instrument_use_fixed_seed = FALSE; guint64 instrument_fixed_seed = 0; +char * instrument_coverage_unstable_filename = NULL; static GumStalkerTransformer *transformer = NULL; __thread guint64 instrument_previous_pc = 0; static GumAddress previous_rip = 0; +static GumAddress previous_end = 0; static u8 * edges_notified = NULL; +typedef struct { + + GumAddress address; + GumAddress end; + +} block_ctx_t; + static void trace_debug(char *format, ...) { va_list ap; @@ -91,9 +100,11 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context, UNUSED_PARAMETER(context); - GumAddress current_rip = GUM_ADDRESS(user_data); - guint64 current_pc = instrument_get_offset_hash(current_rip); - guint64 edge; + block_ctx_t *ctx = (block_ctx_t *)user_data; + GumAddress current_rip = ctx->address; + guint16 current_end = ctx->end; + guint64 current_pc = instrument_get_offset_hash(current_rip); + guint64 edge; edge = current_pc ^ instrument_previous_pc; @@ -112,12 +123,20 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context, if (instrument_unique) { edges_notified[edge] = 1; } - previous_rip = current_rip; + } + + if (unlikely(instrument_coverage_unstable_filename != NULL)) { + + instrument_coverage_unstable(edge, previous_rip, previous_end, current_rip, + current_end); } - instrument_previous_pc = - ((current_pc & (MAP_SIZE - 1) >> 1)) | ((current_pc & 0x1) << 15); + previous_rip = current_rip; + previous_end = current_end; + + instrument_previous_pc = ((current_pc & (MAP_SIZE - 1) >> 1)) | + ((current_pc & 0x1) << (MAP_SIZE_POW2 - 1)); } @@ -130,6 +149,7 @@ static void instrument_basic_block(GumStalkerIterator *iterator, const cs_insn *instr; gboolean begin = TRUE; gboolean excluded; + block_ctx_t * ctx = NULL; while (gum_stalker_iterator_next(iterator, &instr)) { @@ -164,19 +184,16 @@ static void instrument_basic_block(GumStalkerIterator *iterator, * our AFL_ENTRYPOINT, since it is not until then that we start the * fork-server and thus start executing in the child. */ - excluded = range_is_excluded(GSIZE_TO_POINTER(instr->address)); + excluded = range_is_excluded(GUM_ADDRESS(instr->address)); stats_collect(instr, begin); if (unlikely(begin)) { instrument_debug_start(instr->address, output); + instrument_coverage_start(instr->address); - if (likely(entry_reached)) { - - prefetch_write(GSIZE_TO_POINTER(instr->address)); - - } + prefetch_write(GSIZE_TO_POINTER(instr->address)); if (likely(!excluded)) { @@ -186,8 +203,9 @@ static void instrument_basic_block(GumStalkerIterator *iterator, } else { - gum_stalker_iterator_put_callout( - iterator, on_basic_block, GSIZE_TO_POINTER(instr->address), NULL); + ctx = gum_malloc0(sizeof(block_ctx_t)); + ctx->address = GUM_ADDRESS(instr->address); + gum_stalker_iterator_put_callout(iterator, on_basic_block, ctx, NULL); } @@ -214,8 +232,11 @@ static void instrument_basic_block(GumStalkerIterator *iterator, } + if (ctx != NULL) { ctx->end = (instr->address + instr->size); } + instrument_flush(output); instrument_debug_end(output); + instrument_coverage_end(instr->address + instr->size); } @@ -226,8 +247,11 @@ void instrument_config(void) { instrument_unique = (getenv("AFL_FRIDA_INST_TRACE_UNIQUE") != NULL); instrument_use_fixed_seed = (getenv("AFL_FRIDA_INST_SEED") != NULL); instrument_fixed_seed = util_read_num("AFL_FRIDA_INST_SEED"); + instrument_coverage_unstable_filename = + (getenv("AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE")); instrument_debug_config(); + instrument_coverage_config(); asan_config(); cmplog_config(); @@ -242,6 +266,9 @@ void instrument_init(void) { OKF("Instrumentation - unique [%c]", instrument_unique ? 'X' : ' '); OKF("Instrumentation - fixed seed [%c] [0x%016" G_GINT64_MODIFIER "x]", instrument_use_fixed_seed ? 'X' : ' ', instrument_fixed_seed); + OKF("Instrumentation - unstable coverage [%c] [%s]", + instrument_coverage_unstable_filename == NULL ? ' ' : 'X', + instrument_coverage_unstable_filename); if (instrument_tracing && instrument_optimize) { @@ -250,6 +277,13 @@ void instrument_init(void) { } + if (instrument_coverage_unstable_filename && instrument_optimize) { + + WARNF("AFL_FRIDA_INST_COVERAGE_FILE implies AFL_FRIDA_INST_NO_OPTIMIZE"); + instrument_optimize = FALSE; + + } + if (instrument_unique && instrument_optimize) { WARNF("AFL_FRIDA_INST_TRACE_UNIQUE implies AFL_FRIDA_INST_NO_OPTIMIZE"); @@ -317,6 +351,7 @@ void instrument_init(void) { instrument_hash_zero = instrument_get_offset_hash(0); instrument_debug_init(); + instrument_coverage_init(); asan_init(); cmplog_init(); diff --git a/frida_mode/src/instrument/instrument_coverage.c b/frida_mode/src/instrument/instrument_coverage.c new file mode 100644 index 00000000..513df29a --- /dev/null +++ b/frida_mode/src/instrument/instrument_coverage.c @@ -0,0 +1,896 @@ +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <unistd.h> + +#include "frida-gumjs.h" + +#include "debug.h" + +#include "instrument.h" +#include "util.h" + +char *instrument_coverage_filename = NULL; + +static int normal_coverage_fd = -1; +static int normal_coverage_pipes[2] = {-1, -1}; + +static int unstable_coverage_fd = -1; +static int unstable_coverage_pipes[2] = {-1, -1}; + +static uint64_t normal_coverage_last_start = 0; +static gchar * unstable_coverage_fuzzer_stats = NULL; + +typedef struct { + + GumAddress base_address; + GumAddress limit; + gsize size; + char path[PATH_MAX + 1]; + guint64 offset; + gboolean is_executable; + guint count; + guint16 id; + +} coverage_range_t; + +typedef struct { + + uint64_t start; + uint64_t end; + coverage_range_t *module; + +} normal_coverage_data_t; + +typedef struct { + + guint64 edge; + guint64 from; + guint64 from_end; + guint64 to; + guint64 to_end; + +} unstable_coverage_data_t; + +typedef struct { + + GArray *modules; + guint count; + +} coverage_mark_ctx_t; + +typedef struct { + + guint32 offset; + guint16 length; + guint16 module; + +} coverage_event_t; + +static gboolean coverage_range(const GumRangeDetails *details, + gpointer user_data) { + + GArray * coverage_ranges = (GArray *)user_data; + coverage_range_t coverage = {0}; + + if (details->file == NULL) { return TRUE; } + if (details->protection == GUM_PAGE_NO_ACCESS) { return TRUE; } + + coverage.base_address = details->range->base_address; + coverage.size = details->range->size; + coverage.limit = coverage.base_address + coverage.size; + + strncpy(coverage.path, details->file->path, PATH_MAX); + coverage.offset = details->file->offset; + + if ((details->protection & GUM_PAGE_EXECUTE) == 0) { + + coverage.is_executable = false; + + } else { + + coverage.is_executable = true; + + } + + coverage.count = 0; + coverage.id = 0; + + g_array_append_val(coverage_ranges, coverage); + return TRUE; + +} + +static gint coverage_sort(gconstpointer a, gconstpointer b) { + + coverage_range_t *ma = (coverage_range_t *)a; + coverage_range_t *mb = (coverage_range_t *)b; + + if (ma->base_address < mb->base_address) return -1; + + if (ma->base_address > mb->base_address) return 1; + + return 0; + +} + +void instrument_coverage_print(char *format, ...) { + + char buffer[4096] = {0}; + int len; + + va_list ap; + va_start(ap, format); + + if (vsnprintf(buffer, sizeof(buffer) - 1, format, ap) < 0) { return; } + + len = strnlen(buffer, sizeof(buffer)); + IGNORED_RETURN(write(STDOUT_FILENO, buffer, len)); + va_end(ap); + +} + +static GArray *coverage_get_ranges(void) { + + instrument_coverage_print("Coverage - Collecting ranges\n"); + + GArray *coverage_ranges = + g_array_sized_new(false, false, sizeof(coverage_range_t), 100); + gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, coverage_range, + coverage_ranges); + g_array_sort(coverage_ranges, coverage_sort); + + for (guint i = 0; i < coverage_ranges->len; i++) { + + coverage_range_t *range = + &g_array_index(coverage_ranges, coverage_range_t, i); + instrument_coverage_print("Coverage Range - %3u: 0x%016" G_GINT64_MODIFIER + "X - 0x%016" G_GINT64_MODIFIER "X (%s)\n", + i, range->base_address, range->limit, + range->path); + + } + + return coverage_ranges; + +} + +static GArray *coverage_get_modules(void) { + + instrument_coverage_print("Coverage - Collecting modules\n"); + GArray *coverage_ranges = coverage_get_ranges(); + GArray *coverage_modules = + g_array_sized_new(false, false, sizeof(coverage_range_t), 100); + + coverage_range_t current = {0}; + + for (guint i = 0; i < coverage_ranges->len; i++) { + + coverage_range_t *range = + &g_array_index(coverage_ranges, coverage_range_t, i); + + if (range->offset == 0 || + (strncmp(range->path, current.path, PATH_MAX) != 0)) { + + if (current.is_executable) { + + g_array_append_val(coverage_modules, current); + memset(¤t, '\0', sizeof(coverage_range_t)); + + } + + memcpy(¤t, range, sizeof(coverage_range_t)); + + } else { + + current.limit = range->limit; + current.size = current.limit - current.base_address; + if (range->is_executable) { current.is_executable = true; } + + } + + } + + if (current.is_executable) { g_array_append_val(coverage_modules, current); } + g_array_free(coverage_ranges, TRUE); + + for (guint i = 0; i < coverage_modules->len; i++) { + + coverage_range_t *module = + &g_array_index(coverage_modules, coverage_range_t, i); + instrument_coverage_print("Coverage Module - %3u: 0x%016" G_GINT64_MODIFIER + "X - 0x%016" G_GINT64_MODIFIER "X (%s)\n", + i, module->base_address, module->limit, + module->path); + + } + + return coverage_modules; + +} + +static void instrument_coverage_mark(void *key, void *value, void *user_data) { + + UNUSED_PARAMETER(key); + coverage_mark_ctx_t * ctx = (coverage_mark_ctx_t *)user_data; + GArray * coverage_modules = ctx->modules; + normal_coverage_data_t *val = (normal_coverage_data_t *)value; + guint i; + + for (i = 0; i < coverage_modules->len; i++) { + + coverage_range_t *module = + &g_array_index(coverage_modules, coverage_range_t, i); + if (val->start > module->limit) continue; + + if (val->end >= module->limit) break; + + val->module = module; + ctx->count = ctx->count + 1; + module->count++; + return; + + } + + instrument_coverage_print( + "Coverage cannot find module for: 0x%016" G_GINT64_MODIFIER + "X - 0x%016" G_GINT64_MODIFIER "X\n", + val->start, val->end); + +} + +static void coverage_write(void *data, size_t size) { + + ssize_t written; + size_t remain = size; + + for (char *cursor = (char *)data; remain > 0; + remain -= written, cursor += written) { + + written = write(normal_coverage_fd, cursor, remain); + + if (written < 0) { + + FATAL("Coverage - Failed to write: %s (%d)\n", (char *)data, errno); + + } + + } + +} + +static void coverage_format(char *format, ...) { + + va_list ap; + char buffer[4096] = {0}; + int ret; + int len; + + va_start(ap, format); + ret = vsnprintf(buffer, sizeof(buffer) - 1, format, ap); + va_end(ap); + + if (ret < 0) { return; } + + len = strnlen(buffer, sizeof(buffer)); + + coverage_write(buffer, len); + +} + +static void coverage_write_modules(GArray *coverage_modules) { + + guint emitted = 0; + for (guint i = 0; i < coverage_modules->len; i++) { + + coverage_range_t *module = + &g_array_index(coverage_modules, coverage_range_t, i); + if (module->count == 0) continue; + + coverage_format("%3u, ", emitted); + coverage_format("%016" G_GINT64_MODIFIER "X, ", module->base_address); + coverage_format("%016" G_GINT64_MODIFIER "X, ", module->limit); + /* entry */ + coverage_format("%016" G_GINT64_MODIFIER "X, ", 0); + /* checksum */ + coverage_format("%016" G_GINT64_MODIFIER "X, ", 0); + /* timestamp */ + coverage_format("%08" G_GINT32_MODIFIER "X, ", 0); + coverage_format("%s\n", module->path); + emitted++; + + } + +} + +static void coverage_write_events(void *key, void *value, void *user_data) { + + UNUSED_PARAMETER(key); + UNUSED_PARAMETER(user_data); + normal_coverage_data_t *val = (normal_coverage_data_t *)value; + + if (val->module == NULL) { return; } + + coverage_event_t evt = { + + .offset = val->start - val->module->base_address, + .length = val->end - val->start, + .module = val->module->id, + + }; + + coverage_write(&evt, sizeof(coverage_event_t)); + +} + +static void coverage_write_header(guint coverage_marked_modules) { + + char version[] = "DRCOV VERSION: 2\n"; + char flavour[] = "DRCOV FLAVOR: frida\n"; + char columns[] = "Columns: id, base, end, entry, checksum, timestamp, path\n"; + coverage_write(version, sizeof(version) - 1); + coverage_write(flavour, sizeof(flavour) - 1); + coverage_format("Module Table: version 2, count %u\n", + coverage_marked_modules); + coverage_write(columns, sizeof(columns) - 1); + +} + +static guint coverage_mark_modules(GArray *coverage_modules) { + + guint coverage_marked_modules = 0; + guint i; + for (i = 0; i < coverage_modules->len; i++) { + + coverage_range_t *module = + &g_array_index(coverage_modules, coverage_range_t, i); + + instrument_coverage_print( + "Coverage Module - %3u: [%c] 0x%016" G_GINT64_MODIFIER + "X - 0x%016" G_GINT64_MODIFIER "X [%u] (%u:%s)\n", + i, module->count == 0 ? ' ' : 'X', module->base_address, module->limit, + module->count, module->id, module->path); + + if (module->count == 0) { continue; } + + module->id = coverage_marked_modules; + coverage_marked_modules++; + + } + + return coverage_marked_modules; + +} + +static void instrument_coverage_normal_run() { + + int bytes; + normal_coverage_data_t data; + normal_coverage_data_t *value; + instrument_coverage_print("Coverage - Running\n"); + + if (close(normal_coverage_pipes[STDOUT_FILENO]) != 0) { + + FATAL("Failed to close parent read pipe"); + + } + + GHashTable *coverage_hash = + g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); + if (coverage_hash == NULL) { + + FATAL("Failed to g_hash_table_new, errno: %d", errno); + + } + + for (bytes = read(normal_coverage_pipes[STDIN_FILENO], &data, + sizeof(normal_coverage_data_t)); + bytes == sizeof(normal_coverage_data_t); + bytes = read(normal_coverage_pipes[STDIN_FILENO], &data, + sizeof(normal_coverage_data_t))) { + + value = + (normal_coverage_data_t *)gum_malloc0(sizeof(normal_coverage_data_t)); + memcpy(value, &data, sizeof(normal_coverage_data_t)); + g_hash_table_insert(coverage_hash, GSIZE_TO_POINTER(data.start), value); + + } + + if (bytes != 0) { FATAL("Coverage data truncated"); } + + instrument_coverage_print("Coverage - Preparing\n"); + + GArray *coverage_modules = coverage_get_modules(); + + guint size = g_hash_table_size(coverage_hash); + instrument_coverage_print("Coverage - Total Entries: %u\n", size); + + coverage_mark_ctx_t ctx = {.modules = coverage_modules, .count = 0}; + + g_hash_table_foreach(coverage_hash, instrument_coverage_mark, &ctx); + instrument_coverage_print("Coverage - Marked Entries: %u\n", ctx.count); + + guint coverage_marked_modules = coverage_mark_modules(coverage_modules); + instrument_coverage_print("Coverage - Marked Modules: %u\n", + coverage_marked_modules); + + coverage_write_header(coverage_marked_modules); + coverage_write_modules(coverage_modules); + coverage_format("BB Table: %u bbs\n", ctx.count); + g_hash_table_foreach(coverage_hash, coverage_write_events, NULL); + + g_hash_table_unref(coverage_hash); + + instrument_coverage_print("Coverage - Completed\n"); + +} + +static GArray *instrument_coverage_unstable_read_unstable_ids(void) { + + gchar * contents = NULL; + gsize length = 0; + GArray *unstable_edge_ids = + g_array_sized_new(false, false, sizeof(gpointer), 100); + + if (!g_file_get_contents(unstable_coverage_fuzzer_stats, &contents, &length, + NULL)) { + + FATAL("Failed to read fuzzer_stats"); + + } + + instrument_coverage_print("\n"); + instrument_coverage_print("Unstable coverage stats:\n"); + instrument_coverage_print("========================\n"); + instrument_coverage_print("%s\n", contents); + instrument_coverage_print("\n"); + + gchar **lines = g_strsplit(contents, "\n", -1); + gchar **values = NULL; + + for (guint i = 0; lines[i] != NULL; i++) { + + gchar **fields = g_strsplit(lines[i], ":", 2); + if (fields[0] == NULL) { + + g_strfreev(fields); + continue; + + } + + g_strstrip(fields[0]); + if (g_strcmp0(fields[0], "var_bytes") != 0) { + + g_strfreev(fields); + continue; + + } + + if (fields[1] == NULL) { + + g_strfreev(fields); + continue; + + } + + g_strstrip(fields[1]); + values = g_strsplit(fields[1], " ", -1); + g_strfreev(fields); + + break; + + } + + if (values == NULL) { + + instrument_coverage_print( + "Failed to find var_bytes, did you set AFL_DEBUG?\n"); + + } + + for (guint i = 0; values[i] != NULL; i++) { + + g_strstrip(values[i]); + gpointer val = GSIZE_TO_POINTER(g_ascii_strtoull(values[i], NULL, 10)); + g_array_append_val(unstable_edge_ids, val); + + } + + g_strfreev(values); + g_strfreev(lines); + g_free(contents); + + for (guint i = 0; i < unstable_edge_ids->len; i++) { + + gpointer *id = &g_array_index(unstable_edge_ids, gpointer, i); + + instrument_coverage_print("Unstable edge (%10u): %" G_GINT64_MODIFIER "u\n", + i, GPOINTER_TO_SIZE(*id)); + + } + + return unstable_edge_ids; + +} + +static GHashTable *instrument_collect_unstable_blocks( + GHashTable *unstable_coverage_hash, GArray *unstable_edge_ids) { + + GHashTable *unstable_blocks = + g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); + + for (guint i = 0; i < unstable_edge_ids->len; i++) { + + gpointer *id = &g_array_index(unstable_edge_ids, gpointer, i); + + GHashTable *child = + (GHashTable *)g_hash_table_lookup(unstable_coverage_hash, *id); + + if (child == NULL) { FATAL("Failed to find edge ID"); } + + GHashTableIter iter = {0}; + gpointer value; + g_hash_table_iter_init(&iter, child); + while (g_hash_table_iter_next(&iter, NULL, &value)) { + + unstable_coverage_data_t *unstable = (unstable_coverage_data_t *)value; + normal_coverage_data_t * from = + gum_malloc0(sizeof(normal_coverage_data_t)); + normal_coverage_data_t *to = gum_malloc0(sizeof(normal_coverage_data_t)); + from->start = unstable->from; + from->end = unstable->from_end; + from->module = NULL; + + to->start = unstable->to; + to->end = unstable->to_end; + to->module = NULL; + + g_hash_table_insert(unstable_blocks, GSIZE_TO_POINTER(from->start), from); + g_hash_table_insert(unstable_blocks, GSIZE_TO_POINTER(to->start), to); + + } + + } + + return unstable_blocks; + +} + +static void instrument_coverage_unstable_run(void) { + + int bytes; + unstable_coverage_data_t data; + unstable_coverage_data_t *value; + instrument_coverage_print("Unstable coverage - Running\n"); + + if (close(unstable_coverage_pipes[STDOUT_FILENO]) != 0) { + + FATAL("Failed to close parent read pipe"); + + } + + GHashTable *unstable_coverage_hash = g_hash_table_new_full( + g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)g_hash_table_unref); + if (unstable_coverage_hash == NULL) { + + FATAL("Failed to g_hash_table_new, errno: %d", errno); + + } + + guint edges = 0; + + for (bytes = read(unstable_coverage_pipes[STDIN_FILENO], &data, + sizeof(unstable_coverage_data_t)); + bytes == sizeof(unstable_coverage_data_t); + bytes = read(unstable_coverage_pipes[STDIN_FILENO], &data, + sizeof(unstable_coverage_data_t))) { + + value = (unstable_coverage_data_t *)gum_malloc0( + sizeof(unstable_coverage_data_t)); + memcpy(value, &data, sizeof(unstable_coverage_data_t)); + + gpointer hash_value = g_hash_table_lookup(unstable_coverage_hash, + GSIZE_TO_POINTER(value->edge)); + if (hash_value == NULL) { + + hash_value = + g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); + + if (!g_hash_table_insert(unstable_coverage_hash, + GSIZE_TO_POINTER(value->edge), hash_value)) { + + FATAL("Entry already in hashtable"); + + } + + } + + if (g_hash_table_insert(hash_value, GSIZE_TO_POINTER(value->from), value)) { + + edges++; + + } + + } + + if (bytes != 0) { FATAL("Unstable coverage data truncated"); } + + instrument_coverage_print("Coverage - Preparing\n"); + + GArray *coverage_modules = coverage_get_modules(); + + instrument_coverage_print("Found edges: %u\n", edges); + + GArray *unstable_edge_ids = instrument_coverage_unstable_read_unstable_ids(); + + GHashTable *unstable_blocks = instrument_collect_unstable_blocks( + unstable_coverage_hash, unstable_edge_ids); + + guint size = g_hash_table_size(unstable_blocks); + instrument_coverage_print("Unstable blocks: %u\n", size); + + coverage_mark_ctx_t ctx = {.modules = coverage_modules, .count = 0}; + + g_hash_table_foreach(unstable_blocks, instrument_coverage_mark, &ctx); + instrument_coverage_print("Coverage - Marked Entries: %u\n", ctx.count); + + guint coverage_marked_modules = coverage_mark_modules(coverage_modules); + instrument_coverage_print("Coverage - Marked Modules: %u\n", + coverage_marked_modules); + + coverage_write_header(coverage_marked_modules); + coverage_write_modules(coverage_modules); + coverage_format("BB Table: %u bbs\n", ctx.count); + g_hash_table_foreach(unstable_blocks, coverage_write_events, NULL); + + g_hash_table_unref(unstable_blocks); + g_array_free(unstable_edge_ids, TRUE); + g_hash_table_unref(unstable_coverage_hash); + + instrument_coverage_print("Coverage - Completed\n"); + +} + +void instrument_coverage_config(void) { + + instrument_coverage_filename = getenv("AFL_FRIDA_INST_COVERAGE_FILE"); + +} + +void instrument_coverage_normal_init(void) { + + OKF("Coverage - enabled [%c]", + instrument_coverage_filename == NULL ? ' ' : 'X'); + + if (instrument_coverage_filename == NULL) { return; } + + OKF("Coverage - file [%s]", instrument_coverage_filename); + + char *path = g_canonicalize_filename(instrument_coverage_filename, + g_get_current_dir()); + + OKF("Coverage - path [%s]", path); + + normal_coverage_fd = open(path, O_RDWR | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + + if (normal_coverage_fd < 0) { + + FATAL("Failed to open coverage file '%s'", path); + + } + + g_free(path); + + if (pipe(normal_coverage_pipes) != 0) { FATAL("Failed to create pipes"); } + + pid_t pid = fork(); + if (pid == -1) { FATAL("Failed to start coverage process"); } + + if (pid == 0) { + + instrument_coverage_normal_run(); + kill(getpid(), SIGKILL); + _exit(0); + + } + + if (close(normal_coverage_fd) < 0) { + + FATAL("Failed to close coverage output file"); + + } + + if (close(normal_coverage_pipes[STDIN_FILENO]) != 0) { + + FATAL("Failed to close parent read pipe"); + + } + +} + +void instrument_coverage_unstable_find_output(void) { + + gchar *fds_name = g_strdup_printf("/proc/%d/fd/", getppid()); + + gchar *root = g_file_read_link("/proc/self/root", NULL); + if (root == NULL) { FATAL("Failed to read link"); } + + GDir *dir = g_dir_open(fds_name, 0, NULL); + + OKF("Coverage Unstable - fds: %s", fds_name); + + for (const gchar *filename = g_dir_read_name(dir); filename != NULL; + filename = g_dir_read_name(dir)) { + + gchar *fullname = g_build_path("/", fds_name, filename, NULL); + + gchar *link = g_file_read_link(fullname, NULL); + if (link == NULL) { FATAL("Failed to read link: %s", fullname); } + + gchar *basename = g_path_get_basename(link); + if (g_strcmp0(basename, "default") != 0) { + + g_free(basename); + g_free(link); + g_free(fullname); + continue; + + } + + gchar *relative = NULL; + size_t root_len = strnlen(root, PATH_MAX); + if (g_str_has_suffix(link, root)) { + + relative = g_build_path("/", &link[root_len], NULL); + + } else { + + relative = g_build_path("/", link, NULL); + + } + + gchar *cmdline = g_build_path("/", relative, "cmdline", NULL); + if (!g_file_test(cmdline, G_FILE_TEST_EXISTS)) { + + g_free(cmdline); + g_free(basename); + g_free(relative); + g_free(link); + g_free(fullname); + continue; + + } + + unstable_coverage_fuzzer_stats = + g_build_path("/", relative, "fuzzer_stats", NULL); + g_free(cmdline); + g_free(basename); + g_free(relative); + g_free(link); + g_free(fullname); + break; + + } + + g_dir_close(dir); + g_free(fds_name); + + if (unstable_coverage_fuzzer_stats == NULL) { + + FATAL("Failed to find fuzzer stats"); + + } + + OKF("Fuzzer stats: %s", unstable_coverage_fuzzer_stats); + +} + +void instrument_coverage_unstable_init(void) { + + if (instrument_coverage_unstable_filename == NULL) { return; } + + char *path = g_canonicalize_filename(instrument_coverage_unstable_filename, + g_get_current_dir()); + + OKF("Coverage - unstable path [%s]", instrument_coverage_unstable_filename); + + unstable_coverage_fd = open(path, O_RDWR | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + + if (unstable_coverage_fd < 0) { + + FATAL("Failed to open unstable coverage file '%s'", path); + + } + + g_free(path); + + instrument_coverage_unstable_find_output(); + + if (pipe(unstable_coverage_pipes) != 0) { + + FATAL("Failed to create unstable pipes"); + + } + + pid_t pid = fork(); + if (pid == -1) { FATAL("Failed to start coverage process"); } + + if (pid == 0) { + + instrument_coverage_unstable_run(); + kill(getpid(), SIGKILL); + _exit(0); + + } + + if (close(unstable_coverage_fd) < 0) { + + FATAL("Failed to close unstable coverage output file"); + + } + + if (close(unstable_coverage_pipes[STDIN_FILENO]) != 0) { + + FATAL("Failed to close parent read pipe"); + + } + +} + +void instrument_coverage_init(void) { + + instrument_coverage_normal_init(); + instrument_coverage_unstable_init(); + +} + +void instrument_coverage_start(uint64_t address) { + + if (instrument_coverage_filename == NULL) { return; } + + normal_coverage_last_start = address; + +} + +void instrument_coverage_end(uint64_t address) { + + if (instrument_coverage_filename == NULL) { return; } + + normal_coverage_data_t data = { + + .start = normal_coverage_last_start, .end = address, .module = NULL}; + + if (write(normal_coverage_pipes[STDOUT_FILENO], &data, + sizeof(normal_coverage_data_t)) != sizeof(normal_coverage_data_t)) { + + FATAL("Coverage I/O error"); + + } + +} + +void instrument_coverage_unstable(guint64 edge, guint64 previous_rip, + guint64 previous_end, guint64 current_rip, + guint64 current_end) { + + if (instrument_coverage_unstable_filename == NULL) { return; } + unstable_coverage_data_t data = { + + .edge = edge, + .from = previous_rip, + .from_end = previous_end, + .to = current_rip, + .to_end = current_end}; + + if (write(unstable_coverage_pipes[STDOUT_FILENO], &data, + sizeof(unstable_coverage_data_t)) != + sizeof(unstable_coverage_data_t)) { + + FATAL("Unstable coverage I/O error"); + + } + +} + diff --git a/frida_mode/src/js/api.js b/frida_mode/src/js/api.js index b8f2d39a..40bb4a16 100644 --- a/frida_mode/src/js/api.js +++ b/frida_mode/src/js/api.js @@ -86,6 +86,14 @@ class Afl { Afl.jsApiAflSharedMemFuzzing.writeInt(1); } /** + * See `AFL_FRIDA_INST_COVERAGE_FILE`. This function takes a single `string` + * as an argument. + */ + static setInstrumentCoverageFile(file) { + const buf = Memory.allocUtf8String(file); + Afl.jsApiSetInstrumentCoverageFile(buf); + } + /** * See `AFL_FRIDA_INST_DEBUG_FILE`. This function takes a single `string` as * an argument. */ @@ -130,6 +138,14 @@ class Afl { Afl.jsApiSetInstrumentTraceUnique(); } /** + * See `AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE`. This function takes a single + * `string` as an argument. + */ + static setInstrumentUnstableCoverageFile(file) { + const buf = Memory.allocUtf8String(file); + Afl.jsApiSetInstrumentUnstableCoverageFile(buf); + } + /** * This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a * `NativePointer` should be provided as it's argument. */ @@ -164,11 +180,25 @@ class Afl { Afl.jsApiSetPersistentReturn(address); } /** + * See `AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH`. + */ + static setPrefetchBackpatchDisable() { + Afl.jsApiSetPrefetchBackpatchDisable(); + } + /** * See `AFL_FRIDA_INST_NO_PREFETCH`. */ static setPrefetchDisable() { Afl.jsApiSetPrefetchDisable(); } + /** + * See `AFL_FRIDA_SECCOMP_FILE`. This function takes a single `string` as + * an argument. + */ + static setSeccompFile(file) { + const buf = Memory.allocUtf8String(file); + Afl.jsApiSetSeccompFile(buf); + } /* * Set a function to be called for each instruction which is instrumented * by AFL FRIDA mode. @@ -177,6 +207,12 @@ class Afl { Afl.jsApiSetStalkerCallback(callback); } /** + * See `AFL_FRIDA_STALKER_IC_ENTRIES`. + */ + static setStalkerIcEntries(val) { + Afl.jsApiSetStalkerIcEntries(val); + } + /** * See `AFL_FRIDA_STATS_FILE`. This function takes a single `string` as * an argument. */ @@ -192,12 +228,6 @@ class Afl { Afl.jsApiSetStatsInterval(interval); } /** - * See `AFL_FRIDA_STATS_TRANSITIONS` - */ - static setStatsTransitions() { - Afl.jsApiSetStatsTransitions(); - } - /** * See `AFL_FRIDA_OUTPUT_STDERR`. This function takes a single `string` as * an argument. */ @@ -233,6 +263,7 @@ Afl.jsApiDone = Afl.jsApiGetFunction("js_api_done", "void", []); Afl.jsApiError = Afl.jsApiGetFunction("js_api_error", "void", ["pointer"]); Afl.jsApiSetDebugMaps = Afl.jsApiGetFunction("js_api_set_debug_maps", "void", []); Afl.jsApiSetEntryPoint = Afl.jsApiGetFunction("js_api_set_entrypoint", "void", ["pointer"]); +Afl.jsApiSetInstrumentCoverageFile = Afl.jsApiGetFunction("js_api_set_instrument_coverage_file", "void", ["pointer"]); Afl.jsApiSetInstrumentDebugFile = Afl.jsApiGetFunction("js_api_set_instrument_debug_file", "void", ["pointer"]); Afl.jsApiSetInstrumentJit = Afl.jsApiGetFunction("js_api_set_instrument_jit", "void", []); Afl.jsApiSetInstrumentLibraries = Afl.jsApiGetFunction("js_api_set_instrument_libraries", "void", []); @@ -240,16 +271,19 @@ Afl.jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction("js_api_set_instrument_n Afl.jsApiSetInstrumentSeed = Afl.jsApiGetFunction("js_api_set_instrument_seed", "void", ["uint64"]); Afl.jsApiSetInstrumentTrace = Afl.jsApiGetFunction("js_api_set_instrument_trace", "void", []); Afl.jsApiSetInstrumentTraceUnique = Afl.jsApiGetFunction("js_api_set_instrument_trace_unique", "void", []); +Afl.jsApiSetInstrumentUnstableCoverageFile = Afl.jsApiGetFunction("js_api_set_instrument_unstable_coverage_file", "void", ["pointer"]); Afl.jsApiSetPersistentAddress = Afl.jsApiGetFunction("js_api_set_persistent_address", "void", ["pointer"]); Afl.jsApiSetPersistentCount = Afl.jsApiGetFunction("js_api_set_persistent_count", "void", ["uint64"]); Afl.jsApiSetPersistentDebug = Afl.jsApiGetFunction("js_api_set_persistent_debug", "void", []); Afl.jsApiSetPersistentHook = Afl.jsApiGetFunction("js_api_set_persistent_hook", "void", ["pointer"]); Afl.jsApiSetPersistentReturn = Afl.jsApiGetFunction("js_api_set_persistent_return", "void", ["pointer"]); +Afl.jsApiSetPrefetchBackpatchDisable = Afl.jsApiGetFunction("js_api_set_prefetch_backpatch_disable", "void", []); Afl.jsApiSetPrefetchDisable = Afl.jsApiGetFunction("js_api_set_prefetch_disable", "void", []); +Afl.jsApiSetSeccompFile = Afl.jsApiGetFunction("js_api_set_seccomp_file", "void", ["pointer"]); Afl.jsApiSetStalkerCallback = Afl.jsApiGetFunction("js_api_set_stalker_callback", "void", ["pointer"]); +Afl.jsApiSetStalkerIcEntries = Afl.jsApiGetFunction("js_api_set_stalker_ic_entries", "void", ["uint32"]); Afl.jsApiSetStatsFile = Afl.jsApiGetFunction("js_api_set_stats_file", "void", ["pointer"]); Afl.jsApiSetStatsInterval = Afl.jsApiGetFunction("js_api_set_stats_interval", "void", ["uint64"]); -Afl.jsApiSetStatsTransitions = Afl.jsApiGetFunction("js_api_set_stats_transitions", "void", []); Afl.jsApiSetStdErr = Afl.jsApiGetFunction("js_api_set_stderr", "void", ["pointer"]); Afl.jsApiSetStdOut = Afl.jsApiGetFunction("js_api_set_stdout", "void", ["pointer"]); Afl.jsApiWrite = new NativeFunction( diff --git a/frida_mode/src/js/js_api.c b/frida_mode/src/js/js_api.c index 930a6dc0..9dba79aa 100644 --- a/frida_mode/src/js/js_api.c +++ b/frida_mode/src/js/js_api.c @@ -7,8 +7,11 @@ #include "persistent.h" #include "prefetch.h" #include "ranges.h" +#include "seccomp.h" +#include "stalker.h" #include "stats.h" #include "util.h" + __attribute__((visibility("default"))) void js_api_done() { js_done = TRUE; @@ -107,6 +110,13 @@ __attribute__((visibility("default"))) void js_api_set_instrument_libraries() { } +__attribute__((visibility("default"))) void js_api_set_instrument_coverage_file( + char *path) { + + instrument_coverage_filename = g_strdup(path); + +} + __attribute__((visibility("default"))) void js_api_set_instrument_debug_file( char *path) { @@ -120,6 +130,13 @@ __attribute__((visibility("default"))) void js_api_set_prefetch_disable(void) { } +__attribute__((visibility("default"))) void +js_api_set_prefetch_backpatch_disable(void) { + + prefetch_backpatch = FALSE; + +} + __attribute__((visibility("default"))) void js_api_set_instrument_no_optimize( void) { @@ -148,6 +165,20 @@ __attribute__((visibility("default"))) void js_api_set_instrument_trace_unique( } +__attribute__((visibility("default"))) void +js_api_set_instrument_unstable_coverage_file(char *path) { + + instrument_coverage_unstable_filename = g_strdup(path); + +} + +__attribute__((visibility("default"))) void js_api_set_seccomp_file( + char *file) { + + seccomp_filename = g_strdup(file); + +} + __attribute__((visibility("default"))) void js_api_set_stdout(char *file) { output_stdout = g_strdup(file); @@ -173,12 +204,6 @@ __attribute__((visibility("default"))) void js_api_set_stats_interval( } -__attribute__((visibility("default"))) void js_api_set_stats_transitions() { - - stats_transitions = TRUE; - -} - __attribute__((visibility("default"))) void js_api_set_persistent_hook( void *address) { @@ -199,3 +224,10 @@ __attribute__((visibility("default"))) void js_api_set_stalker_callback( } +__attribute__((visibility("default"))) void js_api_set_stalker_ic_entries( + guint val) { + + stalker_ic_entries = val; + +} + diff --git a/frida_mode/src/main.c b/frida_mode/src/main.c index 91687046..c0de9c6b 100644 --- a/frida_mode/src/main.c +++ b/frida_mode/src/main.c @@ -25,6 +25,7 @@ #include "persistent.h" #include "prefetch.h" #include "ranges.h" +#include "seccomp.h" #include "stalker.h" #include "stats.h" #include "util.h" @@ -177,6 +178,7 @@ __attribute__((visibility("default"))) void afl_frida_start(void) { persistent_config(); prefetch_config(); ranges_config(); + seccomp_config(); stalker_config(); stats_config(); @@ -191,6 +193,7 @@ __attribute__((visibility("default"))) void afl_frida_start(void) { lib_init(); persistent_init(); prefetch_init(); + seccomp_init(); stalker_init(); ranges_init(); stats_init(); diff --git a/frida_mode/src/persistent/persistent.c b/frida_mode/src/persistent/persistent.c index 639a694e..b2915a2f 100644 --- a/frida_mode/src/persistent/persistent.c +++ b/frida_mode/src/persistent/persistent.c @@ -89,7 +89,7 @@ void persistent_init(void) { void persistent_prologue(GumStalkerOutput *output) { OKF("AFL_FRIDA_PERSISTENT_ADDR reached"); - entry_reached = TRUE; + entry_compiled = TRUE; ranges_exclude(); stalker_trust(); persistent_prologue_arch(output); diff --git a/frida_mode/src/prefetch.c b/frida_mode/src/prefetch.c index 50d10c9e..0efbc9bf 100644 --- a/frida_mode/src/prefetch.c +++ b/frida_mode/src/prefetch.c @@ -6,32 +6,66 @@ #include "debug.h" +#include "entry.h" #include "intercept.h" #include "prefetch.h" #include "stalker.h" +#include "util.h" #define TRUST 0 #define PREFETCH_SIZE 65536 #define PREFETCH_ENTRIES ((PREFETCH_SIZE - sizeof(size_t)) / sizeof(void *)) +#define BP_SIZE 524288 + typedef struct { size_t count; void * entry[PREFETCH_ENTRIES]; + guint8 backpatch_data[BP_SIZE]; + gsize backpatch_size; + } prefetch_data_t; gboolean prefetch_enable = TRUE; +gboolean prefetch_backpatch = TRUE; static prefetch_data_t *prefetch_data = NULL; static int prefetch_shm_id = -1; +static void gum_afl_stalker_backpatcher_notify(GumStalkerObserver *self, + const GumBackpatch *backpatch, + gsize size) { + + UNUSED_PARAMETER(self); + if (!entry_run) { return; } + gsize remaining = + sizeof(prefetch_data->backpatch_data) - prefetch_data->backpatch_size; + if (sizeof(gsize) + size > remaining) { return; } + + *(gsize *)(&prefetch_data->backpatch_data[prefetch_data->backpatch_size]) = + size; + prefetch_data->backpatch_size += sizeof(gsize); + + memcpy(&prefetch_data->backpatch_data[prefetch_data->backpatch_size], + backpatch, size); + prefetch_data->backpatch_size += size; + +} + /* * We do this from the transformer since we need one anyway for coverage, this * saves the need to use an event sink. */ void prefetch_write(void *addr) { +#if defined(__aarch64__) + if (!entry_compiled) { return; } +#else + if (!entry_run) { return; } +#endif + /* Bail if we aren't initialized */ if (prefetch_data == NULL) return; @@ -51,10 +85,7 @@ void prefetch_write(void *addr) { } -/* - * Read the IPC region one block at the time and prefetch it - */ -void prefetch_read(void) { +static void prefetch_read_blocks(void) { GumStalker *stalker = stalker_get(); if (prefetch_data == NULL) return; @@ -74,10 +105,60 @@ void prefetch_read(void) { } +static void prefetch_read_patches(void) { + + gsize offset = 0; + GumStalker * stalker = stalker_get(); + GumBackpatch *backpatch = NULL; + + for (gsize remaining = prefetch_data->backpatch_size - offset; + remaining > sizeof(gsize); + remaining = prefetch_data->backpatch_size - offset) { + + gsize size = *(gsize *)(&prefetch_data->backpatch_data[offset]); + offset += sizeof(gsize); + + if (prefetch_data->backpatch_size - offset < size) { + + FATAL("Incomplete backpatch entry"); + + } + + backpatch = (GumBackpatch *)&prefetch_data->backpatch_data[offset]; + gum_stalker_prefetch_backpatch(stalker, backpatch); + offset += size; + + } + + prefetch_data->backpatch_size = 0; + +} + +/* + * Read the IPC region one block at the time and prefetch it + */ +void prefetch_read(void) { + + prefetch_read_blocks(); + prefetch_read_patches(); + +} + void prefetch_config(void) { prefetch_enable = (getenv("AFL_FRIDA_INST_NO_PREFETCH") == NULL); + if (prefetch_enable) { + + prefetch_backpatch = + (getenv("AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH") == NULL); + + } else { + + prefetch_backpatch = FALSE; + + } + } static int prefetch_on_fork(void) { @@ -97,8 +178,9 @@ static void prefetch_hook_fork(void) { void prefetch_init(void) { - g_assert_cmpint(sizeof(prefetch_data_t), ==, PREFETCH_SIZE); OKF("Instrumentation - prefetch [%c]", prefetch_enable ? 'X' : ' '); + OKF("Instrumentation - prefetch_backpatch [%c]", + prefetch_backpatch ? 'X' : ' '); if (!prefetch_enable) { return; } /* @@ -131,5 +213,11 @@ void prefetch_init(void) { prefetch_hook_fork(); + if (!prefetch_backpatch) { return; } + + GumStalkerObserver * observer = stalker_get_observer(); + GumStalkerObserverInterface *iface = GUM_STALKER_OBSERVER_GET_IFACE(observer); + iface->notify_backpatch = gum_afl_stalker_backpatcher_notify; + } diff --git a/frida_mode/src/ranges.c b/frida_mode/src/ranges.c index 6fdd65a7..5b6eb462 100644 --- a/frida_mode/src/ranges.c +++ b/frida_mode/src/ranges.c @@ -635,9 +635,7 @@ void ranges_init(void) { } -gboolean range_is_excluded(gpointer address) { - - GumAddress test = GUM_ADDRESS(address); +gboolean range_is_excluded(GumAddress address) { if (ranges == NULL) { return false; } @@ -646,9 +644,9 @@ gboolean range_is_excluded(gpointer address) { GumMemoryRange *curr = &g_array_index(ranges, GumMemoryRange, i); GumAddress curr_limit = curr->base_address + curr->size; - if (test < curr->base_address) { return false; } + if (address < curr->base_address) { return false; } - if (test < curr_limit) { return true; } + if (address < curr_limit) { return true; } } diff --git a/frida_mode/src/seccomp/seccomp.c b/frida_mode/src/seccomp/seccomp.c new file mode 100644 index 00000000..99111591 --- /dev/null +++ b/frida_mode/src/seccomp/seccomp.c @@ -0,0 +1,41 @@ +#include "frida-gumjs.h" + +#include "debug.h" + +#include "seccomp.h" +#include "util.h" + +char *seccomp_filename = NULL; + +void seccomp_on_fork(void) { + + if (seccomp_filename == NULL) { return; } + +#ifdef __APPLE__ + FATAL("Seccomp not supported on OSX"); +#else + seccomp_callback_parent(); +#endif + +} + +void seccomp_config(void) { + + seccomp_filename = getenv("AFL_FRIDA_SECCOMP_FILE"); + +} + +void seccomp_init(void) { + + OKF("Seccomp - file [%s]", seccomp_filename); + + if (seccomp_filename == NULL) { return; } + +#ifdef __APPLE__ + FATAL("Seccomp not supported on OSX"); +#else + seccomp_callback_initialize(); +#endif + +} + diff --git a/frida_mode/src/seccomp/seccomp_atomic.c b/frida_mode/src/seccomp/seccomp_atomic.c new file mode 100644 index 00000000..5097511a --- /dev/null +++ b/frida_mode/src/seccomp/seccomp_atomic.c @@ -0,0 +1,32 @@ +#ifndef __APPLE__ + + #include <stdbool.h> + #include <stdio.h> + + #include "debug.h" + +void seccomp_atomic_set(volatile bool *ptr, bool val) { + + if (!__sync_bool_compare_and_swap(ptr, !val, val)) { + + FATAL("Failed to set event"); + + } + +} + +bool seccomp_atomic_try_set(volatile bool *ptr, bool val) { + + return __sync_bool_compare_and_swap(ptr, !val, val); + +} + +void seccomp_atomic_wait(volatile bool *ptr, bool val) { + + while (!__sync_bool_compare_and_swap(ptr, val, !val)) + ; + +} + +#endif + diff --git a/frida_mode/src/seccomp/seccomp_callback.c b/frida_mode/src/seccomp/seccomp_callback.c new file mode 100644 index 00000000..4af2ed0c --- /dev/null +++ b/frida_mode/src/seccomp/seccomp_callback.c @@ -0,0 +1,120 @@ +#ifndef __APPLE__ + + #include <execinfo.h> + #include <fcntl.h> + + #include "seccomp.h" + + #include "debug.h" + +static void seccomp_callback_filter(struct seccomp_notif * req, + struct seccomp_notif_resp *resp, + GumReturnAddressArray * frames) { + + GumDebugSymbolDetails details = {0}; + if (req->data.nr == SYS_OPENAT) { + + seccomp_print("SYS_OPENAT: (%s)\n", (char *)req->data.args[1]); + + } + + seccomp_print( + "\nID (%#llx) for PID %d - %d (%s) [0x%llx 0x%llx 0x%llx 0x%llx 0x%llx " + "0x%llx ]\n", + req->id, req->pid, req->data.nr, seccomp_syscall_lookup(req->data.nr), + req->data.args[0], req->data.args[1], req->data.args[2], + req->data.args[3], req->data.args[4], req->data.args[5]); + + seccomp_print("FRAMES: (%u)\n", frames->len); + char **syms = backtrace_symbols(frames->items, frames->len); + if (syms == NULL) { FATAL("Failed to get symbols"); } + + for (guint i = 0; i < frames->len; i++) { + + if (gum_symbol_details_from_address(frames->items[i], &details)) { + + seccomp_print("\t%3d. %s!%s\n", i, details.module_name, + details.symbol_name); + + } else { + + seccomp_print("\t%3d. %s\n", i, syms[i]); + + } + + } + + free(syms); + + resp->error = 0; + resp->val = 0; + resp->id = req->id; + resp->flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE; + +} + +static void seccomp_callback_child(int signal_parent, void *ctx) { + + int sock_fd = *((int *)ctx); + int fd = seccomp_socket_recv(sock_fd); + + if (close(sock_fd) < 0) { FATAL("child - close"); } + + seccomp_event_signal(signal_parent); + seccomp_filter_child_install(); + seccomp_filter_run(fd, seccomp_callback_filter); + +} + +void seccomp_callback_parent(void) { + + int sock[2] = {-1, -1}; + pid_t child = -1; + int child_fd = -1; + + seccomp_socket_create(sock); + seccomp_child_run(seccomp_callback_child, sock, &child, &child_fd); + + if (dup2(child_fd, SECCOMP_PARENT_EVENT_FD) < 0) { FATAL("dup2"); } + + if (close(child_fd) < 0) { FATAL("seccomp_on_fork - close (1)"); } + + if (close(sock[STDIN_FILENO]) < 0) { FATAL("grandparent - close (2)"); } + + int fd = seccomp_filter_install(child); + seccomp_socket_send(sock[STDOUT_FILENO], fd); + + if (close(sock[STDOUT_FILENO]) < 0) { FATAL("grandparent - close (3)"); } + + if (close(fd) < 0) { FATAL("grandparent - close (4)"); } + + seccomp_child_wait(SECCOMP_PARENT_EVENT_FD); + +} + +void seccomp_callback_initialize(void) { + + char *path = NULL; + int fd; + + path = g_canonicalize_filename(seccomp_filename, g_get_current_dir()); + + OKF("Seccomp - path [%s]", path); + + fd = open(path, O_RDWR | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + + if (dup2(fd, SECCOMP_OUTPUT_FILE_FD) < 0) { + + FATAL("Failed to duplicate seccomp output file"); + + } + + if (close(fd) < 0) { FATAL("Failed to close seccomp output file fd"); } + + g_free(path); + +} + +#endif + diff --git a/frida_mode/src/seccomp/seccomp_child.c b/frida_mode/src/seccomp/seccomp_child.c new file mode 100644 index 00000000..f665f472 --- /dev/null +++ b/frida_mode/src/seccomp/seccomp_child.c @@ -0,0 +1,73 @@ +#ifndef __APPLE__ + + #include <fcntl.h> + #include <sched.h> + #include <signal.h> + #include <stdio.h> + #include <stdlib.h> + #include <sys/mman.h> + #include <sys/prctl.h> + #include <sys/types.h> + #include <unistd.h> + + #include "debug.h" + + #include "seccomp.h" + + #define SECCOMP_CHILD_STACK_SIZE (1UL << 20) + +typedef void (*seccomp_child_func_t)(int event_fd, void *ctx); + +typedef struct { + + seccomp_child_func_t func; + int event_fd; + void * ctx; + +} seccomp_child_func_ctx_t; + +static int seccomp_child_func(void *ctx) { + + seccomp_child_func_ctx_t *args = (seccomp_child_func_ctx_t *)ctx; + args->func(args->event_fd, args->ctx); + _exit(0); + return 0; + +} + +void seccomp_child_run(seccomp_child_func_t child_func, void *ctx, pid_t *child, + int *event_fd) { + + int fd = seccomp_event_create(); + + seccomp_child_func_ctx_t *child_ctx = + malloc(sizeof(seccomp_child_func_ctx_t)); + child_ctx->func = child_func; + child_ctx->ctx = ctx; + child_ctx->event_fd = fd; + + int flags = CLONE_VM | CLONE_UNTRACED; + + char *stack = + (char *)mmap(NULL, SECCOMP_CHILD_STACK_SIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (stack == MAP_FAILED) { FATAL("mmap"); } + + pid_t child_pid = clone(seccomp_child_func, &stack[SECCOMP_CHILD_STACK_SIZE], + flags, child_ctx, NULL, NULL, NULL); + if (child_pid < 0) { FATAL("clone"); } + + if (child != NULL) { *child = child_pid; } + if (event_fd != NULL) { *event_fd = fd; } + +} + +void seccomp_child_wait(int event_fd) { + + seccomp_event_wait(event_fd); + seccomp_event_destroy(event_fd); + +} + +#endif + diff --git a/frida_mode/src/seccomp/seccomp_event.c b/frida_mode/src/seccomp/seccomp_event.c new file mode 100644 index 00000000..dd4abde7 --- /dev/null +++ b/frida_mode/src/seccomp/seccomp_event.c @@ -0,0 +1,49 @@ +#ifndef __APPLE__ + + #include <stdint.h> + #include <stdio.h> + #include <sys/syscall.h> + #include <unistd.h> + + #include "debug.h" + + #include "seccomp.h" + +int seccomp_event_create(void) { + + int fd = syscall(SYS_eventfd, 0, 0); + if (fd < 0) { FATAL("seccomp_event_create"); } + return fd; + +} + +void seccomp_event_signal(int fd) { + + uint64_t val = 1; + if (write(fd, &val, sizeof(uint64_t)) != sizeof(uint64_t)) { + + FATAL("seccomp_event_signal"); + + } + +} + +void seccomp_event_wait(int fd) { + + uint64_t val = 1; + if (read(fd, &val, sizeof(uint64_t)) != sizeof(uint64_t)) { + + FATAL("seccomp_event_wait"); + + } + +} + +void seccomp_event_destroy(int fd) { + + if (close(fd) < 0) { FATAL("seccomp_event_destroy"); } + +} + +#endif + diff --git a/frida_mode/src/seccomp/seccomp_filter.c b/frida_mode/src/seccomp/seccomp_filter.c new file mode 100644 index 00000000..13ff7522 --- /dev/null +++ b/frida_mode/src/seccomp/seccomp_filter.c @@ -0,0 +1,265 @@ +#ifndef __APPLE__ + + #include <alloca.h> + #include <errno.h> + #include <execinfo.h> + #include <linux/filter.h> + #include <sys/ioctl.h> + #include <sys/prctl.h> + #include <sys/syscall.h> + #include <signal.h> + #include <stdbool.h> + #include <stddef.h> + #include <stdio.h> + #include <stdlib.h> + #include <string.h> + #include <unistd.h> + + #include "debug.h" + + #include "frida-gumjs.h" + + #include "seccomp.h" + #include "util.h" + + #define SECCOMP_FILTER_NUM_FRAMES 512 + +extern void gum_linux_parse_ucontext(const ucontext_t *uc, GumCpuContext *ctx); + +static struct sock_filter filter[] = { + + /* Allow us sendmsg to SECCOMP_FD */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_sendmsg, 0, 3), + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, + (offsetof(struct seccomp_data, args[0]))), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SECCOMP_SOCKET_SEND_FD, 0, 1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + + /* Allow close */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_close, 0, 1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + + /* Allow sigreturn */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_rt_sigreturn, 0, 1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + + /* Allow sigprocmaksk */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_rt_sigprocmask, 0, 1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + + /* Allow console output*/ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_lseek, 2, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_fstat, 1, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_write, 0, 4), + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, + (offsetof(struct seccomp_data, args[0]))), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, STDERR_FILENO, 1, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, STDOUT_FILENO, 0, 1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + + /* Allow waiting for the child */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_read, 0, 3), + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, + (offsetof(struct seccomp_data, args[0]))), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SECCOMP_PARENT_EVENT_FD, 0, 1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + + /* Allow us to make anonymous maps */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_mmap, 0, 3), + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, + (offsetof(struct seccomp_data, args[4]))), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, -1, 0, 1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + + /* Allow msync/mincore used by cmplog */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_msync, 1, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_mincore, 0, 1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + + /* + * Allow tgkill (SIGKILL, SIGSTOP) used in persistent mode. Also + * allow seccomp to send (SIGUSR1) to the child to collect trace. + */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_tgkill, 0, 5), + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, + (offsetof(struct seccomp_data, args[2]))), + /* Used by seccomp to signal the child to collect a callstack*/ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SIGUSR1, 2, 0), + /* Used when handling faults */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SIGKILL, 1, 0), + /* Used by target app of interest */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SIGSTOP, 0, 1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + + /* Allow getpid / gettid */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_getpid, 1, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_gettid, 0, 1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + + /* Allow exit_group */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_exit_group, 0, 1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + + /* Allow brk */ + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_brk, 0, 1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + + /* Send the rest to user-mode to filter */ + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF)}; + +static volatile bool seccomp_filter_parent_done = false; +static volatile bool seccomp_filter_child_done = false; +static pid_t seccomp_filter_child = -1; +static GumCpuContext seccomp_filter_cpu_context = {0}; +static GumReturnAddressArray seccomp_filter_frames = {.len = 0, .items = {0}}; +static GumBacktracer * seccomp_filter_backtracer = NULL; + +static void seccomp_filter_child_handler(int sig, siginfo_t *info, + void *ucontext) { + + UNUSED_PARAMETER(sig); + UNUSED_PARAMETER(info); + UNUSED_PARAMETER(ucontext); + + if (seccomp_filter_backtracer == NULL) { + + seccomp_filter_backtracer = gum_backtracer_make_fuzzy(); + + } + + gum_backtracer_generate(seccomp_filter_backtracer, + &seccomp_filter_cpu_context, &seccomp_filter_frames); + + seccomp_atomic_set(&seccomp_filter_child_done, true); + +} + +static void seccomp_filter_parent_handler(int sig, siginfo_t *info, + void *ucontext) { + + UNUSED_PARAMETER(sig); + UNUSED_PARAMETER(info); + + ucontext_t *uc = (ucontext_t *)ucontext; + gum_linux_parse_ucontext(uc, &seccomp_filter_cpu_context); + + if (syscall(SYS_tgkill, seccomp_filter_child, seccomp_filter_child, SIGUSR1) < + 0) { + + FATAL("kill"); + + } + + seccomp_atomic_wait(&seccomp_filter_child_done, true); + seccomp_atomic_set(&seccomp_filter_parent_done, true); + +} + +void seccomp_filter_child_install(void) { + + const struct sigaction sa = {.sa_sigaction = seccomp_filter_child_handler, + .sa_flags = SA_SIGINFO | SA_RESTART}; + if (sigaction(SIGUSR1, &sa, NULL) < 0) { FATAL("sigaction"); } + +} + +int seccomp_filter_install(pid_t child) { + + seccomp_filter_child = child; + + const struct sigaction sa = {.sa_sigaction = seccomp_filter_parent_handler, + .sa_flags = SA_SIGINFO | SA_RESTART}; + + struct sock_fprog filter_prog = { + + .len = sizeof(filter) / sizeof(struct sock_filter), .filter = filter}; + + if (sigaction(SIGUSR1, &sa, NULL) < 0) { FATAL("sigaction"); } + + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + + FATAL("PR_SET_NO_NEW_PRIVS %d", errno); + + } + + int fd = syscall(SYS_seccomp, SECCOMP_SET_MODE_FILTER, + SECCOMP_FILTER_FLAG_NEW_LISTENER, &filter_prog); + if (fd < 0) { FATAL("SYS_seccomp %d", fd); } + + return fd; + +} + +void seccomp_filter_run(int fd, seccomp_filter_callback_t callback) { + + struct seccomp_notif * req = NULL; + struct seccomp_notif_resp *resp = NULL; + struct seccomp_notif_sizes sizes; + + if (syscall(SYS_seccomp, SECCOMP_GET_NOTIF_SIZES, 0, &sizes) == -1) { + + FATAL("seccomp-SECCOMP_GET_NOTIF_SIZES"); + + } + + if (sizes.seccomp_notif != sizeof(struct seccomp_notif)) { + + FATAL("size - seccomp_notif"); + + } + + if (sizes.seccomp_notif_resp != sizeof(struct seccomp_notif_resp)) { + + FATAL("size - seccomp_notif"); + + } + + req = alloca(sizes.seccomp_notif); + resp = alloca(sizes.seccomp_notif_resp); + + while (true) { + + memset(req, 0, sizes.seccomp_notif); + + if (ioctl(fd, SECCOMP_IOCTL_NOTIF_RECV, req) < 0) { + + if (errno == EINTR) { continue; } + FATAL("SECCOMP_IOCTL_NOTIF_RECV: %d\n", fd); + + } + + if (seccomp_atomic_try_set(&seccomp_filter_parent_done, false)) { + + callback(req, resp, &seccomp_filter_frames); + + } else { + + if (kill(req->pid, SIGUSR1) < 0) { FATAL("kill"); } + + } + + if (ioctl(fd, SECCOMP_IOCTL_NOTIF_SEND, resp) < 0) { + + if (errno == ENOENT) { continue; } + OKF("SECCOMP_IOCTL_NOTIF_SEND"); + continue; + + } + + } + +} + +#endif + diff --git a/frida_mode/src/seccomp/seccomp_print.c b/frida_mode/src/seccomp/seccomp_print.c new file mode 100644 index 00000000..be4d80ce --- /dev/null +++ b/frida_mode/src/seccomp/seccomp_print.c @@ -0,0 +1,30 @@ +#ifndef __APPLE__ + + #include <stdarg.h> + + #include "seccomp.h" + #include "util.h" + +static void seccomp_print_v(int fd, char *format, va_list ap) { + + char buffer[4096] = {0}; + int len; + + if (vsnprintf(buffer, sizeof(buffer) - 1, format, ap) < 0) { return; } + + len = strnlen(buffer, sizeof(buffer)); + IGNORED_RETURN(write(fd, buffer, len)); + +} + +void seccomp_print(char *format, ...) { + + va_list ap; + va_start(ap, format); + seccomp_print_v(SECCOMP_OUTPUT_FILE_FD, format, ap); + va_end(ap); + +} + +#endif + diff --git a/frida_mode/src/seccomp/seccomp_socket.c b/frida_mode/src/seccomp/seccomp_socket.c new file mode 100644 index 00000000..fae95805 --- /dev/null +++ b/frida_mode/src/seccomp/seccomp_socket.c @@ -0,0 +1,125 @@ +#ifndef __APPLE__ + + #include <stdio.h> + #include <string.h> + #include <sys/socket.h> + #include <unistd.h> + + #include "debug.h" + + #include "seccomp.h" + +union cmsg { + + char buf[CMSG_SPACE(sizeof(int))]; + struct cmsghdr hdr; + +}; + +void seccomp_socket_create(int *sock) { + + int tmp_sock[2] = {-1, -1}; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, tmp_sock) < 0) { + + FATAL("socketpair"); + + } + + if (dup2(tmp_sock[STDIN_FILENO], SECCOMP_SOCKET_RECV_FD) < 0) { + + FATAL("seccomp_socket_create - dup2 (1)"); + + } + + if (dup2(tmp_sock[STDOUT_FILENO], SECCOMP_SOCKET_SEND_FD) < 0) { + + FATAL("seccomp_socket_create - dup2 (1)"); + + } + + if (close(tmp_sock[STDIN_FILENO]) < 0) { + + FATAL("seccomp_socket_create - close (1)"); + + } + + if (close(tmp_sock[STDOUT_FILENO]) < 0) { + + FATAL("seccomp_socket_create - close (2)"); + + } + + sock[STDIN_FILENO] = SECCOMP_SOCKET_RECV_FD; + sock[STDOUT_FILENO] = SECCOMP_SOCKET_SEND_FD; + +} + +void seccomp_socket_send(int sockfd, int fd) { + + int data = 12345; + struct iovec iov = {.iov_base = &data, .iov_len = sizeof(data)}; + union cmsg control_msg = {.hdr = { + + .cmsg_len = CMSG_LEN(sizeof(int)), + .cmsg_level = SOL_SOCKET, + .cmsg_type = SCM_RIGHTS, + + }}; + + struct msghdr message = {.msg_control = control_msg.buf, + .msg_controllen = sizeof(control_msg.buf), + .msg_flags = 0, + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_name = NULL, + .msg_namelen = 0}; + + memcpy(CMSG_DATA(&control_msg.hdr), &fd, sizeof(int)); + + if (sendmsg(sockfd, &message, 0) == -1) { FATAL("sendmsg"); } + +} + +int seccomp_socket_recv(int sockfd) { + + int data; + struct iovec iov = {.iov_base = &data, .iov_len = sizeof(data)}; + union cmsg control_msg = {0}; + struct msghdr message = {.msg_control = control_msg.buf, + .msg_controllen = sizeof(control_msg.buf), + .msg_flags = 0, + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_name = NULL, + .msg_namelen = 0}; + + int fd; + + if (recvmsg(sockfd, &message, 0) < 0) { FATAL("recvmsg"); } + + if (control_msg.hdr.cmsg_len != CMSG_LEN(sizeof(int))) { + + FATAL("control_msg.hdr.cmsg_len"); + + } + + if (control_msg.hdr.cmsg_level != SOL_SOCKET) { + + FATAL("control_msg.hdr.cmsg_level"); + + } + + if (control_msg.hdr.cmsg_type != SCM_RIGHTS) { + + FATAL("control_msg.hdr.cmsg_type"); + + } + + memcpy(&fd, CMSG_DATA(&control_msg.hdr), sizeof(int)); + + return fd; + +} + +#endif + diff --git a/frida_mode/src/seccomp/seccomp_syscall.c b/frida_mode/src/seccomp/seccomp_syscall.c new file mode 100644 index 00000000..e023c131 --- /dev/null +++ b/frida_mode/src/seccomp/seccomp_syscall.c @@ -0,0 +1,339 @@ +#ifndef __APPLE__ + + #include <limits.h> + #include <stdio.h> + + #include "debug.h" + + #include "seccomp.h" + +typedef struct { + + int id; + char name[PATH_MAX]; + +} syscall_entry_t; + +static syscall_entry_t seccomp_syscall_table[] = { + + {SYS_READ, "SYS_READ"}, + {SYS_WRITE, "SYS_WRITE"}, + {SYS_OPEN, "SYS_OPEN"}, + {SYS_CLOSE, "SYS_CLOSE"}, + {SYS_STAT, "SYS_STAT"}, + {SYS_FSTAT, "SYS_FSTAT"}, + {SYS_LSTAT, "SYS_LSTAT"}, + {SYS_POLL, "SYS_POLL"}, + {SYS_LSEEK, "SYS_LSEEK"}, + {SYS_MMAP, "SYS_MMAP"}, + {SYS_MPROTECT, "SYS_MPROTECT"}, + {SYS_MUNMAP, "SYS_MUNMAP"}, + {SYS_BRK, "SYS_BRK"}, + {SYS_RT_SIGACTION, "SYS_RT_SIGACTION"}, + {SYS_RT_SIGPROCMASK, "SYS_RT_SIGPROCMASK"}, + {SYS_RT_SIGRETURN, "SYS_RT_SIGRETURN"}, + {SYS_IOCTL, "SYS_IOCTL"}, + {SYS_PREAD64, "SYS_PREAD64"}, + {SYS_PWRITE64, "SYS_PWRITE64"}, + {SYS_READV, "SYS_READV"}, + {SYS_WRITEV, "SYS_WRITEV"}, + {SYS_ACCESS, "SYS_ACCESS"}, + {SYS_PIPE, "SYS_PIPE"}, + {SYS_SELECT, "SYS_SELECT"}, + {SYS_SCHED_YIELD, "SYS_SCHED_YIELD"}, + {SYS_MREMAP, "SYS_MREMAP"}, + {SYS_MSYNC, "SYS_MSYNC"}, + {SYS_MINCORE, "SYS_MINCORE"}, + {SYS_MADVISE, "SYS_MADVISE"}, + {SYS_SHMGET, "SYS_SHMGET"}, + {SYS_SHMAT, "SYS_SHMAT"}, + {SYS_SHMCTL, "SYS_SHMCTL"}, + {SYS_DUP, "SYS_DUP"}, + {SYS_DUP2, "SYS_DUP2"}, + {SYS_PAUSE, "SYS_PAUSE"}, + {SYS_NANOSLEEP, "SYS_NANOSLEEP"}, + {SYS_GETITIMER, "SYS_GETITIMER"}, + {SYS_ALARM, "SYS_ALARM"}, + {SYS_SETITIMER, "SYS_SETITIMER"}, + {SYS_GETPID, "SYS_GETPID"}, + {SYS_SENDFILE, "SYS_SENDFILE"}, + {SYS_SOCKET, "SYS_SOCKET"}, + {SYS_CONNECT, "SYS_CONNECT"}, + {SYS_ACCEPT, "SYS_ACCEPT"}, + {SYS_SENDTO, "SYS_SENDTO"}, + {SYS_RECVFROM, "SYS_RECVFROM"}, + {SYS_SENDMSG, "SYS_SENDMSG"}, + {SYS_RECVMSG, "SYS_RECVMSG"}, + {SYS_SHUTDOWN, "SYS_SHUTDOWN"}, + {SYS_BIND, "SYS_BIND"}, + {SYS_LISTEN, "SYS_LISTEN"}, + {SYS_GETSOCKNAME, "SYS_GETSOCKNAME"}, + {SYS_GETPEERNAME, "SYS_GETPEERNAME"}, + {SYS_SOCKETPAIR, "SYS_SOCKETPAIR"}, + {SYS_SETSOCKOPT, "SYS_SETSOCKOPT"}, + {SYS_GETSOCKOPT, "SYS_GETSOCKOPT"}, + {SYS_CLONE, "SYS_CLONE"}, + {SYS_FORK, "SYS_FORK"}, + {SYS_VFORK, "SYS_VFORK"}, + {SYS_EXECVE, "SYS_EXECVE"}, + {SYS_EXIT, "SYS_EXIT"}, + {SYS_WAIT4, "SYS_WAIT4"}, + {SYS_KILL, "SYS_KILL"}, + {SYS_UNAME, "SYS_UNAME"}, + {SYS_SEMGET, "SYS_SEMGET"}, + {SYS_SEMOP, "SYS_SEMOP"}, + {SYS_SEMCTL, "SYS_SEMCTL"}, + {SYS_SHMDT, "SYS_SHMDT"}, + {SYS_MSGGET, "SYS_MSGGET"}, + {SYS_MSGSND, "SYS_MSGSND"}, + {SYS_MSGRCV, "SYS_MSGRCV"}, + {SYS_MSGCTL, "SYS_MSGCTL"}, + {SYS_FCNTL, "SYS_FCNTL"}, + {SYS_FLOCK, "SYS_FLOCK"}, + {SYS_FSYNC, "SYS_FSYNC"}, + {SYS_FDATASYNC, "SYS_FDATASYNC"}, + {SYS_TRUNCATE, "SYS_TRUNCATE"}, + {SYS_FTRUNCATE, "SYS_FTRUNCATE"}, + {SYS_GETDENTS, "SYS_GETDENTS"}, + {SYS_GETCWD, "SYS_GETCWD"}, + {SYS_CHDIR, "SYS_CHDIR"}, + {SYS_FCHDIR, "SYS_FCHDIR"}, + {SYS_RENAME, "SYS_RENAME"}, + {SYS_MKDIR, "SYS_MKDIR"}, + {SYS_RMDIR, "SYS_RMDIR"}, + {SYS_CREAT, "SYS_CREAT"}, + {SYS_LINK, "SYS_LINK"}, + {SYS_UNLINK, "SYS_UNLINK"}, + {SYS_SYMLINK, "SYS_SYMLINK"}, + {SYS_READLINK, "SYS_READLINK"}, + {SYS_CHMOD, "SYS_CHMOD"}, + {SYS_FCHMOD, "SYS_FCHMOD"}, + {SYS_CHOWN, "SYS_CHOWN"}, + {SYS_FCHOWN, "SYS_FCHOWN"}, + {SYS_LCHOWN, "SYS_LCHOWN"}, + {SYS_UMASK, "SYS_UMASK"}, + {SYS_GETTIMEOFDAY, "SYS_GETTIMEOFDAY"}, + {SYS_GETRLIMIT, "SYS_GETRLIMIT"}, + {SYS_GETRUSAGE, "SYS_GETRUSAGE"}, + {SYS_SYSINFO, "SYS_SYSINFO"}, + {SYS_TIMES, "SYS_TIMES"}, + {SYS_PTRACE, "SYS_PTRACE"}, + {SYS_GETUID, "SYS_GETUID"}, + {SYS_SYSLOG, "SYS_SYSLOG"}, + {SYS_GETGID, "SYS_GETGID"}, + {SYS_SETUID, "SYS_SETUID"}, + {SYS_SETGID, "SYS_SETGID"}, + {SYS_GETEUID, "SYS_GETEUID"}, + {SYS_GETEGID, "SYS_GETEGID"}, + {SYS_SETPGID, "SYS_SETPGID"}, + {SYS_GETPPID, "SYS_GETPPID"}, + {SYS_GETPGRP, "SYS_GETPGRP"}, + {SYS_SETSID, "SYS_SETSID"}, + {SYS_SETREUID, "SYS_SETREUID"}, + {SYS_SETREGID, "SYS_SETREGID"}, + {SYS_GETGROUPS, "SYS_GETGROUPS"}, + {SYS_SETGROUPS, "SYS_SETGROUPS"}, + {SYS_SETRESUID, "SYS_SETRESUID"}, + {SYS_GETRESUID, "SYS_GETRESUID"}, + {SYS_SETRESGID, "SYS_SETRESGID"}, + {SYS_GETRESGID, "SYS_GETRESGID"}, + {SYS_GETPGID, "SYS_GETPGID"}, + {SYS_SETFSUID, "SYS_SETFSUID"}, + {SYS_SETFSGID, "SYS_SETFSGID"}, + {SYS_GETSID, "SYS_GETSID"}, + {SYS_CAPGET, "SYS_CAPGET"}, + {SYS_CAPSET, "SYS_CAPSET"}, + {SYS_RT_SIGPENDING, "SYS_RT_SIGPENDING"}, + {SYS_RT_SIGTIMEDWAIT, "SYS_RT_SIGTIMEDWAIT"}, + {SYS_RT_SIGQUEUEINFO, "SYS_RT_SIGQUEUEINFO"}, + {SYS_RT_SIGSUSPEND, "SYS_RT_SIGSUSPEND"}, + {SYS_SIGALTSTACK, "SYS_SIGALTSTACK"}, + {SYS_UTIME, "SYS_UTIME"}, + {SYS_MKNOD, "SYS_MKNOD"}, + {SYS_USELIB, "SYS_USELIB"}, + {SYS_PERSONALITY, "SYS_PERSONALITY"}, + {SYS_USTAT, "SYS_USTAT"}, + {SYS_STATFS, "SYS_STATFS"}, + {SYS_FSTATFS, "SYS_FSTATFS"}, + {SYS_SYSFS, "SYS_SYSFS"}, + {SYS_GETPRIORITY, "SYS_GETPRIORITY"}, + {SYS_SETPRIORITY, "SYS_SETPRIORITY"}, + {SYS_SCHED_SETPARAM, "SYS_SCHED_SETPARAM"}, + {SYS_SCHED_GETPARAM, "SYS_SCHED_GETPARAM"}, + {SYS_SCHED_SETSCHEDULER, "SYS_SCHED_SETSCHEDULER"}, + {SYS_SCHED_GETSCHEDULER, "SYS_SCHED_GETSCHEDULER"}, + {SYS_SCHED_GET_PRIORITY_MAX, "SYS_SCHED_GET_PRIORITY_MAX"}, + {SYS_SCHED_GET_PRIORITY_MIN, "SYS_SCHED_GET_PRIORITY_MIN"}, + {SYS_SCHED_RR_GET_INTERVAL, "SYS_SCHED_RR_GET_INTERVAL"}, + {SYS_MLOCK, "SYS_MLOCK"}, + {SYS_MUNLOCK, "SYS_MUNLOCK"}, + {SYS_MLOCKALL, "SYS_MLOCKALL"}, + {SYS_MUNLOCKALL, "SYS_MUNLOCKALL"}, + {SYS_VHANGUP, "SYS_VHANGUP"}, + {SYS_MODIFY_LDT, "SYS_MODIFY_LDT"}, + {SYS_PIVOT_ROOT, "SYS_PIVOT_ROOT"}, + {SYS__SYSCTL, "SYS__SYSCTL"}, + {SYS_PRCTL, "SYS_PRCTL"}, + {SYS_ARCH_PRCTL, "SYS_ARCH_PRCTL"}, + {SYS_ADJTIMEX, "SYS_ADJTIMEX"}, + {SYS_SETRLIMIT, "SYS_SETRLIMIT"}, + {SYS_CHROOT, "SYS_CHROOT"}, + {SYS_SYNC, "SYS_SYNC"}, + {SYS_ACCT, "SYS_ACCT"}, + {SYS_SETTIMEOFDAY, "SYS_SETTIMEOFDAY"}, + {SYS_MOUNT, "SYS_MOUNT"}, + {SYS_UMOUNT2, "SYS_UMOUNT2"}, + {SYS_SWAPON, "SYS_SWAPON"}, + {SYS_SWAPOFF, "SYS_SWAPOFF"}, + {SYS_REBOOT, "SYS_REBOOT"}, + {SYS_SETHOSTNAME, "SYS_SETHOSTNAME"}, + {SYS_SETDOMAINNAME, "SYS_SETDOMAINNAME"}, + {SYS_IOPL, "SYS_IOPL"}, + {SYS_IOPERM, "SYS_IOPERM"}, + {SYS_CREATE_MODULE, "SYS_CREATE_MODULE"}, + {SYS_INIT_MODULE, "SYS_INIT_MODULE"}, + {SYS_DELETE_MODULE, "SYS_DELETE_MODULE"}, + {SYS_GET_KERNEL_SYMS, "SYS_GET_KERNEL_SYMS"}, + {SYS_QUERY_MODULE, "SYS_QUERY_MODULE"}, + {SYS_QUOTACTL, "SYS_QUOTACTL"}, + {SYS_NFSSERVCTL, "SYS_NFSSERVCTL"}, + {SYS_GETPMSG, "SYS_GETPMSG"}, + {SYS_PUTPMSG, "SYS_PUTPMSG"}, + {SYS_AFS_SYSCALL, "SYS_AFS_SYSCALL"}, + {SYS_TUXCALL, "SYS_TUXCALL"}, + {SYS_SECURITY, "SYS_SECURITY"}, + {SYS_GETTID, "SYS_GETTID"}, + {SYS_READAHEAD, "SYS_READAHEAD"}, + {SYS_SETXATTR, "SYS_SETXATTR"}, + {SYS_LSETXATTR, "SYS_LSETXATTR"}, + {SYS_FSETXATTR, "SYS_FSETXATTR"}, + {SYS_GETXATTR, "SYS_GETXATTR"}, + {SYS_LGETXATTR, "SYS_LGETXATTR"}, + {SYS_FGETXATTR, "SYS_FGETXATTR"}, + {SYS_LISTXATTR, "SYS_LISTXATTR"}, + {SYS_LLISTXATTR, "SYS_LLISTXATTR"}, + {SYS_FLISTXATTR, "SYS_FLISTXATTR"}, + {SYS_REMOVEXATTR, "SYS_REMOVEXATTR"}, + {SYS_LREMOVEXATTR, "SYS_LREMOVEXATTR"}, + {SYS_FREMOVEXATTR, "SYS_FREMOVEXATTR"}, + {SYS_TKILL, "SYS_TKILL"}, + {SYS_TIME, "SYS_TIME"}, + {SYS_FUTEX, "SYS_FUTEX"}, + {SYS_SCHED_SETAFFINITY, "SYS_SCHED_SETAFFINITY"}, + {SYS_SCHED_GETAFFINITY, "SYS_SCHED_GETAFFINITY"}, + {SYS_SET_THREAD_AREA, "SYS_SET_THREAD_AREA"}, + {SYS_IO_SETUP, "SYS_IO_SETUP"}, + {SYS_IO_DESTROY, "SYS_IO_DESTROY"}, + {SYS_IO_GETEVENTS, "SYS_IO_GETEVENTS"}, + {SYS_IO_SUBMIT, "SYS_IO_SUBMIT"}, + {SYS_IO_CANCEL, "SYS_IO_CANCEL"}, + {SYS_GET_THREAD_AREA, "SYS_GET_THREAD_AREA"}, + {SYS_LOOKUP_DCOOKIE, "SYS_LOOKUP_DCOOKIE"}, + {SYS_EPOLL_CREATE, "SYS_EPOLL_CREATE"}, + {SYS_EPOLL_CTL_OLD, "SYS_EPOLL_CTL_OLD"}, + {SYS_EPOLL_WAIT_OLD, "SYS_EPOLL_WAIT_OLD"}, + {SYS_REMAP_FILE_PAGES, "SYS_REMAP_FILE_PAGES"}, + {SYS_GETDENTS64, "SYS_GETDENTS64"}, + {SYS_SET_TID_ADDRESS, "SYS_SET_TID_ADDRESS"}, + {SYS_RESTART_SYSCALL, "SYS_RESTART_SYSCALL"}, + {SYS_SEMTIMEDOP, "SYS_SEMTIMEDOP"}, + {SYS_FADVISE64, "SYS_FADVISE64"}, + {SYS_TIMER_CREATE, "SYS_TIMER_CREATE"}, + {SYS_TIMER_SETTIME, "SYS_TIMER_SETTIME"}, + {SYS_TIMER_GETTIME, "SYS_TIMER_GETTIME"}, + {SYS_TIMER_GETOVERRUN, "SYS_TIMER_GETOVERRUN"}, + {SYS_TIMER_DELETE, "SYS_TIMER_DELETE"}, + {SYS_CLOCK_SETTIME, "SYS_CLOCK_SETTIME"}, + {SYS_CLOCK_GETTIME, "SYS_CLOCK_GETTIME"}, + {SYS_CLOCK_GETRES, "SYS_CLOCK_GETRES"}, + {SYS_CLOCK_NANOSLEEP, "SYS_CLOCK_NANOSLEEP"}, + {SYS_EXIT_GROUP, "SYS_EXIT_GROUP"}, + {SYS_EPOLL_WAIT, "SYS_EPOLL_WAIT"}, + {SYS_EPOLL_CTL, "SYS_EPOLL_CTL"}, + {SYS_TGKILL, "SYS_TGKILL"}, + {SYS_UTIMES, "SYS_UTIMES"}, + {SYS_VSERVER, "SYS_VSERVER"}, + {SYS_MBIND, "SYS_MBIND"}, + {SYS_SET_MEMPOLICY, "SYS_SET_MEMPOLICY"}, + {SYS_GET_MEMPOLICY, "SYS_GET_MEMPOLICY"}, + {SYS_MQ_OPEN, "SYS_MQ_OPEN"}, + {SYS_MQ_UNLINK, "SYS_MQ_UNLINK"}, + {SYS_MQ_TIMEDSEND, "SYS_MQ_TIMEDSEND"}, + {SYS_MQ_TIMEDRECEIVE, "SYS_MQ_TIMEDRECEIVE"}, + {SYS_MQ_NOTIFY, "SYS_MQ_NOTIFY"}, + {SYS_MQ_GETSETATTR, "SYS_MQ_GETSETATTR"}, + {SYS_KEXEC_LOAD, "SYS_KEXEC_LOAD"}, + {SYS_WAITID, "SYS_WAITID"}, + {SYS_ADD_KEY, "SYS_ADD_KEY"}, + {SYS_REQUEST_KEY, "SYS_REQUEST_KEY"}, + {SYS_KEYCTL, "SYS_KEYCTL"}, + {SYS_IOPRIO_SET, "SYS_IOPRIO_SET"}, + {SYS_IOPRIO_GET, "SYS_IOPRIO_GET"}, + {SYS_INOTIFY_INIT, "SYS_INOTIFY_INIT"}, + {SYS_INOTIFY_ADD_WATCH, "SYS_INOTIFY_ADD_WATCH"}, + {SYS_INOTIFY_RM_WATCH, "SYS_INOTIFY_RM_WATCH"}, + {SYS_MIGRATE_PAGES, "SYS_MIGRATE_PAGES"}, + {SYS_OPENAT, "SYS_OPENAT"}, + {SYS_MKDIRAT, "SYS_MKDIRAT"}, + {SYS_MKNODAT, "SYS_MKNODAT"}, + {SYS_FCHOWNAT, "SYS_FCHOWNAT"}, + {SYS_FUTIMESAT, "SYS_FUTIMESAT"}, + {SYS_NEWFSTATAT, "SYS_NEWFSTATAT"}, + {SYS_UNLINKAT, "SYS_UNLINKAT"}, + {SYS_RENAMEAT, "SYS_RENAMEAT"}, + {SYS_LINKAT, "SYS_LINKAT"}, + {SYS_SYMLINKAT, "SYS_SYMLINKAT"}, + {SYS_READLINKAT, "SYS_READLINKAT"}, + {SYS_FCHMODAT, "SYS_FCHMODAT"}, + {SYS_FACCESSAT, "SYS_FACCESSAT"}, + {SYS_PSELECT6, "SYS_PSELECT6"}, + {SYS_PPOLL, "SYS_PPOLL"}, + {SYS_UNSHARE, "SYS_UNSHARE"}, + {SYS_SET_ROBUST_LIST, "SYS_SET_ROBUST_LIST"}, + {SYS_GET_ROBUST_LIST, "SYS_GET_ROBUST_LIST"}, + {SYS_SPLICE, "SYS_SPLICE"}, + {SYS_TEE, "SYS_TEE"}, + {SYS_SYNC_FILE_RANGE, "SYS_SYNC_FILE_RANGE"}, + {SYS_VMSPLICE, "SYS_VMSPLICE"}, + {SYS_MOVE_PAGES, "SYS_MOVE_PAGES"}, + {SYS_UTIMENSAT, "SYS_UTIMENSAT"}, + {SYS_EPOLL_PWAIT, "SYS_EPOLL_PWAIT"}, + {SYS_SIGNALFD, "SYS_SIGNALFD"}, + {SYS_TIMERFD_CREATE, "SYS_TIMERFD_CREATE"}, + {SYS_EVENTFD, "SYS_EVENTFD"}, + {SYS_FALLOCATE, "SYS_FALLOCATE"}, + {SYS_TIMERFD_SETTIME, "SYS_TIMERFD_SETTIME"}, + {SYS_TIMERFD_GETTIME, "SYS_TIMERFD_GETTIME"}, + {SYS_ACCEPT4, "SYS_ACCEPT4"}, + {SYS_SIGNALFD4, "SYS_SIGNALFD4"}, + {SYS_EVENTFD2, "SYS_EVENTFD2"}, + {SYS_EPOLL_CREATE1, "SYS_EPOLL_CREATE1"}, + {SYS_DUP3, "SYS_DUP3"}, + {SYS_PIPE2, "SYS_PIPE2"}, + {SYS_INOTIFY_INIT1, "SYS_INOTIFY_INIT1"}, + {SYS_PREADV, "SYS_PREADV"}, + {SYS_PWRITEV, "SYS_PWRITEV"}, + {SYS_RT_TGSIGQUEUEINFO, "SYS_RT_TGSIGQUEUEINFO"}, + {SYS_PERF_EVENT_OPEN, "SYS_PERF_EVENT_OPEN"}, + {SYS_RECVMMSG, "SYS_RECVMMSG"}, + {SYS_FANOTIFY_INIT, "SYS_FANOTIFY_INIT"}, + {SYS_FANOTIFY_MARK, "SYS_FANOTIFY_MARK"}, + {SYS_PRLIMIT64, "SYS_PRLIMIT64"}, + +}; + +char *seccomp_syscall_lookup(int id) { + + if (id < 0) { FATAL("Invalid id: %d", id); } + if ((uint32_t)id >= sizeof(seccomp_syscall_table) / sizeof(syscall_entry_t)) { + + FATAL("Invalid id: %d", id); + + } + + return seccomp_syscall_table[id].name; + +} + +#endif + diff --git a/frida_mode/src/stalker.c b/frida_mode/src/stalker.c index 5df0386f..814aaeb3 100644 --- a/frida_mode/src/stalker.c +++ b/frida_mode/src/stalker.c @@ -1,15 +1,67 @@ #include "debug.h" #include "instrument.h" +#include "prefetch.h" #include "stalker.h" +#include "stats.h" #include "util.h" +guint stalker_ic_entries = 0; + static GumStalker *stalker = NULL; +struct _GumAflStalkerObserver { + + GObject parent; + +}; + +#define GUM_TYPE_AFL_STALKER_OBSERVER (gum_afl_stalker_observer_get_type()) +G_DECLARE_FINAL_TYPE(GumAflStalkerObserver, gum_afl_stalker_observer, GUM, + AFL_STALKER_OBSERVER, GObject) + +static void gum_afl_stalker_observer_iface_init(gpointer g_iface, + gpointer iface_data); +static void gum_afl_stalker_observer_class_init( + GumAflStalkerObserverClass *klass); +static void gum_afl_stalker_observer_init(GumAflStalkerObserver *self); + +G_DEFINE_TYPE_EXTENDED( + GumAflStalkerObserver, gum_afl_stalker_observer, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE(GUM_TYPE_STALKER_OBSERVER, + gum_afl_stalker_observer_iface_init)) + +static GumAflStalkerObserver *observer = NULL; + +static void gum_afl_stalker_observer_iface_init(gpointer g_iface, + gpointer iface_data) { + + UNUSED_PARAMETER(g_iface); + UNUSED_PARAMETER(iface_data); + +} + +static void gum_afl_stalker_observer_class_init( + GumAflStalkerObserverClass *klass) { + + UNUSED_PARAMETER(klass); + +} + +static void gum_afl_stalker_observer_init(GumAflStalkerObserver *self) { + + UNUSED_PARAMETER(self); + +} + void stalker_config(void) { if (!gum_stalker_is_supported()) { FATAL("Failed to initialize embedded"); } + stalker_ic_entries = util_read_num("AFL_FRIDA_STALKER_IC_ENTRIES"); + + observer = g_object_new(GUM_TYPE_AFL_STALKER_OBSERVER, NULL); + } static gboolean stalker_exclude_self(const GumRangeDetails *details, @@ -35,7 +87,26 @@ static gboolean stalker_exclude_self(const GumRangeDetails *details, void stalker_init(void) { + OKF("Stalker - ic_entries [%u]", stalker_ic_entries); + +#if !(defined(__x86_64__) || defined(__i386__)) + if (stalker_ic_entries != 0) { + + FATAL("AFL_FRIDA_STALKER_IC_ENTRIES not supported"); + + } + +#endif + + if (stalker_ic_entries == 0) { stalker_ic_entries = 32; } + +#if defined(__x86_64__) || defined(__i386__) + stalker = + g_object_new(GUM_TYPE_STALKER, "ic-entries", stalker_ic_entries, NULL); +#else stalker = gum_stalker_new(); +#endif + if (stalker == NULL) { FATAL("Failed to initialize stalker"); } gum_stalker_set_trust_threshold(stalker, -1); @@ -57,6 +128,8 @@ void stalker_start(void) { GumStalkerTransformer *transformer = instrument_get_transformer(); gum_stalker_follow_me(stalker, transformer, NULL); + gum_stalker_set_observer(stalker, GUM_STALKER_OBSERVER(observer)); + } void stalker_trust(void) { @@ -65,3 +138,10 @@ void stalker_trust(void) { } +GumStalkerObserver *stalker_get_observer(void) { + + if (observer == NULL) { FATAL("Stalker not yet initialized"); } + return GUM_STALKER_OBSERVER(observer); + +} + 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 + diff --git a/frida_mode/test/cmplog/GNUmakefile b/frida_mode/test/cmplog/GNUmakefile index 4c71bb33..bcaff42d 100644 --- a/frida_mode/test/cmplog/GNUmakefile +++ b/frida_mode/test/cmplog/GNUmakefile @@ -1,5 +1,5 @@ PWD:=$(shell pwd)/ -ROOT:=$(shell realpath $(PWD)../../../)/ +ROOT:=$(PWD)../../../ BUILD_DIR:=$(PWD)build/ TEST_CMPLOG_SRC=$(PWD)cmplog.c diff --git a/frida_mode/test/deferred/GNUmakefile b/frida_mode/test/deferred/GNUmakefile index f7520051..22aeb2bf 100644 --- a/frida_mode/test/deferred/GNUmakefile +++ b/frida_mode/test/deferred/GNUmakefile @@ -1,5 +1,5 @@ PWD:=$(shell pwd)/ -ROOT:=$(shell realpath $(PWD)../../..)/ +ROOT:=$(PWD)../../../ BUILD_DIR:=$(PWD)build/ TESTINSTR_DATA_DIR:=$(BUILD_DIR)in/ TESTINSTR_DATA_FILE:=$(TESTINSTR_DATA_DIR)in diff --git a/frida_mode/test/entry_point/GNUmakefile b/frida_mode/test/entry_point/GNUmakefile index 5453c1ad..08c660f7 100644 --- a/frida_mode/test/entry_point/GNUmakefile +++ b/frida_mode/test/entry_point/GNUmakefile @@ -1,5 +1,5 @@ PWD:=$(shell pwd)/ -ROOT:=$(shell realpath $(PWD)../../..)/ +ROOT:=$(PWD)../../../ BUILD_DIR:=$(PWD)build/ TESTINSTR_DATA_DIR:=$(BUILD_DIR)in/ TESTINSTR_DATA_FILE:=$(TESTINSTR_DATA_DIR)in diff --git a/frida_mode/test/exe/GNUmakefile b/frida_mode/test/exe/GNUmakefile index 86e5a461..c86ae894 100644 --- a/frida_mode/test/exe/GNUmakefile +++ b/frida_mode/test/exe/GNUmakefile @@ -1,5 +1,5 @@ PWD:=$(shell pwd)/ -ROOT:=$(shell realpath $(PWD)../../..)/ +ROOT:=$(PWD)../../../ BUILD_DIR:=$(PWD)build/ TESTINSTR_DATA_DIR:=$(BUILD_DIR)in/ TESTINSTR_DATA_FILE:=$(TESTINSTR_DATA_DIR)in diff --git a/frida_mode/test/fasan/GNUmakefile b/frida_mode/test/fasan/GNUmakefile index c971c724..e150a6db 100644 --- a/frida_mode/test/fasan/GNUmakefile +++ b/frida_mode/test/fasan/GNUmakefile @@ -1,5 +1,5 @@ PWD:=$(shell pwd)/ -ROOT:=$(shell realpath $(PWD)../../..)/ +ROOT:=$(PWD)../../../ BUILD_DIR:=$(PWD)build/ TEST_DATA_DIR:=$(BUILD_DIR)in/ diff --git a/frida_mode/test/jpeg/GNUmakefile b/frida_mode/test/jpeg/GNUmakefile index 1c124743..ca5101cb 100644 --- a/frida_mode/test/jpeg/GNUmakefile +++ b/frida_mode/test/jpeg/GNUmakefile @@ -1,5 +1,5 @@ PWD:=$(shell pwd)/ -ROOT:=$(shell realpath $(PWD)../../..)/ +ROOT:=$(PWD)../../../ BUILD_DIR:=$(PWD)build/ AFLPP_FRIDA_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/frida_hook.so diff --git a/frida_mode/test/js/GNUmakefile b/frida_mode/test/js/GNUmakefile index ee8d4ebc..aad81d08 100644 --- a/frida_mode/test/js/GNUmakefile +++ b/frida_mode/test/js/GNUmakefile @@ -1,5 +1,5 @@ PWD:=$(shell pwd)/ -ROOT:=$(shell realpath $(PWD)../../..)/ +ROOT:=$(PWD)../../../ BUILD_DIR:=$(PWD)build/ TEST_DATA_DIR:=$(BUILD_DIR)in/ TEST_DATA_FILE:=$(TEST_DATA_DIR)in diff --git a/frida_mode/test/libpcap/GNUmakefile b/frida_mode/test/libpcap/GNUmakefile index 1263ce60..6f2b58af 100644 --- a/frida_mode/test/libpcap/GNUmakefile +++ b/frida_mode/test/libpcap/GNUmakefile @@ -1,5 +1,5 @@ PWD:=$(shell pwd)/ -ROOT:=$(shell realpath $(PWD)../../..)/ +ROOT:=$(PWD)../../../ BUILD_DIR:=$(PWD)build/ AFLPP_FRIDA_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/frida_hook.so @@ -142,7 +142,7 @@ $(TEST_BIN): $(HARNESS_OBJ) $(PCAPTEST_OBJ) $(LIBPCAP_LIB) ########## DUMMY ####### $(AFLPP_DRIVER_DUMMY_INPUT): | $(TCPDUMP_TESTS_DIR) - dd if=/dev/zero bs=1M count=1 of=$@ + dd if=/dev/zero bs=1048576 count=1 of=$@ ###### TEST DATA ####### diff --git a/frida_mode/test/osx-lib/GNUmakefile b/frida_mode/test/osx-lib/GNUmakefile index de0af27b..96dbb5ad 100644 --- a/frida_mode/test/osx-lib/GNUmakefile +++ b/frida_mode/test/osx-lib/GNUmakefile @@ -52,7 +52,7 @@ $(TESTINSTR_DATA_FILE): | $(TESTINSTR_DATA_DIR) echo -n "$$FA$$" > $@ $(AFLPP_DRIVER_DUMMY_INPUT): | $(BUILD_DIR) - dd if=/dev/zero bs=1M count=1 of=$@ + dd if=/dev/zero bs=1048576 count=1 of=$@ $(HARNESS_BIN): $(HARNESS_SRC) | $(BUILD_DIR) $(CC) $(CFLAGS) $(LDFLAGS) $(HARNESS_LDFLAGS) -o $@ $< diff --git a/frida_mode/test/output/GNUmakefile b/frida_mode/test/output/GNUmakefile index eaa1c4dc..201c23b7 100644 --- a/frida_mode/test/output/GNUmakefile +++ b/frida_mode/test/output/GNUmakefile @@ -1,5 +1,5 @@ PWD:=$(shell pwd)/ -ROOT:=$(shell realpath $(PWD)../../..)/ +ROOT:=$(PWD)../../../ BUILD_DIR:=$(PWD)build/ TESTINSTR_DATA_DIR:=$(BUILD_DIR)in/ TESTINSTR_DATA_FILE:=$(TESTINSTR_DATA_DIR)in diff --git a/frida_mode/test/persistent_ret/GNUmakefile b/frida_mode/test/persistent_ret/GNUmakefile index adcacf5a..71f6c693 100644 --- a/frida_mode/test/persistent_ret/GNUmakefile +++ b/frida_mode/test/persistent_ret/GNUmakefile @@ -1,5 +1,5 @@ PWD:=$(shell pwd)/ -ROOT:=$(shell realpath $(PWD)../../..)/ +ROOT:=$(PWD)../../../ BUILD_DIR:=$(PWD)build/ TESTINSTR_DATA_DIR:=$(BUILD_DIR)in/ TESTINSTR_DATA_FILE:=$(TESTINSTR_DATA_DIR)in diff --git a/frida_mode/test/png/GNUmakefile b/frida_mode/test/png/GNUmakefile index a1a7f1a5..0f591508 100644 --- a/frida_mode/test/png/GNUmakefile +++ b/frida_mode/test/png/GNUmakefile @@ -1,5 +1,5 @@ PWD:=$(shell pwd)/ -ROOT:=$(shell realpath $(PWD)../../..)/ +ROOT:=$(PWD)../../../ BUILD_DIR:=$(PWD)build/ LIBPNG_BUILD_DIR:=$(BUILD_DIR)libpng/ diff --git a/frida_mode/test/png/persistent/GNUmakefile b/frida_mode/test/png/persistent/GNUmakefile index f6ccfcb5..c6526fee 100644 --- a/frida_mode/test/png/persistent/GNUmakefile +++ b/frida_mode/test/png/persistent/GNUmakefile @@ -1,9 +1,9 @@ PWD:=$(shell pwd)/ -ROOT:=$(shell realpath $(PWD)../../../..)/ +ROOT:=$(PWD)../../../../ BUILD_DIR:=$(PWD)build/ TEST_BIN:=$(PWD)../build/test -TEST_DATA_DIR:=../build/libpng/libpng-1.2.56/contrib/pngsuite/ +TEST_DATA_DIR:=$(PWD)../build/libpng/libpng-1.2.56/contrib/pngsuite/ AFLPP_DRIVER_DUMMY_INPUT:=$(BUILD_DIR)in QEMU_OUT:=$(BUILD_DIR)qemu-out @@ -49,7 +49,7 @@ $(BUILD_DIR): mkdir -p $@ $(AFLPP_DRIVER_DUMMY_INPUT): | $(BUILD_DIR) - dd if=/dev/zero bs=1M count=1 of=$@ + dd if=/dev/zero bs=1048576 count=1 of=$@ qemu: | $(BUILD_DIR) AFL_QEMU_PERSISTENT_ADDR=$(AFL_QEMU_PERSISTENT_ADDR) \ diff --git a/frida_mode/test/png/persistent/hook/GNUmakefile b/frida_mode/test/png/persistent/hook/GNUmakefile index 049861dd..5010662b 100644 --- a/frida_mode/test/png/persistent/hook/GNUmakefile +++ b/frida_mode/test/png/persistent/hook/GNUmakefile @@ -1,5 +1,5 @@ PWD:=$(shell pwd)/ -ROOT:=$(shell realpath $(PWD)../../../../..)/ +ROOT:=$(PWD)../../../../../ BUILD_DIR:=$(PWD)build/ AFLPP_FRIDA_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/frida_hook.so @@ -72,7 +72,7 @@ $(TEST_DATA_DIR): | $(BUILD_DIR) mkdir -p $@ $(AFLPP_DRIVER_DUMMY_INPUT): | $(BUILD_DIR) - dd if=/dev/zero bs=1M count=1 of=$@ + dd if=/dev/zero bs=1048576 count=1 of=$@ qemu: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_QEMU_DRIVER_HOOK_OBJ) | $(BUILD_DIR) AFL_QEMU_PERSISTENT_HOOK=$(AFLPP_QEMU_DRIVER_HOOK_OBJ) \ @@ -127,6 +127,21 @@ frida_entry: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) | $(BUIL -- \ $(TEST_BIN) $(AFLPP_DRIVER_DUMMY_INPUT) +frida_entry_slow: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) | $(BUILD_DIR) + AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_FRIDA_DRIVER_HOOK_OBJ) \ + AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \ + AFL_ENTRYPOINT=$(AFL_FRIDA_PERSISTENT_ADDR) \ + AFL_FRIDA_STALKER_IC_ENTRIES=2 \ + AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH=1 \ + $(ROOT)afl-fuzz \ + -D \ + -V 30 \ + -O \ + -i $(TEST_DATA_DIR) \ + -o $(FRIDA_OUT) \ + -- \ + $(TEST_BIN) $(AFLPP_DRIVER_DUMMY_INPUT) + frida_js_load: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) | $(BUILD_DIR) AFL_PRELOAD=$(AFL_PRELOAD) \ AFL_FRIDA_JS_SCRIPT=load.js \ diff --git a/frida_mode/test/proj4/GNUmakefile b/frida_mode/test/proj4/GNUmakefile index 8555ebad..7dffab2e 100644 --- a/frida_mode/test/proj4/GNUmakefile +++ b/frida_mode/test/proj4/GNUmakefile @@ -1,5 +1,5 @@ PWD:=$(shell pwd)/ -ROOT:=$(shell realpath $(PWD)../../..)/ +ROOT:=$(PWD)../../../ BUILD_DIR:=$(PWD)build/ AFLPP_FRIDA_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/frida_hook.so diff --git a/frida_mode/test/re2/GNUmakefile b/frida_mode/test/re2/GNUmakefile index 8e1f3682..67b37a89 100644 --- a/frida_mode/test/re2/GNUmakefile +++ b/frida_mode/test/re2/GNUmakefile @@ -1,5 +1,5 @@ PWD:=$(shell pwd)/ -ROOT:=$(shell realpath $(PWD)../../..)/ +ROOT:=$(PWD)../../../ BUILD_DIR:=$(PWD)build/ AFLPP_FRIDA_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/frida_hook.so @@ -124,7 +124,7 @@ $(TEST_DATA_DIR): | $(BUILD_DIR) mkdir -p $@ $(AFLPP_DRIVER_DUMMY_INPUT): | $(TEST_DATA_DIR) - dd if=/dev/zero bs=1M count=1 of=$@ + dd if=/dev/zero bs=1048576 count=1 of=$@ ###### TEST DATA ####### diff --git a/frida_mode/test/sqlite/GNUmakefile b/frida_mode/test/sqlite/GNUmakefile index 1c856d1e..85f213a9 100644 --- a/frida_mode/test/sqlite/GNUmakefile +++ b/frida_mode/test/sqlite/GNUmakefile @@ -1,5 +1,5 @@ PWD:=$(shell pwd)/ -ROOT:=$(shell realpath $(PWD)../../..)/ +ROOT:=$(PWD)../../../ BUILD_DIR:=$(PWD)build/ SQLITE_BUILD_DIR:=$(BUILD_DIR)sqlite/ @@ -120,7 +120,7 @@ sqlite: $(SQLITE_TEST_DIR) $(TEST_BIN) ########## DUMMY ####### $(AFLPP_DRIVER_DUMMY_INPUT): | $(SQLITE_TEST_DIR) - dd if=/dev/zero bs=1M count=1 of=$@ + dd if=/dev/zero bs=1048576 count=1 of=$@ ###### TEST DATA ####### @@ -156,6 +156,22 @@ frida: $(TEST_BIN) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) $(AFLPP_DRIVER_DUMMY_INPUT) | -- \ $(TEST_BIN) $(AFLPP_DRIVER_DUMMY_INPUT) +frida_slow: $(TEST_BIN) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) $(AFLPP_DRIVER_DUMMY_INPUT) | $(SQLITE_TEST_DIR) + AFL_FRIDA_PERSISTENT_CNT=1000000 \ + AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_FRIDA_DRIVER_HOOK_OBJ) \ + AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \ + AFL_ENTRYPOINT=$(AFL_FRIDA_PERSISTENT_ADDR) \ + AFL_FRIDA_STALKER_IC_ENTRIES=2 \ + AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH=1 \ + $(ROOT)afl-fuzz \ + -D \ + -V 30 \ + -O \ + -i $(SQLITE_TEST_DIR) \ + -o $(FRIDA_OUT) \ + -- \ + $(TEST_BIN) $(AFLPP_DRIVER_DUMMY_INPUT) + debug: gdb \ --ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \ diff --git a/frida_mode/test/testinstr/GNUmakefile b/frida_mode/test/testinstr/GNUmakefile index 3701ddc8..79eee213 100644 --- a/frida_mode/test/testinstr/GNUmakefile +++ b/frida_mode/test/testinstr/GNUmakefile @@ -1,5 +1,5 @@ PWD:=$(shell pwd)/ -ROOT:=$(shell realpath $(PWD)../../..)/ +ROOT:=$(PWD)../../../ BUILD_DIR:=$(PWD)build/ TESTINSTR_DATA_DIR:=$(BUILD_DIR)in/ TESTINSTR_DATA_FILE:=$(TESTINSTR_DATA_DIR)in diff --git a/frida_mode/test/unstable/GNUmakefile b/frida_mode/test/unstable/GNUmakefile index 938d7c17..0ccc5fb1 100644 --- a/frida_mode/test/unstable/GNUmakefile +++ b/frida_mode/test/unstable/GNUmakefile @@ -1,5 +1,5 @@ PWD:=$(shell pwd)/ -ROOT:=$(shell realpath $(PWD)../../..)/ +ROOT:=$(PWD)../../../ BUILD_DIR:=$(PWD)build/ UNSTABLE_DATA_DIR:=$(BUILD_DIR)in/ UNSTABLE_DATA_FILE:=$(UNSTABLE_DATA_DIR)in @@ -85,6 +85,21 @@ frida: $(UNSTABLE_BIN) $(UNSTABLE_DATA_FILE) -- \ $(UNSTABLE_BIN) @@ +frida_coverage: $(UNSTABLE_BIN) $(UNSTABLE_DATA_FILE) + AFL_DEBUG=1 \ + AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \ + AFL_FRIDA_OUTPUT_STDOUT=/tmp/stdout.txt \ + AFL_FRIDA_OUTPUT_STDERR=/tmp/stderr.txt \ + AFL_FRIDA_INST_COVERAGE_FILE=/tmp/coverage.dat \ + AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE=/tmp/unstable.dat \ + $(ROOT)afl-fuzz \ + -D \ + -O \ + -i $(UNSTABLE_DATA_DIR) \ + -o $(FRIDA_OUT) \ + -- \ + $(UNSTABLE_BIN) @@ + debug: gdb \ --ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \ diff --git a/frida_mode/ts/lib/afl.ts b/frida_mode/ts/lib/afl.ts index 6326c099..8a1ebf1b 100644 --- a/frida_mode/ts/lib/afl.ts +++ b/frida_mode/ts/lib/afl.ts @@ -104,6 +104,15 @@ class Afl { } /** + * See `AFL_FRIDA_INST_COVERAGE_FILE`. This function takes a single `string` + * as an argument. + */ + public static setInstrumentCoverageFile(file: string): void { + const buf = Memory.allocUtf8String(file); + Afl.jsApiSetInstrumentCoverageFile(buf); + } + + /** * See `AFL_FRIDA_INST_DEBUG_FILE`. This function takes a single `string` as * an argument. */ @@ -155,6 +164,15 @@ class Afl { } /** + * See `AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE`. This function takes a single + * `string` as an argument. + */ + public static setInstrumentUnstableCoverageFile(file: string): void { + const buf = Memory.allocUtf8String(file); + Afl.jsApiSetInstrumentUnstableCoverageFile(buf); + } + + /** * This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a * `NativePointer` should be provided as it's argument. */ @@ -194,12 +212,28 @@ class Afl { } /** + * See `AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH`. + */ + public static setPrefetchBackpatchDisable(): void { + Afl.jsApiSetPrefetchBackpatchDisable(); + } + + /** * See `AFL_FRIDA_INST_NO_PREFETCH`. */ public static setPrefetchDisable(): void { Afl.jsApiSetPrefetchDisable(); } + /** + * See `AFL_FRIDA_SECCOMP_FILE`. This function takes a single `string` as + * an argument. + */ + public static setSeccompFile(file: string): void { + const buf = Memory.allocUtf8String(file); + Afl.jsApiSetSeccompFile(buf); + } + /* * Set a function to be called for each instruction which is instrumented * by AFL FRIDA mode. @@ -209,6 +243,13 @@ class Afl { } /** + * See `AFL_FRIDA_STALKER_IC_ENTRIES`. + */ + public static setStalkerIcEntries(val: number): void { + Afl.jsApiSetStalkerIcEntries(val); + } + + /** * See `AFL_FRIDA_STATS_FILE`. This function takes a single `string` as * an argument. */ @@ -226,13 +267,6 @@ class Afl { } /** - * See `AFL_FRIDA_STATS_TRANSITIONS` - */ - public static setStatsTransitions(): void { - Afl.jsApiSetStatsTransitions(); - } - - /** * See `AFL_FRIDA_OUTPUT_STDERR`. This function takes a single `string` as * an argument. */ @@ -282,6 +316,11 @@ class Afl { "void", ["pointer"]); + private static readonly jsApiSetInstrumentCoverageFile = Afl.jsApiGetFunction( + "js_api_set_instrument_coverage_file", + "void", + ["pointer"]); + private static readonly jsApiSetInstrumentDebugFile = Afl.jsApiGetFunction( "js_api_set_instrument_debug_file", "void", @@ -317,6 +356,11 @@ class Afl { "void", []); + private static readonly jsApiSetInstrumentUnstableCoverageFile = Afl.jsApiGetFunction( + "js_api_set_instrument_unstable_coverage_file", + "void", + ["pointer"]); + private static readonly jsApiSetPersistentAddress = Afl.jsApiGetFunction( "js_api_set_persistent_address", "void", @@ -342,16 +386,31 @@ class Afl { "void", ["pointer"]); + private static readonly jsApiSetPrefetchBackpatchDisable = Afl.jsApiGetFunction( + "js_api_set_prefetch_backpatch_disable", + "void", + []); + private static readonly jsApiSetPrefetchDisable = Afl.jsApiGetFunction( "js_api_set_prefetch_disable", "void", []); + private static readonly jsApiSetSeccompFile = Afl.jsApiGetFunction( + "js_api_set_seccomp_file", + "void", + ["pointer"]); + private static readonly jsApiSetStalkerCallback = Afl.jsApiGetFunction( "js_api_set_stalker_callback", "void", ["pointer"]); + private static readonly jsApiSetStalkerIcEntries = Afl.jsApiGetFunction( + "js_api_set_stalker_ic_entries", + "void", + ["uint32"]); + private static readonly jsApiSetStatsFile = Afl.jsApiGetFunction( "js_api_set_stats_file", "void", @@ -362,11 +421,6 @@ class Afl { "void", ["uint64"]); - private static readonly jsApiSetStatsTransitions = Afl.jsApiGetFunction( - "js_api_set_stats_transitions", - "void", - []); - private static readonly jsApiSetStdErr = Afl.jsApiGetFunction( "js_api_set_stderr", "void", diff --git a/frida_mode/ub1804/Dockerfile b/frida_mode/ub1804/Dockerfile new file mode 100644 index 00000000..e1f5a339 --- /dev/null +++ b/frida_mode/ub1804/Dockerfile @@ -0,0 +1,6 @@ +FROM ubuntu:xenial + +WORKDIR /AFLplusplus + +RUN apt-get update +RUN apt-get install -y make gcc g++ xz-utils curl wget clang zlib1g-dev \ No newline at end of file diff --git a/frida_mode/ub1804/GNUmakefile b/frida_mode/ub1804/GNUmakefile new file mode 100644 index 00000000..4247916e --- /dev/null +++ b/frida_mode/ub1804/GNUmakefile @@ -0,0 +1,37 @@ +PWD:=$(shell pwd)/ +ROOT:=$(PWD)../../ + +.PHONY: all build docker clean shell test + +all: docker + docker run --rm \ + -v $(ROOT):/AFLplusplus \ + ub1804-afl-frida \ + /bin/sh -c \ + 'make -C /AFLplusplus/ clean all && \ + make -C /AFLplusplus/frida_mode clean all' + +test: + docker run --rm \ + -v $(ROOT):/AFLplusplus \ + ub1804-afl-frida \ + /bin/sh -c \ + 'make -C /AFLplusplus/frida_mode/test/png clean all && \ + make -C /AFLplusplus/frida_mode/test/png/persistent/hook frida' + +build: + docker run --rm \ + -v $(ROOT):/AFLplusplus \ + ub1804-afl-frida \ + /bin/sh -c \ + 'make -C /AFLplusplus/ all && \ + make -C /AFLplusplus/frida_mode all' + +docker: + docker build --tag ub1804-afl-frida . + +clean: + docker images --filter 'dangling=true' -q --no-trunc | xargs -L1 docker rmi --force + +shell: + docker run -ti --rm ub1804-afl-frida /bin/bash diff --git a/frida_mode/ub1804/Makefile b/frida_mode/ub1804/Makefile new file mode 100644 index 00000000..f3c3cd55 --- /dev/null +++ b/frida_mode/ub1804/Makefile @@ -0,0 +1,9 @@ +all: + @echo trying to use GNU make... + @gmake all || echo please install GNUmake + +clean: + @gmake clean + +shell: + @gmake shell |