那些年病毒用過的損招——攻擊調試器

昨天跟大家了反調試技術裡面的調試狀態監測機制,今天把坑繼續填完。病毒有時候檢測調試器其實一方面為了保護自己的同時還有一種情況就是面向調試器攻擊,也就是今天我們要說的干擾調試器的功能。n病毒常見的種植調試功能有以下幾個:線程本地儲存(TLS)回調、異常、插入中斷等等。這些技術的目的很明顯,就是為了不讓調試器正常工作。

0x01 線程本地儲存(TLS)回調:

我們知道在一個進程中,所有線程是共享同一個地址空間的。所以,如果一個變數是全局的或者是靜態的,那麼所有線程訪問的是同一份,如果某一個線程對其進行了修改,也就會影響到其他所有的線程。不過我們可能並不希望這樣,所以更多的推薦用基於堆棧的自動變數或函數參數來訪問數據,因為基於堆棧的變數總是和特定的線程相聯繫的。

不過如果某些時候(比如可能是特定設計的dll),我們就是需要依賴全局變數或者靜態變數,那有沒有辦法保證在多線程程序中能訪問而不互相影響呢?答案是有的。操作系統幫我們提供了這個功能——TLS線程本地存儲。TLS的作用是能將數據和執行的特定的線程聯繫起來。

其實有些人會認為程序載入到調試器後,會在第一條指令處暫停程序的運行,但是就跟昨天0xCC的問題一樣,事實不是你想像的那麼簡單,大部分調試器從程序PE頭部指定的入口點開始。TLS回調是被用來在程序入口點執行之前運行的代碼,因策這些代碼可以在調試器裡面悄悄地運行。如果說在調試代碼的時候過度依賴調試器,那麼你可能就會漏掉一些病毒的行為,因為請看前面。

TLS是Windows的一個儲存類,其中數據對象不是一個自動的堆棧變數,而是代碼中運行的每個線程的一個本地變數。簡單點說,TLS允許每個線程維護一個用TLS聲明的專有變數。

TLS回調函數是指,每當創建/終止進程的線程時會自動調用執行的函數。創建的主線程也會自動調用回調函數,且其調用執行先於EP代碼。貼一段代碼:

IMAGE_DATA_DIRECTORY[9]:IMAGE_TLS_DIRECTORYntypedef struct _IMAGE_TLS_DIRECTORY64 {n ULONGLONG StartAddressOfRawData;n ULONGLONG EndAddressOfRawData;n ULONGLONG AddressOfIndex; // PDWORDn ULONGLONG AddressOfCallBacks; // PIMAGE_TLS_CALLBACK *;n DWORD SizeOfZeroFill;n DWORD Characteristics;n} IMAGE_TLS_DIRECTORY64;ntypedef IMAGE_TLS_DIRECTORY64* PIMAGE_TLS_DIRECTORY64;nntypedef struct _IMAGE_TLS_DIRECTORY32 {n DWORD StartAddressOfRawData;n DWORD EndAddressOfRawData;n DWORD AddressOfIndex; // PDWORDn DWORD AddressOfCallBacks; // PIMAGE_TLS_CALLBACK *;n DWORD SizeOfZeroFill;n DWORD Characteristics;n} IMAGE_TLS_DIRECTORY32;ntypedef IMAGE_TLS_DIRECTORY32* PIMAGE_TLS_DIRECTORY32;nn#ifdef _WIN64ntypedef IMAGE_TLS_DIRECTORY64 IMAGE_TLS_DIRECTORY;ntypedef PIMAGE_TLS_DIRECTORY64 PIMAGE_TLS_DIRECTORY;n#elsentypedef IMAGE_TLS_DIRECTORY32 IMAGE_TLS_DIRECTORY;ntypedef PIMAGE_TLS_DIRECTORY32 PIMAGE_TLS_DIRECTORY;n#endifn

其實這段代碼描述的就是一個TLS的數據結構。

使用PEview就可以查看一個應用程序的PE信息,在應用程序實現TLS的情況下,可執行文件的PE頭部會包含一個.tls段,TLS提供了初始化和終止TLS數據對象的毀掉函數,Windows系統在執行程序正常的入口點之前運行這些回調函數。查看.tls段,你就能發現TLS的回調函數,一般來說正常程序不適用.tls段,如果可執行程序中按到了TLS段,那就說明有可能這貨用了反調試技術。

其實用IDA就可能分析TLS回調函數,一旦IDA完成了分析,你就能看到二進位的入口點,裡面就包含了TLS回調。

雖然說有時候調試器會在中斷程序入口點之前運行回調,但是TLS也可以在調試器裡面處理,可以通過修改調試器配置來避免這個問題。n由於這個方法知道的人太多了,病毒現在使用的越來越少,很多寫代碼不地道的童鞋經常會這麼玩。

0x02 使用異常機制反調試

前面說過,調試器利用中斷來產生異常,來執行跟斷點差不多的操作。我們可以使用SEH來截獲一個無條件跳轉,修改SEH鏈可以用用在對抗反彙編和反調試技術,所以利用SEH異常實現反調試我們就不細說了,推薦大家看一下這個:SEH反調試的實現與調試 - 小駒的專欄 - CSDN博客。

我們重點要講的是利用異常去干擾病毒分析師的操作,惡意代碼可以使用異常去破壞或者探測調試器,調試器捕獲異常之後,其實並不會立即將處理權返回被調試的進程來處理,大多數利用異常的反調試技術往往根據這個去探測調試器。多數調試器默認的設置是捕獲異常手不把異常傳回應用程序。那這麼說的話,如果調試器不能將異常結果正確的返回被調試進程,那麼這種異常失效就可以被進城內部的異常處理機制探測。給大家一個小提示,分析病毒的時候,要記得把od等調試器的選項改一下,設置為把所有異常傳遞給應用程序。

0x03 插入中斷來干擾調試

既然中斷可以干擾調試,那麼為什麼我們不手動插入一個去干擾呢。這種方法其實還有點傳統,不怎麼新鮮。通過在合法指令序列中插入中斷來破壞程序的正常運行確實可以干擾調試過程,而且如果調試器的設置有問題,會導致調試器停止運行,原因是這種中斷機制和調試其自身設置軟體斷點機制相同,也就是編譯器懵逼了,他不知道這個點是自己設置的還是人為插進去的。

(1)插入int 3:

int 3是調試器設置軟體斷點的指令,我們在合法的代碼段中插入0xCC讓調試器懵逼,讓他認為這個斷點時他自己設置的,單純的調試器就這麼被我們玩了。除此之外,雙位元組操作碼0xCD03也可以產生int 3中斷,這個方法可以去干擾windbg,在調試器外,0xCD03產生一個STATUS_BREAKPOINT異常,愛windbg調試器內,由於斷點通常是單位元組機器碼0xCC,所以windbg會捕獲這個斷點,然後將EIP+1B。這可能導致陳谷在被正常運行的windbg調試的時候執行了不同的指令集,但是od不會被這個方法影響。我們上代碼:

push offset continuenpush dword fs:[0]nmov fs:[0], espnint 3//被調試ncontinue//沒有被調試n

首先先設置一個新的SEH,然後調用int 3來強制代碼繼續執行。

(2)插入int 2d

int 2d和int 3蕾西,但是int 0x2D指令是針對內核態調試器的,int 0x2D是內核態設置斷點的方法。貼一段代碼:

push offset continuenpush dword fs:[0]nmov fs:[0], espnint 2d//被調試ncontinue//沒有被調試n

其實和上面的代碼差不多,只是執行的指令不同。

(2)插入ICEnICE是片內模擬器,icebp是ICE斷點指令,這個指令其實沒有被Intel公開,由於ICE不太方便在任何位置設置斷點,所以icebp指令被設計用來降低使用ICE設置斷點的難度。n運行icebp的時候會產生一個單步異常。如果通過單步調試追蹤程序,調試器會認為這是單步調試產生的異常,從而不執行之前設置的異常處理常式。我們可以利用這一點來干擾調試器。防止這種方法的話,調試的時候執行icebp指令的時候不要去單步執行。

0x04 調試器漏洞攻擊

有時候病毒為了防止被調試,會利用一些調試器里已知存在或者0day的漏洞去攻擊調試器不讓其正常運行。重點來說說PE頭漏洞和OutputDebugString漏洞。

(1)PE頭漏洞:

由於OD非常嚴格的遵循了微軟爸爸對於PE文件頭部的規定,在PE文件的頭部會存在一個叫做IMAGE_OPTIIONAL_HEADER的結構,跟下面這個圖說的一樣:

我們從這張圖可以看到,在DataDirectory中存在16個數組元素,其中我們應該注意一下NumberOfRvaAndSizes這個地方,值為0x99其實是一個無效值。NumberOfRvaAndSize屬性標示後面的DataDirectory元素的個數,DataDirectory數組標示在這個可執行文件中的什麼地方可以找到其他導入可執行模塊的位置,它位於可選頭部的末尾,是一個比IMAGE_DATS_DIRECTORY略大一些的數組。數組中每個結構目錄都指明了目錄相對的虛擬地址和大小。

DataDirectory數組大小被設置為IMAGE_NUMBEROF_DIRECTORY_ENTRIES,大小為0x10,因為DataDirectory數組不足以容納超過0x10個目錄項,所以當NumberOfRvaAndSizes大於0x10的時候,Windows載入器會忽略NumberOfRvaAndSizes。OD因為遵循了這個標準,而且OD不管NumberOfRvaAndSizes是什麼值都會去使用。因此,設置NumberOfRvaAndSizes為一個超過0x10的值,比如說0x99,這個時候會導致程序退出前,OD彈出一個窗口。

說到這裡,我們如果載入修改後的二進位文件,就會導致OD崩潰,當然Windbg和OD2.0不受影響,至於OD2.0嘛,,,呵呵。

對付這種反調試的方法,修改PE頭部可以實現,直接修改為0x10就可以防止了。

第二種方法跟PE的節頭部有關係,這種技術會導致OD在載入程序的過程中崩潰。文件內容包含的節包括代碼節、數據節、資源節和其他信息節,每個節都擁有一個IMAGE_SECTION_HEADER結構的頭部,跟下面這張圖說的一樣。

其實這張圖上SizeOfRawData這個0x77777777是無效的。VirtualSize和SizeOfRawData是其中比較重要的兩個屬性,根據微軟爸爸對PE的規定,VirtualSize應該包含載入到內存的節大小,SizeOfRawData應該包含節在硬碟當中的大小。Windows載入器使用VirtualSize和SizeOfRawData中的最小值講節數據映射到內存。如果SizeOfRawData大於VirtualSize的話,就把VirtualSize大小的數據複製進內存,忽略其他的數據。OD就僅僅使用SizeOfRawData,吧SizeOfRawData設置為0x77777777的話,會導致OD崩潰。。你死的好慘啊。n對抗這種技術的方法需要利用hex編輯器手動修改PE頭部並設置SizeOfRawData的值,讓其接近VirtualSize,但是微軟爸爸說了,VirtualSize必須是IMAGE_OPTIONAL_HEADER結構中的FileAlignment的整數倍。所以改的時候小心點。n(2)OutputDebugString漏洞利用nOD1.1有一個格式化字元串漏洞,OutputDebugString提供一個%s字元串的參數,如果OutputDebugString(「%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s」)這種調用的話,OD又掛了。。

0x03 總結:

反調試這個,必須得有足夠的耐心和耐力去識別這些坑,而且要擅長利用筆記本去記錄反調試位置和方法。大部分反調試技術都會涉及存取fs:[30h],也就是PEB的基址、調用一些查詢調試相關的API或者是檢測時鐘。

攻防這個事兒,是動態的,永遠都是和人在對抗。

推薦閱讀:

安卓逆向入門(一)
演算法逆向1——簡單演算法逆向
GCHQ 對卡巴斯基實驗室的商業軟體進行逆向工程,卡巴斯基實驗室是否可以提起法律訴訟?
為何我國可以生產j20,卻生產不出好的汽車?

TAG:计算机病毒 | 信息安全 | 逆向工程 |