殼的工作原理脫殼
阿新 • • 發佈:2019-01-30
相關名詞
1)Entry Point (入口點)
PE格式的可執行檔案的執行時的入口點,即是PE格式中的Entry Point。
用PEditor或者LordPE之類的PE檢視工具看看NotePad.exe,你就會看到Entry Point的值。
也就是說NotePad.exe在執行時的第一行程式碼的地址應該就是這個值。(當然應該加上基地址)
2)Section (節區)
PE格式檔案是按節區進行規劃組織的,不同的節區一般儲存的資料的作用也不相同。通常使用預設方式編譯的程式檔案,有CODE/DATA/TLS/.text/.data/.tls/.rsrc/.rdata/.edata/.reloc等不同的名稱,有的用於儲存程式程式碼,如CODE和.text節區,有的用於儲存程式中的變數的,如DATA/.data節區,有的儲存重定位資訊,如.reloc,有的用於儲存資源資料,如.rsrc。等等等等,當然這是預設情況下編譯器產生的結構。
而節區名稱和節區中的資料其實沒有必然的聯絡,節區中儲存的資料也沒有什麼硬性的限制。所以你可以在編譯時用開關引數改變這些情況。
3)ImageBase (基地址)
不僅程式檔案按節區規劃,而且程式檔案在執行時Windows系統也是按節區載入的。那麼每一塊的節區的順序如何?起始的地址是什麼呢?
這就由基地址決定。在程式的檔案頭部儲存了每個節區的描述資訊,比如有前面提到的節區名稱,還有節區的大小,以及節區的相對虛擬地址(RVA)。
如果我們把節區的相對虛擬地址(RVA)加上基地址(ImageBase)就可以知道節區在記憶體中的虛擬地址(VA)了。Windows系統就是按照這個要求來載入各個節區的。這樣Windows系統依次把各個節區放到了它相應的虛擬地址空間。
所以如果我們把相對虛擬地址(RVA)看成是座標的偏移量的話,那麼ImageBase就是原點了。有了這個原點,一切都簡單了。
好了有了簡要的介紹,我們來看看殼的載入過程吧。注意這裡說的是一般情況,不特指某個殼,如果那樣的話,我想那大概是洋洋灑灑幾萬字的了,好象我沒有寫過這麼長的。雖然我的五筆練得還不錯。
1)獲取殼自己所需要使用的API地址
如果你用PE檢視工具看看加殼後的程式檔案,會發現未加殼的程式檔案和加殼後的程式檔案的Import Table不太一樣,加殼後的Import Table一般所引入的DLL和API很少,甚至只有Kernel32.dll以及GetProcAddress這個API。
我想你不會認為殼只用這個API就可以做所有的事吧。殼還需要很多其他的API來完成它的工作。當然他並不想讓你知道他想用哪個API,所以一般他只是在殼的程式碼中動態載入這些API,而只把一些你嗅不過什麼味道的幾個API放在Import Table中。當然這其中殼可能會用到一些Anti技術,不過這和本文主旨無關,所以就不說了。
2)解密原程式的各個節區(Section)的資料
殼出於保護原程式程式碼和資料的目的,一般都會加密原程式檔案的各個節區。既然是加密儲存,那麼在程式執行時你總不能也保持加密狀態吧,所以解密是殼必做的工作之一。一般殼按節區加密的,那麼在解密時也按節區解密,並且把解密的節區資料按照節區的定義放在合適的記憶體位置。
如果加殼時用到了壓縮技術,那麼在解密之前還有一道工序,當然是解壓縮。這也是一些殼的特色之一,比如說原來的程式檔案未加殼時1-2M大小,加殼後反而只有幾百K,這種瘦身技術當然會吸引了不少眼球。
3)重定位
前面我們提到了ImageBase,即程式的基地址,當然這只是程式檔案中宣告的,程式執行時能夠保證系統一定滿足你的要求嗎?
對於EXE的程式檔案來說,Windows系統會盡量滿足你的要求。
比如一般EXE檔案的基地址為0x400000,而執行時Windows系統提供給程式的基地址也同樣是0x400000。在這種情況下就不需要進行地址"重定位"了。
由於不需要對EXE檔案進行"重定位",所以很多殼在加殼時把原程式檔案中用於儲存重定位資訊的節區乾脆也去掉了,這樣使得加殼後的檔案更加小巧。有些工具提供Wipe Reloc的功能,其實就是這個作用。
不過對於DLL的動態連結庫檔案來說,Windows系統沒有辦法保證每一次DLL執行時提供相同的基地址。這樣"重定位"就很重要了。
此時殼中也需要提供進行"重定位"的程式碼,否則原程式中的程式碼是無法正常執行起來的。從這點來說,加殼的DLL比加殼的EXE更難修正。
4)HOOK-API
我們知道程式檔案中的Import Table的作用是讓Windows系統在程式執行時提供API的實際地址給程式使用。在程式的第一行程式碼執行之前,Windows系統就完成了這個工作。
而殼一般都修改了原程式檔案的Import Table,那麼原程式檔案的Import Table由誰來處理呢?這當然由殼來自己處理了,因此殼不得不模仿Windows系統的工作來填充Import Table中相關的資料。
Import Table結構中與實際執行相關的主要是IAT結構,這個結構中用於儲存API的實際地址,因此殼所需要的就是填充這個結構中的資料。
不過殼不是填充這些實際的API地址,而是填充殼中用來HOOK-API的程式碼的地址。
這樣殼中的程式碼一旦完成了載入工作,在進入原程式的程式碼之後,仍然能夠間接地獲得程式的控制權。
因為程式總是需要與系統打交道,與系統交道的途徑是API,而API的地址已經替換成了殼的HOOK-API的地址,那麼每一次程式與系統打交道,都會讓殼的程式碼獲得一次控制權,一來殼可以進行反跟蹤繼續保護軟體,二來可以完成某些特殊的任務。其實這就是所謂HOOK技術。
5)最後當然是跳轉到程式原入口點
這個大家比較熟悉,找的就是它。脫殼時大多數也是在這個時候。從這個時候起殼要把控制權交還給原程式了。
以上是一個簡單的總結。這代表了大多數殼的載入過程,不過特殊的不包括在內,介紹一下讓大家瞭解一些。當然還有一些殼充分利用了PE結構的特點,比如利用TLS載入的特點也挺有趣。
一點經驗
手動脫殼不一定要在程式原入口點。沒有人規定你一定要在那裡動手。只要你能保證脫殼後程序正常執行就行了。
當然我們必需得知道這個原入口點的值,否則我們無法修復程式檔案中的Entry Point的值了。
那麼手動脫殼還有比較常用的地方嗎?有就是前面所提到的第2步完成後。此時Section資料已經解密,而且殼還沒有來得及進行Hook-Api,很多殼圖簡單沒有把原程式的Import Table破壞掉,這個時候正是我們下手的好時機。
要發現這個地方也很容易,一般重定位程式碼的特徵是很明顯的。特別是有些殼有壓縮功能的時候,更是容易發現。這個時候脫出來的檔案組織一下可以達到和原程式幾乎相同的大小。而不是通常的記憶體映象大小。
1)Entry Point (入口點)
PE格式的可執行檔案的執行時的入口點,即是PE格式中的Entry Point。
用PEditor或者LordPE之類的PE檢視工具看看NotePad.exe,你就會看到Entry Point的值。
也就是說NotePad.exe在執行時的第一行程式碼的地址應該就是這個值。(當然應該加上基地址)
2)Section (節區)
PE格式檔案是按節區進行規劃組織的,不同的節區一般儲存的資料的作用也不相同。通常使用預設方式編譯的程式檔案,有CODE/DATA/TLS/.text/.data/.tls/.rsrc/.rdata/.edata/.reloc等不同的名稱,有的用於儲存程式程式碼,如CODE和.text節區,有的用於儲存程式中的變數的,如DATA/.data節區,有的儲存重定位資訊,如.reloc,有的用於儲存資源資料,如.rsrc。等等等等,當然這是預設情況下編譯器產生的結構。
而節區名稱和節區中的資料其實沒有必然的聯絡,節區中儲存的資料也沒有什麼硬性的限制。所以你可以在編譯時用開關引數改變這些情況。
3)ImageBase (基地址)
不僅程式檔案按節區規劃,而且程式檔案在執行時Windows系統也是按節區載入的。那麼每一塊的節區的順序如何?起始的地址是什麼呢?
這就由基地址決定。在程式的檔案頭部儲存了每個節區的描述資訊,比如有前面提到的節區名稱,還有節區的大小,以及節區的相對虛擬地址(RVA)。
如果我們把節區的相對虛擬地址(RVA)加上基地址(ImageBase)就可以知道節區在記憶體中的虛擬地址(VA)了。Windows系統就是按照這個要求來載入各個節區的。這樣Windows系統依次把各個節區放到了它相應的虛擬地址空間。
所以如果我們把相對虛擬地址(RVA)看成是座標的偏移量的話,那麼ImageBase就是原點了。有了這個原點,一切都簡單了。
好了有了簡要的介紹,我們來看看殼的載入過程吧。注意這裡說的是一般情況,不特指某個殼,如果那樣的話,我想那大概是洋洋灑灑幾萬字的了,好象我沒有寫過這麼長的。雖然我的五筆練得還不錯。
1)獲取殼自己所需要使用的API地址
如果你用PE檢視工具看看加殼後的程式檔案,會發現未加殼的程式檔案和加殼後的程式檔案的Import Table不太一樣,加殼後的Import Table一般所引入的DLL和API很少,甚至只有Kernel32.dll以及GetProcAddress這個API。
我想你不會認為殼只用這個API就可以做所有的事吧。殼還需要很多其他的API來完成它的工作。當然他並不想讓你知道他想用哪個API,所以一般他只是在殼的程式碼中動態載入這些API,而只把一些你嗅不過什麼味道的幾個API放在Import Table中。當然這其中殼可能會用到一些Anti技術,不過這和本文主旨無關,所以就不說了。
2)解密原程式的各個節區(Section)的資料
殼出於保護原程式程式碼和資料的目的,一般都會加密原程式檔案的各個節區。既然是加密儲存,那麼在程式執行時你總不能也保持加密狀態吧,所以解密是殼必做的工作之一。一般殼按節區加密的,那麼在解密時也按節區解密,並且把解密的節區資料按照節區的定義放在合適的記憶體位置。
如果加殼時用到了壓縮技術,那麼在解密之前還有一道工序,當然是解壓縮。這也是一些殼的特色之一,比如說原來的程式檔案未加殼時1-2M大小,加殼後反而只有幾百K,這種瘦身技術當然會吸引了不少眼球。
3)重定位
前面我們提到了ImageBase,即程式的基地址,當然這只是程式檔案中宣告的,程式執行時能夠保證系統一定滿足你的要求嗎?
對於EXE的程式檔案來說,Windows系統會盡量滿足你的要求。
比如一般EXE檔案的基地址為0x400000,而執行時Windows系統提供給程式的基地址也同樣是0x400000。在這種情況下就不需要進行地址"重定位"了。
由於不需要對EXE檔案進行"重定位",所以很多殼在加殼時把原程式檔案中用於儲存重定位資訊的節區乾脆也去掉了,這樣使得加殼後的檔案更加小巧。有些工具提供Wipe Reloc的功能,其實就是這個作用。
不過對於DLL的動態連結庫檔案來說,Windows系統沒有辦法保證每一次DLL執行時提供相同的基地址。這樣"重定位"就很重要了。
此時殼中也需要提供進行"重定位"的程式碼,否則原程式中的程式碼是無法正常執行起來的。從這點來說,加殼的DLL比加殼的EXE更難修正。
4)HOOK-API
我們知道程式檔案中的Import Table的作用是讓Windows系統在程式執行時提供API的實際地址給程式使用。在程式的第一行程式碼執行之前,Windows系統就完成了這個工作。
而殼一般都修改了原程式檔案的Import Table,那麼原程式檔案的Import Table由誰來處理呢?這當然由殼來自己處理了,因此殼不得不模仿Windows系統的工作來填充Import Table中相關的資料。
Import Table結構中與實際執行相關的主要是IAT結構,這個結構中用於儲存API的實際地址,因此殼所需要的就是填充這個結構中的資料。
不過殼不是填充這些實際的API地址,而是填充殼中用來HOOK-API的程式碼的地址。
這樣殼中的程式碼一旦完成了載入工作,在進入原程式的程式碼之後,仍然能夠間接地獲得程式的控制權。
因為程式總是需要與系統打交道,與系統交道的途徑是API,而API的地址已經替換成了殼的HOOK-API的地址,那麼每一次程式與系統打交道,都會讓殼的程式碼獲得一次控制權,一來殼可以進行反跟蹤繼續保護軟體,二來可以完成某些特殊的任務。其實這就是所謂HOOK技術。
5)最後當然是跳轉到程式原入口點
這個大家比較熟悉,找的就是它。脫殼時大多數也是在這個時候。從這個時候起殼要把控制權交還給原程式了。
以上是一個簡單的總結。這代表了大多數殼的載入過程,不過特殊的不包括在內,介紹一下讓大家瞭解一些。當然還有一些殼充分利用了PE結構的特點,比如利用TLS載入的特點也挺有趣。
一點經驗
手動脫殼不一定要在程式原入口點。沒有人規定你一定要在那裡動手。只要你能保證脫殼後程序正常執行就行了。
當然我們必需得知道這個原入口點的值,否則我們無法修復程式檔案中的Entry Point的值了。
那麼手動脫殼還有比較常用的地方嗎?有就是前面所提到的第2步完成後。此時Section資料已經解密,而且殼還沒有來得及進行Hook-Api,很多殼圖簡單沒有把原程式的Import Table破壞掉,這個時候正是我們下手的好時機。
要發現這個地方也很容易,一般重定位程式碼的特徵是很明顯的。特別是有些殼有壓縮功能的時候,更是容易發現。這個時候脫出來的檔案組織一下可以達到和原程式幾乎相同的大小。而不是通常的記憶體映象大小。