• 沒有找到結果。

第二章 設計與實作

2.4 緩衝區的轉換

保護機制若是藉修改程式碼引入,通常時間點可分為編譯前、編譯時 和編譯後這三種。第一種使用重寫程式碼工具,將保護碼插入到程式中,

再將重寫過後的程式碼交由原來的編譯器編譯。第二種修改編譯器,經修 改後的編譯器編譯出的可執行檔將含有保護碼。第三種修改編譯出的目的 檔(object file)或可執行檔。引入保護機制的時間越早,能得到越多的緩衝區 資訊,並越具有可攜性,保護碼也可經過越多的最佳化處理。相反地,時 間越晚雖然在程式執行動作上越清楚,最佳化處理得以改由保護機制掌 握,但是與其他程式或函式庫的相容性將會降低。

除了上面三種時間點以外,藉由靜態連結(static linking)或動態載入 (dynamic loading)亦是一個簡單可行的方法,只要有一個與緩衝區相關的進 入點,即可透過該進入點提供保護,而不需要修改原來的程式。下面小節 敘述BODAR 如何提供各種緩衝區保護。

2.4.1 堆積緩衝區的轉換

因為程式可攜性的關係,大部份堆積緩衝區的配置與釋放都是透過 malloc/realloc/free這類C 標準函式,故我們可以重新實作這類函式,

由靜態連結或動態載入修改過後的函式庫達到將新函式取代舊函式的目 的。動態載入可透過現今作業系統普遍支援的預載(preloading)特性[3],藉 環境變數LD_PRELOAD 或定義檔/etc/ld.so.preload 定義可動態載入的函式 庫,使引入更為容易與彈性。

2.4.2 堆疊緩衝區的轉換

導入BODAR 到堆疊緩衝區或靜態緩衝區不是像堆積緩衝區那麼容 易。由於堆疊緩衝區的配置僅需改變CPU 框架指標暫存器(frame pointer register),程序執行時完全不會呼叫任何配置函式;為了要導入 BODAR 到 這類非堆積緩衝區中,我們採用程式碼重寫技術,轉換所有的堆疊緩衝區 為堆積緩衝區並且不影響程式的語意。重寫過後再重新經由編譯器編譯,

應用程式將可以使用我們的恢復技術在堆疊緩衝區溢位上。

我們用來做轉換的方法主要是根據Dahn 與 Mancoridis 的研究[8],這 篇論文為了要避免堆疊緩衝區溢位攻擊,提出轉換所有的堆疊緩衝區為堆 積緩衝區的方法。他們的方法中使用了TXL[32]去處理程式碼重寫。TXL 是一個混合規則導向(rule-based)與函式導向(functional)的程式語言,特別適 合用在程式碼的重寫。為了要能執行C 程式的重寫,我們需要 C 的 TXL 文法定義和規則說明如何重寫程式碼。前者可在TXL 的網站上找到,但後 者就只能自己定義了。另外我們也利用Perl 編寫能夠自動做重寫動作的工 具,使得BODAR 在應用時能更方便。

Original code Modified code void foo() { void foo() {

int buf[64]; int *buf=(int*)malloc(64*sizeof(int));

bzero(buf,sizeof(buf)); bzero(buf,64*sizeof(int));

... ...

} free(buf);

}

圖2-4 堆疊緩衝區修改前後的程式碼

圖2-4 顯示如何轉變堆疊緩衝區為堆積緩衝區。在原來的程式碼中,

buf是在一個函式foo中的陣列即是堆疊緩衝區。在重寫過的程式中,buf 被修改成一個指標,並且以malloc做初始化,其大小將同於原先的陣列 大小。要注意的是原來的buf並不需要有回收的程式碼,因為在函式回傳 時將自動釋放該緩衝區。但相反的,在修改過的程式碼中,緩衝區並不會 自動被釋放,因此我們必須在函式回傳前加入釋放的程式碼,確保記憶體 使用正確。

在C 語言中的 sizeof敘述必須小心處理,原來程式碼中的

sizeof(buf)會傳回陣列的位元組長度32*4,但轉換過後 buf變為一個指 標,sizeof(buf)只會回傳指標的位元組長度4,故我們必須以呼叫malloc 時傳入的大小取代原來的sizeof(buf)敘述。

2.4.3 靜態緩衝區的轉換

靜態緩衝區的配置是在編譯階段就決定了配置的位址與空間,載入時 實際將空間配置給緩衝區。為了要將靜態緩衝區轉換為堆積緩衝區,我們 將靜態緩衝區宣告全部改為指標,在程式開始的時候才進行配置。

根據ELF(Executable Language Format)的定義,可執行檔中的建構區段 CTOR 與解構區段 DTOR 存放許多函式指標。程式開始時會先執行去 CTOR 中的函式,之後才會呼叫標準程式進入點main,當程式結束後會去呼叫 DTOR 中的函式。實作上可透過 GCC[12]所定義的建構解構函式來插入函 式指標至CTOR/DTOR 區段。以下為建構解構函式的宣告原型:

建構函式原型:

void ctor() __attribute__ ((constructor));

解構函式原型:

void dtor() __attribute__ ((destructor));

經由建構解構函式,我們可以將所有的靜態緩衝區改宣告為指標,而 在建構函式中才透過malloc配置 BODAR 保護的緩衝區,最後透過解構函 式將這些緩衝區釋放(free)。此外,在程式中出現的 sizeof 以前一小節所 述相同方式處理。圖2-5 顯示如何轉變靜態緩衝區為堆積緩衝區。

Original code Modified code int buf[64]; int *buf;

void bodar_ctor() __attribute__ ((constructor));

void bodar_ctor() {

buf=(int*)malloc(64*sizeof(int));

}

void bodar_dtor() __attribute__ ((destructor));

void bodar_dtor() { free(buf);

}

圖2-5 靜態緩衝區修改前後的程式碼

相關文件