◆陳煒昊 于子洋
針對Windows下閉源二進(jìn)制可執(zhí)行文件熱補丁的研究
◆陳煒昊 于子洋
(中國礦業(yè)大學(xué) 計算機科學(xué)與技術(shù)學(xué)院 江蘇 221116)
隨著計算機科學(xué)技術(shù)的飛速發(fā)展,越來越多的廠商與作者為保護(hù)程序算法知識產(chǎn)權(quán),選擇將自己的代碼程序以閉源的形式發(fā)布,導(dǎo)致很多閉源軟件存在不同程度的安全問題。如何對已經(jīng)產(chǎn)生安全事件的閉源二進(jìn)制文件進(jìn)行修復(fù)已逐漸成了安全應(yīng)用的重要課題,本文就此問題進(jìn)行探討,以Windows x86架構(gòu)為框架,詳細(xì)分析了文件靜態(tài)補丁技術(shù)的可行性,并給出了針對此問題的基本解決方案,最終達(dá)到在無源碼的情況下對文件漏洞修復(fù)的目的。
Windows x86;相對虛擬地址;動態(tài)鏈接庫
隨著計算機信息技術(shù)的快速發(fā)展,各行各業(yè)的人都加入了軟件開發(fā)的行列,他們會將自己的專業(yè)知識封裝成各種各樣的軟件,供有需求的人們?nèi)ナ褂?。開發(fā)出來的軟件主要分為兩種:開源軟件與閉源軟件。閉源軟件無法在源代碼的層級上對程序進(jìn)行修改,刪除,更新等操作。由于開發(fā)人員沒有接觸過系統(tǒng)的安全教育以及如何進(jìn)行底層CVE的發(fā)掘,導(dǎo)致大多數(shù)的閉源軟件都存在安全隱患。主要包括危險函數(shù)的誤用導(dǎo)致的棧溢出、內(nèi)存管理釋放部分缺失導(dǎo)致的堆溢出等隱患。
本文主要是對Windows下閉源二進(jìn)制可執(zhí)行文件的修補方法進(jìn)行研究,目的是修復(fù)無人維護(hù)的閉源軟件的安全問題和在官方未發(fā)布補丁時的1Day漏洞。
Windows自帶的的可執(zhí)行文件格式是PE文件格式。PE文件中的內(nèi)容會被劃分成一個個小塊,這一個個小塊就是Section(節(jié))。Windows可執(zhí)行程序擁有一個獨特的機制--導(dǎo)入表與導(dǎo)出表。導(dǎo)入表與導(dǎo)出表中保存了PE文件中調(diào)用的所有動態(tài)鏈接庫的函數(shù),以及這些函數(shù)來自哪些動態(tài)鏈接庫。Windows加載器在運行PE時會將導(dǎo)入表中聲明的動態(tài)鏈接庫一并加載到進(jìn)程的地址空間,并修正指令代碼中調(diào)用的函數(shù)地址。這個機制的存在保證了PE文件中的函數(shù)可以被重復(fù)調(diào)用。導(dǎo)入表與導(dǎo)出表機制決定了本小節(jié)需要分為以下兩種情況加以討論。
PE文件的導(dǎo)入表與導(dǎo)出表中存在相應(yīng)函數(shù),可以直接對函數(shù)進(jìn)行調(diào)用,對更加安全的函數(shù)進(jìn)行重復(fù)調(diào)用。在可執(zhí)行文件調(diào)用了危險函數(shù)時,可以直接在原text段進(jìn)行匯編的修改從而達(dá)到修復(fù)程序的目的。由于不同的函數(shù)有不同的參數(shù)個數(shù)種類等獨特的用法,所以一般情況下需要對函數(shù)前后的調(diào)用進(jìn)行部分調(diào)整。比如程序中調(diào)用了一個gets函數(shù),這個函數(shù)會導(dǎo)致堆棧的溢出,為程序帶來不可控的風(fēng)險。而程序?qū)氡砼c導(dǎo)出表中加載了read函數(shù),此時需要使用read函數(shù)替換gets函數(shù)。對比兩個函數(shù)原型,可知兩個函數(shù)的調(diào)用參數(shù)不同,所以不可以直接進(jìn)行簡單的替換,需要對函數(shù)傳遞進(jìn)行適當(dāng)?shù)恼{(diào)整。在調(diào)整部分參數(shù)后,才可以使用read函數(shù)對gets函數(shù)進(jìn)行整體替換。
對于導(dǎo)入表和導(dǎo)出表未加載同類型可替換的函數(shù),一般在不借助其他方法的情況下,只能進(jìn)行小修小改。危險函數(shù)在沒有同等功能的其他函數(shù)可以進(jìn)行替換的情況下,只能對本身已有函數(shù)的參數(shù)進(jìn)行修改,使其盡可能安全可靠。比如printf函數(shù)沒有使用規(guī)范的格式化字符會導(dǎo)致格式化字符串的漏洞,從而泄露棧中敏感信息。修復(fù)printf漏洞時可以在參數(shù)傳遞處加入相應(yīng)的格式化字符參數(shù),以提高整體程序的穩(wěn)定性。相似的漏洞還有read函數(shù)造成的單字節(jié)溢出等漏洞。
本小節(jié)主要針對一些特定的函數(shù)修復(fù),這些函數(shù)通常自身存在一些安全隱患,但是功能比較簡單,一般可以直接通過幾條簡單的匯編語言函數(shù)功能重現(xiàn)。比如stcopy函數(shù),它在程序中很容易產(chǎn)生棧溢出漏洞,本節(jié)就將以它作為例子簡述函數(shù)的匯編代碼重寫方法。雖然Microsoft提供了更安全的strcpy_s()函數(shù)供編程人員調(diào)用,但是一些老舊程序并沒有載入這個函數(shù),所以不能采取上節(jié)中的方法對stcopy函數(shù)進(jìn)行直接替換。所以為了讓程序正常工作并不引發(fā)錯誤,需要對函數(shù)進(jìn)行重寫以實現(xiàn)拷貝的功能。拷貝函數(shù)重寫如圖1。
圖1 拷貝函數(shù)重寫
重寫函數(shù)匯編字節(jié)碼字節(jié)數(shù)往往會多于原函數(shù),額外空間的獲取就成了研究的關(guān)鍵。本節(jié)主要探討兩種獲取額外空間的方法,通過同義指令替換獲得額外空間與額外添加新段區(qū)。
由于編譯器的原因,每一個PE文件都會出現(xiàn)一些不那么簡潔的匯編指令。如moveax,0等需要傳遞常數(shù)的匯編指令,這類指令一般可以被簡化修改為xoreax,eax,進(jìn)而為整個二進(jìn)制文件節(jié)省出空間以便于插入補丁。
在所需空間很大的情況下,可以考慮為程序添加新的段區(qū)。新添段區(qū)的一般需要三步,首先需要PE頭部增添塊頭,其次為添加的區(qū)塊頭添加數(shù)據(jù)段,最后修正映像文件即可。這種方法可以為PE文件快速創(chuàng)建一個空的區(qū)塊段,以便填入補丁代碼,更改程序流程。
當(dāng)補丁代碼過于復(fù)雜時,可以通過編寫第三方dll并將動態(tài)鏈接庫注入二進(jìn)制文件中的方式來替換危險函數(shù)?;咀⑷肓鞒倘鐖D2所示。
程序調(diào)用動態(tài)鏈接庫需要先使用LoadLibrary函數(shù)對指定的動態(tài)鏈接庫進(jìn)行加載,然后再使用GetProcAddress獲取目標(biāo)鏈接庫中相關(guān)函數(shù)的地址。這兩個函數(shù)存在于Kernel32.dll中,絕大多數(shù)Windows程序都會加載ntdll.dll和kernel32.dll,故我們首先獲取kernel32.dll的基址,相關(guān)匯編代碼如圖3所示。
圖2 基本注入流程
圖3 相關(guān)匯編代碼
圖4 完成函數(shù)的調(diào)用
由于函數(shù)名字換算成ASCII碼后會占用比較大的空間,這里采用hash的方式對目標(biāo)函數(shù)進(jìn)行查找,在成功加載動態(tài)鏈接庫并獲取函數(shù)的地址后,就可以直接對函數(shù)進(jìn)行調(diào)用了。在目標(biāo)調(diào)用處插入調(diào)用代碼,即可完成函數(shù)的調(diào)用,如圖4。
將上述匯編編譯成二進(jìn)制字節(jié)碼后,就可對目標(biāo)程序進(jìn)行注入,方法與第二三小節(jié)基本相同,在函數(shù)擁有足夠的空間時,可選擇在源碼處進(jìn)行動態(tài)鏈接庫的加載和函數(shù)的調(diào)用,空間不足時,可為程序添加必要的段區(qū)。
本文主要探討了有關(guān)閉源二進(jìn)制可執(zhí)行文件的靜態(tài)補丁的基本理論方法,主要應(yīng)用于未加密Windows x86的可執(zhí)行文件。對于動態(tài)加密,程序的自校驗程序還需要做進(jìn)一步的研究。
[1]何迎生,段明秀. 32位Windows系統(tǒng)下的PE文件結(jié)構(gòu)及其應(yīng)用[J]. 吉首大學(xué)學(xué)報(自然科學(xué)版),2003(01).
[2]劉科. 計算機科學(xué)技術(shù)的發(fā)展現(xiàn)狀及發(fā)展趨勢展望 [J]. 信息記錄材料,2020,21(07).
[3]昝道廣. 計算機科學(xué)技術(shù)的應(yīng)用現(xiàn)狀及其發(fā)展前景探析 [J]. 通訊世界,2017(6):133.
[4]李晨燕. 計算機網(wǎng)絡(luò)信息安全問題策略探討[J]. 電子世界,2020(24).
[5]王眾魁. 探索計算機信息網(wǎng)絡(luò)安全技術(shù)及發(fā)展方向[J].電腦知識與技術(shù),2021,17(08).