天天看點

Fuzz概述AFL一些概念AFL調試準備

文章目錄

  • AFL一些概念
    • 插樁與覆寫率
      • 邊和塊
      • 覆寫率
        • afl自實作劫持彙編器
        • clang内置
    • 覆寫率回報與引導變異
      • 遺傳算法
      • fork server機制
  • AFL調試準備

AFL一些概念

插樁與覆寫率

邊和塊

首先,要明白邊和塊的定義

Fuzz概述AFL一些概念AFL調試準備

正方形的就是塊,箭頭表示邊,邊表示程式執行哪一條分支

覆寫率

程式和fuzzer執行在不同程序,要通過共享記憶體的方式進行程序間通信。共享記憶體即在多個程序之間共享,每個index索引對應一條邊,執行次數增加,邊的數值就會增加

各個邊如何映射到索引?為每個代碼塊配置設定一個随機值,然後計算前一個代碼塊和目前代碼塊的随機值的異或值,寫入共享記憶體索引,即代表走到哪條邊,執行什麼樣的分支。代碼塊之間的執行邏輯是怎樣的,執行次數是怎樣的。

cur_location = <COMPILE_TIME_RANDOM>;//目前位置
shared_mem[cur_location ^ prev_location]++; //将目前位置和前一個位置異或,得到一個索引,将共享記憶體中該索引對應的計數器加一,表示該位置被執行了。
prev_location = cur_location >> 1;//将目前位置右移一位,作為下一次計算的前一個位置。
           

通過這種方式,AFL 可以統計出每個輸入檔案在程式中到達了哪些位置,進而幫助程式找到更多的路徑,提高覆寫率。同時由于使用了随機數初始化,也提高了運作時的覆寫率統計随機性,避免了測試過程中出現重複的路徑覆寫。

afl自實作劫持彙編器

在将程式轉換為二進制代碼的過程中。可以劫持彙編器。識别其中的跳轉指令,插入彙編指令(即以上一小段的邊映射為核心的代碼),通過afl-gcc/afl-clang/afl-g++實作

afl-gcc會通過設定環境變量的方式,添加一些必要的參數和宏定義,以及設定一些搜尋路徑和連結選項。然後會将實際地連結任務交給gcc,也就是說afl-gcc僅僅是一層wrapper

[email protected]-virtual-machine:~/Desktop/afl$ ./afl-gcc ../test.c -o test
afl-cc 2.52b by <[email protected].com>
	arg0: gcc
	arg1: ../test.c
	arg2: -o
	arg3: test
	arg4: -B
	arg5: .
	arg6: -g
	arg7: -O3

           

clang内置

運用clang編譯的時候,llvm計算出一個edge集合,每條edge對應一個guard指針

__sanitizer_cov_trace_pc_guard(uint32_t* guard)
/*執行時機: 每當對應的edge被執行到的時候,就會執行這個函
數,向共享記憶體裡寫值。寫入的值就是 *guard ,即guard指針指向的值,就是為每個邊
插入的随機值*/
void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {
  __afl_area_ptr[*guard]++;
}
__sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop)
/*每個邊對應着一個guard指針指向的值,start是第一個guard
指針,代表第一條邊,stop是最後一個guard指針,代表最後
一條邊,周遊start到stop,就可以給每個guard指針指向的值
初始化一個随機數。*/
/*afl-llvm-rt.o.c裡while周遊,然後賦一個随機數*/
while (start < stop) {
/*分别指向覆寫記錄數組的起始位址和結束位址。通過一個 while 循環,逐個周遊數組中的元素。*/

    if (R(100) < inst_ratio) *start = R(MAP_SIZE - 1) + 1;//給guard指針賦一個随機數
    else *start = 0;

    start++;

  }
           

AFL 會以一定的機率(由 inst_ratio 決定)為其賦一個随機數,或者指派為 0。這裡的随機數是指在 1 和 MAP_SIZE - 1 之間的一個随機整數。在程式運作時,這個随機數會用于輔助判斷目前指令是否已經被執行過。

如果 guard 上的值不是 0,則認為這個指令是第一次執行。在第一次執行之後,__sanitizer_cov_trace_pc_guard()會将該 guard 上的值置為 0,表示該指令已經執行過了。在後續執行過程中,如果該指令再次被執行,對應的 guard 位置上的值已經是 0,不會再次被記錄為覆寫資訊。是以,通過這種方法,AFL 可以輔助判斷目前指令是否已經被執行過。

GCOV和LCOV主要是程式員統計代碼覆寫率使用,優點是能可視化展示,不用于fuzzer

覆寫率回報與引導變異

遺傳算法

當使用afl時,需要提供一個種子,從所有種子樣本進行變異。即位元組流變異,翻轉等。

從一個A輸入變成B,C,D,E等各種不一樣的輸入。如果從A到B變異的輸入發現了不同路徑。就把這個B記錄下來,稱為有趣樣例(interesting case)

逐代雜交選優,達到一個局部更優,解因為遺傳算法的特征總是來自于初始種子樣本和變異政策,是以改進也主要在這兩方面進行改進

fork server機制

fuzzer變異生成樣本後,寫入到執行的檔案(.cur_input裡)

通過管道通知fork server要進行一次fuzz,fork server會fork出一個子程序去

執行這個檔案,并通過管道傳回子程序的執行結果的傳回值,通知fuzzer。

Fuzz概述AFL一些概念AFL調試準備

如圖所示,程式在fuzz一個檔案時,先把他卡在初始化完成,但讀入資料之前

int main()
{
    read();
}
           

相當于卡在程式的read之前,當再次進行fuzz的時候,并不需要重新再把這個程式執行一遍。隻想在read這裡開始執行

通知fork server去fork一個子程序,讓子程序讀取.cur_input,開始一次執行,執行完後會把程式執行的資訊傳回給fuzzer

AFL調試準備

用clion打開AFLcpp

Fuzz概述AFL一些概念AFL調試準備

第一行,填寫我們的輸入的參數,首先是輸入輸出,以及最大配置設定記憶體以及逾時時間、分隔後填入我們@@讀檔案,會寫入.cur_input裡,如果沒有@@就是從标準輸入中讀

-i
/home/ubuntu20/Desktop/AFLcpp/test_dir/fuzz_input
-o
/home/ubuntu20/Desktop/AFLcpp/test_dir/fuzz_output
-m
none
-t
500+
--
/home/ubuntu20/fuzz/out/fuzzbuild
@@
           

繼續閱讀