脫殼基礎知識以及簡單應用

sfftlt_發表於2019-06-17
閒談: 最近因為需要,需要完成幾個脫殼的小例子,之前只是單純的瞭解過linux平臺下的漏洞利用,對於脫殼其實並不十分熟悉,拿到手的資料也只是流程性的操作,並不知道其中的原理,就學習了一下《加密與解密》裡面的脫殼部分,記錄下來,最近看到一個比較完整的脫殼教程,很不錯,推薦給大家。使用OllyDbg從零開始Cracking--安於此生

一 、殼概述

1 . 殼的概念

正常的軟體開發者不對開發的軟體進行保護的話,會導致開發的軟體原始碼很容易的被心懷不軌的人竊取到,而程式的殼便是抵抗第一過程的手段,就如自然界中的昆蟲殼的作用一樣,程式殼便是保護程式不被非法分子獲取甚至破壞的。

2 . 殼的工作過程

加殼過程是用加殼程式對源程式進行壓縮、加密、轉換指令等操作,然後一般是在程式的開頭加上一段殼程式。當加殼程式執行時,在程式開頭的殼程式會對加殼程式進行解壓縮、解密程式碼或者資料、解釋執行相應程式碼等過程,殼程式執行完之後一般會將加殼程式恢復成源程式,加殼程式便依舊可以執行相應的功能。

3 . 殼的幾種分類

壓縮殼:UPX 、 ASPack

壓縮殼主要是為了讓程式檔案佔用更小的體積,對於程式的加密保護方面側重性不是很強。

加密殼 : ASProtect 、Armadillo 、EXECryptor 、Themida

加密殼的功能更加側重安全性,對於加殼後程式的體積大小並不是十分關注,它能夠將程式的重要程式碼、資料等進行加密,甚至可以提供額外的功能,比如序號產生器制、使用次數、時間限制等。有的加密殼也涉及到了虛擬機器保護技術。

虛擬機器保護軟體: VMProtect

虛擬機器保護技術最近經常聽到,但是也是剛剛才弄明白是什麼意思,簡單來說也是軟體保護技術的一種,他可以將我們的程式程式碼轉換成位元組碼,然後跳轉到虛擬機器解釋執行,這樣程式碼保護程式就是相當高的了。《加密與解密》裡面的一句話形容的我覺得十分恰當而且有趣,“就好比把一篇文章從英文翻譯成中文後,發現文章裡的很多段落是用孟加拉語寫的一樣。”當然這樣的破解成本也是相當的高了。但是虛擬機器保護技術的運用會使得程式程式碼的效率降低,對此一般只會對於特定的關鍵部分程式碼進行虛擬機器保護處理,這是對安全和效率的權衡。

4 . 其他

雖然殼的保護效果很好,但是目前大多的商業軟體並不會透過殼來保護他們的軟體。首先因為加殼程式的相容性問題,某些程式加殼後可能因為相容性不能在某些平臺上使用;其次也因為目前大多數的防毒軟體會對程式軟體進行安全監測,其可以自動識別並脫去公開的商業殼,然後分析程式的安全性,但是如果是防毒軟體都能脫去的殼那麼對於軟體的保護也就是微乎其微了,如果使用防毒軟體不能識別的自己開發的個人的殼,防毒軟體出於安全考慮會自動識別軟體為病毒,因為對於程式加殼似乎並不能實現程式的推廣。

因此目前大多數廠商會設計包含複雜演算法的序列號、設定多個“暗樁”等技術來實現程式保護。

其實照我說,開源就完了,你開源我開源大家都開源,那樣就省的破解了。


二、脫殼技術


1 . 殼的載入過程

1. 儲存入口引數(pushad/popad pushfd/popfd)

2. 獲取殼本身使用的API地址

    關鍵函式:

    LoadLibraryA(W)、LoadLibraryExA(W) : 將制定的DLL檔案對映進記憶體,返回模組的控制程式碼

    GetmoduleHandle: 獲取已經對映進記憶體的dll檔案的控制程式碼,返回值控制程式碼

    GetProcAddress : 獲取dll內製定的函式地址,返回函式地址

3.解密源程式的各個區塊的資料

4. IAT的初始化

5. 重定位項的處理(DLL檔案脫殼)

6. HOOK API

7. 跳轉到OEP


2 . 脫殼三部曲

1. 尋找EOP

2. 抓取記憶體映像

3. 重建PE檔案

3 . 脫殼一般工具脫殼基礎知識以及簡單應用


4 . 尋找OEP


4.1 . 按照跨段指令尋找OEP

加殼後

脫殼基礎知識以及簡單應用

加殼前

脫殼基礎知識以及簡單應用

程式開頭

00413000 >  60              pushad  # 儲存暫存器的值
00413001    E8 C2000000     call    004130C8
00413006    2E:3001         xor     byte ptr cs:[ecx], al
00413009    0000            add     byte ptr [eax], al
0041300B    0000            add     byte ptr [eax], al
0041300D    0000            add     byte ptr [eax], al
0041300F    0000            add     byte ptr [eax], al
00413011    003E            add     byte ptr [esi], bh
00413013    3001            xor     byte ptr [ecx], al
00413015    002E            add     byte ptr [esi], ch
00413017    3001            xor     byte ptr [ecx], al
00413019    0000            add     byte ptr [eax], al
0041301B    0000            add     byte ptr [eax], al
0041301D    0000            add     byte ptr [eax], al
0041301F    0000            add     byte ptr [eax], al

申請記憶體

004130FE    50              push    eax
004130FF    FF55 2E         call    dword ptr [ebp+2E]
00413102    8985 B8000000   mov     dword ptr [ebp+B8], eax
00413108    6A 04           push    4
0041310A    68 00100000     push    1000
0041310F    FFB5 8F000000   push    dword ptr [ebp+8F]
00413115    6A 00           push    0
00413117    FF95 B8000000   call    dword ptr [ebp+B8]               ; KERNEL32.VirtualAlloc
0041311D    50              push    eax
0041311E    8985 C4000000   mov     dword ptr [ebp+C4], eax
00413124    8B9D 8B000000   mov     ebx, dword ptr [ebp+8B]

跳轉到殼的第二部分

0041310A    68 00100000     push    1000
0041310F    FFB5 8F000000   push    dword ptr [ebp+8F]
00413115    6A 00           push    0
00413117    FF95 B8000000   call    dword ptr [ebp+B8]
0041311D    50              push    eax
0041311E    8985 C4000000   mov     dword ptr [ebp+C4], eax
00413124    8B9D 8B000000   mov     ebx, dword ptr [ebp+8B]
0041312A    03DD            add     ebx, ebp
0041312C    50              push    eax
0041312D    53              push    ebx
0041312E    E8 04000000     call    00413137
00413133    5A              pop     edx
00413134    55              push    ebp
00413135  - FFE2            jmp     edx

獲取oep並跳轉

00020263    C742 50 0010000>mov     dword ptr [edx+50], 1000
0002026A    FF85 59030000   inc     dword ptr [ebp+359]
00020270    8B85 89020000   mov     eax, dword ptr [ebp+289]
00020276    0385 51030000   add     eax, dword ptr [ebp+351]
0002027C    0185 84020000   add     dword ptr [ebp+284], eax
00020282    61              popad
00020283    68 30114000     push    401130
00020288    C3              retn

跳轉到oep開啟正常程式邏輯

00401130  /.  55            push    ebp
00401131  |.  8BEC          mov     ebp, esp
00401133  |.  6A FF         push    -1
00401135  |.  68 B8504000   push    004050B8
0040113A  |.  68 FC1D4000   push    00401DFC                         ;  SE 處理程式安裝
0040113F  |.  64:A1 0000000>mov     eax, dword ptr fs:[0]
00401145  |.  50            push    eax
00401146  |.  64:8925 00000>mov     dword ptr fs:[0], esp
0040114D  |.  83EC 58       sub     esp, 58
00401150  |.  53            push    ebx

檢視程式的整個section資訊位於.text內。

Memory map
地址       大小       屬主       區段       包含          型別   訪問      初始訪問  已對映為
00010000   00010000                                       Map    RW        RW
00020000   00001000                                       Priv   RW        RW
00030000   00001000                                       Priv   RW        RW
00040000   00016000                                       Map    R         R
00095000   0000B000                                       Priv   RW  保護    RW
0019C000   00002000                                       Priv   RW  保護    RW
0019E000   00002000                         堆疊 於  主?      Priv   RW  保護    RW
001A0000   00004000                                       Map    R         R
001B0000   00002000                                       Priv   RW        RW
001F5000   0000B000                                       Priv   RW  保護    RW
00274000   00004000                                       Priv   RW        RW
00278000   00003000                         資料塊 於  ?      Priv   RW        RW
0027B000   00003000                         資料塊 於  ?      Priv   RW        RW
0027E000   00001000                         資料塊 於  ?      Priv   RW        RW
00400000   00001000   RebPE                 PE 檔案頭        Imag   R         RWE
00401000   00004000   RebPE      .text      程式碼            Imag   R         RWE
00405000   00001000   RebPE      .rdata                   Imag   R         RWE
00406000   00003000   RebPE      .data      資料            Imag   R         RWE
00409000   0000A000   RebPE      .rsrc      資源            Imag   R         RWE
00413000   00007000   RebPE      .pediy     SFX,輸入表       Imag   R         RWE


4.2 用記憶體訪問斷點尋找oep

OD可以對程式設定記憶體訪問斷點,這樣當程式開始讀取或者執行相應程式碼時,程式會中斷,並且把斷點清除,利用這個辦法我們可以尋找oep。

因為普通的壓縮、加密殼等是按區段處理資料的,一般的順序是.text 、 .rdate、.data 、 .rsrc的順序處理資料,我們可以現在.rsrc處設定按F2設定記憶體訪問斷點,當程式斷下後可表明.text段已經處理完畢,那麼我們可以在接著對.text段設定記憶體訪問斷點,這樣當程式執行.text程式碼時便會中斷在oep處。

對data端設定記憶體訪問斷點

脫殼基礎知識以及簡單應用

然後F9執行到中斷處

00413145    A4              movs    byte ptr es:[edi], byte ptr [esi>
00413146    B3 02           mov     bl, 2
00413148    E8 6D000000     call    004131BA
0041314D  ^ 73 F6           jnb     short 00413145
0041314F    33C9            xor     ecx, ecx
00413151    E8 64000000     call    004131BA
00413156    73 1C           jnb     short 00413174
00413158    33C0            xor     eax, eax
0041315A    E8 5B000000     call    004131BA
0041315F    73 23           jnb     short 00413184
00413161    B3 02           mov     bl, 2
00413163    41              inc     ecx

在對text端設定記憶體訪問斷點

脫殼基礎知識以及簡單應用

按F9執行到中斷處,會停在oep的位置。

00401130  /.  55            push    ebp
00401131  |.  8BEC          mov     ebp, esp
00401133  |.  6A FF         push    -1
00401135  |.  68 B8504000   push    004050B8
0040113A  |.  68 FC1D4000   push    00401DFC                         ;  SE 處理程式安裝
0040113F  |.  64:A1 0000000>mov     eax, dword ptr fs:[0]
00401145  |.  50            push    eax
00401146  |.  64:8925 00000>mov     dword ptr fs:[0], esp
0040114D  |.  83EC 58       sub     esp, 58
00401150  |.  53            push    ebx
00401151  |.  56            push    esi
00401152  |.  57            push    edi
00401153  |.  8965 E8       mov     dword ptr [ebp-18], esp
00401156  |.  FF15 28504000 call    dword ptr [405028]               ;  KERNEL32.GetVersion


4.3 根據棧平衡原理尋找OEP

方法一 :殼程式在執行前,會將程式的暫存器的值利用push指令壓入棧內,那麼我可以對壓入的棧地址設定硬體訪問斷點,當訪問這個地址時,說明殼程式正在恢復暫存器的值,也就是說即將跳轉到OEP處。

首先執行殼程式的push指令,使得暫存器現場壓入棧內。

00413000 >  60              pushad
00413001    E8 C2000000     call    004130C8
00413006    2E:3001         xor     byte ptr cs:[ecx], al
00413009    0000            add     byte ptr [eax], al
0041300B    0000            add     byte ptr [eax], al

此時棧內的資訊如下

0019FF64   00413000  ASCII "`杪"
0019FF68   00413000  ASCII "`杪"
0019FF6C   0019FF94
0019FF70   0019FF84
0019FF74   003A9000
0019FF78   00413000  ASCII "`杪"
0019FF7C   00413000  ASCII "`杪"

對0x19ff64設定硬體訪問斷點

脫殼基礎知識以及簡單應用

然後F9

0002027C    0185 84020000   add     dword ptr [ebp+284], eax
00020282    61              popad
00020283    68 30114000     push    401130
00020288    C3              retn

程式在執行完popad後停下,可以看到已經來到了OEP附近。


方法二 : 我們已經知道殼程式執行前後的esp地址是不變的,而且正常程式的第一條指令通常是push ebp, 那麼我們可以在殼程式剛開始時記錄stack地址,然後對於stack-4地址處設定硬體寫入斷點,這樣程式執行到 中斷時,便會在OEP處停下。

程式剛開始執行時的stack地址如下

0019FF84   747762C4  返回到 KERNEL32.747762C4
0019FF88   00205000
0019FF8C   747762A0  KERNEL32.BaseThreadInitThunk
0019FF90   F648F892
0019FF94  /0019FFDC

我們隊0x19ff80地址設定硬體寫入斷點。

脫殼基礎知識以及簡單應用

然後F9程式便會停在oep處。

00401130  /.  55            push    ebp
00401131  |.  8BEC          mov     ebp, esp
00401133  |.  6A FF         push    -1
00401135  |.  68 B8504000   push    004050B8
0040113A  |.  68 FC1D4000   push    00401DFC                         ;  SE 處理程式安裝

程式直接停在了 mov ebp , esp處。


4.4 根據編譯語言特點尋找OEP

各類語言編譯的檔案入口點都有自己的特點。

比如在VC6的啟動部分有GetCommandLineA(W)、GetVersion函式等。

我們對GetVersion函式設定斷點,中斷兩次後,就可以回到OEP附近。

4.5 最後一次異常法

程式在解密或者解壓縮時,會產生許多次異常,那麼最後一次異常的後面往往不遠處就會跳轉到OEP。 

5 . 抓取記憶體映像

也稱dump,指的是把指定記憶體地址的映像轉存起來。

工具 : LordPE , 將記憶體中的資料與磁碟中的檔案PE頭連結起來。

基本用法 :

圖中紅框框出來的部分要勾選,表示連結磁碟檔案中的檔案頭

脫殼基礎知識以及簡單應用

然後準備dump的時候 , 選擇dump full選項。


針對一些Anti-dump技術:

糾正SizeOfImage

我們可以使用LordPE的corrct ImageSize功能,其功能室直接在PE檔案頭中的SizeOfImage來實現的。

脫殼基礎知識以及簡單應用

修改記憶體屬性

某些Anti-dump技術對於檔案的PE檔案頭等做了許可權限制,比如不允許訪問之類的,這樣LordPE便不能正常dump程式,那麼我們就可以利用OD載入程式,然後切換到Alt + M視窗, 對於沒有指定許可權區段賦予指定許可權。

脫殼基礎知識以及簡單應用

然後再利用LordPE正常進行dump即可。

6 . 重建輸入表

一般做法是指,跟蹤加殼程式對IAT的處理過程,修改相關指令,阻止外殼加密API,獲得未加密的IAT。

6.1 確定IAT的地址以及大小

隨便檢視一個call 函式 , 檢視[00405020] = 0x7477cfc0 , 相當於elf檔案的got表。

脫殼基礎知識以及簡單應用

然後我們檢視資料欄,搜尋0x405020 , IAT的結尾處是一個DOWRD的\x00。

脫殼基礎知識以及簡單應用

所以我們確定IAT的大小是0xB8。


6.2 根據IAT重建輸入表(手動)

有點繁瑣,沒有動手去試,但是原理還是要鬧清楚的嗎,建議大家去看一下相關的資料,但是要注意的是 , 最好不要把改變FirstThunk指向的IAT的地址。


6.3 用ImportREC重建輸入表

前提條件: 目標程式已經被dump,目標程式正在執行,已知OEP或者IAT的偏移量以及大小。

首先我們讓程式執行到OEP,然後dump記憶體映像,記得要修正imageSize。

脫殼基礎知識以及簡單應用

然後用ImportREC開啟程式,輸入OEP使得程式自動獲取IAT。

脫殼基礎知識以及簡單應用

然後選擇我們dump出來程式進行修復。

脫殼基礎知識以及簡單應用


6.4 處理不連續的IAT

有些程式的IAT有可能被斷成幾份,不連續。

例子 : TestWin.exe

查殼

脫殼基礎知識以及簡單應用

這一個沒殼,直接不用脫,直接dump就可以直接用,也沒有dump的必要。就是介紹一下不連續的IAT怎麼辦。

查詢IAT的起始地址與size

脫殼基礎知識以及簡單應用

因為有多個不連續IAT,這裡只能找到最開始的IAT,最好的辦法就是將size改的足夠大,然後讓Im.portREC自動分析。

脫殼基礎知識以及簡單應用

然後點選show invalid , 分析無效資訊 , 在無效資訊處點選右鍵cut chunk ,得到正確的IAT表資訊,然後選擇dump進行fix。

脫殼基礎知識以及簡單應用


6.5 修復函式

有時會出現函式名稱識別出錯的問題,利用ImportICE可以修改。



相關文章