劉絮穎, 尹 青, 蔣烈輝, 劉建林
(解放軍信息工程大學(xué)信息工程學(xué)院,河南鄭州450002)
目前,遺產(chǎn)軟件的理解、維護(hù)和移植等工作變得日益重要,可執(zhí)行代碼的逆向分析成為當(dāng)前研究的熱點(diǎn)問題[1-2]。反編譯將低級(jí)代碼轉(zhuǎn)換為功能和語義上等價(jià)的高級(jí)語言代碼形式,是代碼逆向分析中的關(guān)鍵部分[3-5]。高級(jí)控制結(jié)構(gòu)恢復(fù)是反編譯的重要組成部分,恢復(fù)出的高級(jí)控制結(jié)構(gòu)信息用于后期的高級(jí)語言代碼生成,恢復(fù)出的高級(jí)控制結(jié)構(gòu)信息的準(zhǔn)確性與完備性直接影響反編譯結(jié)果的準(zhǔn)確性[6-7]。
針對高級(jí)控制結(jié)構(gòu)恢復(fù)問題,Cifuentes提出了啟發(fā)式算法,理論基礎(chǔ)是圖論和編譯技術(shù)[8-10];Moretti等人提出了歸納算法[11-12]來識(shí)別分支指令,區(qū)間方法識(shí)別循環(huán),但這些方法需要使用復(fù)雜Interval/DSG等數(shù)據(jù)結(jié)構(gòu),復(fù)雜數(shù)據(jù)結(jié)構(gòu)會(huì)降低算法運(yùn)行效率,且該方法在編譯優(yōu)化的情形下無法區(qū)分相鄰分支語句,無法處理循環(huán)分支中的復(fù)合條件;Kaspersky提出了手工分析復(fù)合條件分支的方法[13],該方法與Moretti所提的方法類似,但其自動(dòng)化實(shí)現(xiàn)還存在很多困難。經(jīng)典高級(jí)控制結(jié)構(gòu)恢復(fù)算法在識(shí)別高級(jí)控制結(jié)構(gòu)的正確性、效率等方面各有優(yōu)勢,但是在解決高級(jí)控制結(jié)構(gòu)嵌套關(guān)系的問題上都沒有提出較好的解決方案。為了解決高級(jí)控制結(jié)構(gòu)嵌套關(guān)系的問題,本文提出了結(jié)構(gòu)語義樹的概念,在進(jìn)行控制流圖結(jié)構(gòu)化之后,構(gòu)建結(jié)構(gòu)語義樹,從而得到高級(jí)控制結(jié)構(gòu)的嵌套關(guān)系信息。最后通過前序遍歷結(jié)構(gòu)語義樹,則可恢復(fù)過程的高級(jí)控制結(jié)構(gòu)。準(zhǔn)確恢復(fù)過程的高級(jí)控制結(jié)構(gòu)對提高反編譯結(jié)果的正確性,代碼語義與功能的等價(jià)性等方面具有重要意義。
定義1 基本塊[14-15]b=[i1,…,in-1,in],n≥1是一個(gè)滿足下列條件的指令序列:
[i1,…,in-1]∈NTI
in∈NTI
或
[i1,…,in-1,in]∈NTI
in+1是另一個(gè)基本塊的第一條指令。
其中NTI是指非轉(zhuǎn)移類指令。
由上述定義可知一個(gè)基本塊就是一個(gè)連續(xù)的單入口單出口的指令序列,如果基本塊內(nèi)一條指令被執(zhí)行,那么其它指令也會(huì)被執(zhí)行。一個(gè)程序的指令集合能夠從程序的入口點(diǎn)開始被唯一地分成一組相互不重疊的基本塊。
定義2 控制流圖CFG[14-15]是一個(gè)有向圖,可定義為三元組G=(N,E,h),其中N是結(jié)點(diǎn)集合,結(jié)點(diǎn)n∈N代表一個(gè)基本塊,E是有向邊集合,如果在控制流圖中結(jié)點(diǎn)m的語句能夠到達(dá)結(jié)點(diǎn)n的語句,則邊(m,n)在E中,其中n∈N,m∈N,h是該有向圖的頭結(jié)點(diǎn)。
定義3 前序遍歷[14-15]是指對圖的遍歷過程中,每個(gè)結(jié)點(diǎn)n的處理早于其后裔ne,入口結(jié)點(diǎn)h最先處理。前序遍歷不唯一。
定義4 復(fù)合結(jié)點(diǎn)nc代表一個(gè)高級(jí)語言中的高級(jí)控制結(jié)構(gòu),是結(jié)構(gòu)語義樹中子樹的父結(jié)點(diǎn),其孩子結(jié)點(diǎn)是由構(gòu)成該高級(jí)控制結(jié)構(gòu)的結(jié)點(diǎn)構(gòu)成的,這些孩子結(jié)點(diǎn)可以是葉子結(jié)點(diǎn)nl也可以是復(fù)合結(jié)點(diǎn)nc,如果是復(fù)合結(jié)點(diǎn)nc,則說明這兩個(gè)高級(jí)控制結(jié)構(gòu)之間存在嵌套關(guān)系,每個(gè)結(jié)點(diǎn)屬于且僅屬于一個(gè)復(fù)合結(jié)點(diǎn)。
定義5 結(jié)構(gòu)語義樹SST是一個(gè)二元組(N,E),其中N是結(jié)點(diǎn)集合,結(jié)點(diǎn)n∈N可以是葉結(jié)點(diǎn)nl或者復(fù)合結(jié)點(diǎn)nc,其中葉結(jié)點(diǎn)nl對應(yīng)的是控制流圖中的基本塊,復(fù)合結(jié)點(diǎn)代表一個(gè)高級(jí)控制結(jié)構(gòu),E是樹的邊,邊e∈E表示高級(jí)控制結(jié)構(gòu)由哪些孩子結(jié)點(diǎn)構(gòu)成。
基于結(jié)構(gòu)語義樹的高級(jí)控制結(jié)構(gòu)恢復(fù)框架如圖1所示。高級(jí)控制結(jié)構(gòu)恢復(fù)框架由控制流圖結(jié)構(gòu)化模塊,結(jié)構(gòu)語義樹構(gòu)建模塊,高級(jí)控制結(jié)構(gòu)恢復(fù)模塊3個(gè)模塊組成。輸入控制流信息,控制流圖結(jié)構(gòu)化模塊對目標(biāo)代碼的控制流圖進(jìn)行結(jié)構(gòu)化,獲得目標(biāo)程序的高級(jí)控制結(jié)構(gòu)信息,結(jié)構(gòu)語義樹構(gòu)建模塊根據(jù)這些高級(jí)控制結(jié)構(gòu)信息對控制流圖進(jìn)行遍歷構(gòu)建控制流圖的結(jié)構(gòu)語義樹,高級(jí)控制結(jié)構(gòu)恢復(fù)模塊對構(gòu)建成功的結(jié)構(gòu)語義樹進(jìn)行前序遍歷恢復(fù)目標(biāo)代碼的高級(jí)控制結(jié)構(gòu),生成結(jié)構(gòu)化的結(jié)果文件。
降序反向后序遍歷控制流圖,采用先結(jié)構(gòu)化n路條件結(jié)構(gòu),再結(jié)構(gòu)化循環(huán)結(jié)構(gòu),最后結(jié)構(gòu)化二路條件結(jié)構(gòu)的順序?qū)刂屏鲌D進(jìn)行結(jié)構(gòu)化??刂屏鲌D結(jié)構(gòu)化過程要進(jìn)行三遍,在整個(gè)結(jié)構(gòu)化過程中每識(shí)別出一種高級(jí)控制結(jié)構(gòu),就將該結(jié)構(gòu)的頭結(jié)點(diǎn)、所對應(yīng)的循環(huán)關(guān)閉結(jié)點(diǎn)或者二路分支后隨結(jié)點(diǎn)、N路分支后隨結(jié)點(diǎn),以及各種高級(jí)控制結(jié)構(gòu)的類型進(jìn)行標(biāo)記,最后得到具有標(biāo)記信息的控制流圖。
結(jié)構(gòu)語義樹構(gòu)建模塊對前期得到的含有標(biāo)記信息的控制流圖進(jìn)行降序反向后序遍歷,每遍歷一個(gè)結(jié)點(diǎn)就根據(jù)其是否含有標(biāo)記信息對其進(jìn)行區(qū)分,如果是葉子結(jié)點(diǎn)就直接加入結(jié)構(gòu)語義樹中,如果是復(fù)合結(jié)點(diǎn),還要再根據(jù)其標(biāo)記信息采用不同方式加入到結(jié)構(gòu)語義樹中??刂屏鲌D遍歷結(jié)束后得到能夠表達(dá)目標(biāo)代碼高級(jí)控制結(jié)構(gòu)以及高級(jí)控制結(jié)構(gòu)關(guān)系的結(jié)構(gòu)語義樹。
如圖2所示,結(jié)構(gòu)語義樹采用類似鄰接表的數(shù)據(jù)結(jié)構(gòu)來表示,主要包括頂點(diǎn)結(jié)點(diǎn)vexnode,邊表結(jié)點(diǎn)edgenode。
圖2 結(jié)構(gòu)語義樹的數(shù)據(jù)結(jié)構(gòu)
程序的結(jié)構(gòu)化信息包括結(jié)構(gòu)模式和非結(jié)構(gòu)模式,結(jié)構(gòu)模式又包括順序模式,條件模式和循環(huán)模式,非結(jié)構(gòu)模式主要包括非結(jié)構(gòu)循環(huán)和非結(jié)構(gòu)條件。為了使得反編譯的結(jié)果可讀性更好,在進(jìn)行高級(jí)控制結(jié)構(gòu)恢復(fù)時(shí)只考慮結(jié)構(gòu)模式,對于非結(jié)構(gòu)模式的情況使用goto語句來代替。
高級(jí)控制結(jié)構(gòu)恢復(fù)模塊對構(gòu)建成功的結(jié)構(gòu)語義樹進(jìn)行左優(yōu)先前序遍歷,生成結(jié)構(gòu)化的中間語言代碼,其中可以包括多種高級(jí)控制結(jié)構(gòu),如if-then,if-then-else,自循環(huán),前測試循環(huán),后測試循環(huán),switch-case等。
圖1 高級(jí)控制結(jié)構(gòu)恢復(fù)框架
降序反向后序遍歷控制流圖構(gòu)建結(jié)構(gòu)語義樹,構(gòu)建過程中每遇到一個(gè)未曾訪問過的結(jié)點(diǎn),就根據(jù)控制流圖中結(jié)點(diǎn)類型按照以下步驟進(jìn)行相應(yīng)處理,直到控制流圖遍歷結(jié)束:
(1)對于沒有任何標(biāo)記的控制流圖結(jié)點(diǎn)即葉子結(jié)點(diǎn),直接加結(jié)構(gòu)語義樹;
(2)對于有標(biāo)記的高級(jí)控制結(jié)構(gòu)頭結(jié)點(diǎn)header,把header加入結(jié)構(gòu)語義樹;
1)根據(jù)header上的標(biāo)識(shí)獲得header所屬高級(jí)控制結(jié)構(gòu)的高級(jí)控制結(jié)構(gòu)類型等相關(guān)信息;
2)根據(jù)上述信息,針對該高級(jí)控制結(jié)構(gòu)找到屬于該結(jié)構(gòu)體的所有結(jié)點(diǎn);
3)在結(jié)構(gòu)語義樹中添加一復(fù)合結(jié)點(diǎn)nc1,將高級(jí)控制結(jié)構(gòu)類型以及結(jié)點(diǎn)之間的關(guān)系附加到nc1中;
4)將屬于該高級(jí)控制結(jié)構(gòu)的所有結(jié)點(diǎn)都鏈接到nc1上以作為其后裔。其中可以包括葉結(jié)點(diǎn)和復(fù)合結(jié)點(diǎn),若屬于該高級(jí)控制結(jié)構(gòu)的葉結(jié)點(diǎn)nl已經(jīng)是另一個(gè)復(fù)合結(jié)點(diǎn)nc2的后裔,則將nc2作為孩子結(jié)點(diǎn)鏈接到nc1上。
構(gòu)建成功的結(jié)構(gòu)語義樹中如果某復(fù)合結(jié)點(diǎn)的孩子結(jié)點(diǎn)依然是復(fù)合結(jié)點(diǎn),則說明該復(fù)合結(jié)點(diǎn)與其孩子結(jié)點(diǎn)之間存在嵌套關(guān)系。
算法1:遍歷控制流圖構(gòu)建結(jié)構(gòu)語義樹
//輸入:控制流圖
//輸出:SST
FirstNode=NULL;
n_c=sizeof(N);
For n∈N in descending reverse post order{
If(n.isloopheader){
n_c=ConLoopAdj(CFG,n,n_c);}
Else{
If(n.is2wayheader){
n_c=ConTwayAdj(CFG,n,n_c);}
Else{
If(n.isNwayheader){
n_c=ConNwayAdj(CFG,n,n_c);}
Else{
VexN=AddVexNode(n.id,FirstNode,NULL,0);
FirstNode=VexN;}}}}
算法2:檢查結(jié)點(diǎn)是否已經(jīng)加入邊表中,若沒有則加入,否則查看其邊表的頂點(diǎn)結(jié)點(diǎn)是否已加入邊表中,遞歸查看直到找到?jīng)]有加入的結(jié)點(diǎn)將其加入。
//輸入:結(jié)點(diǎn)n,頂點(diǎn)結(jié)點(diǎn)VexN,邊表結(jié)點(diǎn)AdjN
//輸出:SST
If(n is a edgenode){
p=FindVexNode(n.id);
If(p is a edgenode){
CheckNodeIsIn(p,VexN,AdjN);}
Else{
q=AddAdjNode(p.id,NULL,VexN);
AdjN.next=q;
AdjN=q;}}
Else{
q=AddAdjNode(n.id,NULL,VexN);
AdjN.next=q;
AdjN=q;}
其中,ConTwayAdj()、ConNwayAdj()和ConLoopAdj()算法類似,分別用來構(gòu)建二路分支結(jié)構(gòu),N路分支結(jié)構(gòu),以及循環(huán)結(jié)構(gòu),AddVexNode()建立頂點(diǎn)結(jié)點(diǎn),AddAdjNode()建立邊表結(jié)點(diǎn)。
結(jié)構(gòu)語義樹構(gòu)建完成后對其進(jìn)行從左至右的前序遍歷,如果當(dāng)前結(jié)點(diǎn)未被訪問過,則分兩種情況對其進(jìn)行分析:
(1)如果結(jié)點(diǎn)是葉子結(jié)點(diǎn),則直接輸出該結(jié)點(diǎn)內(nèi)的語句。
(2)如果結(jié)點(diǎn)是復(fù)合結(jié)點(diǎn)nc,則根據(jù)nc上所標(biāo)記的高級(jí)控制結(jié)構(gòu)的類型輸出不同的關(guān)鍵詞,再根據(jù)該高級(jí)控制結(jié)構(gòu)不同分支判斷基本塊屬于哪路分支,按照控制流圖的關(guān)系輸出基本塊內(nèi)語句。
判定各種高級(jí)控制結(jié)構(gòu)的條件,當(dāng)條件為真的時(shí)候,高級(jí)控制結(jié)構(gòu)體被執(zhí)行。如果進(jìn)入結(jié)構(gòu)體內(nèi)的分支是“假”分支,則說明當(dāng)條件為假的時(shí)候,結(jié)構(gòu)體被執(zhí)行,那么條件需要求否。
恢復(fù)過程中比較復(fù)雜的是if-then-else結(jié)構(gòu)的恢復(fù),因?yàn)樵摻Y(jié)構(gòu)有兩條分支,所以必須區(qū)分在該高級(jí)控制結(jié)構(gòu)內(nèi)的結(jié)點(diǎn)分別屬于哪一條分支子樹。算法描述如下:
(1)輸出關(guān)鍵字,根據(jù)頭結(jié)點(diǎn)Root和左孩子結(jié)點(diǎn)S的邊的屬性輸出條件;
(2)輸出左孩子結(jié)點(diǎn)S;
(3)在S的下一個(gè)兄弟結(jié)點(diǎn)T不為空的情況下:
1)如果S是葉子結(jié)點(diǎn),查看結(jié)點(diǎn)T是否是S的后繼結(jié)點(diǎn):
A如果是,說明T和S在同一棵子樹中即同一條分支,輸出結(jié)點(diǎn)T中語句;
B如果不是,則查看T是否是復(fù)合結(jié)點(diǎn):
a如果是,則查看T的左孩子是否是S的后繼:
a)如果是,則T和S在同一棵子樹中,繼續(xù)輸出該子樹;
b)如果不是,則T和S不在同一棵子樹中,那么輸出“else”關(guān)鍵字,開始輸出右子樹;
b如果不是,則T和S不在同一棵子樹中,那么輸出“else”關(guān)鍵字,開始輸出右子樹;
2)如果S是復(fù)合結(jié)點(diǎn),那么結(jié)點(diǎn)T必然和S在同一子樹中,則繼續(xù)輸出該子樹;
(4)將 S 置結(jié)點(diǎn) T,轉(zhuǎn)到(3);
因?yàn)闃?gòu)建結(jié)構(gòu)語義樹過程中最后遍歷的結(jié)點(diǎn)是控制流圖的根結(jié)點(diǎn),如果該結(jié)點(diǎn)不是任何高級(jí)控制結(jié)構(gòu)的頭結(jié)點(diǎn),則在結(jié)構(gòu)語義樹中只加入該葉子結(jié)點(diǎn)即可,而結(jié)構(gòu)語義樹的根結(jié)點(diǎn)即為一個(gè)虛根結(jié)點(diǎn),即在實(shí)際構(gòu)建的過程中不需要再構(gòu)建該復(fù)雜結(jié)點(diǎn),這樣在高級(jí)語言代碼生成過程中沿著結(jié)構(gòu)語義樹依次遍歷即可;如果該結(jié)點(diǎn)是某種高級(jí)控制結(jié)構(gòu)的頭結(jié)點(diǎn),則需要在結(jié)構(gòu)語義樹中加入該頭結(jié)點(diǎn)的葉子結(jié)點(diǎn),且加入一個(gè)復(fù)雜結(jié)點(diǎn)表示整個(gè)高級(jí)控制結(jié)構(gòu),這時(shí)候要根據(jù)高級(jí)控制結(jié)構(gòu)類型來判定:如果高級(jí)控制結(jié)構(gòu)是無窮循環(huán),即高級(jí)控制結(jié)構(gòu)沒有出口,那么結(jié)構(gòu)語義樹的根結(jié)點(diǎn)是一個(gè)實(shí)際存在的結(jié)點(diǎn),即實(shí)根結(jié)點(diǎn),如圖3所示;否則高級(jí)控制結(jié)構(gòu)有出口,即高級(jí)控制結(jié)構(gòu)必然有后隨結(jié)點(diǎn),那么結(jié)構(gòu)語義樹的根結(jié)點(diǎn)依然是虛根結(jié)點(diǎn),如圖4所示。其中:矩形表示復(fù)合結(jié)點(diǎn),復(fù)合結(jié)點(diǎn)中包含了有關(guān)該高級(jí)控制結(jié)構(gòu)的一切信息;圓表示葉子結(jié)點(diǎn),即控制流圖中的基本塊。
圖3 實(shí)根結(jié)構(gòu)語義樹
斐波那契數(shù)列函數(shù)的源碼及其匯編表示如圖5所示。
圖4 虛根結(jié)構(gòu)語義樹
對該可執(zhí)行文件進(jìn)行高級(jí)控制結(jié)構(gòu)恢復(fù)后的部分中間表示結(jié)果如圖6所示,其中r0負(fù)責(zé)傳遞參數(shù)以及存儲(chǔ)函數(shù)返回值,m32[r13-4-16]對應(yīng)于源程序中的m,while循環(huán)對應(yīng)于源程序中的for循環(huán),其中m32[r13-4-32]對應(yīng)于循環(huán)計(jì)數(shù)i,循環(huán)體內(nèi)的m32[r13-4-28]、m32[r13-4-24]、m32[r13-4-20]分別對應(yīng)于源程序中的f2、f1、f0。實(shí)驗(yàn)結(jié)果表明該方法能夠正確無誤地恢復(fù)整個(gè)程序的高級(jí)控制結(jié)構(gòu)。
并且我們選取了x86和ARM體系結(jié)構(gòu)下的多個(gè)常用可執(zhí)行文件進(jìn)行測試,測試平臺(tái)為:Windows XP操作系統(tǒng),Pentium4處理器,1G內(nèi)存,測試結(jié)果如表1所示。
表1中S/F代表恢復(fù)成功或者失敗。測試結(jié)果表明該算法能夠成功恢復(fù)出不同體系結(jié)構(gòu)下的可執(zhí)行代碼的高級(jí)控制結(jié)構(gòu),且因其無需復(fù)雜數(shù)據(jù)結(jié)構(gòu),因此算法運(yùn)行速度快,效率高,效果好。
圖5 測試用例源碼及匯編表示
圖6 高級(jí)控制結(jié)構(gòu)恢復(fù)結(jié)果
表1 測試結(jié)果
本文提出了反編譯中恢復(fù)高級(jí)控制結(jié)構(gòu)的新方法,即采用結(jié)構(gòu)語義樹來表達(dá)目標(biāo)代碼中的控制結(jié)構(gòu)以及控制結(jié)構(gòu)間關(guān)系信息,通過對結(jié)構(gòu)語義樹進(jìn)行前序遍歷可以完整恢復(fù)目標(biāo)代碼中的高級(jí)控制結(jié)構(gòu),該方法解決了經(jīng)典算法中高級(jí)控制結(jié)構(gòu)嵌套關(guān)系難以恢復(fù)的關(guān)鍵問題。經(jīng)過實(shí)驗(yàn)驗(yàn)證,該算法具有通用性,且運(yùn)行效果良好,完成了反編譯中高級(jí)控制結(jié)構(gòu)恢復(fù)的功能,恢復(fù)出的高級(jí)控制結(jié)構(gòu)信息為后面生成高級(jí)語言代碼提供了非常大的幫助,提高了反編譯結(jié)果的準(zhǔn)確性與可讀性。
[1]蔣烈輝.固件代碼逆向分析關(guān)鍵技術(shù)研究[D].鄭州:解放軍信息工程大學(xué),2007:1-6.
[2]Eldad Eilam,Elliot Chikofsky.Reversing:逆向工程解密[M].韓琪,譯.北京:電子工業(yè)出版社,2007:13-46.
[3]José Manuel Rios Fonseca.Interactive decompilation[D].Portugal:Faculty of Engineering of the University of Porto,2006.
[4]Huang Hai,Jiang Liehui.A decompilation model based multiple disassemble front-end result[C].Jiaozuo:Proceeding of Information Technology and Environmental System Sciences,2008:769-773.
[5]Kinder J,Veith H.Jakstab:a static analysis platform for binaries[C].Proceedings of the 20th International Conference on Computer Aided Verification,2008:423-427.
[6]韋韜,毛劍,鄒維.反編譯中的復(fù)合條件分支識(shí)別算法[J].北京大學(xué)學(xué)報(bào),2008,44(1):37-43.
[7]Tao Wei,Jian Mao,Wei Zou,et al.A newalgorithm for identifying loops in decompilation[C].Riis Nielson H,File G.SAS.Berlin,Heidelberg:Springer-Verlag,2007:170-183.
[8]Ung D,Cifuentes C.Dynamic re-engineering of binary code with run-time feedback[C].Science of Computer Programming,2006:189-204.
[9]Mike Van Emmerik.Boomerang[EB/OL].http://boomerang.sourceforge.net,2006.
[10]Grammatech Inc.CodeSurfer/x86[EB/OL].http://cayuga.grammatech.com/research/cs-x86,2009.
[11]Zhang Jingbo,Zhao Rongcai,Pang Jianmin,et al.A high-level control structure recovery method based on propositional calculus[C].Second International Conference on Future Information Technology and Management Engineering,2009:155-158.
[12]Eric Moretti,Gilles Chanteperdrix,Angel Osorio.New algorithms for control-flow graph structuring[C].Fifth European Conference on Software Maintenance and Reengineering,2001:184-187.
[13]Kaspersky K.Hacker disassembling uncovered[M].A-List LLC,2004:378-385.
[14]馬其尼克.高級(jí)編譯器設(shè)計(jì)與實(shí)現(xiàn)[M].趙克佳,沈志宇,譯.北京:機(jī)械工業(yè)出版社,2005:163-165.
[15]Alfred V Aho,Monica S Lam,Ravi Sethi,et al.Ullman編譯原理[M].趙建華,譯.2版.北京:機(jī)械工業(yè)出版社,2009:338-353.
計(jì)算機(jī)工程與設(shè)計(jì)2011年9期