winafl icon indicating copy to clipboard operation
winafl copied to clipboard

An additional parameter when running WinAFL to synchronize the coverage with DynamoRIO

Open TwoSevenOneT opened this issue 9 months ago • 5 comments

As far as I know, DRCOV only captures a basic block when that block is executed for the first time, whereas WinAFL has a counter to track the number of times a block is executed. This leads to certain cases with complex programs, although the number of basic blocks executed with the same input remains the same, the number of times each block is called may differ. This results in the warning "Instrumentation output varies across runs", preventing WinAFL from executing persistence mode with the harness. If there were an additional parameter for WinAFL to disable the counter (similar to DRCOV, counting only on the first execution), it could potentially resolve the aforementioned cases. However, this might impact the effectiveness of fuzzing.

TwoSevenOneT avatar Mar 10 '25 14:03 TwoSevenOneT

If you wanted to avoid counters, you could change the instrumentation used by WinAFL to use an or instruction instead the inc instruction.

However, this is likely not the reason for "Instrumentation output varies across runs" warning. Rather, a more likely reason for that warning is persistent mode fuzzing itself, which executes target function multiple times without restarting the process. For example, some structure might not be initialized the first time target function is called so initialization code would run, but this code wouldn't run the second time a function is called. Or if some array gets filled it might need to be reallocated etc.

But also, I don't think "Instrumentation output varies across runs" warning prevents anything, my understanding is that it's just a warning that can be ignored.

For comparison, Jackalope deals with such behavior by splitting newly discovered coverage into two sets: "stable" coverage, which doesn't vary across sample run and "variable" coverage which is present in some but not all runs with a given sample. A sample is only saved in the queue if it contains at least one basic block of "stable" coverage.

ifratric avatar Mar 10 '25 15:03 ifratric

Hi Ivan,

I encounter some cases where cmin.py occasionally executes successfully in dry-run mode, while at other times it fails, resulting in the message "Tuples matching? False". However, even when the dry-run with cmin.py executes successfully, I still receive the warning "Instrumentation output varies across runs" during fuzzing. At this point, WinAFL continuously restarts the harness, leading me to speculate that the issue may stem from the way basic blocks are counted.

I often observe this unstable execution case when fuzzing COM objects. Even when I include both the COM Init and COM Un-Init functions in the fuzzed function, the instability persists. I suspect it may be due to some functionality executing asynchronously or some other unknown reason.

I have tried using Jackalope, and occasionally the fuzzer crashes on its own, even when I run it in "dumb mode".

Could you show me the code snippet in WinAFL where I can experiment with replacing 'or' with 'inc'?

TwoSevenOneT avatar Mar 10 '25 23:03 TwoSevenOneT

I have tried using Jackalope, and occasionally the fuzzer crashes on its own, even when I run it in "dumb mode".

When a fuzzer crashed, was there an error message?

Could you show me the code snippet in WinAFL where I can experiment with replacing 'or' with 'inc'?

For DynamoRIO mode, you could replace occurrences of INSTR_CREATE_inc in https://github.com/googleprojectzero/winafl/blob/master/winafl.c with another instruction (or of the memory operand and 1), but I haven't done any DR work in a while so don't remember the specifics.

For TinyInst mode, the corresponding inc instruction is emitted here https://github.com/googleprojectzero/winafl/blob/master/tinyinst_covmap.cpp#L97 or here https://github.com/googleprojectzero/winafl/blob/master/tinyinst_covmap.cpp#L114 depending if 32-bit or 64-bit mode. In TinyInst you just write x86 instructions directly using the WriteCode call.

ifratric avatar Mar 11 '25 12:03 ifratric

Now I can execute with Jackalope. Just as you said: "stable" coverage, which doesn't vary across sample runs, and "variable" coverage, which is present in some but not all runs with a given sample. This might be the reason why I often receive the warning "Input sample has no new stable coverage" every time I execute Jackalope.

The last time I crashed, I immediately shut down the system and went to sleep, so I don't remember the crash info clearly.

When a fuzzer crashed, was there an error message?

I will experiment with modifying the code using WinAFL later. For now, if I encounter the case where DRCOV != WinAFL counter, I'll switch to Jackalope.

TwoSevenOneT avatar Mar 11 '25 23:03 TwoSevenOneT

In Jackalope, "Input sample has no new stable coverage" (if it's not the case that the coverage from this sample is already fully covered by some previous sample) could also mean for example that

  • An additional module doing sample processing should be instrumented
  • Sample is, for some reason, not reaching instrumented module.
  • An error in the harness where some state is not properly cleared between persistent runs, so processing will error out and return too early

ifratric avatar Mar 12 '25 08:03 ifratric