说是移植AFL,其实是移植android-afl,因为AFL的插桩代码都是汇编写的,原版都是ATT汇编,要直接写ARM汇编有点难度,android-afl里有这部分内容,所以改起来容易一些。但是Android和普通Linux还是有一些区别的,我用的是树莓派3B,Raspberry系统。
首先修改的是插桩部分的代码,在afl-as.c中的add_instrumentation_arm函数里,里面的插桩条件需要修改,一开始我只改了:
1 | if (!strncmp(line, "\t.fnstart", 9)) { |
因为我看在pi上用gcc -S vuln.c -o vuln.s编译出来的汇编文件中没有.fnstart和.fnend,就参照原版改成了.text和.section,然后就可以插桩了,我以为这样就可以了,结果发现far from it。。
后来我修改edit_params()里的modified_file,将AFL编译的汇编文件保存在当前目录,看到这个汇编文件有一千多行,比gcc编译的多了好多,不知道这些多的代码是AFL里哪些代码插入的,后来又修改了afl-gcc文件,打印编译时的选项,发现多了-g
、-O3
、-funroll-loop
等。所以在编译汇编文件的时候加上了这些选项,编译出来的汇编文件就有一千多行了,只是还没有插桩,因此就对照这个汇编文件修改afl-as.c里的插桩条件的代码,这次修改之后就可以正确插桩了。
还有一个修改的地方是afl-as.h里的插桩trampoline代码和payload代码,跳板代码里有一个movw指令,afl-gcc编译目标代码的时候会出错,查了一下mov指令只能把小于0x100的立即数放到寄存器,大于0x100就可能出错,所以这里用的movw,但是编译不过,于是又找到了一个替代的方法,用ldr r0, =#%u
方法,由编译器选择一个内存位置存放立即数,然后从内存中加载立即数到寄存器。
payload中的错误原因是Android的共享内存使用的是匿名共享内存,和普通Linux上使用方式有区别,找到这个错误是在运行afl-fuzz时fuzzer进程和fork server不能正常通信。在Android上获取共享内存的方法external/fio/os/os-android.h
1 | static inline void *shmat (int __shmid, const void *__shmaddr, int __shmflg) |
修改方法就是将payload的代码改成用shmat()获取共享内存。