references:
- https://bushido-sec.com/index.php/2023/06/25/the-art-of-fuzzing-windows-binaries/
windows下的闭源二进制文件灰盒测试,所谓闭源,就是说我们没有待测试二进制文件的源代码,闭源测试很麻烦,所以搞这个的人比较少,然后能他人之所不能,即是我们自身价值的体现,进行这种类型的模糊测试,我们需要克服以下挑战:
- 插桩
- 定位值得被fuzz的函数
- 修改二进制文件使得其能够被fuzz
我们将会使用WinAFL作为我们的fuzz工具,WinAFL提供了三种插桩方式:
- DynamoRIO,在二进制运行过程中对代码进行修改
- Syzygy,静态插桩,在二进制编译阶段修改代码
- Intel PTrace,硬件追踪,这个是CPU的一个特性,可以以异步的方式跟踪程序执行流
这三种方式各有优劣,我们将主要使用DynamoRIO,DynamoRIO和WinAFL的配合流程如下图所示:
编译WinAFL
-
- 下载DynamoRIO,最新版本二进制文件下载地址
-
- 如果你需要Intel PTrace支持,你需要在WinAFL源代码目录下执行如下命令:
git submodule update --init --recursive
- 如果你需要Intel PTrace支持,你需要在WinAFL源代码目录下执行如下命令:
-
- 打开VS Command Prompt(VS 2022)
-
- 进入WinAFL源码目录
-
- 输入如下命令进行构建:
mkdir build64 cd build64 cmake -G "Visual Studio 17 2022" -A x64 .. -DDynamoRIO_DIR=C:\Users\x\Downloads\DynamoRIO-Windows-11.90.20133\cmake -DINTELPT=1 -DUSE_COLOR=1 cmake --build . --config Release
寻找要Fuzz的目标
一个比较有效的方法是在ZDI网站上寻找目标,特别是那种以前出过洞且仍然处于活跃状态的项目
修改程序,使得程序可以被fuzz
有些程序会有一些烦人的弹窗,这个时候我们就需要把这些弹窗给patch掉,省得他影响我们的fuzz工具
这里有一个示例程序,解压密码是bushido
这个双击运行之后有一个弹窗,我们需要patch掉这个弹窗,在ida里面打开这个程序,搜索字符串Click Yes or No
我们就直接把这个函数的第一个字节修改成c3,让他直接返回就行了,反正他的caller也不检查返回值
选中要修改的地址,直接patch,然后点击Apply patches to input file
应用修改即可
我好像不能直接这样改,我这样改好像把他的栈给损坏了,但是我又不知道我是怎么损坏了他的栈的,这x86的程序好操蛋啊,我都不知道他的calling convention是啥
写了个测试程序,x86的calling convention是这个样子的
说白了,就是倒序push
观察这个地方
可以看到,在call完test函数之后,会使用add esp, 0x18
来修复栈,因为我们前面push了6个dword进去,一个DWORD是4bytes,6个正好就是0x18 bytes,我们之前直接把函数修改为ret导致没有人修复栈,所以在最后main函数中返回的时候就崩溃了
不对啊,我修改的是里面按理说对外面的代码是没有影响的呀卧槽
我刚才又仔细调试了一下示例代码,发现我们patch的程序修复了栈,所以我们直接把这个程序修改为ret,会导致最终的main函数在返回时崩溃,所以我们需要换一种patch方式
我们可以将这个函数中的call指令全部修改为nop指令,我靠,这么改也不行,还是会崩溃
仔细观察该函数,发现他最后的返回指令是
就是这条指令修复了栈,我们直接从函数开始的地方复改为者3个字节即可
这下可以了,不崩溃了,栈平衡了,我们再来看一下原作者是怎么patch的
原作者是直接把call 401000替换成nop了,他这样肯定会在返回的时候崩溃,因为这个函数负责平衡栈,没有了这个函数栈就不可能是平衡的
我试了一下,确实是会崩溃