1 前言
在使用Vuzzer重制其論文中的實驗時,對mpg321的fuzzing發現三個crash,其中一個為segementation fault.下文為對這個segmentation fault的分析過程。
2 環境準備
mpg321編譯:
mpg321_0.3.2.orig.tar.gz (-g -O0編譯)
依賴庫編譯安裝:
- libao-1.2.0(-g -O0編譯)
- libmad-0.15.1b(-g -O0編譯)
- libid3tag-0.15.1b (-g -O0編譯)
- libasound2-dev (apt-get install)
ubuntu 14.04 X64
3 調試過程
A coredump調試
首先配置core dump 記錄 crash的 coredump 資訊。
$: ulimit -c unlimited (不限制core檔案大小)
使用crash input重新運作程式,得到coredump 資訊:
使用gdb調試 coredump,得到崩潰時的調用棧:
從崩潰的調用棧處,看到的資訊是調用calloc配置設定84位元組記憶體的時候導緻了segmentataion fault。從調用棧追蹤,看不出什麼問題。
在網上搜尋了關于calloc導緻segmentaion fault,大部分都是由于在程式的其他地方堆被破壞,然後在後續配置設定記憶體的時候導緻出錯。推薦的heap check 工具為 gperftools http://https://github.com/gperftools/gperftools。
gperftools配置安裝後使用沒有什麼發現(安裝使用可能還有問題,沒有繼續研究),改用 LLVM的 AddressSanitizer(安裝LLVM...)
LLVM 的安裝源:http://apt.llvm.org/
B 堆溢出調試
在使用LLVM ASAN編譯mpg321時,發現了一件奇怪的事情,使用-O0 -O1優化,clang都無法識别libmad庫中的一個函數,而使用-O2卻可以。。。
編譯好之後,運作crash input結果如下:
這裡顯示的結果是:
在 0x530602處 向記憶體 0x602...398 開始處寫入8位元組
但是 0x602....398處于配置設定的記憶體區域[...390 ,...398)偏移0個位元組的地方
意思就是配置設定了8位元組的記憶體,但是在寫的時候,寫到第9位元組去了。
這個輸出并不是很友好,看不出在哪裡配置設定,哪裡寫入的,繼續檢視AddressSanitizer的使用說明,發現通過導出 ASAN_SYMBOLIZER_PATH環境變量後,可以看到更具體的符号資訊。
導出環境變量後得到的結果如下:
這樣我們得到配置設定記憶體的代碼和寫入記憶體代碼的位置:
配置設定記憶體:
寫入記憶體:
通過檢視源碼上下文,猜測在配置設定時,配置設定了8個位元組的記憶體,在使用時,通路到第九個位元組。
使用gdb調試下斷點,在mad.c:289行處,變量current_frame為1,而playbuf->frames存儲的元素為指針,在X64系統中,指針大小為8位元組,current_frame為1,通路到的是第二個指針,即playbuf->frames開始處第九個位元組。如果配置設定了8位元組記憶體,這裡的通路就越界了。
再使用gdb調試,在配置設定記憶體處下斷點,得到malloc的參數為8,即配置設定了8位元組記憶體。驗證了上面的猜想。
從源碼看,如果這裡配置設定了8位元組記憶體,那是不是playbuf.num_frames值為0,然後malloc((playbuf.num_frames + 1) * sizeof(void*))配置設定8位元組記憶體? 但是 在寫入記憶體時,判斷了 current_frame
繼續調試,看看playbuf.num_frames值到底是是什麼。
這裡,使用gdb 指令單步模式,并打開彙編代碼顯示:
發現 playbuf.num_frames 的值為0x8000000000000000
lea 0x8(,%rax,8) %rdi 這裡 %rax即為 playbuf.num_frames
也就是(playbuf.num_frames + 1)*8 在彙編層面,是(playbuf.num_frames*8 + 8) 而playbuf.num_frames為 unsigned long 類型,playbuf.num_frames*8 之後溢出為0,最後導緻malloc配置設定了8位元組記憶體。
但是在寫入記憶體處,current_frame也是 unsigned long類型,current_frame