AFL-unicorn is well explained here:
https://www.youtube.com/watch?v=OheODvF0884
And the source code is here:
https://github.com/jdbirdwell/afl
Installation is well described here:
https://hackernoon.com/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf
Just git clone and make:
and followed by build_unicorn_suport.sh:
Here I will describe how I run AFL unicorn on the following C program:
#include <stdio.h>
int main(){
int i=0, j=0;
for(i=200;i>100;i++) {
j=i+1;
i=j-3;
//printf(“%dn”, i);
}
return i;
}
First compile it and disassemble it using IDA Pro:
Here we can see that at address 0x4004DA we have setup the first breakpoint (in RED).
Next is to setup the debugger:
After there that we click on “Start Process” to enable it to run inside the IDA Pro envionment.
At the breakpoint where it stopped, we capture the process state via “process dumper”: In IDA Pro top menu bar: “File”->”Script File” and select “unicorn_dumper_ida.py” and here is the output:
Notice the directory where the dumper direct the CPU state to. And inside this directory there is a file “_index.json” which record all the CPU/process memory information:
Notice from the above the value of “rip”, which is where we set the breakpoint in IDA Pro to dump out the process information. Convert this “rip” value to hexadecimal and it will be the same as the “START_ADDRESS” to be used in the Unicorn emulator script.
And so from there just this directory and the input files are all that is needed – the binary and its memory requirements are already captured indie the binary informatin.
From AFL-unicorn_battele copy the file “template_test_harness.py” and modify several things:
a. Since it is x86 64-bit we are going to emulate, therefore we use x86_const.py (which can be seen as part of download from https://github.com/unicorn-engine/unicorn)
b. Generally if is just non-supervisor mode (for ARM) or ring 3 for x86 there is no need to change CPU mode during emulation. But for emulating ring 0 or syscall, it is more complicated and slower, and thus it is preferred that we should emulate by hand-supplying the return results from making these syscalls. This is why when the assembly called functions like malloc(), or free(), or open(), or read() etc – all these has to be emulated, as shown below (notice it is for 32-bit x86 architecture, as compared with the present case which is 64-bit x86) from the original author (but not done here):
https://github.com/unicorn-engine/unicorn/issues/694
https://github.com/unicorn-engine/unicorn/issues/1048
https://www.reddit.com/r/linux/comments/48fted/unicorn_the_ultimate_cpu_emulator/
c. Next is to allocate the stack, heap or any memory as required at the start point of emulation. If it is stack, then the stack pointer (EBP/RBP or ESP/RSP) has to point to the end of the memory block allocated (stack_addr+512).
d. Lastly is the loop that read the next assembly instruction and emulate it:
(Notice the “START_ADDRESS” above? It is the same value we have highlighted earlier) And the full python script is here below:
https://gist.github.com/tthtlc/3b9fff8e0e4c016fd5cb09a78dc94d2d
Overall execution in debug mode(-d):
For the integration of this python script with AFL-fuzzer – this is not done as the C program has to be modified to receive input through a file mechanism.
References:
More tricks or caveats for Unicorn engine emulation:
https://www.reddit.com/r/linux/comments/48fted/unicorn_the_ultimate_cpu_emulator/
How Unicorn can be used to emulate interrupt processing:
https://github.com/unicorn-engine/unicorn/issues/376
https://github.com/unicorn-engine/unicorn/issues/825
The fuzzing internals are explained here:
https://hackernoon.com/afl-unicorn-part-2-fuzzing-the-unfuzzable-bea8de3540a5
And AFL-unicorn itself is based on the Unicorn and Capstone Engine:
https://www.capstone-engine.org
https://github.com/unicorn-engine/unicorn
https://news.ycombinator.com/item?id=9989609
https://news.ycombinator.com/item?id=11437962
http://eternal.red/2018/unicorn-engine-tutorial/
https://hackmd.io/s/rJTUtGwuW#
You must be logged in to post a comment.