易格式可執行檔案脫殼方法一則

看雪資料發表於2004-11-16

易格式可執行檔案脫殼方法一則

前言:

易格式殼執行檔案就是用易語言編譯生成的可執行檔案。由於易語言的全中文化和簡單易用,現在有不少軟體都是用它寫的。我們今天就來研究一下關於這種檔案的脫殼問題。有人說:脫殼的方法還用你說!是的,我脫殼的水平的確很爛,不過易格式可執行檔案和標準的PE檔案有所不同(植於不同在何處下面還要詳細敘述),所以平常我們用的脫殼的辦法就顯得不是很方便了,於是就有了這篇文章。其實關於易格式可執行檔案脫殼的方法我以前在《易格式初步分析》一文中作過說明,不過那個說明實在是太簡單了,而且當時還沒有稱手的工具,所以現在準備詳細的說一下那個方法。

起因:

前幾天有人在罈子裡放出一個程式請求。據帖子裡面留言,是用“軟體保護神”加的殼。下載回來開啟一看,是一個示例程式,估計是想檢驗加殼強度吧?

要脫殼的軟體:

由於要脫殼的不是什麼正式的軟體,也就沒有必要把地址放上來了,自己用易語言寫一個程式再加上殼就可以了。

原理及分析:

易格式可執行檔案是一種特殊的可執行檔案。它由PE骨骼和易格式原體兩部分組成。PE部分用來釋放、尋找、載入易格式裝載器。易格式原體部分包含了程式執行所需要的程式碼、資料和資源。易格式可執行檔案在被PE裝載器裝載後,首先PE部分尋找並載入易格式裝載器(易格式裝載器在krnln.fne這個檔案裡,這個檔案實質上是一個DLL檔案),然後把執行權轉交給易格式裝載器。易格式裝載器得到控制權後,載入易格式原體,載入介面地址,並對需要的部分進行重定位。接著又把執行權轉交給了易格式的程式碼部分。到這個時候,程式才開始正式的運作,你在易語言中寫的程式碼才開始被執行。有人認為易語言編譯出來的程式碼不是全編譯,是解釋執行的。其實透過載入器和支援庫的做法只是為了可移植性考慮的做法。至於程式碼是否是全編譯的,自己跟蹤一下就知道了:)不好意思,有點跑題了,我們接著剛才的說。說到這兒,不知道有沒有人發現問題:因為易格式最終是要被易格式裝載器裝載的,所以在被裝載的時候,易格式的原體部分應該是完整且正確的,否則裝載過程將發生錯誤。這也就是說為什麼在用ASProtect對易格式殼執行檔案加殼時要選擇“保留附加資料”的原因了。根據前面的分析,只要能獲得易格式原體,然後我們再給它加上一副PE骨骼,一切就完成了。這樣就給了我們可乘之機。在裝載器裝載完成後,我們就可以從記憶體中把易格式提取出來。
但是事情不會這麼簡單的,經過試驗(過程下面會詳細描述,這裡就不多說了),最終合成出了一個可執行檔案,執行的時候卻出現了非法操作。難道上面的分析有誤?調出OD跟蹤:

00401000 >/$ E8 06000000    CALL foo24.0040100B
00401005  |. 50             PUSH EAX                                 ; /ExitCode
00401006  \. E8 BB010000    CALL <JMP.&KERNEL32.ExitProcess>         ; \ExitProcess
0040100B  /$ 55             PUSH EBP
0040100C  |. 8BEC           MOV EBP,ESP
0040100E  |. 81C4 F0FEFFFF  ADD ESP,-110
00401014  |. E9 83000000    JMP foo24.0040109C
00401019  |. 6B 72 6E 6C 6E>ASCII "krnln.fnr",0
00401023  |. 6B 72 6E 6C 6E>ASCII "krnln.fne",0
0040102D  |. 47 65 74 4E 65>ASCII "GetNewSock",0
00401038  |. 53 6F 66 74 77>ASCII "Software\FlySky\"
00401048  |. 45 5C 49 6E 73>ASCII "E\Install",0
00401052  |. 50 61 74 68 00>ASCII "Path",0
00401057  |. 4E 6F 74 20 66>ASCII "Not found the ke"
00401067  |. 72 6E 65 6C 20>ASCII "rnel library or "
00401077  |. 74 68 65 20 6B>ASCII "the kernel libra"
00401087  |. 72 79 20 69 73>ASCII "ry is invalid!",0
00401096  |. 45 72 72 6F 72>ASCII "Error",0
0040109C  |> 8D85 FCFEFFFF  LEA EAX,DWORD PTR SS:[EBP-104]
004010A2  |. 50             PUSH EAX
004010A3  |. E8 44010000    CALL foo24.004011EC
004010A8  |. 68 19104000    PUSH foo24.00401019                      ; /StringToAdd = "krnln.fnr"
004010AD  |. 8D85 FCFEFFFF  LEA EAX,DWORD PTR SS:[EBP-104]           ; |
004010B3  |. 50             PUSH EAX                                 ; |ConcatString
004010B4  |. E8 25010000    CALL <JMP.&KERNEL32.lstrcatA>            ; \lstrcatA
004010B9  |. 50             PUSH EAX                                 ; /FileName
004010BA  |. E8 19010000    CALL <JMP.&KERNEL32.LoadLibraryA>        ; \LoadLibraryA 載入核心支援庫
004010BF  |. 85C0           TEST EAX,EAX
004010C1  |. 0F85 9E000000  JNZ foo24.00401165
004010C7  |. 8D85 F4FEFFFF  LEA EAX,DWORD PTR SS:[EBP-10C]
004010CD  |. 50             PUSH EAX                                 ; /pHandle
004010CE  |. 68 19000200    PUSH 20019                               ; |Access = KEY_READ
004010D3  |. 6A 00          PUSH 0                                   ; |Reserved = 0
004010D5  |. 68 38104000    PUSH foo24.00401038                      ; |Subkey = "Software\FlySky\E\Install"
004010DA  |. 68 01000080    PUSH 80000001                            ; |hKey = HKEY_CURRENT_USER
004010DF  |. E8 36010000    CALL <JMP.&ADVAPI32.RegOpenKeyExA>       ; \RegOpenKeyExA 從登錄檔中查詢核心支援庫的位置
004010E4  |. 83F8 00        CMP EAX,0
004010E7  |. 0F85 B8000000  JNZ foo24.004011A5
004010ED  |. C785 F0FEFFFF >MOV DWORD PTR SS:[EBP-110],103
004010F7  |. 8D85 F0FEFFFF  LEA EAX,DWORD PTR SS:[EBP-110]
004010FD  |. 50             PUSH EAX                                 ; /pBufSize
004010FE  |. 8D85 FCFEFFFF  LEA EAX,DWORD PTR SS:[EBP-104]           ; |
00401104  |. 50             PUSH EAX                                 ; |Buffer
00401105  |. 6A 00          PUSH 0                                   ; |pValueType = NULL
00401107  |. 6A 00          PUSH 0                                   ; |Reserved = NULL
00401109  |. 68 52104000    PUSH foo24.00401052                      ; |ValueName = "Path"
0040110E  |. FFB5 F4FEFFFF  PUSH DWORD PTR SS:[EBP-10C]              ; |hKey
00401114  |. E8 07010000    CALL <JMP.&ADVAPI32.RegQueryValueExA>    ; \RegQueryValueExA
00401119  |. 50             PUSH EAX
0040111A  |. FFB5 F4FEFFFF  PUSH DWORD PTR SS:[EBP-10C]              ; /hKey
00401120  |. E8 EF000000    CALL <JMP.&ADVAPI32.RegCloseKey>         ; \RegCloseKey
00401125  |. 58             POP EAX
00401126  |. 83F8 00        CMP EAX,0
00401129  |. 75 7A          JNZ SHORT foo24.004011A5
0040112B  |. 8D85 FCFEFFFF  LEA EAX,DWORD PTR SS:[EBP-104]
00401131  |. 50             PUSH EAX                                 ; /String
00401132  |. E8 AD000000    CALL <JMP.&KERNEL32.lstrlenA>            ; \lstrlenA
00401137  |. 8D9D FCFEFFFF  LEA EBX,DWORD PTR SS:[EBP-104]
0040113D  |. 03D8           ADD EBX,EAX
0040113F  |. 4B             DEC EBX
00401140  |. 803B 5C        CMP BYTE PTR DS:[EBX],5C
00401143  |. 74 05          JE SHORT foo24.0040114A
00401145  |. 66:C703 5C00   MOV WORD PTR DS:[EBX],5C
0040114A  |> 68 23104000    PUSH foo24.00401023                      ; /StringToAdd = "krnln.fne"
0040114F  |. 8D85 FCFEFFFF  LEA EAX,DWORD PTR SS:[EBP-104]           ; |
00401155  |. 50             PUSH EAX                                 ; |ConcatString
00401156  |. E8 83000000    CALL <JMP.&KERNEL32.lstrcatA>            ; \lstrcatA
0040115B  |. 50             PUSH EAX                                 ; /FileName
0040115C  |. E8 77000000    CALL <JMP.&KERNEL32.LoadLibraryA>        ; \LoadLibraryA
00401161  |. 85C0           TEST EAX,EAX
00401163  |. 74 40          JE SHORT foo24.004011A5
00401165  |> 8985 F8FEFFFF  MOV DWORD PTR SS:[EBP-108],EAX
0040116B  |. 68 2D104000    PUSH foo24.0040102D                      ; /ProcNameOrOrdinal = "GetNewSock"
00401170  |. 50             PUSH EAX                                 ; |hModule
00401171  |. E8 5C000000    CALL <JMP.&KERNEL32.GetProcAddress>      ; \GetProcAddress
00401176  |. 85C0           TEST EAX,EAX
00401178  |. 74 20          JE SHORT foo24.0040119A
0040117A  |. 68 E8030000    PUSH 3E8
0040117F  |. FFD0           CALL EAX
00401181  |. 85C0           TEST EAX,EAX
00401183  |. 74 15          JE SHORT foo24.0040119A
00401185  |. E8 00000000    CALL foo24.0040118A
0040118A  |$ 810424 761E000>ADD DWORD PTR SS:[ESP],1E76
00401191  |. FFD0           CALL EAX                                 ;裝跳到核心支援庫領空

10029E72   55               PUSH EBP
10029E73   8BEC             MOV EBP,ESP
10029E75   8B45 08          MOV EAX,DWORD PTR SS:[EBP+8]
10029E78   50               PUSH EAX
10029E79   B9 F8E81210      MOV ECX,krnln.1012E8F8
10029E7E   E8 ACF5FFFF      CALL krnln.1002942F                      ;繼續跟進
10029E83   5D               POP EBP
10029E84   C2 0400          RETN 4

1002942F   55               PUSH EBP
10029430   8BEC             MOV EBP,ESP
10029432   83EC 08          SUB ESP,8
10029435   53               PUSH EBX
10029436   56               PUSH ESI
10029437   57               PUSH EDI
10029438   894D F8          MOV DWORD PTR SS:[EBP-8],ECX
1002943B   FF15 C8230D10    CALL DWORD PTR DS:[<&KERNEL32.GetProcess>; KERNEL32.GetProcessHeap
10029441   8B4D F8          MOV ECX,DWORD PTR SS:[EBP-8]
10029444   8981 EC030000    MOV DWORD PTR DS:[ECX+3EC],EAX
1002944A   8B55 08          MOV EDX,DWORD PTR SS:[EBP+8]
1002944D   8B42 30          MOV EAX,DWORD PTR DS:[EDX+30]
10029450   83E0 01          AND EAX,1
10029453   85C0             TEST EAX,EAX
10029455   75 10            JNZ SHORT krnln.10029467
10029457   8B4D 08          MOV ECX,DWORD PTR SS:[EBP+8]
1002945A   51               PUSH ECX
1002945B   8B4D F8          MOV ECX,DWORD PTR SS:[EBP-8]
1002945E   E8 FD400300      CALL krnln.1005D560
10029463   FFE0             JMP EAX                                  ;易格式已經裝載完成了,轉交指揮權

00404207   FC               CLD                                      ;易格式程式碼的起始位置
00404208   DBE3             FINIT
0040420A   68 027F8000      PUSH 807F02
0040420F   B8 03000000      MOV EAX,3
00404214   E8 31000000      CALL foo24.0040424A
00404219   83C4 04          ADD ESP,4
0040421C   68 01000152      PUSH 52010001
00404221   E8 1E000000      CALL foo24.00404244                      
00404226   83C4 04          ADD ESP,4
00404229   6A 00            PUSH 0
0040422B   E8 0E000000      CALL foo24.0040423E
00404230   E8 03000000      CALL foo24.00404238
00404235   83C4 04          ADD ESP,4
00404238   FF25 94788000    JMP DWORD PTR DS:[807894]
0040423E   FF25 98788000    JMP DWORD PTR DS:[807898]
00404244   FF25 9C788000    JMP DWORD PTR DS:[80789C]
0040424A   FF25 A0788000    JMP DWORD PTR DS:[8078A0]               ;最後在這個地方程式當掉了
00404250   FF25 90788000    JMP DWORD PTR DS:[807890]
00404256   FF25 78788000    JMP DWORD PTR DS:[807878]
0040425C   FF25 7C788000    JMP DWORD PTR DS:[80787C]
00404262   FF25 80788000    JMP DWORD PTR DS:[807880]

轉跳指向了一個莫名其妙的地址,程式不當掉才怪。
啟動W32Dasm載入程式,進行靜態分析。在相同位置得到了這樣的反彙編程式碼:

:00404207 FC                      cld
:00404208 DBE3                    finit
:0040420A 6806424000              push 00404206
:0040420F B803000000              mov eax, 00000003
:00404214 E831000000              call 0040424A
:00404219 83C404                  add esp, 00000004
:0040421C 6801000152              push 52010001
:00404221 E81E000000              call 00404244
:00404226 83C404                  add esp, 00000004
:00404229 6A00                    push 00000000
:0040422B E80E000000              call 0040423E
:00404230 E803000000              call 00404238
:00404235 83C404                  add esp, 00000004

* Referenced by a CALL at Address:
|:00404230   
|
:00404238 FF255C3C4000            jmp dword ptr [00403C5C]

* Referenced by a CALL at Address:
|:0040422B   
|
:0040423E FF25603C4000            jmp dword ptr [00403C60]

* Referenced by a CALL at Address:
|:00404221   
|
:00404244 FF25643C4000            jmp dword ptr [00403C64]

* Referenced by a CALL at Address:
|:00404214   
|
:0040424A FF25683C4000            jmp dword ptr [00403C68]

* Referenced by a CALL at Addresses:
|:00403E10   , :00403EE0   , :00403F67   , :00403F77   , :004040A1   
|:00404107   , :00404194   , :004041FA   
|
:00404250 FF25583C4000            jmp dword ptr [00403C58]

* Referenced by a CALL at Addresses:
|:00403E75   , :00403F37   , :00404014   , :0040403A   , :004040F7   
|:0040412D   , :004041EA   
|
:00404256 FF25403C4000            jmp dword ptr [00403C40]

* Referenced by a CALL at Addresses:
|:00403E4A   , :00403ECD   , :00403FB1   , :0040408E   , :00404181   
|
:0040425C FF25443C4000            jmp dword ptr [00403C44]
:00404262 FF25483C4000            jmp dword ptr [00403C48]

經過對比很明顯就能發現,轉跳的地址不同。原因呢?很簡單:重定位。
用EcE載入程式,檢視@code段的重定位資訊,得到如下結果:
0:RT_HELP_FUNC    0000053E
0:RT_HELP_FUNC    00000544
0:RT_HELP_FUNC    0000054A
0:RT_HELP_FUNC    00000550
0:RT_HELP_FUNC    00000556
0:RT_HELP_FUNC    0000055C
0:RT_HELP_FUNC    00000562
0:RT_HELP_FUNC    00000568
在“重要裝載資訊”裡顯示,易格式所在節的RVA在0x00003000處。@code段的偏移為0x00000CFC。然後計算0x004000000+0x00003000+0x00000cfc+0x0000053E = 0x0040423A,正是那個錯誤的地址。
也就是說,我們從記憶體中提取出來的易格式原體是經過裝載器重定位後的,所以當我們以重定位後的易格式原體被裝載器載入,然後再次進行重定位,自然會指向錯誤的地址。所以只要我們修復了從記憶體中提取到易格式原體的重定位資訊即可。
總結一下,脫殼的思路如下:
載入要脫殼的程式,等待裝載器把易格式裝入記憶體 -〉從記憶體中獲取易格式原體並儲存下來 -〉修復易格式原體的重定位資訊 -〉將修復後的原體植入PE骨骼。

實施步驟:
1。雙擊執行加了殼的程式,這裡是2-2.exe。(廢話:))
2。啟動WinHex,在“工具”-〉“RAM編輯器”中選擇2-2.exe程式,開啟其主要記憶體。
3。查詢字串“WTNE”,這是易格式的驗證字串。在0x00403000處找到了。
4。然後向下找,找到“@reloc”(如果沒有,就找“@var”),結果在0x004042AC處找到了。
5。一般易格式是按照FileAlignment對齊的,也就是說,易格式原體應該是從0x00403000――0x00403400的部分。
6。把這一部分的資料“複製塊”-〉“進入正常檔案”,我們把它命名為yt.dat。
7。用EcE載入yt.dat,在載入之前,必須要在“綜合功能中心”-〉“綜合設定”-〉“分析設定”中,設定“分析物件型別”為“易格式原體檔案”。如果可以正常載入的話,說明我們的第一步提取成功了。
8。下面我們要進行的是修復重定位的工作。在以前這可是個極度痛苦的工作。試想一下吧,計算修復成千上萬個重定位資料後,人會變成什麼樣子……不過現在情況好多了,可以利用EcE裡面帶的“易格式重定位資訊修復”工具來代替我們完成這個工作,不過你必須要有0.60版本以上的EcE,在罈子裡有最新的下載(算是廣告吧:))。
9。選擇EcE左邊工具欄中“綜合功能中心”-〉“工具箱”,啟動“易格式重定位資訊修復”。在一個警告提示之後,會出現一個輸入框,讓你輸入“重定位基址”。這個基址也就是易格式原體實際上的RVA了。由於我們是從程式記憶體0x00403000處體取得易格式原體,這個地方就輸入00403000。確定後稍等片刻,會提示“重定位資訊修復完成”。
10。接下來改把易格式重新封裝起來了。PE骨骼要求能完成找到易語言核心支援庫,裝載易格式裝載器等一系列工作。到哪兒去製作一個符合要求的PE骨骼呢?EcE的工具箱也提供了這樣的功能(好像工具箱是專門為脫殼設計的:))。
11。關閉剛才開啟的原體。在“工具箱”中選擇“易格式原體植入PE骨骼”。首先會讓你選擇易格式原體檔案,我們選擇“yt.dat”。然後在輸入要儲存到的檔名,這裡用new.exe。稍等片刻會提示已經完成。
12。現在基本上大功告成了。執行一下new.exe試試看能否正常執行:)

後記:
之所以沒有提供加殼後的軟體下載,前面我也說過了,是因為我感覺這種方法適用於大部分殼(當然了,只是針對易格式殼執行程式)。至少我試過的UPX,ASProtect,軟體保護神,比泰加密等均殼易用這種方法脫殼或解密,所以也沒有很強的針對性了。不過對於某些殼也是無效的,這樣就需要變通變通了。
也許有會說這樣看來易格式可執行檔案的安全性不高啊。其實並非如此。易格式很特殊,而大部分加殼軟體的作者並非都熟悉易格式,而且那些軟體也沒有針對性的保護措施,所以才會很容易的被脫掉。我相信隨著人們對易格式的認識加深,用不了多長時間就會有專門的加殼軟體 for 易格式的版本或者保護選項了。
另外,加殼不是保護軟體的最終手段,不要太依靠那個東西,畢竟,鎖是防君子不防小人的。




monkeycz
2004年11月16日

相關文章