摘 要:代碼迷惑(保護程序的一種手段)通過增加程序的分析理解難度來阻止攻擊者對代碼進行有用的竄改。從攻擊視角指出,盲目采用代碼迷惑并不能有效增強程序安全性,而根據攻擊模型,從多個角度綜合運用各種碼迷惑方法將能有效提高程序安全。隨后建立攻擊模型,將攻擊描述為一個不斷提取程序信息并據此分析具體語義,進而通過抽象和判斷獲取抽象語義的過程,并從攻擊各層面出發引入相應的代碼迷惑方法,進而保護代碼安全。
關鍵詞:代碼迷惑; 軟件保護; 程序分析
中圖分類號:TP309文獻標志碼:A
文章編號:1001-3695(2009)09-3502-04
doi:10.3969/j.issn.1001-3695.2009.09.086
Research of code obfuscation and its efficiency
XU Chang-zheng1, DU Yu-jie2, CHEN Yan1, WANG Qing-xian1
(1. Institute of Information Engireering, PLA Information Engineering University, Zhengzhou 450002, China; 2.Shandong Zhaoyuan Statistic Bureau, Zhaoyuan Shandong 265400, China)
Abstract:Code obfuscation is an approach of protecting program. It protects code from attackers’ useful tamper through enhancing the difficulty of understanding program. This paper pointed out the code obfuscation that was adopted blindly was not sure to protect program efficiently and various obfuscation methods should be combined from multi-point of view based on attacking model. Then it built an attacking model, which described attack as a procedure of distilling program information and analyzing concrete semantic and acquiring abstract semantic. At the same time, introduced the corresponding code obfuscation methods according to the model.
Key words:code obfuscation; software protection; program analysis
0 引言
軟件保護和移動代理的惡意主機問題都涉及到如何保護程序代碼的安全性。由于程序代碼處于攻擊者的完全掌控之下,攻擊者可以隨意窺探和竄改程序代碼。
采用加密體系結構是保護程序代碼安全的一種方法。該方法將程序代碼加密使攻擊者無法讀懂,從而不能進行有效竄改。但由于客戶機上必須保存解密密鑰,一般需要安全硬件[1]的支持,如安全協處理器,將密鑰及其他關鍵信息存放其中,并在其中解密程序代碼,這樣才能有效發揮該方法的作用。代碼迷惑是另外一種保護程序安全的方法。該方法通過程序等價變換,在保持可觀察語義等價的前提下,盡量增大分析理解程序的難度,使攻擊者不能輕易讀懂程序,從而保證程序在有效期內不被攻擊者攻破。這種方法不需要額外的硬件支持,因此具有很好的靈活易用性。盡管Barak等人[2]證明了不存在適用于任何程序代碼的迷惑方法,但是在實際應用中,對于大部分的程序,許多代碼迷惑方法依然能夠增大程序分析理解的難度,因此代碼迷惑的研究一直很活躍。
目前已經有許多代碼迷惑方法,如Linn等人[3]的插入花指令(junk code)方法、Li等人[4]的基于函數指針的迷惑算法、Wang[5]的壓平算法等。Collberg等人[6]詳細介紹了Java程序代碼迷惑方法,主要分為層次迷惑、控制迷惑、數據迷惑和預防性程序變換四類。
這些方法大部分都只在某個方面增大了攻擊者分析理解程序的難度。例如Linn的插入花指令方法增加了對程序進行正確反匯編的難度,Li的基于函數指針迷惑算法增加了分析函數調用的難度,而Wang的壓平算法則增加了理解控制流轉換關系的難度。如果盲目依賴其中的一兩種迷惑方法并不一定能有效增強程序安全,因為攻擊方法靈活多變,攻擊者可以綜合運用各種技巧避開這些復雜性,從其他薄弱環節來進行攻擊,竄改代碼以達到預期目的。
本文從攻擊者的視角出發,建立攻擊模型,將攻擊描述為一個不斷提取程序信息并據此分析具體語義,進而通過抽象和判斷獲取抽象語義的過程。同時指出,在此模型基礎上,根據程序本身的特點,從多個層面綜合運用碼迷惑方法能有效提高程序安全。
1 攻擊模型
攻擊者對程序的攻擊過程實質是逐步理解程序語義的過程。這個過程主要分為三個層面:
a)提取程序信息。程序信息是攻擊者進行后續分析的基礎,分為靜態和動態程序信息。靜態程序信息主要包括反匯編信息、程序結構信息(如函數調用關系圖CG和控制流圖CFG)、特征字符串及其引用位置信息等。動態程序信息是指在特定輸入下的程序行為信息、狀態信息、執行信息等,如訪問了哪些文件,顯示了什么提示消息,在特定程序點的寄存器內容以及程序執行路徑等。
b)分析代碼具體語義。根據提取的程序信息,攻擊者需要分析程序代碼,觀察代碼在執行過程中的各種狀態轉換關系,如各寄存器及內存變量值的變化情況等。分析方法包括靜態查看匯編代碼和使用調試工具動態跟蹤代碼執行。
c)獲取代碼抽象語義。在分析代碼具體語義的基礎上,攻擊者需要進行抽象和判斷,以確定代碼執行什么功能,完成什么任務。
圖1為攻擊的整個流程。當攻擊者理解了程序后,就可以對程序進行有效竄改以實現其攻擊意圖。通常情況下,攻擊者不會去理解掌握整個程序,他將根據攻擊要求來選擇關鍵程序代碼進行分析,這樣可以大大提高攻擊效率。
本文以一個簡單的例子來說明該模型。以下是采用磁盤序列號來限制程序在特定機器上執行功能的原理框架,用VC++6.0將其編譯成PE文件后,該程序將根據C盤序列號來判斷是否執行功能函數。
void fc(){//功能函數
……}
bool ck(u_int serialnum) {//檢驗序列號
……
return true;/*如果檢驗通過,返回真*/}
void main() {
……
GetVolumeInformation(“c:\\\\”,0,0,serialnum, 0,0,0,0);
//調用系統API獲取C盤序列號
if(!ck(serialnum))
MessageBox(0, “can not run in this host”,0, 0);
//如果序列號不正確,則彈出錯誤對話框
else
fc()//執行程序功能
……
}
現在,攻擊者希望解除該程序只能在特定機器上執行功能函數的限制。首先,通過運行程序并觀察程序行為,他將發現程序在特定機器上執行正常,而在其他機器上將顯示“can not run in this host”。然后,他使用IDA Pro對程序反匯編并獲取各函數的CFG信息,同時確定引用該字符串的地址,即找到以該串為參數的MessageBox()調用點。而后,根據這些信息,攻擊者將對調用該API的函數代碼(即main())具體語義進行分析,通過CFG可以發現該API調用點所在基本塊的前驅節點最后三條匯編指令進行的操作是:調用函數ck,將eax內容與0比較,如果不等則跳轉,據此,攻擊者可以判斷ck函數的功能是檢驗程序是否運行在特定機器上。最后,通過竄改ck函數返回值實現其攻擊意圖。
2 有效運用代碼迷惑保護程序安全
對于上例,采用字符串混亂技術將使攻擊者無法通過分析PE文件輕易提取特征字符串及其引用位置信息,但這并不能有效增強程序的安全性,因為攻擊者可以采用其他并不復雜的方法來攻擊程序。例如,通過動態監視程序調用系統API的情況,攻擊者可以獲取如下信息:該程序調用了系統接口GetVo-lumeInformation(),而且其第一個參數為“c:\\\\”。據此,對調用該API的函數進行動態跟蹤和分析,攻擊者可以推測該程序是以C盤序列號來判斷是否執行功能函數的,因此竄改返回序列號也能實現其攻擊意圖。
由此可見,在運用代碼迷惑保護程序安全時,需要從多個方面入手才能增強其有效性。從攻擊模型可以看到,程序信息是整個攻擊的基礎,如果攻擊者獲得的信息越少,越不準確,其攻擊的難度也就越大,程序也就越安全。因此在保護程序時,首先應該針對程序中各種關鍵的可能會對攻擊者有幫助的信息,采用相應的迷惑方法進行混淆;其次,還應該增大攻擊者分析具體語義的難度以及對抽象語義進行混亂,這樣才能有效增強程序的安全性。以下分別從這三個方面來介紹。
2.1 阻礙攻擊者獲得準確程序信息
反匯編信息、程序結構信息、特征字符串及引用位置信息、程序動態行為信息是攻擊者需要獲取的基本程序信息。下面針對這四類信息分別介紹相應的迷惑方法。
2.1.1 干擾反匯編
二進制代碼反匯編是攻擊者理解程序的重要基礎,不準確的匯編代碼信息將增大攻擊難度。由于攻擊者需要借助反匯編器自動完成反匯編,干擾反匯編就是讓這些反匯編器很難獲取正確的匯編代碼,通常可采用以下兩種方法:
a)插入花指令。在指令流中插入一些不完整指令可以干擾反匯編器,這些不完整指令稱為花指令。插入的花指令必須滿足兩個條件:為了保持程序語義,花指令不能被實際執行;為了保證有效性,反匯編器應認為花指令會被執行。
Linn等人[3]介紹了引入花指令的幾種方法,如使用分支函數替代絕對跳轉、修改函數返回地址等。測試結果表明這些方法能有效干擾反匯編器。
b)混亂代碼塊。其將程序代碼中的某一區域進行混亂,使反匯編器無法正確翻譯,只有當程序運行到混亂的區域時,才將其還原。使用這種方法應選擇程序中的關鍵區域進行混亂,同時應考慮其執行頻度,否則將會嚴重影響程序執行速度。
2.1.2 降低程序結構信息準確度
程序結構信息主要包括函數調用關系圖CG和各函數控制流圖CFG,這些信息是攻擊者分析程序代碼的重要依據。如果攻擊者獲取的程序結構信息不準確,他在分析程序時將很難作出正確的判斷。攻擊者通常借助一些靜態分析工具(如IDA Pro)來自動生成CG和CFG。采用以下方法可使這些工具生成CG和CFG的準確度大大降低:
a)使用復雜的間接控制轉移。控制轉移指令在很大程度上影響CG和CFG的構造,而控制轉移地址則是自動分析工具判斷函數調用關系以及基本塊流程關系的重要線索。采用復雜間接控制轉移增大靜態分析轉移地址的難度,能有效降低這些工具的分析準確性。Li等人[4]的基于函數指針的迷惑算法正是利用了這種復雜的間接控制轉移。b)使用等價指令序列。與前述方法相似,這種方法也從控制轉移指令入手,但它通過等價指令替換,將控制轉移指令替換為其他指令序列,以此來降低分析的準確性。有許多替換方法,如文獻[3]中介紹的使用對分支函數的call調用來替換jmp指令等。
c)采用自修改代碼。程序運行時,自修改代碼會對程序代碼區域進行寫操作,因此程序的CG和CFG會隨著程序的運行而動態變化[7]。
mov eax, offset L1
mov [eax], 0xeb
inc eax
L1:mov [eax], 0xfd
L2:add eax,eax
dec eax
對于以上代碼,其初始CFG如圖2(a)所示。
當程序運行過L1后,CFG變為圖2(b)。諸如IDA Pro的靜態分析工具只能獲取初始的CFG。
d)采用異常處理。異常處理是系統提供給應用程序處理非正常事件的一種機制。使用異常處理程序可以動態修改程序執行流程,而靜態分析通常很難發現異常事件,因此可在程序中制造異常使得靜態分析工具難以發現真實的執行流程。以Windows異常機制為例進行說明。
HANDLER://異常處理程序,修改執行上下文
mov eax,dword ptr ss:[ebp+0x10]
mov ebx,offset L2
mov dword ptr ds:[eax+0xb8],ebx
MAIN:
push offset HANDLER//設置異常處理程序
push dword ptr fs:[0]
mov dword ptr fs:[0], esp
L1: mov esi,0//制造異常
mov eax,dword ptr ds:[esi]
inc eax
jmp L1
L2:add eax,7
IDA Pro生成MAIN的CFG如圖3(a)所示,而實際執行的CFG如圖3(b)所示。
2.1.3 阻止獲取特征字符串及引用位置信息
二進制可執行文件中可能包含許多文本字符串,如程序版本號、函數名以及程序在執行過程中向用戶提示的信息。這些字符串有許多是攻擊程序的重要線索。例如在第1章的例子中,“can not run in this host”就可以幫助攻擊者迅速判斷函數ck()的作用是檢測能否在本機執行功能函數,又如一些靜態鏈接函數名本身就指明了函數的功能,而動態鏈接函數名則能幫助攻擊者了解程序使用動態鏈接函數的情況。因此阻止攻擊者獲得這些特征字符串及其引用位置信息將增大攻擊難度。
a)把不必要的文本字符串信息從程序中剔除。例如在Linux下,使用strip命令可以把ELF程序中各種不必要的變量函數名剔除。
b)對于用做提示信息的字符串,需要將其混亂。以下是對“can not run in this host”字符串進行混亂的代碼示例,代碼中tips存放的是該字符串與\\x69異或的結果,這樣攻擊者通過靜態分析文件將得不到有意義的符號串,而當程序處于運行狀態,并在顯示該符號串時才將其還原。
static char tips[]=“\\x09\\x0b\\x04\\x4a\\x05\\x1e \\x4a\\x18\\x1f\\x04\\x4a\\x03\\x04\\x4a\\x1e\\x02\\x03\\x19\\x4a\\x02\\x05\\x19\\x1e”;/*“can not run in this host”與\\x69按字節異或后的結果*/
char *buff=malloc(128);
memcpy(buff,tips,23);
/*在顯示字符串之前將其還原*/
for(i=0;i<23;i++)
buff[i]=buff[i]^0x69;
MessageBox(0,buff,0,0);
free(buff);
c)對于動態鏈接函數名,也需將其混亂,不過為了不影響程序執行,必須采用顯式方式來獲取這些動態鏈接函數的地址。例如,為了調用GetVolumeInformation(),需要使用LoadLibrary和GetProcAddress函數來得到該系統API的地址, 而作為其參數的庫名Kernel32.dll和函數名GetVolumeInformation可以使用前述方法進行混亂。
2.1.4 混亂程序行為信息
程序行為信息是程序在特定輸入下表現出的各種操作信息,如調用了哪些系統API、訪問了哪些文件等等。這些信息是攻擊者攻擊程序的重要參考。通過增加冗余操作來混亂程序行為信息,使程序原有操作淹沒在許多無用的操作之中,將使攻擊者無法判斷哪些操作是有用的,從而增大攻擊的難度。
對于第1章的示例,攻擊者通過動態監視程序調用系統API的情況,可以發現其中有一個對GetVolumeInformation()的調用,根據該調用的參數,他就能比較容易地推斷該程序是以C盤序列號來判斷是否執行功能函數的。但是如果在程序中添加若干無用的API調用,如增加獲取系統MAC地址的API調用,或采用不同參數來調用GetVolumeInformation(),這樣,在攻擊者獲得的系統API調用列表中,將有許多冗余調用信息,而這些調用將使攻擊者很難判斷哪一個調用返回的信息是關鍵的。
2.2 增大分析具體語義的難度
分析具體語義有靜態分析匯編代碼和動態跟蹤代碼執行兩種方式。靜態分析匯編代碼需要準確的反匯編信息,因此2.1.1節中介紹的干擾反匯編方法實際上是增大了這種分析方法的難度。對于動態跟蹤代碼執行的分析方法,目前比較常用的對抗方法是狀態檢測法,即檢測程序是否處于被調試狀態,如果是則拒絕繼續運行。檢測方法有許多,如檢測系統內是否有調試進程,或檢測一段連續代碼的執行時間是否超過一個閾值等[8]。
除此之外,利用2.1.2節中介紹的自修改代碼也能增大動態跟蹤代碼執行的難度。自修改代碼不但能使程序邏輯結構(FG和CFG)隨著程序的運行而變化,也能使程序代碼在內存中的布局隨程序運行發生變化,這將使動態跟蹤更加復雜。例如自修改代碼能有效對抗程序斷點,如圖4所示,攻擊者在內存位置b處設置斷點,初始情況下該斷點位于函數f1中,當f1被調用執行時將停在b處,另外f1需要調用f2,而f2中有一段自修改代碼,該代碼將修改f1在內存中的位置,同時調整堆棧中的返回地址及其他相關數據,當f1調用f2后,斷點b將位于f1之外而失效。
2.3 對抽象語義進行混亂
對抽象語義進行混亂就是要讓攻擊者難以理解代碼的功能。有許多方法可以實施這種混亂,如在代碼中增加許多冗余無用的垃圾代碼,或者將實現不同功能的代碼交織在一起等。Wang[5]的壓平算法是其中的一種典型方法。對于以下代碼:
int f(int i , int j) {
int a=1;
if (i< j) { a=j;}
else do {a *=i--;}while (i>0);
return a;
}
其CFG如圖5(a)所示,代碼經過壓平變換后,CFG變為圖5(b)。直觀上看,理解邏輯結構為(b)的代碼比邏輯結構為(a)的更難,在理論上,Gao[9]等人證明了壓平算法的確能夠混亂代碼的抽象語義。
3 代碼迷惑保護程序安全有效性評估
第2章分別從攻擊模型的三個層面介紹了相應的迷惑方法,但對于一個具體的程序,如何綜合運用這些迷惑方法才能有效保護其安全是一個值得研究的問題,這需要提出一個有效性評估框架。目前,已有的評估框架都只局限于評估某一種代碼迷惑方法的有效性,包括:
c)Collberg等人[6]采用軟件工程中評測軟件品質的軟件復雜性尺度(如程序長度、函數數目等)以及程序結構圖屬性(如CG和CFG的節點數、邊數等)作為度量來進行評估。如果迷惑后程序的這些屬性變復雜則認為迷惑方法是有效的。
b)Wang和Ogiso[5,10]等人以反迷惑算法的計算復雜度為度量來進行評估。他們將線性有界圖靈機(LBTM)可接受問題規約為靜態分析迷惑后代碼CFG的問題。由于LBTM可接受問題是PSPACE-Complete問題,靜態分析迷惑后代碼CFG的問題也屬于PSPACE-Complete,從而認為迷惑方法是有效的。
c)Dalla等人[11,12]從語義的角度來分析迷惑算法的有效性,他提出使用程序迷惑前后分析結果的不等來定義有效性度量。如果程序變換后影響了原來的抽象域則認為迷惑是有效的。
僅僅評估某種代碼迷惑的有效性是不夠的,因為由前述可知,即使某種代碼迷惑方法在某些方面能有效增大攻擊者分析理解程序的難度,也不一定能有效提高程序的安全,所以需要建立一個更加寬泛的評估框架來評估綜合運用代碼迷惑的有效性。如何進一步準確刻畫攻擊模型以及在此模型基礎上建立這樣一個評估框架,將是筆者下一步的研究方向。
4 結束語
本文從攻擊視角出發,建立攻擊模型,將攻擊描述為一個不斷提取程序信息并據此分析具體語義,進而通過抽象和判斷獲取抽象語義的過程。同時,指出綜合運用代碼迷惑將能有效增強程序安全性。當然,這些方法并不全面,還有許多其他方法可以采用,如使用Collberg的不變謂詞(opaque predicate)[6,13]技術也可以用來降低靜態分析工具生成CG和CFG的準確度。最后指出了下一步的研究方向。
參考文獻:
[1]SMITH S. Secure coprocessing applications and research issues, LA-UR-96-2805[R].Los Alamos: Los Alamos National Laboratory, 1996.
[2]BARAK B, GOLDREICH O, IMPAGLIAZZO R, et al. On the (im)possibility of obfuscation programs[C]//Advances in Cryptology, Proc of CRYPTO’01. Berlin: Springer,2001:1-18.
[3]LINN C, DEBRAY S. Obfuscation of executable code to improve resistance to static disassembly[C]//Proc of the 10th ACM CCS’03. New York: ACM Press,2003:290-299.
[4]LI Yong-xiang, CHEN Yi-yun. Technique of code obfuscation based on function rointer array[J]. Chinese Journal of Computers, 2004, 27(12):1706-1711.
[5]WANG Chen-xi. A security architecture for survivability mechanisms[D]. Charlottes: Department of Computer Science, University of Virginia, 2000.
[6]COLLBERG C, CLARK T, DOUGLAS L. A taxonomy of obfuscating transformations[R]. Auckland: Department of Computer Science, University of Auckland, 1997.
[7]ANCKAERT B, MADOU M, BOSSCHERE K D. A model for self-modifying code[C]//Proc of the 8th Information Hiding. Berlin: Springer,2006.
[8]CESARE S. Linux anti-debugging techniques[R]. [S.l.]:Security Focus, 1999.
[9]GAO Ying, CHEN Yi-yun. A comparable code obfuscation framework measuring efficiency based on abstract interpretation[EB/OL].(2006-11). http://ssg.ustcsz.edu.cn.
[10]OGISO T, SAKABE Y. Software obfuscation on a theoretical basis and its implementation[J]. IEEE Trans on Fundamentals, 2003,E86-A(1):176-186.
[11]DALLA M, GIACOBAZZI R. Semantic-based code obfuscation by abstract interpretation[C]//Proc of ICALP’05. Berlin:Springer,2005:1325-1336.
[12]DALLA M, GIACOBAZZI R. Control code obfuscation by abstract interpretation[C]//Proc of SEFM’05. Washington DC: IEEE Compu-ter Society,2005:301-310.
[13]COLLBERG C, THOMBORSON, LOW D. Manufacturing cheap, resilient, and stealthy opaque constructs[C]//Proc of the 25th ACM SIGPLAN-SIGACT Symposium on Principles of Programming Langua-ges. New York: ACM Press, 1998:184-196.