Inspiration to create a fuzzing harness for the latest version of FoxitReader’s ConvertToPDF function (currently 9.7) came from discovering Richard Johnson’s fuzzer for a previous version on FoxitReader (found here: https://www.cnblogs.com/st404/p/9384704.html)
Multiple changes have since been introduced into the way FoxitReader converts an image to a PDF, including the introduction of new vtable entries, the necessity to load in the main FoxitReader.exe binary (including fixing the IAT and modifying data sections to contain valid handles to the current process heap) + more.
The source for my version of the fuzzing harness targeting version 9.7 can be found on my github: https://github.com/Kharos102/FoxitFuzz9.7
Below is a quick walkthrough of the reversing and coding I had to do to get this harness working.
First, based on the existing work from the previous fuzzers available we know that most of the calls for the conversion of an image to a PDF occur via vtable function calls from an object returned from ConvertToPDF_x86!CreateFXPDFConvertor, however this could also be gleamed manually by breaking on file read access to the image we supply as a parameter to the conversion function and walking the call stack.
So first thing I decide to do is analyse how the actual FoxitReader.exe process sets up objects required for the conversion function, first I set a breakpoint for the CreateFXPDFConvertor function
Next I step out and set a breakpoint on all the vtable function pointers for the returned object, this way I can discover in which order these functions are called alongside their parameters as this will be necessary to setup the object before calling the actual conversion functionality.
We know how to view the vtable as the pointer to the vtable is the first 4-bytes (32bit) when dumping the object.
During this process we notice multiple differences compared to the older versions of FoxitReader, including changes to existing function prototypes and also the introduction of new vtable functions that require to be called.
After executing and noting the details of execution, we hit the main conversion function from the vtable of our object, here we can analyse the main parameter (some sort of conversion buffer structure) by viewing its memory and noting its contents.
First we see the initial 4-bytes are a pointer to an offset within the FoxitReader.exe image:
This means our harness will have to load the FoxitReader image in-memory to also supply a valid pointer (we also have to fix it’s IAT and modify the image too, as we discover after testing the harness).
Then we continue noting down the buffer’s contents, including the input file path at offset +0x1624, the output file path at offset +0x182c, and more (including a version string and other little bits we must fill in the buffer).
Finally after the conversion the object is released and the buffer is freed.
After noting all the above we can make a harness from the information discovered and give it a test.
Issues arose shortly afterward, including exceptions in FoxitReader.exe that I loaded into memory, this was due to import table addresses not being resolved in the FoxitReader binary that were being called during conversion, to fix this we manually fix the import table of the loaded FoxitReader binary.
Additionally, calls to HeapAlloc were occurring where the heap handle was obtained via an offset into the FoxitReader binary, however since the binary wasn’t properly run (as it would be in CreateProcess) the offset did not contain a valid handle value for the heap, to overcome this I simply wrote the value of the current process heap handle into the offset.
Overall the process was not long, due to the existence of a fuzzer for an older (albeit different) version and the simplicity of the conversion function’s requirements.
The difference in executions per second fuzzing the conversion functionality against this harness vs not using a harness (e.g. spawning foxitreader, performing conversion, closing foxitreader, rinse&repeat) is huge, and the fuzzing harness integrates well with WinAFL :).