mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-09 08:41:32 +00:00
Add the AFL_FRIDA_INST_NO_DYNAMIC_LOAD environment variable and its associated JS function setInstrumentNoDynamicLoad to prevent the instrumentation of late dynamic loaded code. Resolve #1708
399 lines
20 KiB
Markdown
399 lines
20 KiB
Markdown
# FRIDA mode
|
|
|
|
The purpose of FRIDA mode is to provide an alternative binary only fuzzer for
|
|
AFL++ just like that provided by QEMU mode. The intention is to provide a very
|
|
similar user experience, right down to the options provided through environment
|
|
variables.
|
|
|
|
In FRIDA mode, binary programs are instrumented, similarly to QEMU mode.
|
|
|
|
## Current progress
|
|
|
|
As FRIDA mode is new, it is missing a lot of features. The design is such that
|
|
it should be possible to add these features in a similar manner to QEMU mode and
|
|
perhaps leverage some of its design and implementation.
|
|
|
|
| Feature/Instrumentation | FRIDA mode | Notes |
|
|
| -------------------------|:----------:|:---------------------------------------------:|
|
|
| NeverZero | x | |
|
|
| Persistent Mode | x | (x86/x64/aarch64 only) |
|
|
| LAF-Intel / CompCov | - | (CMPLOG is better 90% of the time) |
|
|
| CMPLOG | x | (x86/x64/aarch64 only) |
|
|
| Selective Instrumentation| x | |
|
|
| Non-Colliding Coverage | - | (not possible in binary-only instrumentation) |
|
|
| Ngram prev_loc Coverage | - | |
|
|
| Context Coverage | - | |
|
|
| Auto Dictionary | - | |
|
|
| Snapshot LKM Support | - | |
|
|
| In-Memory Test Cases | x | (x86/x64/aarch64 only) |
|
|
|
|
## Compatibility
|
|
|
|
Currently FRIDA mode supports Linux and macOS targets on both x86/x64
|
|
architecture and aarch64. Later releases may add support for aarch32 and Windows
|
|
targets as well as embedded linux environments.
|
|
|
|
FRIDA has been used on various embedded targets using both uClibc and musl C
|
|
runtime libraries, so porting should be possible. However, the current build
|
|
system does not support cross compilation.
|
|
|
|
## Getting started
|
|
|
|
To build everything, run `make`. To build for x86, run `make 32`. Note that in
|
|
x86 bit mode, it is not necessary for afl-fuzz to be built for 32-bit. However,
|
|
the shared library for FRIDA mode must be since it is injected into the target
|
|
process.
|
|
|
|
Various tests can be found in subfolders within the `test/` directory. To use
|
|
these, first run `make` to build any dependencies. Then run `make qemu` or `make
|
|
frida` to run on either QEMU of FRIDA mode respectively. To run frida tests in
|
|
32-bit mode, run `make ARCH=x86 frida`. When switching between architectures, it
|
|
may be necessary to run `make clean` first for a given build target to remove
|
|
previously generated binaries for a different architecture.
|
|
|
|
### Android
|
|
|
|
In order to build, you need to download the Android SDK:
|
|
|
|
[https://developer.android.com/ndk/downloads](https://developer.android.com/ndk/downloads)
|
|
|
|
Then creating locally a standalone chain as follows:
|
|
|
|
[https://developer.android.com/ndk/guides/standalone_toolchain](https://developer.android.com/ndk/guides/standalone_toolchain)
|
|
|
|
## Usage
|
|
|
|
FRIDA mode added some small modifications to `afl-fuzz` and similar tools in
|
|
AFL++. The intention was that it behaves identically to QEMU, but it uses the
|
|
'O' switch rather than 'Q'. Whilst the options 'f', 'F', 's' or 'S' may have
|
|
made more sense for a mode powered by FRIDA Stalker, they were all taken, so
|
|
instead we use 'O' in homage to the [author](https://github.com/oleavr) of
|
|
FRIDA.
|
|
|
|
Similarly, the intention is to mimic the use of environment variables used by
|
|
QEMU where possible (by replacing `s/QEMU/FRIDA/g`). Accordingly, the following
|
|
options are currently supported:
|
|
|
|
* `AFL_FRIDA_DEBUG_MAPS` - See `AFL_QEMU_DEBUG_MAPS`.
|
|
* `AFL_FRIDA_EXCLUDE_RANGES` - See `AFL_QEMU_EXCLUDE_RANGES`.
|
|
* `AFL_FRIDA_INST_RANGES` - See `AFL_QEMU_INST_RANGES`.
|
|
* `AFL_FRIDA_PERSISTENT_ADDR` - See `AFL_QEMU_PERSISTENT_ADDR`.
|
|
* `AFL_FRIDA_PERSISTENT_CNT` - See `AFL_QEMU_PERSISTENT_CNT`.
|
|
* `AFL_FRIDA_PERSISTENT_HOOK` - See `AFL_QEMU_PERSISTENT_HOOK`.
|
|
* `AFL_FRIDA_PERSISTENT_RET` - See `AFL_QEMU_PERSISTENT_RET`.
|
|
|
|
To enable the powerful CMPLOG mechanism, set `-c 0` for `afl-fuzz`.
|
|
|
|
## Scripting
|
|
|
|
One of the more powerful features of FRIDA mode is its support for
|
|
configuration by JavaScript, rather than using environment variables. For
|
|
details of how this works, see [Scripting.md](Scripting.md).
|
|
|
|
## Performance
|
|
|
|
Additionally, the intention is to be able to make a direct performance
|
|
comparison between the two approaches. Accordingly, FRIDA mode includes various
|
|
test targets based on the [libpng](https://libpng.sourceforge.io/) benchmark
|
|
used by [fuzzbench](https://google.github.io/fuzzbench/) and integrated with the
|
|
[StandaloneFuzzTargetMain](https://raw.githubusercontent.com/llvm/llvm-project/main/compiler-rt/lib/fuzzer/standalone/StandaloneFuzzTargetMain.c)
|
|
from the llvm project. These tests include basic fork-server support, persistent
|
|
mode and persistent mode with in-memory test-cases. These are built and linked
|
|
without any special modifications to suit FRIDA or QEMU. The test data provided
|
|
with libpng is used as the corpus.
|
|
|
|
The intention is to add support for FRIDA mode to the FuzzBench project and
|
|
perform a like-for-like comparison with QEMU mode to get an accurate
|
|
appreciation of its performance.
|
|
|
|
## Design
|
|
|
|
FRIDA mode is supported by using `LD_PRELOAD` (`DYLD_INSERT_LIBRARIES` on macOS)
|
|
to inject a shared library (`afl-frida-trace.so`) into the target. This shared
|
|
library is built using the [frida-gum](https://github.com/frida/frida-gum)
|
|
devkit from the [FRIDA](https://github.com/frida/frida) project. One of the
|
|
components of frida-gum is
|
|
[Stalker](https://medium.com/@oleavr/anatomy-of-a-code-tracer-b081aadb0df8),
|
|
this allows the dynamic instrumentation of running code for AARCH32, AARCH64,
|
|
x86 and x64 architectures. Implementation details can be found
|
|
[here](https://frida.re/docs/stalker/).
|
|
|
|
Dynamic instrumentation is used to augment the target application with similar
|
|
coverage information to that inserted by `afl-gcc` or `afl-clang`. The shared
|
|
library is also linked to the `compiler-rt` component of AFL++ to feedback this
|
|
coverage information to AFL++ and also provide a fork server. It also makes use
|
|
of the FRIDA
|
|
[prefetch](https://github.com/frida/frida-gum/blob/56dd9ba3ee9a5511b4b0c629394bf122775f1ab7/gum/gumstalker.h#L115)
|
|
support to feedback instrumented blocks from the child to the parent using a
|
|
shared memory region to avoid the need to regenerate instrumented blocks on each
|
|
fork.
|
|
|
|
Whilst FRIDA allows for a normal C function to be used to augment instrumented
|
|
code, FRIDA mode instead makes use of optimized assembly instead on AARCH64 and
|
|
x86/64 targets. By injecting these small snippets of assembly, we avoid having
|
|
to push and pop the full register context. Note that since this instrumentation
|
|
is used on every basic block to generate coverage, it has a large impact on
|
|
performance.
|
|
|
|
CMPLOG support also adds code to the assembly, however, at present this code
|
|
makes use of a basic C function and is yet to be optimized. Since not all
|
|
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_DRIVER_NO_HOOK` - See `AFL_QEMU_DRIVER_NO_HOOK`. When using the
|
|
QEMU driver to provide a `main` loop for a user provided
|
|
`LLVMFuzzerTestOneInput`, this option configures the driver to read input from
|
|
`stdin` rather than using in-memory test cases.
|
|
* `AFL_FRIDA_INST_COVERAGE_ABSOLUTE` - Generate coverage files using absolute
|
|
virtual addresses rather than relative virtual addresses.
|
|
* `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.
|
|
|
|
```
|
|
Creating block for 0x7ffff7953313:
|
|
0x7ffff7953313 mov qword ptr [rax], 0
|
|
0x7ffff795331a add rsp, 8
|
|
0x7ffff795331e ret
|
|
|
|
Generated block 0x7ffff75e98e2
|
|
0x7ffff75e98e2 mov qword ptr [rax], 0
|
|
0x7ffff75e98e9 add rsp, 8
|
|
0x7ffff75e98ed lea rsp, [rsp - 0x80]
|
|
0x7ffff75e98f5 push rcx
|
|
0x7ffff75e98f6 movabs rcx, 0x7ffff795331e
|
|
0x7ffff75e9900 jmp 0x7ffff75e9384
|
|
|
|
|
|
***
|
|
```
|
|
* `AFL_FRIDA_INST_CACHE_SIZE` - Set the size of the instrumentation cache used
|
|
as a look-up table to cache real to instrumented address block translations.
|
|
Default is 256Mb.
|
|
* `AFL_FRIDA_INST_INSN` - Generate instrumentation for conditional
|
|
instructions (e.g. `CMOV` instructions on x64).
|
|
* `AFL_FRIDA_INST_JIT` - Enable the instrumentation of Just-In-Time compiled
|
|
code. Code is considered to be JIT if the executable segment is not backed by
|
|
a file.
|
|
* `AFL_FRIDA_INST_NO_DYNAMIC_LOAD` - Don't instrument the code loaded late at
|
|
runtime. Strictly limits instrumentation to what has been included.
|
|
* `AFL_FRIDA_INST_NO_OPTIMIZE` - Don't use optimized inline assembly coverage
|
|
instrumentation (the default where available). Required to use
|
|
`AFL_FRIDA_INST_TRACE`.
|
|
* `AFL_FRIDA_INST_REGS_FILE` - File to write raw register contents at the start
|
|
of each block.
|
|
* `AFL_FRIDA_INST_NO_CACHE` - Don't use a look-up table to cache real to
|
|
instrumented address block translations.
|
|
* `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, 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_NO_SUPPRESS` - Disable deterministic branch suppression.
|
|
Deterministic branch suppression skips the preamble which generates coverage
|
|
information at the start of each block, if the block is reached by a
|
|
deterministic branch. This reduces map polution, and may improve performance
|
|
when all the executing blocks have been prefetched and backpatching applied.
|
|
However, in the event that backpatching is incomplete, this may incur a
|
|
performance penatly as branch instructions are disassembled on each branch.
|
|
* `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.
|
|
* `AFL_FRIDA_INST_TRACE` - Log to stdout the address of executed blocks, 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_JS_SCRIPT` - Set the script to be loaded by the FRIDA scripting
|
|
engine. See [Scipting.md](Scripting.md) for details.
|
|
* `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
|
|
application to the named file (supersedes the setting of `AFL_DEBUG_CHILD`).
|
|
* `AFL_FRIDA_PERSISTENT_DEBUG` - Insert a Breakpoint into the instrumented code
|
|
at `AFL_FRIDA_PERSISTENT_HOOK` and `AFL_FRIDA_PERSISTENT_RET` to allow the
|
|
user to detect issues in the persistent loop using a debugger.
|
|
|
|
```
|
|
gdb \
|
|
--ex 'set environment AFL_FRIDA_PERSISTENT_ADDR=XXXXXXXXXX' \
|
|
--ex 'set environment AFL_FRIDA_PERSISTENT_RET=XXXXXXXXXX' \
|
|
--ex 'set environment AFL_FRIDA_PERSISTENT_DEBUG=1' \
|
|
--ex 'set environment AFL_DEBUG_CHILD=1' \
|
|
--ex 'set environment LD_PRELOAD=afl-frida-trace.so' \
|
|
--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_ADJACENT_BLOCKS` - Configure the number of adjacent blocks
|
|
to fetch when generating instrumented code. By fetching blocks in the same
|
|
order they appear in the original program, rather than the order of execution
|
|
should help reduce locality and adjacency. This includes allowing us to vector
|
|
between adjacent blocks using a NOP slide rather than an immediate branch.
|
|
* `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_STALKER_NO_BACKPATCH` - Disable backpatching. At the end of executing
|
|
each block, control will return to FRIDA to identify the next block to
|
|
execute.
|
|
* `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
|
|
`AFL_FRIDA_STATS_INTERVAL` has expired). Note that just because a new path is
|
|
found does not mean a new block needs to be compiled. It could be that the
|
|
existing blocks instrumented have been executed in a different order.
|
|
|
|
```
|
|
stats
|
|
-----
|
|
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_TRACEABLE` - Set the child process to be traceable by any process
|
|
to aid debugging and overcome the restrictions imposed by YAMA. Supported on
|
|
Linux only. Permits a non-root user to use `gcore` or similar to collect a
|
|
core dump of the instrumented target. Note that in order to capture the core
|
|
dump you must set a sufficient timeout (using `-t`) to avoid `afl-fuzz`
|
|
killing the process whilst it is being dumped.
|
|
* `AFL_FRIDA_VERBOSE` - Enable verbose output from FRIDA mode.
|
|
|
|
## 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.
|
|
|
|
### Address Sanitizer basics
|
|
|
|
When Address Sanitizer is used to instrument programs built from source, the
|
|
compiler first adds a dependency (`DT_NEEDED` entry) for the Address Sanitizer
|
|
dynamic shared object (DSO). This shared object contains the main logic for
|
|
Address Sanitizer, including setting and managing up the shadow memory. It also
|
|
provides replacement implementations for a number of functions in standard
|
|
libraries.
|
|
|
|
These replacements include things like `malloc` and `free` which allows for
|
|
those allocations to be marked in the shadow memory, but also a number of other
|
|
functions. Consider `memcpy`, for example. This is instrumented to validate the
|
|
parameters (test the source and destination buffers against the shadow memory).
|
|
This is much easier than instrumenting those standard libraries, since first, it
|
|
would require you to re-compile them and secondly it would mean that the
|
|
instrumentation would be applied at a more expensive granular level. Lastly,
|
|
load-widening (typically found in highly optimized code) can also make this
|
|
instrumentation more difficult.
|
|
|
|
Since the DSO is loaded before all of the standard libraries (in fact it insists
|
|
on being first), the dynamic loader will use it to resolve imports from other
|
|
modules which depend on it.
|
|
|
|
### FASAN implementation
|
|
|
|
FASAN takes a similar approach. It requires the user to add the Address
|
|
Sanitizer DSO to the `AFL_PRELOAD` environment variable such that it is loaded
|
|
into the target. Again, it must be first in the list. This means that it is not
|
|
necessary to instrument the standard libraries to detect when an application has
|
|
provided an incorrect argument to `memcpy`, for example. This avoids issues with
|
|
load-widening and should also mean a huge improvement in performance.
|
|
|
|
FASAN then adds instrumentation for any instructions which use memory operands
|
|
and then calls into the `__asan_loadN` and `__asan_storeN` functions provided by
|
|
the DSO to validate memory accesses against the shadow memory.
|
|
|
|
## Collisions
|
|
|
|
FRIDA mode has also introduced some improvements to reduce collisions in the
|
|
map. For details, see [MapDensity.md](MapDensity.md).
|
|
|
|
## OSX library fuzzing
|
|
|
|
An example of how to fuzz a dynamic library on OSX is included, see
|
|
[test/osx-lib](test/osx-lib). This requires the use of a simple test harness
|
|
executable which will load the 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 be
|
|
linked against it. It is important that the target library is loaded before
|
|
execution 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
|
|
|
|
Should you encounter problems with FRIDA mode, refer to
|
|
[DEBUGGING.md](DEBUGGING.md) for assistance.
|
|
|
|
## To do
|
|
|
|
The next features to be added are Aarch32 support as well as looking at
|
|
potential performance improvements. The intention is to achieve feature parity
|
|
with QEMU mode in due course. Contributions are welcome, but please get in touch
|
|
to ensure that efforts are deconflicted.
|