
Static Binary Instrumentation tool for Windows x64 executables
peafl64 is a static instrumentation tool for x64 PEs in Windows.
Static instrumentation is the practice of editing executable files and adding code to specific locations in them.
The instrumentation adds code to the start of every basic block in the binary, logging the execution flow in an AFL-compatible way.
It allows us to fuzz binaries in Usermode (using WinAFL) and Kernelmode (using kAFL) without access to their source code.
There are other ways of fuzzing Windows binaries; in this project we chose to focus on static instrumentation because it's the fastest method.
This project builds on the pe-afl tool by wmliang, with added x64 support.
The instrumentation script requires an IDA analysis output.
To create it, run the provided ida_dumper.py script in IDA.
The script requires IDA 7+ and python3.8+.
usage: pe_afl.py [-h] [-n] [-cb] [-tf] [-te THREAD_ENTRY] [-nt NTOSKRNL] [-e ENTRY] [-l ENLARGE] [-v] [-lf] pefile ida_dump
positional arguments:
pefile Target PE file for instrumentation
ida_dump dump.json from IDA (created by ida_dumper.py)
optional arguments:
-h, --help show this help message and exit
-n, --nop Instrument with NOPs for testing
-cb, --callback Instrument with a callback, which is in the helper driver that's written in C
-tf, --thread-filter Driver instrumentation that filters only on thread ID (must use "-te" with this option)
-te THREAD_ENTRY, --thread-entry THREAD_ENTRY
The address (RVA) of the thread's initialization function
-nt NTOSKRNL, --ntoskrnl NTOSKRNL
ntoskrnl.exe path for offset extraction (non-optional if instrumenting a driver)
-e ENTRY, --entry ENTRY
Inject code on entry point, ie. -e9090
-l ENLARGE, --enlarge ENLARGE
Enlarge factor for sections, default=4
-v, --verbose Print debug log
-lf, --logfile Print log to pe-afl64.log rather than stream
Instrumenting usermode binary with NOPs
PS pe-afl-64> python .\pe_afl.py -n C:\Work\cmd.exe C:\Work\cmd.exe.dump.json
[*] User-mode binary is being instrumented
[*] Single-thread instrument is on
[*] Preparing new sections
[*] Added section .text^
[*] Added section .cov
[*] Expanding relative jumps
[*] Expanded 3874 of 14353 branches
[*] Building address map
[*] Updating relative instructions
[*] Updating relocations...
[*] Updating Export table
[*] Updating load config
[*] Updating exception records
[*] Updating the PE headers
[*] Finalizing...
[*] Creating instrumented code
[*] Writing address mapping to C:\Work\cmd.exe.mapping.txt
[*] Updating jump tables
[*] Updated .text
...
[*] Removing temporary PE files
[*] Fixing PE checksum
[*] Instrumented binary saved to: C:\Work\cmd.instrumented.exe
Instrumenting kernelmode binary with process ID filtering
python .\pe_afl.py -l 6 -nt "C:\Work\ntoskrnl.exe" "C:\Work\driver.sys" "C:\Work\driver.sys.dump.json"
Instrumenting kernelmode binary with thread ID filtering and verbose output
python .\pe_afl.py -v -tf -te 0x40000 -l 6 -nt "C:\Work\ntoskrnl.exe" "C:\Work\ntoskrnl.exe" "C:\Work\ntoskrnl.exe.dump.json"
First you'll need to check if your machine is booting using BIOS or UEFI.
For Hyper-V machines: Gen 1 machines are BIOS based, and the Gen 2 are UEFI based.
If your machine is BIOS based then you need to patch winload.exe:
ImgpValidateImageHash in itmov eax, edi with xor eax, eax in the last code block of the functionbcdedit /set path \Windows\system32\winload2.exeIf your machine is booted using UEFI then use EfiGuard util to patch winload.efi.
Command cheatsheet if using Hyper-V Manager:
FS1:
cd EFI/Boot
Load EfiGuard.Dxe.efi
FS0:
\EFI\Boot\bootx64.efi
Then instrument your driver of choice. To load an instrumented driver on a Windows machine it must be signed, and a self-signing certificate is enough to pass the OS's demands:
# In elevated powershell terminal
$c = New-SelfSignedCertificate -Type CodeSigningCert -KeyUsage DigitalSignature -Subject 'CN=Microsoft Windows, O=Microsoft Corporation, L=Redmond, S=Washington, C=US'
Set-AuthenticodeSignature .\driver.instrumented.sys -Certificate $c -Force
If the driver you instrumented is already used by the system, use these commands (as admin) to replace the driver's file:
set NAME=mydriver.sys
icacls %NAME% /save C:\windows\temp\%NAME%.icacls
takeown /F %NAME%
icacls %NAME% /grant Everyone:F
move %NAME% %NAME%.bak
move instrumented_driver.sys %NAME%
icacls . /restore C:\windows\temp\%NAME%.icacls
Then run:
bcdedit /set recoveryenabled no
bcdedit /set nointegritychecks on
shutdown -t 0 -r
Integration with WinAFL is by compiling a harness using the provided headers.
Alongside the headers there's example.c, which is a sample program that shows how to use them.
The provided headers are a slight modification of the headers that are already provided by WinAFL to integrate with
another Static Binary Instrumentation tool called Syzygy.
The way we integrate with kAFL is pretty simple.
Normally, a kAFL harness is running on a virtual machine and talks to the fuzzer's frontend using special "hypercalls".
These hypercalls tell the fuzzer to do many things, among them is to load coverage data from IntelPT and parse it as an AFL bitmap.
Because peafl64 makes IntelPT tracing obsolete, we must prepare a way to transmit the coverage data to the fuzzer.
Therefore, we expanded qemu and kvm with "hypercalls" that allow the (usermode) harness that runs in a VM to send the coverage data it collected using the helper driver.
This is specifically about the setup on ESXi but should be relevant for other virtualization platforms like AWS.
The setup is pretty simple:
install.sh qemu step, run install.sh qemu_sbiTo fuzz using kAFL and peafl64, we need to setup a fuzzing machine:
Other than that, fuzzing with our kAFL fork is the same as normal fuzzing with kAFL.
Steps Overview: