天天看點

mpg321 堆溢出分析過程1 前言3 調試過程

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 資訊:

mpg321 堆溢出分析過程1 前言3 調試過程

使用gdb調試 coredump,得到崩潰時的調用棧:

mpg321 堆溢出分析過程1 前言3 調試過程

從崩潰的調用棧處,看到的資訊是調用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結果如下:

mpg321 堆溢出分析過程1 前言3 調試過程

這裡顯示的結果是:

在 0x530602處 向記憶體 0x602...398 開始處寫入8位元組

但是 0x602....398處于配置設定的記憶體區域[...390 ,...398)偏移0個位元組的地方

意思就是配置設定了8位元組的記憶體,但是在寫的時候,寫到第9位元組去了。

這個輸出并不是很友好,看不出在哪裡配置設定,哪裡寫入的,繼續檢視AddressSanitizer的使用說明,發現通過導出 ASAN_SYMBOLIZER_PATH環境變量後,可以看到更具體的符号資訊。

導出環境變量後得到的結果如下:

mpg321 堆溢出分析過程1 前言3 調試過程

這樣我們得到配置設定記憶體的代碼和寫入記憶體代碼的位置:

配置設定記憶體:

mpg321 堆溢出分析過程1 前言3 調試過程

寫入記憶體:

mpg321 堆溢出分析過程1 前言3 調試過程

通過檢視源碼上下文,猜測在配置設定時,配置設定了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 指令單步模式,并打開彙編代碼顯示:

mpg321 堆溢出分析過程1 前言3 調試過程

發現 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