EKLAVYA -- 利用神經(jīng)網(wǎng)絡(luò)推斷二進(jìn)制文件中函數(shù)的參數(shù)

EKLAVYA -- 利用神經(jīng)網(wǎng)絡(luò)推斷二進(jìn)制文件中函數(shù)的參數(shù)

這一次介紹一篇文章,名為Neural Nets Can Learn Function Type Signatures From Binaries

來自于新加坡國立大學(xué)Zhenkai Liang團(tuán)隊(duì),發(fā)在了Usenix Security 2017上

問題介紹以及形式化定義

該工作主要關(guān)注的問題是函數(shù)的參數(shù)推斷,包含兩個(gè)部分:

  • 參數(shù)的個(gè)數(shù)
  • 參數(shù)的類型,比如int, float等

傳統(tǒng)方法通常會使用一些先驗(yàn)知識,將指令的語義ABI慣例 (Application Binary Interface),編譯器的風(fēng)格等進(jìn)行編碼。

一旦編譯器發(fā)生了改變,指令集合發(fā)生了改變,那么我們就需要重新引入一些先驗(yàn)知識。

如果我們可以擺脫,或者說是減少這些先驗(yàn)知識的利用,那么就不會受限了!

那么,使用神經(jīng)網(wǎng)絡(luò)來進(jìn)行自動(dòng)化的學(xué)習(xí)和推斷,就是一種思路了。

前提假設(shè)

  • 我們首先能知道一個(gè)函數(shù)的邊界 (boundary)
  • 在一個(gè)函數(shù)內(nèi)部,我們知道它的指令邊界
  • 我們知道代表一個(gè)函數(shù)調(diào)用(function dispatch)的指令,比如call

通過反匯編工具,我們可以滿足上述假設(shè)。

值得一提的是,函數(shù)邊界也可以使用神經(jīng)網(wǎng)絡(luò)來做,有興趣的讀者可以參考 Dawn Song 發(fā)在Usenix Security 2015 的 Recognizing functions in binaries with neural networks.

這里,首先給出一些符號的定義:

  • 我們定義我們的模型為 M(\cdot)

  • 定義函數(shù) a 反匯編出來的代碼為 T_a ,T_a[i] 代表函數(shù) a 的第 i 個(gè)字節(jié)

  • 函數(shù) a 的第 k 條指令可以被寫成 I_a[k]:= <T_a[m], T_a[m+1],...,T_a[m+l]>

    • 其中 m 是對應(yīng)指令的起始字節(jié)的位置索引
    • l 是該指令所包含的字節(jié)數(shù)
  • 一個(gè)包含 p 條指令的函數(shù) a 可以被表示為 T_a:=<I_a[1],I_a[2],I_a[p]>

  • 如果一個(gè)函數(shù) b 有一個(gè)直接調(diào)用 call 對于函數(shù) a, 我們將該條call指令之前的所有指令拿出來,稱為 caller snippet,可譯為調(diào)用者片段。定義為 C_{b,a}[j]:=<I_b[0],...,I_b[j-1]>

    • 其中 I_b[j] 對應(yīng) call 函數(shù) a 的那一條指令
    • 如果 I_b[j] 是一個(gè)間接調(diào)用,我們令 C_{b, a }[j] := null
  • 我們會收集函數(shù) a 的所有調(diào)用者的調(diào)用者片段,記為 \mathcal{ D }_a:=T_a\cup(\bigcup_{b\in S_a}(\bigcup_{0\leq j\leq |T_b|}C_{a,b}[j]))

    • 其中 S_a 是調(diào)用 a 的所有函數(shù)的集合

由于調(diào)用者片段的長度可能非常長,這里文章設(shè)置為不超過500條指令

我們的函數(shù) M(\cdot) 接受輸入 \mathcal{ D }_a , 輸出兩個(gè)變量:

  • 函數(shù) a 的參數(shù)個(gè)數(shù)
  • 函數(shù) a 的每一個(gè)參數(shù)的類型
    • C-風(fēng)格的參數(shù)類型可以被定義為 int, char, float, void*, enum, union, struct

方法設(shè)計(jì)

我們這里先直接給出整體的方法流程圖:

image-20211004214934260

簡單來說,可以劃分成兩個(gè)模塊:

  • 指令編碼模塊
    • 首先,將輸入的二進(jìn)制文件進(jìn)行函數(shù)抽取、指令的分割、調(diào)用點(diǎn)的抽取。
    • 然后對指令進(jìn)行Word Embedding編碼,得到對應(yīng)的向量表示,這一部分可以參照 NLP 中的 Word2Vec。
  • 參數(shù)還原模塊
    • 將這些數(shù)據(jù)切分成訓(xùn)練集和測試集,分別使用4個(gè)遞歸神經(jīng)網(wǎng)絡(luò)(RNN)來從兩個(gè)方面(調(diào)用者和被調(diào)用者)推斷函數(shù)參數(shù)的個(gè)數(shù)以及類型,也就是對應(yīng)上圖中的4個(gè)任務(wù)(Task2,Task4 對應(yīng)被調(diào)用者,Task1,Task3對應(yīng)調(diào)用者)。

究竟是怎么推斷多個(gè)參數(shù)類型的呢?

一個(gè)RNN,輸入一個(gè)序列,只能推斷出來一個(gè)類型。

所以這篇文章的實(shí)現(xiàn)是,訓(xùn)練多個(gè)RNN,每一個(gè)RNN獨(dú)立推斷固定位置的參數(shù)類型。

先用一個(gè)RNN推斷出來參數(shù)個(gè)數(shù),然后分別使用多個(gè)RNN來推斷不同的位置的參數(shù)。

數(shù)據(jù)準(zhǔn)備

該文章使用一些linux的包,然后使用clang和gcc來進(jìn)行編譯,通過設(shè)定debug模式,就可以直接在binary中的DWARF字段找到對應(yīng)函數(shù)邊界、參數(shù)個(gè)數(shù)以及類型,作為ground truth。

構(gòu)建了兩個(gè)數(shù)據(jù)集:

  • 數(shù)據(jù)集1: 包含了3個(gè)流行的linux包(binutils,coreutils 以及 findutils), 使用了O0到O3的優(yōu)化等級進(jìn)行編譯
  • 數(shù)據(jù)集2: 將數(shù)據(jù)集1包含的linux包進(jìn)行擴(kuò)展,多增加5個(gè) (sg3utils, utillinux, inetutils, diffutils 和 usbutils), 也在4個(gè)優(yōu)化等級上進(jìn)行編譯

訓(xùn)練集和測試集的劃分比例為8:2

不平衡的數(shù)據(jù)

在數(shù)據(jù)集的構(gòu)造中,會出現(xiàn)不同類的數(shù)據(jù)比例相距甚遠(yuǎn)的情況。比如參數(shù)為pointer類型的數(shù)據(jù)就是union類型的數(shù)百倍,大部分函數(shù)都是少于3個(gè)參數(shù)。該文章中并沒有解決這個(gè)問題。

實(shí)驗(yàn)結(jié)果

image-20211004232753476

這里貼出來Task1和Task2,也就是通過調(diào)用者和被調(diào)用者,推斷參數(shù)個(gè)數(shù)的結(jié)果

可以看到:

  • 優(yōu)化級別越高,越難推斷,但并沒有嚴(yán)格的遞增關(guān)系
  • 參數(shù)個(gè)數(shù)越多,越難推斷,和訓(xùn)練數(shù)據(jù)量也有關(guān)系
  • 從調(diào)用者方面,更容易推斷出來參數(shù)個(gè)數(shù)
image-20211004233105566

上面是,關(guān)于參數(shù)類型推斷的結(jié)果

可以看到:

  • 優(yōu)化級別似乎干擾性不強(qiáng),甚至優(yōu)化級別越高,推斷類型越精確
  • 參數(shù)的位置越靠后,越難推斷出來了類型
  • 從調(diào)用者和被調(diào)用者兩方面來推斷,差別不是很大
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容