Skip to content

Commit

Permalink
Update documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
ifratric committed Mar 11, 2019
1 parent 01d224a commit afd0f5e
Show file tree
Hide file tree
Showing 4 changed files with 382 additions and 308 deletions.
323 changes: 29 additions & 294 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,14 @@ Windows even for black box binary fuzzing.

## The WinAFL approach

Instead of instrumenting the code at compilation time, WinAFL relies on dynamic
instrumentation using DynamoRIO (http://dynamorio.org/) to measure and extract
target coverage. This approach has been found to introduce an overhead about 2x
compared to the native execution speed, which is comparable to the original AFL
in binary instrumentation mode.
Instead of instrumenting the code at compilation time, WinAFL supports the
following instrumentation modes:
- Dynamic instrumentation using DynamoRIO (http://dynamorio.org/)
- Hardware tracing using Intel PT
- Static instrumentation via Syzygy

These instrumentation modes are described in more detail in the separate
documents.

<p align="center">
<img alt="afl-fuzz.exe" src="screenshots/afl-fuzz.gif"/>
Expand Down Expand Up @@ -70,8 +73,9 @@ WinAFL has been successfully used to identify bugs in Windows software, such as

## Building WinAFL

1. Download and build DynamoRIO sources or download DynamoRIO Windows binary
package from https://github.com/DynamoRIO/dynamorio/wiki/Downloads
1. If you are building with DynamoRIO support, download and build
DynamoRIO sources or download DynamoRIO Windows binary package from
https://github.com/DynamoRIO/dynamorio/wiki/Downloads

2. Open Visual Studio Command Prompt (or Visual Studio x64 Win64 Command Prompt
if you want a 64-bit build). Note that you need a 64-bit winafl.dll build if
Expand All @@ -88,7 +92,7 @@ source directory).
```
mkdir build32
cd build32
cmake -G"Visual Studio 15 2017" .. -DDynamoRIO_DIR=..\path\to\DynamoRIO\cmake
cmake -G"Visual Studio 15 2017" .. -DDynamoRIO_DIR=..\path\to\DynamoRIO\cmake -DINTELPT=1
cmake --build . --config Release
```

Expand All @@ -97,7 +101,7 @@ cmake --build . --config Release
```
mkdir build64
cd build64
cmake -G"Visual Studio 15 2017 Win64" .. -DDynamoRIO_DIR=..\path\to\DynamoRIO\cmake
cmake -G"Visual Studio 15 2017 Win64" .. -DDynamoRIO_DIR=..\path\to\DynamoRIO\cmake -DINTELPT=1
cmake --build . --config Release
```

Expand All @@ -106,7 +110,10 @@ cmake --build . --config Release
The following cmake configuration options are supported:

- `-DDynamoRIO_DIR=..\path\to\DynamoRIO\cmake` - Needed to build the
winafl.dll DynamoRIO module
winafl.dll DynamoRIO client

- `-DINTELPT=1` - Enable Intel PT mode. For more information see
https://github.com/googleprojectzero/winafl/blob/master/readme_pt.md

- `-DUSE_COLOR=1` - color support (Windows 10 Anniversary edition or higher)

Expand All @@ -115,17 +122,8 @@ The following cmake configuration options are supported:
issues on Windows 10 v1809, though there are workarounds,
see https://github.com/googleprojectzero/winafl/issues/145

- `-DINTELPT=1` - Enable experimental Intel PT tracing mode. For more
information see
https://github.com/googleprojectzero/winafl/blob/master/readme_pt.md

## Using WinAFL

Note: If you are using pre-built binaries you'll need to download DynamoRIO
release 6.2.0-2 from https://github.com/DynamoRIO/dynamorio/wiki/Downloads.
If you built WinAFL from source, you can use whatever version of DynamoRIO
you used to build WinAFL.

The command line for afl-fuzz on Windows is different than on Linux. Instead of:

```
Expand Down Expand Up @@ -155,129 +153,8 @@ The following afl-fuzz options are supported:

Please refer to the original AFL documentation for more info on these flags.

The following instrumentation options are used:

```
-covtype - the type of coverage being recorded. Supported options are
bb (basic block, default) or edge.
-coverage_module - module for which to record coverage. Multiple module flags
are supported.
-target_module - module which contains the target function to be fuzzed.
Either -target_method or -target_offset need to be
specified together with this option.
-target_method - name of the method to fuzz in persistent mode. For this to
work either the method needs to be exported or the symbols
for target_module need to be available. Otherwise use
-target_offset instead.
-target_offset - offset of the method to fuzz from the start of the module.
-fuzz_iterations - Maximum number of iterations for the target function to run
before restarting the target process.
-nargs - Number of arguments the fuzzed method takes. This is used
to save/restore the arguments between runs.
-debug - Debug mode. Does not try to connect to the server. Outputs
a log file containing loaded modules, opened files and
coverage information.
-logdir - specifies in which directory the log file will be written
(only to be used with -debug).
-call_convention - The default calling convention is cdecl on 32-bit x86
platforms and Microsoft x64 for Visual Studio 64-bit
applications. Possible values:
* fastcall: fastcall
* ms64: Microsoft x64 (Visual Studio)
* stdcall: cdecl or stdcall
* thiscall: thiscall
-thread_coverage - If set, WinAFL will only collect coverage from a thread
that executed the target function
```

In general, you should perform the following steps when fuzzing a new target:

1. Make sure your target is running correctly without instrumentations.

2. Open the target binary in WinDbg and locate the function you want to fuzz.
Note the offset of the function from the start of the module. For example, if
you want to fuzz the main function and happen to have symbols around, you can
use the following windbg command:

```
x test!main
```

3. Make sure that the target is running correctly under DynamoRIO. For this
purpose you can use the standalone debug mode of WinAFL client which does not
require connecting to afl-fuzz. Make sure you use the drrun.exe and winafl.dll
version which corresponds to your target (32 vs. 64 bit).

Example command line:

```
path\to\DynamoRIO\bin64\drrun.exe -c winafl.dll -debug
-target_module test_gdiplus.exe -target_offset 0x16e0 -fuzz_iterations 10
-nargs 2 -- test_gdiplus.exe input.bmp
```

You should see the output corresponding to your target function being run 10
times after which the target executable will exit. A .log file should be
created in the current directory. The log file contains useful information
such as the files and modules loaded by the target as well as the dump of AFL
coverage map. In the log you should see pre_fuzz_handler and post_fuzz_handler
being run exactly 10 times as well as your input file being open in each
iteration. Note the list of loaded modules for setting the -coverage_module
flag. Note that you must use the same values for module names as seen in the
log file (not case sensitive).

4. Now you should be ready to fuzz the target. First, make sure that both
afl-fuzz.exe and winafl.dll are in the current directory. As stated earlier,
the command line for afl-fuzz on Windows is:

```
afl-fuzz [afl options] -- [instrumentation options] -- target_cmd_line
```

Please refer above for the list of supported AFL and instrumentation options.

In AFL options, you must specify the DynamoRIO binaries directory via the new
-D option. You need to match the DynamoRIO and winafl.dll build (32 vs. 64 bit)
to the target binary. -t (timeout) option is mandatory for WinAFL as execution
time can vary significantly under instrumentation so it's not a good idea to
rely on the auto-determined values.

You can use the same WinAFL options as in step 2 but remember to exclude the
-debug flag and you'll probably want to increase the iteration count.

As in afl-fuzz on Linux you can replace the input file parameter of the target
binary with @@.

An example command line would look like:

```
afl-fuzz.exe -i in -o out -D C:\work\winafl\DynamoRIO\bin64 -t 20000 --
-coverage_module gdiplus.dll -coverage_module WindowsCodecs.dll
-fuzz_iterations 5000 -target_module test_gdiplus.exe -target_offset 0x16e0
-nargs 2 -- test_gdiplus.exe @@
```

Alternately, if symbols for test_gdiplus.exe are available, you can use
-target_method instead of -target_offset like so:

```
afl-fuzz.exe -i in -o out -D C:\work\winafl\DynamoRIO\bin64 -t 20000 --
-coverage_module gdiplus.dll -coverage_module WindowsCodecs.dll
-fuzz_iterations 5000 -target_module test_gdiplus.exe -target_method main
-nargs 2 -- test_gdiplus.exe @@
```

That's it. Happy fuzzing!
To see the supported instrumentation flags, please refer to the documentation
on the specific instrumentation mode you are interested in.

## How does my target run under WinAFL

Expand Down Expand Up @@ -305,33 +182,18 @@ The target function should do these things during its lifetime:
4. Return normally (So that WinAFL can "catch" this return and redirect
execution. "returning" via ExitProcess() and such won't work)

## In App Persistence mode

This feature is a tweak for the traditional "target function" approach and aims
to loosen the requirements of the target function to do both reading
an input file and processing the input file.

In some applications it's quite challenging to find a target function
that with a simple execution redirection won't break global states and will do
both reading and processing of inputs.

This mode assumes that the target application will actually loop
the target function by itself, and will handle properly its global state
For example a udp server handling packets or a js interpreter running inside
a while loop.
## Instrumentation modes

This mode works as following:
1. Your target runs until hitting the target function.
2. The afl server starts instrumenting the target.
3. Your target runs until hitting the target function again.
4. The afl server stops instrumenting current cycle and starts a new one.
The following documents provide information on using different insrumentation
modes with WinAFL:

usage: add the following option to the winafl arguments:
-persistence_mode in_app

example usage on the supplied test.exe:
afl-fuzz.exe -i in -o out -D <dynamorio bin path> -t 100+ -- -coverage_module test.exe -fuzz_iterations 5000 -target_module test.exe -target_offset 0x1000 -nargs 2 -persistence_mode in_app -- test.exe @@ loop
- [Dynamic instrumentation using DynamoRIO](https://github.com/googleprojectzero/winafl/blob/master/readme_dr.md)
- [Hardware tracing using Intel PT](https://github.com/googleprojectzero/winafl/blob/master/readme_pt.md)
- [Static instrumentation via Syzygy](https://github.com/googleprojectzero/winafl/blob/master/readme_syzygy.md)

Before using WinAFL for the first time, you should read the documentation for
the specific instrumentation mode you are interested in. These also contain
usage examples.

## Corpus minimization

Expand Down Expand Up @@ -395,6 +257,7 @@ Additionally, this mode is considered as experimental since we have experienced
There is a second DLL ```custom_winafl_server.dll``` that allows winAFL to act as a server and perform fuzzing of client-based applications. All you need is to setup port to listen on for incoming connections from your target application. The environment variable ```AFL_CUSTOM_DLL_ARGS=<port_id>``` should be used for this purpose.

#### Note

In case of server fuzzing, if the server socket has the `SO_REUSEADDR` option set like the following code, then this may case `10055` error after some time fuzzing due to the accumulation of `TIME_WAIT` sockets when WinAFL restart the fuzzing process.
```
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(int));
Expand All @@ -405,134 +268,6 @@ To avoid this replace the `SO_REUSEADDR` option by `SO_LINGER` option in the ser
setsockopt(s, SOL_SOCKET, SO_LINGER, (char*)&opt, sizeof(int));
```


## Statically instrument a binary via [syzygy](https://github.com/google/syzygy)

### Background

[syzygy](https://github.com/google/syzygy) provides a framework able to _decompose_
PE32 binaries with full PDB. _Decomposing_ a binary is the term used to mean taking
in input a PE32 binary and its PDB, analyze and decompose every functions, every blocks
of code / data in a safe way and present it to transformation "passes".
A transformation pass is a class that transforms the binary in some way; an example is the [syzyasan](https://github.com/google/syzygy/blob/master/syzygy/instrument/transforms/asan_transform.h)
transformation for example. Once the pass has transformed the binary, it passes it back
to the framework which is able to _relink_ an output binary (with the transformations applied
of course).

[AFL instrumentation](https://github.com/google/syzygy/blob/master/syzygy/instrument/transforms/afl_transform.cc) has been added to [syzygy](https://github.com/google/syzygy)'s instrumenter allowing users to instrument PE32
binaries with private symbols statically.

<p align="center">
<img alt="afl instrumentation under IDA" src="screenshots/afl-instr.png"/>
</p>

### How to write a target function

In order to prepare your target, you need to first include `afl-staticinstr.h` then invoke `__afl_persistent_loop` like in `test_static.cpp`:

```
int fuzz(int argc, char**argv) {
while(__afl_persistent_loop()) {
test(argc, argv);
}
return 1;
}
```

`__afl_persistent_loop`'s implementation lives inside `afl-staticinstr.c` and basically reproduces what the DynamoRIO plugin is doing in `pre_fuzz_handler` and `post_fuzz_handler`. Every points mentioned in "How to select a target function" applies here too.

You can invoke AFL tools with the flag `-Y` to enable the static instrumentation mode during fuzzing, corpus minimizing or during test-case minimizing:

```
afl-fuzz.exe -Y -i minset -o o1 -t 10000 -- -fuzz_iterations 5000 -- test_static.instr.exe @@
winafl-cmin.py -Y -t 100000 -i in -o minset -- test_static.instr.exe @@
afl-tmin.exe -Y -i ..\testcases\tests\big.txt -o big.min.txt -- test_static.instr.exe @@
```

### Building instrument.exe

For convenience, a version of instrument.exe confirmed to work with WinAFL is included in the bin32 directory. If you want to build it yourself follow the instructions below.

In order to clone [syzygy](https://github.com/google/syzygy/)'s repository you can follow the instructions outlined here: [SyzygyDevelopmentGuide](https://github.com/google/syzygy/wiki/SyzygyDevelopmentGuide). Once you have `depot_tools` and the repository cloned, you can compile instrument.exe like this:

```
C:\syzygy\src>ninja -C out\Release instrument
```

The current recommended revision of the instrumenter is the following: [190dbfe](https://github.com/google/syzygy/commit/190dbfe74c6f5b5913820fa66d9176877924d7c5)(v0.8.32.0).

### Registering msdia140

Make sure to register `msdia140.dll` on your system by executing once the below command:

```
regsvr32 /s msdia140.dll
```

### Instrumenting a target

Your target binary must have been compiled with the [/PROFILE](https://msdn.microsoft.com/en-us/library/ays5x7b0.aspx) linker flag in order to generate a full PDB.

```
C:\>instrument.exe --mode=afl --input-image=test_static.exe --output-image=test_static.instr.exe --force-decompose --multithread --cookie-check-hook
[0718/224840:INFO:application_impl.h(46)] Syzygy Instrumenter Version 0.8.32.0 (0000000).
[0718/224840:INFO:application_impl.h(48)] Copyright (c) Google Inc. All rights reserved.
[0718/224840:INFO:afl_instrumenter.cc(116)] Force decomposition mode enabled.
[0718/224840:INFO:afl_instrumenter.cc(122)] Thread-safe instrumentation mode enabled.
[0718/224840:INFO:afl_instrumenter.cc(128)] Cookie check hook mode enabled.
[...]
[0718/224840:INFO:security_cookie_check_hook_transform.cc(67)] Found a __report_gsfailure implementation, hooking it now.
[0718/224840:INFO:add_implicit_tls_transform.cc(77)] The binary doesn't have any implicit TLS slot defined, injecting one.
[0718/224840:INFO:afl_transform.cc(144)] Placing TLS slot at offset +4.
[0718/224840:INFO:afl_transform.cc(237)] Code Blocks instrumented: 92 (95%)
[...]
[0718/224841:INFO:pe_relinker.cc(240)] PE relinker finished.
C:\>test_static.instr.exe test
Persistent loop implementation by <[email protected]>
Based on WinAFL by <[email protected]>
[+] Found a statically instrumented module: test_static.instr.exe (multi thread mode).
[-] Not running under afl-fuzz.exe.
[+] Enabling the no fuzzing mode.
Error opening file
```

#### Available options

```
--config=<path> Specifies a JSON file describing, either
a whitelist of functions to instrument or
a blacklist of functions to not instrument.
--cookie-check-hook Hooks __security_cookie_check.
--force-decompose Forces block decomposition.
--multithread Uses a thread-safe instrumentation.
```

* config: The JSON file allows you to scope down the instrumentation to a set of function
names. You can either [white list](https://github.com/google/syzygy/blob/master/syzygy/instrument/test_data/afl-good-whitelist.json), or [black list](https://github.com/google/syzygy/blob/master/syzygy/instrument/test_data/afl-good-blacklist.json) functions. It can be very useful to blacklist
functions generating variable behaviors.

* cookie-check-hook: This ensures that the /GS cookie check function generates an exception that
our [VEH](https://msdn.microsoft.com/en-us/library/windows/desktop/ms681420(v=vs.85).aspx) can catch. Failfast exceptions are not catchable by any EH mechanisms in-proc, so we leverage
[syzygy](https://github.com/google/syzygy) to rewrite the cookie check function in order to generate
[an exception we can catch](https://github.com/google/syzygy/blob/master/syzygy/instrument/transforms/security_cookie_check_hook_transform.cc#L81).

* force-decompose: This switch lets you override the decision that [syzygy](https://github.com/google/syzygy/blob/master/syzygy/pe/pe_transform_policy.cc#L175) makes when evaluating
if a function is safe to decompose. If you turn on this flag, your instrumentation coverage will be
higher but you might end-up in an executable that *crashes* in weird ways. Only use if you know what you
are doing.

* multithread: This switch turns on the thread-safe instrumentation. The major difference with the single
thread instrumentation is that `__afl_prev_loc` will be stored in a TLS slot.

### Limitations

With great power comes great responsibility, so here is the list of limitations:

1. Instrumentation is limited to PE 32bits binaries with full PDB symbols (linker flag `/PROFILE`).

2. [syzygy](https://github.com/google/syzygy/) defines [several pre-requirements](https://github.com/google/syzygy/blob/master/syzygy/pe/pe_transform_policy.cc#L175) for being able to decompose safely a block; this might explain why your instrumentation percentage is low.

## FAQ

```
Expand Down
Loading

0 comments on commit afd0f5e

Please sign in to comment.