# WinAFL: coverage-guided fuzzing for Windows binaries you cannot recompile

> WinAFL is Google Project Zero's fork of AFL that replaces compile-time instrumentation with DynamoRIO, TinyInst, Intel PT or Syzygy. It is for people who need to fuzz closed-source Windows targets, and its persistent mode requirement is the part most users underestimate.

**googleprojectzero/winafl** — A fork of AFL for fuzzing Windows binaries

- Repository: https://github.com/googleprojectzero/winafl
- Stars: 2,607 · Forks: 559
- Language: C
- License: Apache-2.0
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/googleprojectzero-winafl

## The problem WinAFL exists to solve

AFL's original design assumes you can compile the target with its instrumentation and that the operating system behaves like a Unix. WinAFL's README states the constraint plainly: the original AFL does not work on Windows because of what it calls very *nix-specific design, naming instrumentation and the forkserver as the examples. That rules out the common case of a security researcher who has a signed Windows binary, a plugin DLL, or a document parser shipped by a vendor and no build system for it.

WinAFL keeps the AFL fuzzing loop and mutation heuristics and swaps out the coverage mechanism. The audience is narrow and specific: vulnerability researchers and product security teams fuzzing Windows software they cannot recompile. If you own the source and can build it, this fork is the wrong starting point, because you are paying for dynamic instrumentation you do not need.

## Four instrumentation modes and the persistent-mode loop

The README lists four ways WinAFL obtains coverage: dynamic instrumentation using DynamoRIO, dynamic instrumentation using TinyInst, hardware tracing using Intel PT, and static instrumentation via Syzygy. Each has its own document in the repository, readme_dr.md, readme_tinyinst.md, readme_pt.md and readme_syzygy.md. They are not interchangeable settings; they are different execution strategies with different build and runtime requirements, and the repository layout reflects that with separate source files such as winafl.c, tinyinst_afl.cpp, winaflpt.c and afl-staticinstr.c.

The second mechanism matters as much as the first. The README says WinAFL relies heavily on persistent fuzzing mode, executing multiple input samples without restarting the target process, and that this is accomplished by selecting a target function and instrumenting it so it runs in a loop. That is the architecture in one sentence: you do not hand WinAFL an executable and a file path. You hand it a function to call repeatedly, and the fuzzer feeds mutated buffers into that call.

This is why harness writing dominates the work. A target that only accepts input through a file, a socket or a command line has to be wrapped so that the parsing entry point is callable in-process. The repository includes custom_winafl_server.c and custom_net_fuzzer.c along with test_netmode.cpp and test_simple_winsock_client.cpp, which indicates that network-facing targets are handled through a server-style harness rather than the default path.

## Building WinAFL and running a first harness

The repository builds with CMake; the top level contains CMakeLists.txt and the README points to the per-mode documents for the details of each instrumentation choice. The build produces afl-fuzz.exe, which the README's screenshot caption names, along with the companion tools afl-showmap.c, afl-tmin.c and afl-analyze.c carried over from AFL.

The README does not give a build command line. What the repository shows is a CMakeLists.txt at the root, so the build is driven through CMake against that file. The per-mode documents describe the dependencies each instrumentation choice needs, and afl-fuzz.exe is the artefact you run afterward.

Once afl-fuzz.exe exists, the invocation follows the DynamoRIO workflow described in readme_dr.md. That document is where the option names, the DynamoRIO path and the harness contract are specified, so read it against your target rather than copying a command line from somewhere else. The AFL conventions carried over by the fork are the ones to expect: an input directory, an output directory, a timeout, and the @@ token for the input file path when your harness reads from a file. A persistent-mode harness that receives a buffer directly does not use @@.

WinAFL ships two Python helpers that are worth knowing before you start a long campaign: winafl-cmin.py for corpus minimization and winafl-whatsup.py for summarizing a set of fuzzer output directories. Both are in the repository root, so they are run as plain Python scripts against your corpus and output directories.

```bash
python winafl-cmin.py
python winafl-whatsup.py
```

The README does not document rollback or how to resume a stopped campaign, so plan your output directories accordingly.

## Where WinAFL will waste your time

The persistent-mode requirement is the limitation that catches people. A harness that restarts the process for every input defeats the design the README describes, and process startup dominates throughput on Windows. If your target cannot be driven in-process, for example a service that refuses to expose a parsing function or a binary that crashes the whole process on malformed input in a way you cannot isolate, you will spend your effort on harness scaffolding rather than on fuzzing.

Dynamic instrumentation is also slower than compile-time instrumentation by nature. The README presents DynamoRIO, TinyInst, Intel PT and Syzygy as alternatives without ranking them, and it gives no throughput numbers. Choosing among them is a real engineering decision: Intel PT depends on processor support, Syzygy requires static rewriting of the target, and the two dynamic modes trade instrumentation overhead against setup complexity. The documentation does not make that choice for you.

Finally, WinAFL is a fork of an older AFL codebase. The upstream AFL project has continued to evolve, and the repository does not claim parity with it. If you need the newer mutation scheduling or the ecosystem around AFL++, this fork is not that.

## How WinAFL differs from honggfuzz and AFL++

honggfuzz is the closest comparison for a Windows-focused user: it also supports hardware-assisted coverage and can fuzz binaries without source, but it is a standalone fuzzer with its own input mutation and reporting rather than a fork of AFL. That matters if you have existing AFL corpora, dictionaries or scripts, because WinAFL keeps the AFL command-line conventions and the afl-* tool names.

AFL++ is the other direction. It is the actively developed continuation of the original AFL line, and its Windows support and instrumentation story differ from what this repository implements. If your target is open source and builds on Linux, AFL++ is the more direct choice. WinAFL's reason to exist is the case where the target is a Windows binary you cannot rebuild, and the README's list of CVEs found in Adobe and Microsoft products is the evidence that this case is real rather than theoretical.

## Licence and maintenance cost

WinAFL is licensed under Apache-2.0, and the README carries the full header: you may use the software under the License, and it is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND. That is a permissive licence with an explicit disclaimer, and it is the same terms the header attributes to Google Inc. as copyright holder. The README does not discuss how the licence interacts with DynamoRIO, TinyInst, Intel PT or Syzygy, which are separate components with their own terms; if you plan to redistribute a build, check each one rather than assuming Apache-2.0 covers the whole stack. This is not legal advice.

The repository is not archived, and its last push was on 2026-03-13. There are no releases listed, so there is no versioned artefact to pin to and no changelog entry to read for upgrade guidance beyond the ChangeLog file in the root. Upgrading means tracking the master branch, which also means tracking whatever the instrumentation dependencies do independently.

## Conclusion

Adopt WinAFL if you have a closed-source Windows target, a machine where you can run DynamoRIO or TinyInst, and the patience to write a persistent-mode harness around one exported function; skip it if you have source and can build with a native instrumentation toolchain, because the dynamic instrumentation layer costs you execution speed for no benefit. Before committing, verify three things in the repository: which of readme_dr.md, readme_tinyinst.md, readme_pt.md or readme_syzygy.md matches your instrumentation choice, that your target exposes a function you can call repeatedly in a loop, and that the build produces afl-fuzz.exe. The four instrumentation modes are separate workflows with separate documents, not configuration flags on one binary.

## FAQ

### How do you use WinAFL to fuzz a Windows binary?

You build afl-fuzz.exe with CMake, choose one of the four instrumentation modes described in readme_dr.md, readme_tinyinst.md, readme_pt.md or readme_syzygy.md, and write a harness that calls a target function in a loop. WinAFL then feeds mutated inputs into that function in persistent mode instead of restarting the process for each sample.

### Why does WinAFL need persistent mode?

The README states that WinAFL relies heavily on persistent fuzzing mode, executing multiple input samples without restarting the target process, and that this is done by selecting a target function and instrumenting it so it runs in a loop. The purpose is to avoid paying process startup cost for every input.

### Which instrumentation modes does WinAFL support?

The README lists dynamic instrumentation using DynamoRIO, dynamic instrumentation using TinyInst, hardware tracing using Intel PT, and static instrumentation via Syzygy. Each has its own document in the repository rather than being a flag on a single binary.

### Can WinAFL fuzz a target that only reads from a network socket?

The repository includes custom_winafl_server.c, custom_net_fuzzer.c and test_netmode.cpp, which indicates a server-style harness path for network-facing targets. The README itself does not document that workflow; the harness sources and the per-mode documents are where the contract is defined.

### What licence is WinAFL released under?

WinAFL is licensed under the Apache License, Version 2.0, with the header naming Google Inc. as copyright holder and distributing the software on an AS IS BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND. The README does not address the licences of DynamoRIO, TinyInst, Intel PT or Syzygy.

## Sources

- [googleprojectzero/winafl on GitHub](https://github.com/googleprojectzero/winafl)
- [Issues](https://github.com/googleprojectzero/winafl/issues)
- [License: Apache-2.0](https://github.com/googleprojectzero/winafl/blob/master/LICENSE)
- [README](https://github.com/googleprojectzero/winafl/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/googleprojectzero-winafl
