被黑客們使用的程式碼混淆技術

turingbooks發表於2012-04-17

長久以來,程式碼混淆技術一直都被認為是不能登大雅之堂的奇巧淫技,沒有哪個學者會拿正眼瞧它一眼。國際C語言混亂程式碼大賽(International Obfuscated C Code Contest,IOCCC)[177,227]就是一個很好的例子——儘管每年都有不少參賽選手寫出令人嘖嘖稱奇的程式碼,但是大家對此的評價卻總是:“啊!這個很有意思,不過對實際工作有什麼用處嗎?”當時C語言還被認為是寫這類程式的最佳選擇 。人們那時以為:程式碼混淆技術並沒有什麼真正的價值,使用這些技術的人腦子都壞掉了,想靠這種把戲愚弄攻擊者簡直就是痴心妄想。但是突然Java橫空出世了。Java在帶給人們可以在任何平臺上執行的便利的同時,由於Java位元組碼設計的特殊性,使得傳統的針對機器碼的安全保護技巧一夜之間變得毫無用處。這時人們才想起了程式碼混淆技術,因為當時在Java平臺上,這似乎是唯一有效的程式碼保護技術。

不幸的是,程式碼混淆技術被應用的最成功的領域是在黑客的手上。大家對此應該已經見怪不怪了。好像壞蛋們總能比好人們更快地適應新技術——儘管這些新技術都是好人們設計出來的。比方說密碼學吧,它既可以保護執法部門之間的通訊,也能夠用來保護罪犯之間的聯絡。又比如隱寫術,國家安全部門可以用它保護國家機密資訊,但犯罪分子也能利用它以不被發現。目前已知的最早破解基於智慧卡的防護技術的人是破解電視機機頂盒的黑客,但是這些黑客轉手又使用相關防護技術來保護他們自己的產品免受機頂盒廠家的破解。 Axel對他編寫的病毒進行混淆處理,使之免於被Doris發現——這是黑客們手中程式碼混淆技術的用途之一。如圖1-22所示。

enter image description here

病毒由兩個部分組成:載荷(payload)和混淆部件(obfuscator),載荷是用來做壞事的程式碼,而混淆部件則是病毒用來保護自身免於被查殺的。Axel首先要感染程式P,使之攜帶病毒v,這樣得到一個帶病毒的程式P ′。然後Axel開始傳播P ′。如果Doris在她的機器上執行了P ′,那麼病毒就會感染Doris系統中的另一個程式Q。在病毒感染Q之前,會先使用程式碼混淆技術對自身進行修改,生成自身的另一個完全不同的版本v',然後再去感染Q,產生帶毒程式Q ′……Axel這樣做的目的在於:如果每一代病毒都是完全不一樣的話,Doris的防毒軟體就很難完全查殺這些不同版本病毒。這和之前討論的系統變形的情形是類似的,只不過攻守的雙方互換了下位置罷了。

1. 故意出錯——不露痕跡地進行程式碼混淆

如果你見過IOCCC的參賽程式碼,就會發現它們與普通程式碼迥然不同。機器生成的、經過混淆的或者被優化過的程式碼看上去也是這個德行——與人工編寫的程式碼之間存在巨大的差異。比如說,看看程式碼清單1-1,你一下子就能斷定這些程式碼肯定不是人寫的。所以當Axel分析Doris的程式時,這段程式碼首先就會引起他的懷疑——密碼不是很可能就藏在這種被混淆的程式碼中嗎?在本書的許多案例中,你都可以看到不露痕跡地使用保護技術是多麼的重要——不能給對手以提示:我們對這些程式碼使用了軟體保護技術,或者我們按著那樣的一個順序使用了這幾種軟體保護措施。

如果壞人們使用了這種技術,我們分析嫌疑程式時就很頭疼了。黑客們通過故意在程式中插入錯誤的方式來達到隱蔽地使用程式碼混淆技術的目的。我們來看一下程式碼清單1-3,這個程式是用來對“美國偶像”的投票結果進行記錄和統計的。該程式從系統的標準輸入中讀取投票資料,然後經過統計輸出投票結果。下面給出的就是該程式的一次執行例項。

enter image description here

程式碼清單1-3 經過混淆的投票統計程式碼

enter image description here

enter image description here

這個程式的對投票進行統計的演算法正確嗎?其中是否含有作弊程式碼?在閱讀提示之前 ,請先花點時間,看看從這個只有58行程式碼的程式中找出錯誤需要多少時間?好吧,現在你又認為在現實中,從一個擁有成千上百行程式碼的投票系統中找出類似的錯誤要花你多少時間呢?如果程式碼清單1-3中所示的技術與程式碼清單1-1中所示的技術被結合起來使用的話,又會有怎樣的結果呢?這樣是否就能使作弊程式隱蔽地潛伏到投票系統的程式碼中,而不被發覺呢?也許下一期的“美國偶像”早就已經被內定了。(或者,要是這個作弊程式被嵌入在美國總統選舉的計票系統中呢……)

2. 混淆病毒

正如你所見,今天,對於一些很重要的實際問題,程式碼混淆技術可能是唯一可行的解決方案。但不幸的是,程式碼混淆技術最輝煌的成就卻是在黑客們手中實現的——用於保護病毒、蠕蟲、木馬和rootkit免遭查殺。觀察黑客們研發並在實踐中已經證明了其有效性的技術,以及安全研究者們能否再次利用同樣技術確實是件很意思的事情。病毒的作者和防毒軟體廠商已經陷入了一場貓捉老鼠的遊戲中:每當防毒軟體廠商開發出一種新的病毒檢測技術時,病毒的作者們就會使用一種更為巧妙的程式碼混淆技術來抵禦這種檢測技術,而這又促使防毒軟體廠商研發更為強大的病毒檢測技術……到目前為止,好像還是黑客們在這場遊戲中略佔上風。根據最新的報導,世界上有25%的計算機曾經被botnet[368]入侵和控制。當然,這一“成就”也不能光歸功於惡意軟體中使用的程式碼混淆技術,許多人不及時給作業系統打補丁、不經常升級防毒軟體也是一個很重要的原因。

病毒逃避防毒軟體檢測的主要方法是使防毒軟體“看不見” 病毒體中的程式碼。由於防毒軟體只能佔用計算機中極小一部分資源 ,所以它不可能完整地分析硬碟中每個檔案。而且即使它能做到這一點,理論研究的結果也表明仍然會有病毒可能漏網。因此大多數防毒軟體都是使用病毒的特徵碼來識別病毒的。從某種角度上說,如果病毒體的程式碼在病毒每次感染檔案時都不會發生變化的話,特徵碼確實是一個不錯的檢測方法。它就類似於軟體“胎記”(這一技術將在第10章中詳細討論)。

那麼病毒的作者又是使用哪些技術讓防毒軟體“看不見”病毒體中的程式碼呢?我們來看一下程式碼清單1-4中給出的這個Java程式。當然,在實際生活中幾乎不會有人會用Java去編寫病毒,我們編寫這個例子只是為了幫助你理解病毒是怎樣使用程式碼混淆技術保護自身的。所以也請你大人有大量,原諒我們用Java編寫它吧。 在這個Java病毒中有幾個做法值得引起我們的注意。首先,我們注意到病毒好像是把自己的整個原始碼都當成是一個字串,並把它放在了程式中。為了不至於不斷地重複自身導致死迴圈,編寫這個程式的程式設計師使用了一些程式碼複製的技巧 。這個技巧在一種源自基內斯 的搞怪表演中也常被使用——在這種表演中,程式執行之後將會輸出自己的原始碼。

原始碼是被放在一個名為self的變數中的。最終它會被寫到一個檔案(m.java)裡去,並由被感染計算機上安裝的Java編譯器編譯執行。最近出現的Slapper蠕蟲[320]及其變種也使用編譯器來儘量減少自身對於平臺的依賴性。使用編譯器對黑客有利的另一個副作用是——使用不同的編譯器編譯同一份原始碼時可能會產生略微不同的可執行檔案(儘管在語義上這些可執行檔案是完全等價的),這就使得我們提取病毒特徵碼時還要考慮不同編譯器之間的差異,進而增加了特徵碼提取的難度。

程式碼清單1-4中的程式碼更進了一步。在把自身複製到m.java檔案中之前,它先要把自身用morph函式處理一下。morph函式的作用就是在程式的各行之間都插入一句i++語句。這個語句並不會影響程式的行為或者輸出結果,但是這個小花招的確使每個新的病毒樣本都與其前一代不一樣!這也就是說,只使用了簡單的特徵碼技術的防毒軟體是無法發現這個病毒的。

程式碼清單1-4 一個Java變形病毒

enter image description here

enter image description here

你可以想象一下:如果這裡,病毒的作者沒有使用這個簡陋的morph函式,而是使用了那些下文詳細介紹的、更為狡詐的程式碼混淆技術,你認為防毒軟體要用到哪些分析技術才能發現這個病毒呢?

實踐中,病毒會使用程式碼混淆技術儘可能地確保每次感染後產生的新一代病毒的許多屬性都變得完全不同。我們把這種在每次感染之後都用程式碼混淆技術把整個病毒體的程式碼全部改得面目全非的病毒稱為變形病毒。而多型病毒可以認為是變形病毒的更進一步優化的版本。因為它不像變形病毒對整個病毒的程式碼都進行混淆處理 ,而是用不同金鑰把整個病毒的程式碼全都加密起來。當然,為了保證能正常執行,病毒還要自帶一個解密模組,多型病毒中使用程式碼混淆技術處理的就只有這個解密模組。總的來說,在多型病毒中,程式碼混淆技術只是保護瞭解密模組免於被防毒軟體發現,而病毒剩下的部分則全部是由加密技術進行保護的。

本文摘自:《軟體加密與解密》

相關文章