GithubHelp home page GithubHelp logo

0vercl0k / wtf Goto Github PK

View Code? Open in Web Editor NEW
1.4K 23.0 125.0 106.63 MB

wtf is a distributed, code-coverage guided, customizable, cross-platform snapshot-based fuzzer designed for attacking user and / or kernel-mode targets running on Microsoft Windows and Linux user-mode (experimental!).

License: MIT License

Python 3.13% CMake 1.49% Shell 0.24% Batchfile 0.02% C++ 80.70% Rust 2.83% C 1.55% Assembly 7.63% Meson 0.01% HTML 0.08% Less 1.76% Makefile 0.16% Cuda 0.01% CSS 0.01% QMake 0.38% Ruby 0.01%
fuzzer snapshot-fuzzer kvm-api code-coverage winhv bochscpu bochs security fuzzing testing

wtf's Introduction

what the fuzz

A distributed, code-coverage guided, cross-platform snapshot-based fuzzer designed for attacking user and or kernel-mode targets running on Microsoft Windows and Linux user-mode (experimental!).

Overview

what the fuzz or wtf is a distributed, code-coverage guided, customizable, cross-platform snapshot-based fuzzer designed for attacking user and or kernel-mode targets running on Microsoft Windows or Linux (experimental, see linux_mode). Execution of the target can be done inside an emulator with bochscpu (slowest, most precise), inside a Windows VM with the Windows Hypervisor Platform APIs or inside a Linux VM with the KVM APIs (fastest).

It uncovered memory corruption vulnerabilities in a wide range of softwares: IDA Pro, a popular AAA game, the Windows kernel, the Microsoft RDP client, NVIDIA GPU Display driver, etc.

Compiled binaries are available from either the CI artifacts or from the Releases section for both Windows & Linux.

If you would like to read more about its history or how to use it on a real target, I recommend to take a look at those posts to get started ๐Ÿ”ฅ

Usage

The best way to try the features out is to work with the fuzzer_hevd / fuzzer_tlv_server modules. You can grab the target-hevd.7z / target-tlv_server.7z archives and extract them into the targets/ directory. The archives contain the directory trees that are expected for every targets:

  • inputs is the folder where your input test-cases go into,
  • outputs is the folder where the current minset files are saved into,
  • coverage is the folder where the .cov files are expected to be in,
  • crashes is where the crashes gets saved in,
  • state is where the memory dump (mem.dmp) as well as the CPU state (regs.json) and the symbol store are stored in (symbol-store.json). The symbol store is a simple JSON file that is used on Linux systems to know where to put breakpoints as there is no support for symbols / dbgeng on those platforms. wtf generates this file at runtime everytime you run your target on Windows.

What follows assume that you downloaded the target-hevd.7z file attached to the latest release, and extracted it in the targets directory of your clone of wtf. You should have wtf/targets/hevd in which you find the inputs / outputs, etc. directories.

Starting a server node

The server is basically the brain and keeps track of all the state: the aggregated code-coverage, the corpus, it generates and distributes the test-cases to client.

This is how you might choose to launch a local server node:

wtf.exe master --name hevd --max_len=1028 --runs=10000000

The max_len option is used to limit the size of the generated test-case, runs is the number of test-cases it will generate, address specify where wtf needs to be listening on, target is a directory with the directory tree we described above (the user can also choose to override those directories with --input / --output / --crashes) and name specifies your fuzzing module name so that the master can invoke your generator function if you have defined one.

Fuzzing nodes

The client nodes run a test-case that has been generated and distributed by the server and communicates the result back to the server (code-coverage, result, etc.).

This is how you would start a client node that uses the bochscpu backend:

wtf.exe fuzz --name hevd --limit 10000000

The fuzz subcommand is used with the name option to specify which fuzzer module needs to be used, backend specifies the execution backend and limit the maximum number of instruction to execute per testcase (depending on the backend, this option has different meaning).

Running a test-case

If you would like to run a test-case (or a folder filled with test-cases), you can use the run subcommand.

This is how you would would run the crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0 test-case:

wtf.exe run --name hevd --limit 10000000 --input crashes\crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0

Minseting a corpus

To minset a corpus, you need to use a server node and as many client nodes as you need like you would for a fuzzing job. You can simply set the runs optins to 0.

This is how you would minset the corpus in outputs into the minset directory (also highlights how you can override the inputs and outputs directories):

wtf.exe master --name hevd --max_len=1028 --runs=0 --inputs=outputs --outputs=minset

Generating execution traces

The main mechanism available to instrospect in an execution backend is to generate an execution trace. bochscpu is the fastest backend to do that, because exiting VMX mode is very expensive on the other backends.

This is how you would generate an execution trace for the crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0 test-case:

wtf.exe run --name hevd --limit 10000000 --input crashes\crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0 --trace-type=rip

To symbolize execution traces you should use symbolizer-rs. This is how you would symbolize the crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0.trace execution trace generated above:

symbolizer-rs.exe --trace crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0.rip.trace

Generating Tenet traces

If you see yourself needing more contextual awareness, the bochscpu backend allows you to generate execution traces that can be loaded in the Tenet trace explorer. In the below, I start from a crash in memmove and walk back to find out where the source pointer is coming from (user-mode!):

wtf.exe run --name hevd --limit 10000000 --input crashes\crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0 --trace-type=tenet

Generating code-coverage traces

To generate code-coverage traces you can simply use the run subcommand with the --trace-type=cov option.

This is how you would generate code-coverage traces for all the files inside the minset folder and store them in the coverage-traces folder:

wtf.exe run --name hevd --input minset --trace-path=coverage-traces --trace-type=cov

Those traces aren't directly loadable into lighthouse because they aren't symbolized.

This is how you would symbolize all the files inside the coverage-traces folder and write the results into coverage-traces-symbolized:

symbolizer-rs.exe --trace coverage-traces -o coverage-traces-symbolized --style modoff

And finally, you can load those up in lighthouse:

Also if you don't care about individual code-coverage, the master maintains a coverage.cov file contains the unique aggregated code-coverage that has been exercised. It makes it easy to check on the global code-coverage really quick during a fuzzing job.

How does it work?

wtf runs user & kernel mode through an execution backend and relies on the user to insert test-cases in the target. Unlike other classical fuzzer tools, wtf doesn't do much of the heavy lifting; the user does. The user needs to know the harnessed target very well and onboarding a target is an iterative process that will take time. It has a lot of flexibility to offer if you are ready to get hacking though :)

The usual workflow to harness a target is as follows:

  1. Get your target running into a Hyper-V VM running Windows with one virtual CPU and 4GB of RAM.

  2. Put your target into the desired state using KD. For example, to target HEVD's IOCTL handler, I chose to stop the target in user-mode right before the client invokes DeviceIoControl. This will vary depending on your targets but you probably want it to be close to the code you want to fuzz.

    kd> r
    rax=000000dfd98ff3d0 rbx=0000000000000088 rcx=0000000000000088
    rdx=00000000deadbeef rsi=0000000000000000 rdi=0000000000000000
    rip=00007ff6f5bb111e rsp=000000dfd98ff380 rbp=0000000000000000
    r8=000000dfd98ff3d0  r9=0000000000000400 r10=000002263e823055
    r11=00007ff6f5bcb54d r12=0000000000000000 r13=0000000000000000
    r14=0000000000000000 r15=0000000000000000
    iopl=0         nv up ei pl nz na po nc
    cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
    hevd_client!main+0xae:
    00007ff6`f5bb111e ff15dc1e0100    call    qword ptr [hevd_client!_imp_DeviceIoControl (00007ff6`f5bc3000)] ds:002b:00007ff6`f5bc3000={KERNEL32!DeviceIoControlImplementation (00007ff8`3e2e6360)}
    
  3. Use snapshot to generate the kernel crash-dump as well as the regs.json file that contains the CPU state. I recommend to dump those file in a state directory under your target directory (targets/hevd/state for example):

    kd> .load c:\work\codes\snapshot\target\release\snapshot.dll
    
    kd> !snapshot -h
    [snapshot] Usage: snapshot [OPTIONS] [STATE_PATH]
    
    Arguments:
      [STATE_PATH]  The path to save the snapshot to
    
    Options:
      -k, --kind <KIND>  The kind of snapshot to take [default: full] [possible values: active-kernel, full]
      -h, --help         Print help
    
    kd> !snapshot c:\work\codes\wtf\targets\hevd\state
    [snapshot] Dumping the CPU state into c:\work\codes\wtf\targets\hevd\state\regs.json..
    [snapshot] Dumping the memory state into c:\work\codes\wtf\targets\hevd\state\mem.dmp..
    Creating c:\\work\\codes\\wtf\\targets\\hevd\\state\\mem.dmp - Full memory range dump
    0% written.
    5% written. 1 min 50 sec remaining.
    10% written. 1 min 17 sec remaining.
    15% written. 1 min 30 sec remaining.
    [...]
    Wrote 4.0 GB in 1 min 32 sec.
    The average transfer rate was 44.5 MB/s.
    Dump successfully written
    [snapshot] Done!
    
  4. Create a fuzzer module, write the code that inserts a test-case into your target and define the various conditions to detect crashes or the end of a test-case.

  5. You can also create your own mutator / generator by subclassing the Mutator_t interface. The fuzzer_tlv_server.cc is a good example to understand how you would go about implementing your own.

At this point you should start to iterate and verify that the fuzzer module works as expected. The execution backends are a blackbox so you should generate execution traces to make sure it goes through the right paths, does the right things. During this phase I mainly use the bochscpu backend as it is fully deterministic, starts fast, generating execution traces is possible, code-coverage comes for free, etc. Overall, it's a nicer environment to develop and prototype in.

Once you are satisfied with the module, you can start to look at making it work with the winhv / kvm backends if you need it to run under those. One major difference between the bochscpu backend & the others, is that the others use software breakpoints to provide code-coverage information. As a result, you'll need to load the modules you want coverage for under IDA and use the gen_coveragefile_ida.py script to generate a simple JSON file that gets loaded by wtf. You are free to generate this JSON file yourself using whatever tool you would like: it basically is a list of basic-blocks virtual addresses.

You can also target WoW64 applications by using the !wow64exts.sw Windbg command to switch to the 64-bit context right before creating the snapshot (thanks @cube0x8 for sharing this trick!):

32.kd:x86> !wow64exts.sw
The context is partially valid. Only x86 user-mode context is available.
Switched to Host mode

32.kd> !snapshot

How to deliver multi-packets to my target?

Complex targets usually carry complex states as well and chances are that you might need to deliver more than one testcase in a session to trigger complex issues. tlv_server.cc is an example of such a server where exercising the parsing function with only one testcase won't be enough to uncover the bugs.

To handle this case, check out fuzzer_tlv_server.cc that shows an example of how to solve this problem.

How to provide a custom mutator / generator?

wtf comes with two popular generic mutators: libfuzzer & honggfuzz. You might want to provide your own or to generate testcases on your own as well.

To do that, you can subclass the Mutator_t interface, and register the function that instantiate your mutator when you define your fuzzing module:

class CustomMutator_t : public Mutator_t {
public:
  static std::unique_ptr<Mutator_t> Create(std::mt19937_64 &Rng,
                                           const size_t TestcaseMaxSize) {
    return std::make_unique<CustomMutator_t>(Rng, TestcaseMaxSize);
  }
  // ...
};

Target_t target("target", Init, InsertTestcase, Restore, CustomMutator_t::Create);

Check out the CustomMutator_t class in the fuzzer_tlv_server.cc module for a complete example.

Execution backends

In this section I briefly mention various differences between the execution backends.

bochscpu

  • โœ… Full system code-coverage (edge coverage available via --edges),
  • โœ… Demand-paging,
  • โœ… Timeout is the number of instructions which is very precise,
  • โœ… Full execution traces are supported,
  • โœ… Fully deterministic,
  • โŒSpeed seems to be good for short executions but not for long executions (~100x slower than KVM when I was fuzzing IDA).

whv

  • โœ” Code-coverage via software breakpoints,
  • โŒ Demand-paging so start-up is slow (as it needs to load the full crash-dump in memory),
  • โœ” Timeout is implemented with a timer,
  • โœ… Full execution traces are supported but are slow (exiting VMX is costly),
  • โœ” Deterministic if handling source of non determinism manually (for example, patching nt!ExGenRamdom that uses rdrand),
  • โœ” Speed seems to be ok for long executions (lots of bottleneck in whv though; ~10x slower than kvm when I was fuzzing IDA).

KVM

  • โœ” Code-coverage via software breakpoints,
  • โœ… Demand-paging is supported via UFDD,
  • โœ” Timeout is implemented with a timer. โœ… If the hardware supports PMU virtualization, it is used to generate a PMI after X retired instructions (MSR_IA32_FIXED_CTR0),
  • โœ… Full execution traces are supported but are slow (exiting VMX is costly),
  • โœ” Deterministic if handling source of non determinism manually (for example, patching nt!ExGenRamdom that uses rdrand),
  • โœ… Fastest for long executions (~500m - 1.5 billion instructions; ~100x faster than bochscpu, ~10x faster than whv when I was fuzzing IDA).

Build

The CI builds wtf on Ubuntu using both clang++ / g++, on Windows using Microsoft's Visual Studio and on OSX using clang++.

To build it yourself you need to start a Visual Studio Developper Command Prompt and either run build-release.bat which uses the Ninja generator or build-release-msvc.bat to generate a Visual Studio solution file:

(base) wtf\src\build>build-release.bat
[...]
[2/2] Linking CXX executable wtf.exe

(base) wtf\src\build_msvc>..\build\build-release-msvc.bat
[...]
  Finished generating code
  wtf.vcxproj -> wtf\src\build_msvc\RelWithDebInfo\wtf.exe
  Building Custom Rule wtf/src/CMakeLists.txt

Authors

Contributors

Special thanks to:

contributors-img

wtf's People

Contributors

0vercl0k avatar 1ndahous3 avatar australeo avatar clslgrnc avatar gaasedelen avatar huwwp avatar jasocrow avatar johnpmerrill avatar wumb0 avatar x9090 avatar y0ny0ns0n avatar yuawn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wtf's Issues

how to fix kvm_backend build error

it has a build error on ubuntu 18.04,the output as follows.and i install the qemu-kvm by apt

kvm_backend.cc:255:7: error: use of undeclared identifier 'KVM_SYNC_X86_REGS'
KVM_SYNC_X86_REGS | KVM_SYNC_X86_SREGS | KVM_SYNC_X86_EVENTS;
^
kvm_backend.cc:872:16: error: no member named 'sregs' in 'kvm_sync_regs'
Run_->s.regs.sregs.cr8 = CpuState.Cr8;

how do fix it? thanks.

Have the master maintain aggregated code-coverage in a file

The idea is to simply to have the master to maintain an aggregated code-coverage file; it's easy to implement and it makes it much easier to grab the file during a fuzzing session and toss it into IDA to check out on how it's looking. Without this, every time I need to generate a code-coverage trace per input file and move those in my IDA vm, more annoying.

Crash in wtf!Client_t::SendResult+0x1b7 [C:\wtf\src\wtf\client.cc @ 200]

Hello,

I am getting the crash below when trying to fuzz a kernel driver:

EXCEPTION_CODE_STR:  80000003

EXCEPTION_PARAMETER1:  0000000000000000

STACK_TEXT:  
000000bd`f98fd2b0 00007ffc`94177ee2     : 00000209`7e3a0000 00007ffc`941d77f0 00000000`00000003 00000209`7e3a0000 : ntdll!RtlReportCriticalFailure+0x56
000000bd`f98fd3a0 00007ffc`941781ca     : 00000000`00000003 00000000`00000000 00000209`7e3a0000 00000000`0000021c : ntdll!RtlpHeapHandleError+0x12
000000bd`f98fd3d0 00007ffc`9417de51     : 00000209`7e3a0000 00000209`7e3a0000 00000209`3ff7a010 00000000`00000000 : ntdll!RtlpHpHeapHandleError+0x7a
000000bd`f98fd400 00007ffc`94117142     : 00000209`7e3a0000 00000209`7e3a0000 00000000`00000000 00000209`7e3a0000 : ntdll!RtlpLogHeapFailure+0x45
000000bd`f98fd430 00007ffc`940947b1     : 00000209`7e3cc838 00000209`7e3a0000 000000bd`f98fd631 00000000`00000000 : ntdll!RtlpFreeHeapInternal+0x81a32
000000bd`f98fd4f0 00007ffc`9197f05b     : 00000209`3feefd80 00000000`00000001 00000000`00000000 00000000`00000000 : ntdll!RtlFreeHeap+0x51
000000bd`f98fd530 00007ff6`6f2070a8     : 00000000`00000001 00000000`00000000 00008e8e`029f65d5 000000bd`f98fef50 : ucrtbase!_free_base+0x1b
000000bd`f98fd560 00007ff6`6f205167     : 00000209`3feefd80 000000bd`f98fd5b8 00000000`000000af 000000bd`f98fd5b8 : wtf!std::_Ref_count_resource<char *,void (__cdecl*)(char *)>::`scalar deleting destructor'+0x18
000000bd`f98fd590 00007ff6`6f20546b     : 000000bd`f98fd7a0 000000bd`f98fd719 000000bd`f98fef50 00000209`7e3aaab8 : wtf!Client_t::SendResult+0x1b7 [C:\wtf\src\wtf\client.cc @ 200] 
000000bd`f98fd690 00007ff6`6f22e95c     : 000000bd`f98fd7a0 00000000`00000000 000000bd`f98fef50 000000bd`f98fef50 : wtf!Client_t::Run+0x21b [C:\wtf\src\wtf\client.cc @ 251] 
000000bd`f98fd780 00007ff6`6f255258     : 00007ff6`70da05d8 00007ff6`70da05d8 00007ff6`70da05d8 00000209`7e3aaab8 : wtf!FuzzSubcommand+0x2c [C:\wtf\src\wtf\subcommands.cc @ 96] 
000000bd`f98fd7f0 00007ff6`6f425a5c     : 00000000`00000000 00000000`00000000 00000209`7e3a2d30 00000000`00000000 : wtf!main+0x1d38 [C:\wtf\src\wtf\wtf.cc @ 426] 
000000bd`f98ffa20 00007ffc`92ee7034     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : wtf!__scrt_common_main_seh+0x10c [d:\a01\_work\12\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288] 
000000bd`f98ffa60 00007ffc`940c2651     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
000000bd`f98ffa90 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21


SYMBOL_NAME:  ntdll!RtlReportCriticalFailure+56

MODULE_NAME: ntdll

IMAGE_NAME:  ntdll.dll

IMAGE_VERSION:  10.0.19041.1466

STACK_COMMAND:  .thread ; .cxr ; kb

BUCKET_ID_FUNC_OFFSET:  56

FAILURE_BUCKET_ID:  0x0_VRF_ntdll!RtlReportCriticalFailure

OS_VERSION:  10.0.19041.1

BUILDLAB_STR:  vb_release

OSPLATFORM_TYPE:  x64

OSNAME:  Windows 10

FAILURE_ID_HASH:  {73ce741a-e3b8-1889-e74b-f6e20476df31}

Followup:     MachineOwner

I think there is something wrong in my harness because other targets are working properly but i don't see the root cause with the dump information. I just trying to fuzz a specific function in the driver that parse network messages. i got the dump when the BP of the frist function instruction is reached and stop when the RETN of the same function is reached.

Best regards,
Vรญctor

Dbghelp redistributable / Windows 11

Figure out what's the way to be able to redistribute the dbghelp.dll / symsrv.dll. On Windows 11, it seems to work if I also grab dbgeng.dll as well as dbgcore.dll; otherwise I get an export issue when loading wtf.

FPU/x87 state initialization

Have a closer look at how this gets initialized in wtf - I've ignored this for a while but it'd be good to make sure it's correct. And also, maybe figure out the 0xInfinity bit in fpst either in wtf or in bdump or both.

Cannot crash

Hello, I am testing your app with simple file with buffer overflow

I run it with this command

 RelWithDebInfo\wtf.exe run --name simple --backend 0 --state D:\path-to-state\state --input D:\path-to-inputs\inputs --trace-path D:\path-to-trace\trace
 --trace-type 1

when running with input length 1000, after ret command, the rip should be 0x6161616161616161, but trace neither give information the the rip is 0x6161616161616161 nor crash. Can I know what happen and how to solve it? Thanks

here is my state, input and fuzzer module code https://mega.nz/file/Dq40WBQK#u95r27KzwHCteEKEcRoepVZG1Rl9yJs254lj-j9G8hQ

Revisit the way deduplication works

Currently, wtf relies on crash name to 'deduplicate' crashes; basically two parameters are in play: the address where the exception happened & the type of exception. The big issue with this, is that if you have two different bug that lead to a crash in memcpy for example, you might end up with the same crash name and so you won't see the other bug.

The issue about hyper-v in intel cpu

I test the program by the hyper-v backend on intel cpu win10 and win11. And get errors:

Initializing the debugger instance.. (this takes a bit of time)
Setting Efer failed
Failed to LoadState
Backend failed initialization.

I found the WHvX64RegisterEfer set by WHvSetVirtualProcessorRegisters function always failed on intel cpu.

translation of GVA 0xe failed

Hi, I'm following the README to dump, but when I want to fuzz, it throw

H:\fuzz_example\hevd>H:\fuzz\wtf\src\build\wtf.exe fuzz --backend=bochscpu --name hevd --max_len 1028 --limit 10000000
Initializing the debugger instance.. (this takes a bit of time)
Setting debug register status to zero.
Setting debug register status to zero.
Dialing to tcp://localhost:31337/..
Translation of GVA 0xe failed

Translation of GVA 0x8903aff578 failed

hi, first of all, thank you for providing such a cool fuzzer bro :)
but i got an error while trying hevd sample test files.

server

D:\wtf\targets\hevd>..\..\src\build\wtf.exe master --max_len=1028 --runs=10000000 --target .
Seeded with 8037290960217419384
Iterating through the corpus..
Sorting through the 1 entries..
Running server on tcp://localhost:31337..
#0 cov: 0 (+0) corp: 0 (0.0b) exec/s: -nan (1 nodes) lastcov: 6.0s crash: 0 timeout: 0 cr3: 0 uptime: 6.0s
Could not receive size (0)
Receive failed

client

D:\wtf\targets\hevd>..\..\src\build\wtf.exe fuzz --backend=bochscpu --name hevd --max_len 1028 --limit 10000000
Initializing the debugger instance.. (this takes a bit of time)
Setting debug register status to zero.
Setting debug register status to zero.
Dialing to tcp://localhost:31337/..
Translation of GVA 0x8903aff578 failed

and this is target dumps & wtf that i made based on README.md
https://drive.google.com/file/d/1-18rDU_TY8uLHYimMhXO9169iFUVB6o8/view?usp=sharing

thanx...

Build error using clang-12/gcc-10

Hi @0vercl0k

When I tried to build wtf on Ubuntu 20.04 with constexpr bool LoggingOn = true; in fuzzer_hevd.cc I got the below error. I see that by default you are keeping the logging off so you did not encounter this issue.

[1/3] Building CXX object CMakeFiles/wtf.dir/wtf/fuzzer_hevd.cc.o
FAILED: CMakeFiles/wtf.dir/wtf/fuzzer_hevd.cc.o 
/usr/bin/g++-10  -DFMT_HEADER_ONLY -I../libs/bochscpu-bins/include -I../libs/robin-map/include -I../libs/kdmp-parser/src/lib -I../libs/libfuzzer -I../libs/readerwriterqueue -I../libs/json/single_include -I../libs/BLAKE3/c -I../libs/CLI11/include -I../libs/fmt/include -I../libs/yas/include -O3 -DNDEBUG -flto -fno-fat-lto-objects   -std=gnu++2a -Winvalid-pch -include /home/ashfaq/Shared/wtf/src/build/CMakeFiles/wtf.dir/cmake_pch.hxx -MD -MT CMakeFiles/wtf.dir/wtf/fuzzer_hevd.cc.o -MF CMakeFiles/wtf.dir/wtf/fuzzer_hevd.cc.o.d -o CMakeFiles/wtf.dir/wtf/fuzzer_hevd.cc.o -c ../wtf/fuzzer_hevd.cc
../wtf/fuzzer_hevd.cc: In instantiation of โ€˜void Hevd::DebugPrint(const char*, const Args_t& ...) [with Args_t = {}]โ€™:
../wtf/fuzzer_hevd.cc:47:41:   required from here
../wtf/fuzzer_hevd.cc:16:15:   in โ€˜constexprโ€™ expansion of โ€˜fmt::v8::basic_format_string<char>(Format)โ€™
../wtf/fuzzer_hevd.cc:16:15: error: โ€˜Formatโ€™ is not a constant expression
   16 |     fmt::print(Format, args...);
      |     ~~~~~~~~~~^~~~~~~~~~~~~~~~~
../wtf/fuzzer_hevd.cc: In instantiation of โ€˜void Hevd::DebugPrint(const char*, const Args_t& ...) [with Args_t = {std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >}]โ€™:
../wtf/fuzzer_hevd.cc:84:44:   required from here
../wtf/fuzzer_hevd.cc:16:15:   in โ€˜constexprโ€™ expansion of โ€˜fmt::v8::basic_format_string<char, const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&>(Format)โ€™
../wtf/fuzzer_hevd.cc:16:15: error: โ€˜Formatโ€™ is not a constant expression
[2/3] Building CXX object CMakeFiles/wtf.dir/libs/libfuzzer/FuzzerMutate.cpp.o
ninja: build stopped: subcommand failed.

[Question] How to deal with 32bit binaries when doing a snapshot ?

Hi,
First of all this project looks amazing :)

I am trying to replicate the example shown in "Fuzzing Modern UDP Game Protocols With Snapshot-based Fuzzers", but I have a question.

What if my target binary is a 32bit binary running on a 32bit Hyper-V virtual machine ?
I saw that the bdump.js script is looking for some 64bits registers for the "regs.json" output, and the small paragraph about WOW64 in the README.md confuses me.

Will I be able to run the fuzzer if a create a 32bit version of "bdump.js" ?

For the context, I am trying to fuzz a 32bit library used by a 32bit binary.
If I use a 32bit debuggee virtual machine and a 32bit version of WinDBG, everything is fine, I can break on the desired function and replicate "bdump.js"with my own script.

But if I use a 64bit debuggee virtual machine with whatever version of WinDbg, I am not able to see my target DLL anymore, so I can't break on it nor take a process snapshot ... All I see are some wow64 related DLL when I switch into the context of the target process.

I have tried to play with ".effmach" and "!wow64exts.sw" but my target DLL is never accessible that way when running on a 64bit platform.

Unfortunately, I can't find a lot of resources online regarding this issue. I can't even figure out if this is something possible.

The README of the project seems to imply that the snapshot must be done on a 64bit platform, but I'm not sure as this is not explicitly written.

I know that this question is not directly related to the project, and more about the "bdump.js" script usage / WinDbg usage, but since my goal is to fuzz this library using wtf, I take my chance here :)

Thank you

Figure out if revoking ccov is a good thing.

My goal was to incentivize the fuzzer to not find slow test-cases (that times out) and so every-time a client nodes finds a test-case that times out that found new coverage, it gets revoked and not shared with the server.

I am not quite sure if this is a good idea or not; it also adds complexity for maybe not much. It also means the fuzzer doesn't get a chance to mutate a timeout testcase and maybe make it not timeout anymore.

Need to check if there's any literature or see what people do.

KVM_SET_XCRS failed: Invalid argument

Hello, I try to use the hevd example according to the instruction, however when I runing the fuzzing node with the kvm backend, the KVM_SET_XCRS failed with Invalid argument error.
I use the kvm version 12, and I carry out the expriment in Ubuntu 20.04. Do you know the reason about this failure.

Investigate & turn back on ValidateWrite in VirtWrite

I remember I turned this off at one point:

bool Backend_t::VirtWrite(const Gva_t Gva, const uint8_t *Buffer,
                          const uint64_t BufferSize, const bool Dirty) {
  uint64_t Size = BufferSize;
  Gva_t CurrentGva = Gva;
  while (Size > 0) {
    Gpa_t Gpa;
    // XXX: Reenable ValidateReadWrite when bug is figured out.
    const bool Translate = VirtTranslate(
        CurrentGva, Gpa, MemoryValidate_t::ValidateRead /*Write*/);

I would run into cases where I'd VirtWrite onto a PTE that wasn't set writeable; but writing over it ignoring the PTE state never lead to any weird crash so I let it be.

It'd be good to understand why that is and what's the behavior behind; it might be the case that the PF handler turns it to writeable for unknown reason so we might need to emulate that behavior as well.

Investigate @tr.limit special casing

In #29 we got rid of some special casing for configuring segments. There is one left to understand why it is required:

    if constexpr (WHvX64Register##_Whv_ == WHvX64RegisterTr) {                 \
      Reg->Segment.Limit = 0xffffffff;                                         \
    }                                                                          \

It doesn't seem to be dg's fault this time:

kd> dg @tr
                                                    P Si Gr Pr Lo
Sel        Base              Limit          Type    l ze an es ng Flags
---- ----------------- ----------------- ---------- - -- -- -- -- --------
0040 00000000`3ff5c000 00000000`00000067 TSS32 Busy 0 Nb By P  Nl 0000008b

kd> !gdt @tr
dt nt!_KGDTENTRY64 0xfffff8033ff5dff0
     Base: [0xfffff8033ff5c000 -> 0xfffff8033ff5c067]
     Type: TSS64 Busy (0xb)
      DPL: 0x0
  Present: 0x1
     Long: 0x0
Atributes: 0x8b
@$gdt(@tr)      

Page faults on usermode target?

Hello there and thank you for awesome project to play with!

I'm trying to fuzz usermode program using bochscpu backend and only coverage I'm getting (in aggregate.cov) is related to page faults i.e. nt!KiPageFault, nt!MmAccessFault, nt!MiUserFault, ...

My target is fairly simple, memory dump is taken on this first instruction:

0033:00007ff8`5aaa2f20 83fa08         cmp     edx, 8
0033:00007ff8`5aaa2f23 7233           jb      00007ff8`5aaa2f58
0033:00007ff8`5aaa2f25 803931         cmp     byte ptr [rcx], 31h
0033:00007ff8`5aaa2f28 752e           jne     00007ff8`5aaa2f58
0033:00007ff8`5aaa2f2a 80790133       cmp     byte ptr [rcx+1], 33h
0033:00007ff8`5aaa2f2e 7528           jne     00007ff8`5aaa2f58
0033:00007ff8`5aaa2f30 80790233       cmp     byte ptr [rcx+2], 33h
0033:00007ff8`5aaa2f34 7522           jne     00007ff8`5aaa2f58
0033:00007ff8`5aaa2f36 80790337       cmp     byte ptr [rcx+3], 37h
0033:00007ff8`5aaa2f3a 751c           jne     00007ff8`5aaa2f58
0033:00007ff8`5aaa2f3c 80790431       cmp     byte ptr [rcx+4], 31h
0033:00007ff8`5aaa2f40 7516           jne     00007ff8`5aaa2f58
0033:00007ff8`5aaa2f42 80790533       cmp     byte ptr [rcx+5], 33h
0033:00007ff8`5aaa2f46 7510           jne     00007ff8`5aaa2f58
0033:00007ff8`5aaa2f48 80790633       cmp     byte ptr [rcx+6], 33h
0033:00007ff8`5aaa2f4c 750a           jne     00007ff8`5aaa2f58
0033:00007ff8`5aaa2f4e 80790737       cmp     byte ptr [rcx+7], 37h
0033:00007ff8`5aaa2f52 0f841fe2ffff   je      00007ff8`5aaa1177
0033:00007ff8`5aaa2f58 c3             ret     

Command lines, with some logs:

>wtf.exe master --max_len=1028 --runs=10000000 --target .
Seeded with 15448101250580876408
Iterating through the corpus..
Sorting through the 1 entries..
Running server on tcp://localhost:31337..
#0 cov: 0 (+0) corp: 0 (0.0b) exec/s: -nan (1 nodes) lastcov: 5.0s crash: 0 timeout: 0 cr3: 0 uptime: 5.0s
#21754 cov: 830 (+830) corp: 1 (11.0b) exec/s: 2.2k (1 nodes) lastcov: 9.0s crash: 0 timeout: 0 cr3: 21754 uptime: 15.0s
#41192 cov: 830 (+0) corp: 1 (11.0b) exec/s: 2.1k (1 nodes) lastcov: 19.0s crash: 0 timeout: 0 cr3: 41192 uptime: 25.0s
#61699 cov: 830 (+0) corp: 1 (11.0b) exec/s: 2.1k (1 nodes) lastcov: 29.0s crash: 0 timeout: 0 cr3: 61699 uptime: 35.0s
#82182 cov: 830 (+0) corp: 1 (11.0b) exec/s: 2.1k (1 nodes) lastcov: 39.0s crash: 0 timeout: 0 cr3: 82182 uptime: 45.0s
#102441 cov: 830 (+0) corp: 1 (11.0b) exec/s: 2.0k (1 nodes) lastcov: 49.0s crash: 0 timeout: 0 cr3: 102441 uptime: 55.0s
#123153 cov: 830 (+0) corp: 1 (11.0b) exec/s: 2.1k (1 nodes) lastcov: 60.0s crash: 0 timeout: 0 cr3: 123153 uptime: 1.1min
#138078 cov: 830 (+0) corp: 1 (11.0b) exec/s: 2.0k (1 nodes) lastcov: 1.2min crash: 0 timeout: 0 cr3: 138078 uptime: 1.2min
#152423 cov: 830 (+0) corp: 1 (11.0b) exec/s: 1.9k (1 nodes) lastcov: 1.3min crash: 0 timeout: 0 cr3: 152423 uptime: 1.4min
#171748 cov: 830 (+0) corp: 1 (11.0b) exec/s: 1.9k (1 nodes) lastcov: 1.5min crash: 0 timeout: 0 cr3: 171748 uptime: 1.6min
...

>wtf.exe fuzz --backend=bochscpu --name acctest1 --limit 10000000 --max_len=1028
Initializing the debugger instance.. (this takes a bit of time)
Setting debug register status to zero.
Setting debug register status to zero.
acctest1: rip = 7ff85aaa2f20, end = 7ff85aaa2f58
Could not set a breakpoint at hal!HalpPerfInterrupt.
Could not set a breakpoint on hal!HalpPerfInterrupt, but carrying on..
Dialing to tcp://localhost:31337/..
#19237 cov: 830 exec/s: 1.9k lastcov: 8.0s crash: 0 timeout: 0 cr3: 19237 uptime: 10.0s
#38678 cov: 830 exec/s: 1.9k lastcov: 18.0s crash: 0 timeout: 0 cr3: 38678 uptime: 20.0s
#59195 cov: 830 exec/s: 2.0k lastcov: 28.0s crash: 0 timeout: 0 cr3: 59195 uptime: 30.0s
#79681 cov: 830 exec/s: 2.0k lastcov: 38.0s crash: 0 timeout: 0 cr3: 79681 uptime: 40.0s
#99926 cov: 830 exec/s: 2.0k lastcov: 48.0s crash: 0 timeout: 0 cr3: 99926 uptime: 50.0s
#120648 cov: 830 exec/s: 2.0k lastcov: 58.0s crash: 0 timeout: 0 cr3: 120648 uptime: 60.0s
#136372 cov: 830 exec/s: 1.9k lastcov: 1.1min crash: 0 timeout: 0 cr3: 136372 uptime: 1.2min
#150664 cov: 830 exec/s: 1.9k lastcov: 1.3min crash: 0 timeout: 0 cr3: 150664 uptime: 1.3min
#169338 cov: 830 exec/s: 1.9k lastcov: 1.5min crash: 0 timeout: 0 cr3: 169338 uptime: 1.5min
#184285 cov: 830 exec/s: 1.8k lastcov: 1.6min crash: 0 timeout: 0 cr3: 184285 uptime: 1.7min
#200383 cov: 830 exec/s: 1.8k lastcov: 1.8min crash: 0 timeout: 0 cr3: 200383 uptime: 1.8min

fuzzer_acctest1.cc
#include "backend.h"
#include "crash_detection_umode.h"
#include "targets.h"
#include <fmt/format.h>

namespace fs = std::filesystem;

namespace acctest1 {

constexpr bool LoggingOn = true;

template <typename... Args_t>
void DebugPrint(const char *Format, const Args_t &...args) {
  if constexpr (LoggingOn) {
    fmt::print("acctest1: ");
    fmt::print(Format, args...);
  }
}

bool InsertTestcase(const uint8_t *Buffer, const size_t BufferSize) {
  if (BufferSize < sizeof(uint32_t)) {
    return true;
  }

  const auto tgt = Gva_t(g_Backend->Rcx());
  const auto size = g_Backend->Rdx();
  if (!g_Backend->VirtWriteDirty(tgt, Buffer, 
              BufferSize > size ? size : BufferSize)){
    DebugPrint("Failed to write next testcase!");
    return false;
  }
  //DebugPrint("Testcase written\n");
  return true;

}

bool Init(const Options_t &Opts, const CpuState_t &) {
  //
  // Stop the test-case once we return back from the call [DeviceIoControl]
  //

  const Gva_t Rip = Gva_t(g_Backend->Rip());
  DebugPrint("rip = {:x}, end = {:x}\n", Rip, Rip + Gva_t(0x38));
  const Gva_t AfterCall = Rip + Gva_t(0x38); 
  if (!g_Backend->SetBreakpoint(AfterCall, [](Backend_t *Backend) {
        fmt::print("At the end of the function!\n");
        Backend->Stop(Ok_t());
      })) {
    DebugPrint("Failed to SetBreakpoint AfterCall\n");
    return false;
  }
  if (!g_Backend->SetBreakpoint(Rip + Gva_t(3), [](Backend_t *Backend) {
        fmt::print("At the beginnig of the function!\n");
      })) {
    DebugPrint("Failed to SetBreakpoint at the beginning\n");
    return false;
  }

  if (!SetupUsermodeCrashDetectionHooks()) {
    fmt::print("Failed to SetupUsermodeCrashDetectionHooks\n");
    return false;
  }

  return true;
}

bool Restore() { return true; }

//
// Register the target.
//

Target_t Acctest1("acctest1", Init, InsertTestcase, Restore);

} // namespace acctest1

We can see that:

  • Init() is called, RIP value is correct;
  • the setting of breakpoints does not lead to errors;
  • InsertTestcase() is called on each iteration as well

However, non of the break points is really reached and there is no single userspace address in the aggregate.cov, only kernel code related to page faults.

I tried without any luck:

  • regular dump and full dump;
  • g_Backend->VirtWrite, g_Backend->VirtWriteDirty and even empty InsertTestcase();

Any ideas? My goal is at least to see any usermode addresses in the coverage. Thanks!

PS:
The dump is taken using kdnet -- could that cause the problems? (Pipe memory dump taking seems too slow).
During the dump creating there was a line [bdump] rip and gs don't match kernel/user, swapping... but it looks in there README.md there is that like too. So, I would assume it's ok?
Here is target directory archive, just in case someone wants to check the dump.

invalid attributes on Segment with selector 2b

I tried to fuzz some user-mode application and fuzz cause error with following error:

Initializing the debugger instance.. (this takes a bit of time)
Setting debug register to zero.
Setting debug register status to zero.
Setting debug register status to zero.
Segment with selector 2b has invalid attributes.
SanitizeCpuState failed, no take off today.

state\regs.json ( beautifying for clear verbose ):

{
  "rax": "0x189818dbc00",
  "rbx": "0x189f037cf90",
  "rcx": "0x189f037cf00",
  "rdx": "0x189818dbc00",
  "rsi": "0x189818ddfc0",
  "rdi": "0x189f0396f38",
  "rip": "0x7fff30c50748",
  "rsp": "0x860267fcc8",
  "rbp": "0x0",
  "r8": "0x400",
  "r9": "0x0",
  "r10": "0xfffe6189bee",
  "r11": "0x4401000000000",
  "r12": "0x1",
  "r13": "0x189db75dfd0",
  "r14": "0x189818ddfb0",
  "r15": "0x189db75dfd0",
  "rflags": "0x247",
  "dr0": "0x7fff30c50748",
  "dr1": "0x0",
  "dr2": "0x0",
  "dr3": "0x0",
  "dr6": "0xffff0ff1",
  "dr7": "0x401",
  "es": {
    "present": true,
    "selector": "0x2b",
    "base": "0x0",
    "limit": "0xffffffff",
    "attr": "0xcf3"
  },
  "cs": {
    "present": true,
    "selector": "0x33",
    "base": "0x0",
    "limit": "0x0",
    "attr": "0x22fb"
  },
  "ss": {
    "present": true,
    "selector": "0x2b",
    "base": "0x0",
    "limit": "0xffffffff",
    "attr": "0xcf3"
  },
  "ds": {
    "present": true,
    "selector": "0x2b",
    "base": "0x0",
    "limit": "0xffffffff",
    "attr": "0xcf3"
  },
  "fs": {
    "present": true,
    "selector": "0x53",
    "base": "0x0",
    "limit": "0x3c00",
    "attr": "0x4f3"
  },
  "gs": {
    "present": true,
    "selector": "0x2b",
    "base": "0x8601bc6000",
    "limit": "0xffffffff",
    "attr": "0xcf3"
  },
  "tr": {
    "present": true,
    "selector": "0x40",
    "base": "0xfffff80074ba4000",
    "limit": "0x67",
    "attr": "0x8b"
  },
  "ldtr": {
    "present": false,
    "selector": "0x0",
    "base": "0x0",
    "limit": "0x0",
    "attr": "0x0"
  },
  "tsc": "0xdf71a4edf0",
  "apic_base": "0xfee00900",
  "sysenter_cs": "0x0",
  "sysenter_esp": "0x0",
  "sysenter_eip": "0x0",
  "pat": "0x7010600070106",
  "efer": "0xd01",
  "star": "0x23001000000000",
  "lstar": "0xfffff800752236c0",
  "cstar": "0xfffff80075223200",
  "sfmask": "0x4700",
  "kernel_gs_base": "0xfffff8007380b000",
  "tsc_aux": "0x0",
  "fpcw": "0x27f",
  "fpsw": "0x0",
  "fptw": "0x0",
  "fpst": [
    "0x0",
    "0x0",
    "0x0",
    "0x0",
    "0x0",
    "0x0",
    "0x0",
    "0x0"
  ],
  "mxcsr": "0x1f80",
  "cr0": "0x80050031",
  "cr2": "0x189f0e74000",
  "cr3": "0x1a4ed000",
  "cr4": "0x3506f8",
  "cr8": "0x0",
  "xcr0": "0xe7",
  "gdtr": {
    "base": "0xfffff80074ba5fb0",
    "limit": "0x57"
  },
  "idtr": {
    "base": "0xfffff80074ba3000",
    "limit": "0xfff"
  },
  "mxcsr_mask": "0x0",
  "fpop": "0x0"
}

Is this known issue or am i missing something??

Review the client / server socket code

The last thing I added to wtf was the client / server code. I tested it enough but I am not quite sure if using yaas is the right tool for the job; so I'd like to revisit this when I have some time.

using 32bit registers

When fuzzing wow64 application, can I use 32-bit register (eax, ebx, esp...) values in InsertTestcase function? Only the 64-bit register was declared in backend.h. Should I use it in such a way that I get the x64 register and get only the lower 4 bytes?

Discussion: Bochscpu exit during context switch

Bonjour!

First of all, as mentioned in the subject title, this is more like a discussion rather than a real issue itself as I couldn't find the Discussion tab under this repo :)

Here is the context:

Bochscpu exits deliberately when it detects context switching. This is a good approach as the fuzzer is not interested in capturing the code of other processes, especially you are targeting UM program which can reduce the performance overhead at the same time. However, it could be a limitation when your target is in KM. I have a scenario where the testcase buffer will be queued in kernel's work item, where the separate kernel thread will be invoked, and parse the testcase buffer. I believe this is a common scenario if we are targeting a more complex kernel module. With the current approach, stopping the CPU when context switching is detected, the fuzzer definitely could not reach the actual parsing functions.

Since I'm not familiar with the concept of TLB in-depth and I'm not sure what are the side effects of allowing context switching besides the performance overhead, so I'm checking with you does it make sense to allow CR3 change in BochscpuBackend_t::TlbControlHook (i.e. let the bochscpu continue execution) if the user is meant to target kernel modules?

Thanks!

Could not set a breakpoint at nt!KeBugCheck2.

Hi I tried to run HEVD inside my QEMU/KVM Windows 10 x64 and got an error:

..\..\src\build\wtf.exe fuzz --backend=bochscpu --name hevd --max_len 1028 --limit 10000000
Initializing the debugger instance.. (this takes a bit of time)
Setting debug register status to zero.
Setting debug register status to zero.
Could not set a breakpoint at nt!KeBugCheck2.
Failed to initialize the target

Translation of GVA failed

Hello!

I've been trying to fuzz a target using WTF without success.

As explained in the README file, I got a full snapshot of the target using kd and bdump.js.
I ran the server on a Linux VM and the fuzz node on my Windows host.

On the fuzz node, I get this output:

> wtf.exe fuzz --name BDCore --backend=bochscpu --max_len 1024 --limit 500000  --target C:\path\to\targets\targetname --address tcp://192.168.94.129:31337
Initializing the debugger instance.. (this takes a bit of time)
Setting debug register status to zero.
Setting debug register status to zero.
Dialing to tcp://192.168.94.129:31337..
**Translation of GVA 0x23111a27000 failed**

On the server:

$ ./wtf master --max_len 10485760 --runs=10000000 --target /mnt/data/targets/targetname --address tcp://192.168.94.129:31337
Seeded with 7863979151067873873
Iterating through the corpus..
Sorting through the 1 entries..
Running server on tcp://192.168.94.129:31337..
#0 cov: 0 (+0) corp: 0 (0.0b) exec/s: 0.0 (0 nodes) lastcov: 1.0min crash: 0 timeout: 0 cr3: 0 uptime: 1.0min
#0 cov: 0 (+0) corp: 0 (0.0b) exec/s: -nan (1 nodes) lastcov: 1.0min crash: 0 timeout: 0 cr3: 0 uptime: 1.0min
Could not receive size (-1)
Receive failed
#0 cov: 0 (+0) corp: 0 (0.0b) exec/s: 0.0 (0 nodes) lastcov: 1.1min crash: 0 timeout: 0 cr3: 0 uptime: 1.1min

What Translation of GVA 0x23111a27000 failed means and how can I address this error?

Thank you

Debug break in GetModuleBase

While investigating #82 I ran into this __debugbreak() in GetModuleBase:

  [[nodiscard]] uint64_t GetModuleBase(const char *Name) const {
    uint64_t Base = 0;
    const HRESULT Status =
        Symbols_->GetModuleByModuleName(Name, 0, nullptr, &Base);
    if (FAILED(Status)) {
      __debugbreak();
      Base = 0;
    }

Profile backends with small executions

I've mostly used wtf w/ fairly long executions (thousands of millions / tens of billions of instructions) and so I am less familiar with the performance w/ that kind of workload.

I'd like to have a look at how the server behaves as well as identify potential improvements (small executions means more restores for example).

Could not finish a long circles

I noticed that in some situations WTF simply interrupts the execution of the code, this is especially noticeable in cyclic sections, for example, strlen or copying an array.

Enviroment:
Host - Win10
Target - Hyper-V Win10 (2Gb, 1cpu) lockmem + disable KVA

I used the next command to start server:

..\..\src\build\RelWithDebInfo\wtf.exe master --max_len=500 --runs=50 --target .
Seeded with 17923796412559835467
Iterating through the corpus..
Sorting through the 1 entries..
Running server on tcp://localhost:31337..
#0 cov: 0 (+0) corp: 0 (0.0b) exec/s: -nan (1 nodes) lastcov: 4.0s crash: 0 timeout: 0 cr3: 0 uptime: 4.0s
The corpus is empty, exiting

Fuzz output:

..\..\src\build\RelWithDebInfo\wtf.exe fuzz --backend=bochscpu --name vulnapp --max_len 500 --limit 1000
Initializing the debugger instance.. (this takes about 10 minutes)
DebugCreate
OpenDumpFile
WaitForEvent
WaitForEvent done
Setting debug register status to zero.
Setting debug register status to zero.
VulnApp: Init! Current rip: 0x7ff76550154a
VulnApp: set RET breakpoint
VulnApp: set CrashCatch breakpoint
Dialing to tcp://localhost:31337/..
VulnApp: InsertTestcase try Buffer (123456112324345561236456234123412345612345612345645661235612345634565634563456123456112324345561236456234123412345612345612345645661235612345634565634563456123456112324345561236456234123412345612345612345645661235612345634565634563456123456112324345561236456234123412345612345612345645661235612345634565634563456123456112324345561236456234123412345612345612345645661235612345634565634563456) BufferSize (390)
VulnApp: Try write buffer to stack at 0x4228eff880
VulnApp: Rsp is 0x4228eff850 Stack Frame before BoF:
  0000  c0 7b 50 65 f7 7f 00 00 80 f8 ef 28 42 00 00 00  .{Pe.......(B...
  0010  fe 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  0020  00 00 00 00 00 00 00 00 00 00 2f 34 fc 7f 00 00  ........../4....
  0030  31 32 33 34 35 36 31 31 32 33 32 34 33 34 35 35  1234561123243455
  0040  36 31 32 33 36 34 35 36 32 33 34 31 32 33 34 31  6123645623412341
  0050  32 33 34 35 36 31 32 33 34 35 36 31 32 33 34 35  2345612345612345
  0060  36 34 35 36 36 31 32 33 35 36 31 32 33 34 35 36  6456612356123456
  0070  33 34 35 36 35 36 33 34 35 36 33 34 35 36 31 32  3456563456345612
  0080  33 34 35 36 31 31 32 33 32 34 33 34 35 35 36 31  3456112324345561
  0090  32 33 36 34 35 36 32 33 34 31 32 33 34 31 32 33  2364562341234123
  00a0  34 35 36 31 32 33 34 35 36 31 32 33 34 35 36 34  4561234561234564
  00b0  35 36 36 31 32 33 35 36 31 32 33 34 35 36 33 34  5661235612345634
  00c0  35 36 35 36 33 34 35 36 33 34 35 36 31 32 33 34  5656345634561234
  00d0  35 36 31 31 32 33 32 34 33 34 35 35 36 31 32 33  5611232434556123
  00e0  36 34 35 36 32 33 34 31 32 33 34 31 32 33 34 35  6456234123412345
  00f0  36 31 32 33 34 35 36 31 32 33 34 35 36 34 35 36  6123456123456456
  0100  36 31 32 33 35 36 31 32 33 34 35 36 33 34 35 36  6123561234563456
  0110  35 36 33 34 35 36 33 34 35 36 31 32 33 34 35 36  5634563456123456
  0120  31 31 32 33 32 34 33 34 35 35 36 31 32 33 36 34  1123243455612364
  0130  35 36 32 33 34 31 32 33 34 31 32 33 34 35 36 31  5623412341234561
  0140  32 33 34 35 36 31 32 33 34 35 36 34 35 36 36 31  2345612345645661
Could not receive size (-1)
Receive failed
#1 cov: 12 exec/s: 0.2 lastcov: 4.0s crash: 0 timeout: 1 cr3: 0 uptime: 6.0s

It is difficult to notice here, but if you look at the trace.log, you can see that the thread began copying one array to another and simply interrupted its execution.
The same situation if I tried to execute the strlen circle.

What I'm doing wrong?

KiPageFaultShadow in Usermode Snapshot

I just played around with wtf and wanted to snapshot and fuzz a testing application, basically crashing if four conditions are met. I used a Windows 10 1809 machine from here in the Hyper-V manager.

I did try to follow the blog and README.md to obtain a snapshot of the system in the context of the user-space process. But once I plug the snapshot in the fuzzer, the trace with the bochs-backend only yields nt!KiPageFaultShadow and nt!KiPageFault traces, eventually terminating with a context switch cr3. I assume that somehow the RIP-register in the dump is not valid, but I have no idea how to debug this issue any further.

To exclude any error sources, the fuzzer module does not write any data in InsertTestcase and does not set any breakpoints except for the nt!SwapContext.

Attachments:

Those steps were used to obtain the snapshot:

<break in kd>
kd> !process 0 0 FuzzTest.exe
PROCESS ffffca0be10a2440
    SessionId: 1  Cid: 18cc    Peb: af22c6b000  ParentCid: 0b54
    DirBase: ed590002  ObjectTable: ffff9401f245e640  HandleCount:  33.
    Image: FuzzTest.exe

kd> .process /i /p ffffca0be0f3a440
You need to continue execution (press 'g' <enter>) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.
kd> g
Break instruction exception - code 80000003 (first chance)
nt!DbgBreakPointWithStatus:
fffff800`3adc8240 cc              int     3

kd> .reload /user

kd> lmvm FuzzTest
Browse full module list
start             end                 module name
00007ff6`35940000 00007ff6`35947000   FuzzTest C (private pdb symbols)  C:\ProgramData\Dbg\sym\FuzzTest.pdb\79968922B8174C2CADD54E76582127D71\FuzzTest.pdb
    Loaded symbol image file: FuzzTest.exe
    Image path: C:\Users\IEUser\Desktop\FuzzTest.exe
    Image name: FuzzTest.exe
    Browse all global symbols  functions  data
    Timestamp:        Sun Jul 25 11:46:07 2021 (60FD32DF)
    CheckSum:         00000000
    ImageSize:        00007000
    Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4
    Information from resource tables:

kd> bp FuzzTest!main+0x3f
kd> g

<Let the FuzzTest.exe run until it hits the breakpoint>
Breakpoint 0 hit
FuzzTest!main+0x3f:
0033:00007ff6`3594111f 803b74          cmp     byte ptr [rbx],74h
kd> .scriptload c:\Users\user\Downloads\bdump.js

kd> !bdump_active_kernel "c:\\Users\\user\\Downloads\\dump2"
[bdump] creating dir...
[bdump] saving regs...
[bdump] register fixups...
[bdump] don't know how to get mxcsr_mask or fpop, setting to zero...
[bdump]
[bdump] don't know how to get avx registers, skipping...
[bdump]
[bdump] tr.base is not cannonical...
[bdump] old tr.base: 0x3d74d000
[bdump] new tr.base: 0xfffff8003d74d000
[bdump]
[bdump] setting flag 0x2000 on cs.attr...
[bdump] old cs.attr: 0x2fb
[bdump] new cs.attr: 0x22fb
[bdump]
[bdump] rip and gs don't match kernel/user, swapping...
[bdump] rip: 0x7ff63594111f
[bdump] new gs.base: 0xa61452b000
[bdump] new kernel_gs_base: 0xfffff8003ab3a000
[bdump]
[bdump] non-zero IRQL in usermode, resetting to zero...
[bdump] saving mem, get a coffee or have a smoke, this will probably take around 10-15 minutes...
[bdump] Creating c:\Users\user\Downloads\dump2\mem.dmp - Active kernel and user memory bitmap dump
[bdump] Collecting pages to write to the dump. This may take a while.
[bdump] 0% written.
[bdump] 5% written. 1 min 9 sec remaining.
[...]
[bdump] 95% written. 3 sec remaining.
[bdump] Wrote 2.5 GB in 1 min 10 sec.
[bdump] The average transfer rate was 35.8 MB/s.
[bdump] Dump successfully written
[bdump] done!
@$bdump_active_kernel("c:\\Users\\user\\Downloads\\dump2")

Start of the tracer:

c:\Users\user\Downloads\dump2>c:\Users\user\Downloads\wtf\src\build\RelWithDebInfo\wtf.exe    run   --name fuzzer_test   --backend bochscpu   --state "c:\Users\user\Downloads\dump2\state"   --input "c:\Users\user\Downloads\dump2\inputs"   --trace-path "C:\Users\user\Downloads\dump2\traces"   --trace-type rip
Initializing the debugger instance.. (this takes a bit of time)
Setting debug register status to zero.
Setting debug register status to zero.
Trace file C:\Users\user\Downloads\dump2\traces\in.trace
Running c:\Users\user\Downloads\dump2\inputs\in
--------------------------------------------------
Run stats:
Instructions executed: 1091 (846 unique)
          Dirty pages: 28672 bytes (0 MB)
      Memory accesses: 4101 bytes (0 MB)
#1 cov: 846 exec/s: 1.0 lastcov: 0.0s crash: 0 timeout: 0 cr3: 1 uptime: 1.0s

Revisit on-demand paging with whv

If I remember correctly, I wasn't able to implement on-demand paging with whv because doing a WHvTranslateGva with an empty environment doesn't generate memory faults like executing would.

This is annoying because wtf does memory translation to be able to set breakpoints where the user wants to; to do that it needs to do a VirtTranslate so I ended up mapping it all at start-up. It's not ideal for obvious reasons but I'm not too sure how to work around that. Just want to revisit this & do the due diligence to make sure that it works as I described and that I didn't miss anything.

bochscpu

Hi,

I read part of the code and got interested to play a bit with bochscpu, I went to https://github.com/yrp604/bochscpu-build but that seem to be different than what you used in this project!

Like some functions like bochscpu_cpu_new() and many others that you used in this repository doesn't exist there!

    bochscpu_cpu_t Cpu_ = nullptr;
    Cpu_ = bochscpu_cpu_new(0);

Can you please give me some clue on where I can start using bochscpu and what is the different between what is used in this project and the one in yrp604/bochscpu-build?

Thanks,
Nemo

bochscpu backend not setting breakpoints

When using the bochscpu backend, breakpoints requested within the Init callback are seemingly not being set. They are being set when I use the whv backend, but for whatever reason with bochs they are both not being set and not returning false; (g_backend-SetBreakpoint).

Is there something I am missing? I modified the hevd sample harness and it all seemed pretty straight forward...

I can attach my project files, but here is the snippet of how the breakpoints are being set, its the same as the hevd test harness.

  if (!g_Backend->SetBreakpoint(Gva_t(0x7FF72DE25E40), [](Backend_t *Backend) {
        DebugPrint("Back from kernel!\n");
        Backend->Stop(Ok_t());
      })) {
    DebugPrint("Failed to SetBreakpoint AfterCall\n");
    return false;
  }

  //
  // NOP the calls to DbgPrintEx.
  //

  if (!g_Backend->SetBreakpoint("nt!DbgPrintEx", [](Backend_t *Backend) {
        const Gva_t FormatPtr = Backend->GetArgGva(2);
        const std::string &Format = Backend->VirtReadString(FormatPtr);
        DebugPrint("DbgPrintEx: {}", Format);
        Backend->SimulateReturnFromFunction(0);
      })) {
    DebugPrint("Failed to SetBreakpoint DbgPrintEx\n");
   return false;
  }

enhancement suggestion for network protocol fuzzing

Hi.

During fuzzing campaign, I tried to fuzz some network component with wtf and I felt a necessary for persistence option.

Some network protocols requires multi inputs for initialize and processing instead one big testcase like file format.
I tried to split single testcase and create custom format for fuzzing but instead of that, I think it will be better if I can receive testcase from server on Breakpoint handler even multiple times in single execution.

I want to contribute for this part but I dunno where to look at. Do you have recommend for where should I look at or any other ideas for network protocol fuzzing?

Sincerely.

Tenet, empty line

@gaasedelen got a tenet trace with an empty line; according to the code it should happen if @rip doesn't change; figure out if this is ok behavior and if it is, only output a new line if we actually wrote anything.

broken emulation on specific circumstance

Backend : bochscpu
trace creation : bochscpu
Dump VM environment: Hyper-V Gen1 VM, 1 CPU, 4GB RAM


During when I tried to fuzzing some windows kernel component, I encounter some page fault.

Animation
( 0xFFFFF8056CC05000 is nt!KiPageFault )

nt!MmAccessFault's return value was 0x110( =STATUS_PAGE_FAULT_TRANSITION ). I don't know why transition fault was happened at there but I didn't care much about that because it executes normally after that transition fault.

But the real problem happened when transition fault occured on conditional jump instruction.
Animation2

Transition fault occured on FFFFB1929ADE6483 but saved return address is FFFFB1929AE964E7.
It is a next insturction after jz, so FFFFB1929AE964E7 will be executed after execution context restored from interrupt...and it was executed exactly as below.

Animation3

As you can see, emulator is broken and it execute as 2-byte aligned.

0xfffff8056cc05638 <- end of nt!KiPageFault( iretq )
0xffffb1929ae964e7 <- instruction of fuzzed kernel component...and 2 byte aligned execution for unknown reason
0xffffb1929ae964e9
0xffffb1929ae964eb
0xffffb1929ae964ed
0xffffb1929ae964ef
0xffffb1929ae964f1
0xffffb1929ae964f3
0xffffb1929ae964f5
0xffffb1929ae964f7
0xffffb1929ae964f9
0xffffb1929ae964fb
0xffffb1929ae964fd
0xffffb1929ae964ff

I tried to catch that circumstance using SetBreakpoint on nt!KiPageFault but I couldn't find any method to get previous instruction address on breakpoint handler, so I will be very pleased if you let me know any good method for that.

Investigate spdylog

A lot of debug output is gated behind ifdef; this makes it annoying to turn back on / off as you need to recompile every time.

It'd be great to have something that you can turn on / off at runtime & spdylog might be a nice library to use for that.

WHvRunVpExitReasonInvalidVpRegisterValue

@und3ath shared with me a dump that wouldn't load under whv with the following error code:

WHvRunVirtualProcessor exited with WHvRunVpExitReasonInvalidVpRegisterValue

I've dealt a lot with those in the past and, in my experience, it's usually a segment register being not right or the xcr0 register. After looking at the regs.json file I noticed this:

    "ss": {
        "present": true,
        "selector": "0x18",
        "base": "0x0",
        "limit": "0x0",
        "attr": "0x493"
    },

Setting a proper limit fixes the issue:

diff --git a/state/regs.json b/state/regs.json
index ad6760c..11d215e 100644
--- a/state/regs.json
+++ b/state/regs.json
@@ -35,7 +35,7 @@
         "present": true,
         "selector": "0x18",
         "base": "0x0",
-        "limit": "0x0",
+        "limit": "0xffffffff",
         "attr": "0x493"
     },
     "ds": {

I'm filling this issue in case other people run into this (including me when I forget all about it) ๐Ÿ˜

Cheers

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.