本文主要內容包括:WinAFL介紹與安裝、以ABC看圖程序為例構建Fuzz最小案例庫、執(zhí)行Fuzz跑出Crash,以及最終利用Bugid對Crash分類
WinAFL介紹與安裝WinAFL,是Ivan Fratric基于lcumtuf的AFL創(chuàng)建的大型Fuzzing程序,由于AFL無法在windows下直接使用,Winafl彌補了這一空白,使用DynamoRIO來插樁&測量代碼覆蓋率,并使用Windows API進行內存和進程創(chuàng)建。
WinAFL 項目地址:https://github.com/ivanfratric/winafl
(資料圖)
請注意:Windows 10 1809及更高版本的最新Windows版本要使用DynamoRIO 8.0.0以上版本
此時如果直接進行操作可能會出現(xiàn)以下報錯
需要re-compiled ,這個過程為:
(1) 下載安裝DynamoRio源碼,或者直接下載DynamoRio Windows版的二進制包(https://github.com/DynamoRIO/dynamorio/wiki/Downloads)
(2) 打開Visual Studio命令提示工具,如果要安裝成64位版本的則打開Visual Studio x64命令提示工具(一般在【開始—所有程序—Visual Stdio—Visual Studio Tools】中可找到)。因為在對64位程序進行fuzz時,需要有64-bit的winafl.dll,所以安裝時要選擇好版本
(3)在命令提示工具中進入WinAFL的目錄下
(4) 在Visual Studio命令提示工具中輸入如下命令進行WinAFL編譯安裝(需將-DDynamoRIO_DIR參數(shù)設置為你的DynamoRIO cmake文件所在位置)
32-bit build:
mkdir build32cd build32cmake -G"Visual Studio 16 2019" -A Win32 .. -DDynamoRIO_DIR=..\path\to\DynamoRIO\cmake cmake --build . --config Release
64-bit build:
mkdir build64cd build64cmake -G"Visual Studio 16 2019" -A x64 .. -DDynamoRIO_DIR=..\path\to\DynamoRIO\cmakecmake --build . --config Release
這里需要注意一下-G選擇平臺時VS16與之前版本默認目標平臺架構是有些區(qū)別的:
cmake -G "Visual Studio 16 2019" -A Win32 ;x32cmake -G "Visual Studio 16 2019" -A x64 ;x64 默認目標平臺名稱(架構)為Win64cmake -G "Visual Studio 15 2017" ;x32 默認目標平臺名稱(架構)為Win32cmake -G "Visual Studio 15 2017 Win64" ;x64
winafl 命令行參數(shù),主要分為三段,(afl執(zhí)行參數(shù)–dynamoRIO執(zhí)行參數(shù)–程序執(zhí)行參數(shù))
afl執(zhí)行參數(shù)主要包括-i -o指定輸入和輸出文件夾
-D指定DynamoRIO根目錄
-t每一次樣本執(zhí)行的超時時限
-ffuzz 程序讀取的位置
-M \ -S分布式模式
-x可選的fuzz字典
dynamRIO執(zhí)行參數(shù)主要包括-coverage_module計算覆蓋率的模塊
-fuzz_iterations在重新啟動目標進程之前,目標函數(shù)要運行的最大迭代次數(shù)。
-target_module包含目標函數(shù)的模塊(一個可執(zhí)行文件鏡像)需要與該選項一起指定-target_method或-target_offset
-target_method目標函數(shù),需要export或者帶符號
-target_offset目標偏移,相對于target_module的偏移,在method無法導出的時候使用
-nargs程序執(zhí)行所需要的參數(shù)個數(shù)
-debug調試模式。不要嘗試連接到服務器。輸出包含已加載模塊,打開的文件和覆蓋率信息的日志文件
-logdir指定將日志文件寫入哪個目錄(僅與-debug一起使用)
程序執(zhí)行參數(shù)就是要fuzz的程序的命令行構建Fuzz最小案例庫現(xiàn)在我們從網(wǎng)上搜集一堆ABC看圖支持的格式,包括tif、jpg、png、ico等,github上有許多Fuzz的案例庫,這樣的案例庫中包含大量的文件,運行起來效率會很差。根據(jù)學長博客,AFL是存在語料庫蒸餾(Corpus Distillation)工具的,afl-cmin和afl-tmin。
移除執(zhí)行相同代碼的輸入文件——AFL-CMINafl-cmin的核心思想是:嘗試找到與語料庫全集具有相同覆蓋范圍的最小子集。舉個例子:假設有多個文件,都覆蓋了相同的代碼,那么就丟掉多余的文件。減小單個輸入文件的大小——AFL-TMIN整體的大小得到了改善,接下來還要對每個文件進行更細化的處理。afl-tmin盡量縮減文件體積。在winafl中,他們存在于 winafl-cmin.py,對輸入的樣本文件進行最小化處理,以用來提高 WinAFL 的執(zhí)行效率。
篩選命令
python winafl-cmin.py --working-dir C:\Users\test\Desktop\winafl-master\build32\bin\Release -D C:\Users\test\Desktop\DynamoRIO-Windows-8.0.0-1\bin32 -t 100000 -i C:\Users\test\Desktop\jpg -o C:\Users\test\Desktop\jpg\out -coverage_module FreeImage.dll -target_module Project1.exe -target_method main -nargs 2 -- C:\Users\test\source\repos\Project1\Release\Project1.exe @@
此時可能出現(xiàn)[!] Dry-run failed, 2 executions resulted differently:Tuples matching? False的報錯
看來測試用例中存在一些壞的用例,導致不能正確精簡,在語料庫所在文件夾可以利用以下bash腳本簡單判斷一下
λ for file in *; do printf "==== FILE: $file =====\n";/c/Users/test/source/repos/Project1/Release/Project1.exe $file ;echo $?; done
正常運行的文件返回值都是0,有問題的文件返回結果都不太正常
把這些返回結果不太正常的刪除之后,再運行一次語料庫蒸餾,發(fā)現(xiàn)運行成功了
可以看到原本429張被精簡到了148張,確實少了不少,根據(jù)參考教程中的提示,Winafl在處理大于4Kb的圖片時,速度會變得很慢,因此再刪除一波,最終語料庫就剩下這么點了。
開始運行經過動態(tài)和靜態(tài)的簡單分析后,發(fā)現(xiàn)ABC看圖主要調用Freeimage.dll進行圖片解析的,決定對 FreeImage 庫的載入函數(shù)進行模糊測試,針對 FreeImage_LoadU 函數(shù)編寫測試程序
#define _CRT_SECURE_NO_WARNINGS#include
在對該程序進行編譯之后,可以先簡單測試一下WinAFL 是否可以正常使用。-debug 表示設置為調試模式。
\winafl\bin32> C:\Users\test\Desktop\DynamoRIO-Windows-8.0.0-1\bin32\drrun.exe -c winafl.dll -debug -coverage_module FreeImage.dll -target_module Project1.exe -target_method main -fuzz_iterations 10 -nargs 2 -- C:\Users\test\source\repos\Project1\Debug\Project1.exe C:\Users\test\Desktop\jpg\1x1-low.jpg
如下圖所示,日志文件當中模塊加載正常并沒有錯誤顯示
下面就要開始模糊測試了,按照教程進行的,但是它的目標函數(shù)是main,我們來看看結果如何。
afl-fuzz.exe -i C:\Users\test\Desktop\jpg\out -o C:\Users\test\Desktop\jpg\re -D C:\Users\test\Desktop\DynamoRIO-Windows-8.0.0-1\bin32 -t 9000 -- -coverage_module FreeImage.dll -target_module Project1.exe -target_method main -fuzz_iterations 5000 -nargs 2 -- C:\Users\test\source\repos\Project1\Debug\Project1.exe @@
如圖所示,可以跑是可以跑,但是這個速度實在太慢了,執(zhí)行main函數(shù)浪費了太多時間,實際上我們的load函數(shù)只測test函數(shù)就可以,我們將-target_method改為test嘗試一下
afl-fuzz.exe -i C:\Users\test\Desktop\jpg\out -o C:\Users\test\Desktop\jpg\re -D C:\Users\test\Desktop\DynamoRIO-Windows-8.0.0-1\bin32 -t 9000 -- -coverage_module FreeImage.dll -target_module Project1.exe -target_method test -fuzz_iterations 5000 -nargs 2 -- C:\Users\test\source\repos\Project1\Debug\Project1.exe @@
然后就蹦框了,看起來是沒有找到我們寫的test函數(shù),忘了在vs里把test函數(shù)也導出
這里導出后再編譯一遍,再嘗試一下,成功了,這速度明顯提升了好幾倍啊
為了讓fuzz效率更高一點,充分利用cpu的多核,進行多核系統(tǒng)的并行測試
afl-fuzz.exe -i C:\Users\test\Desktop\jpg\out -o C:\Users\test\Desktop\jpg\re -M master -D C:\Users\test\Desktop\DynamoRIO-Windows-8.0.0-1\bin32 -t 9000 -- -coverage_module FreeImage.dll -target_module Project1.exe -target_method test -fuzz_iterations 5000 -nargs 2 -- C:\Users\test\source\repos\Project1\Debug\Project1.exe @@afl-fuzz.exe -i C:\Users\test\Desktop\jpg\out -o C:\Users\test\Desktop\jpg\re -S slaver01 -D C:\Users\test\Desktop\DynamoRIO-Windows-8.0.0-1\bin32 -t 9000 -- -coverage_module FreeImage.dll -target_module Project1.exe -target_method test -fuzz_iterations 5000 -nargs 2 -- C:\Users\test\source\repos\Project1\Debug\Project1.exe @@afl-fuzz.exe -i C:\Users\test\Desktop\jpg\out -o C:\Users\test\Desktop\jpg\re -S slaver02 -D C:\Users\test\Desktop\DynamoRIO-Windows-8.0.0-1\bin32 -t 9000 -- -coverage_module FreeImage.dll -target_module Project1.exe -target_method test -fuzz_iterations 5000 -nargs 2 -- C:\Users\test\source\repos\Project1\Debug\Project1.exe @@
因為我的配置比較垃圾只有四核,所以就開這么多了,我們先試一下
扔到ABC看圖里,發(fā)現(xiàn)真的崩潰了
利用Bugid對Crash分類這樣一堆crash,里面肯定有不少重復的,原因也有各不相同,如何對他們進行快速分類并找到問題點呢?
在這里我使用了BugID,它可以反饋崩潰和死機的可利用性的詳細報告,BugID安裝所需要的環(huán)境如下:
最新的Python 2.7.14Windows的最新調試工具最新的BugId版本如果使用默認設置安裝Windows的Python和調試工具,則BugId應該能夠運行而無需調整任何設置。您可以在本地文件系統(tǒng)上任意位置解壓縮BugId
但是理論上BugID需要一個一個進行文件分析,而Crash這么多,只是就可以寫一個Python腳本來幫助我們
import sysimport ossys.path.append(r"C:\Users\test\Desktop\BugId-master")testcases = []for root, dirs, files in os.walk(r"C:\Users\test\Desktop\jpg\re\slaver01\crashes", topdown=False): for name in files: testcase = os.path.abspath(os.path.join(root, name)) testcases.append(testcase)for testcase in testcases: print ("[*] Gonna run: ", testcase) os.system(r"PageHeap.cmd "Project1.exe" ON") os.system(r"python C:\Users\test\Desktop\BugId-master\BugId.py C:\Users\test\source\repos\Project1\Debug\Project1.exe --isa= x86 -- %s" % testcase)
請注意在最后的程序后面加上--isa= x86哦,不加默認作為64位調試會報錯
運行腳本之后,我們就看到源源不斷的bug信息出來了
最后可以寫到一個文檔里保存下來,看起來字符好像有點問題,不過問題不大
這只是簡要信息,BugID在運行時已經自動生成了較為詳細的分析報告,打開BugID目錄就可以看到
隨便打開一個看看,很詳細
參考文獻winafl使用 http://www.simp1e.site/2020/04/18/winafl/
模糊測試工具WinAFL使用指南 https://www.freebuf.com/articles/system/216437.html
初識 Fuzzing 工具 WinAFL https://paper.seebug.org/323/
Fuzz 工具 WinAFL 的使用感受 https://bbs.pediy.com/thread-255162.htm
Fuzzing the MSXML6 library with WinAFL https://symeonp.github.io/2017/09/17/fuzzing-winafl.html
標簽: