關於Armadillo 1.8x-2.x的anti-debug&加殼原理初步探討和脫殼方法 (12千字)

看雪資料發表於2002-04-11

目標軟體:Wealth-Lab Developer 2.0
目標檔案:WealthLab.exe
加殼方式:Armadillo 1.8x-2.x
使用工具:WinDbg或trw2000, peditor, WinHex 10.2 SR-2,m$的win32 sdk文件
     
URL:      http://www.silicmdr.com/downloads/WealthLabSetup.exe
本文作者:leo_cyl

    論題中關於Armadillo 外殼的討論很少,以前hying曾經寫過一些,但很少人能理解,現在就

以Wealth-Lab Developer 2.0為例子初步探討對付Armadillo 外殼的方法。因為我在xp下沒裝s-ice

,只有WinDbg可用,所以本文兼做WinDbg的簡單教學吧,同時為照顧使用trw2000的朋友,我會列出

等價的trw命令。

    Armadillo 外殼反跟蹤和反dump挺強的,(至少用procdump不行)。當我們執行

WealthLab.exe後,在Program Files\Wealth-Lab Developer 2.0目錄下產生WEALTHLAB.TMP0檔案。

這是個不能執行的脫殼檔案,也是Armadillo 外殼的至命弱點。

(一)對anti-debug的分析

      首先WealthLab.exe透過CreateFileA 產生ArmXXXX.tmp 檔案(在window的temp目錄)並作為

dll載入,Armadillo的一些重要功能在ArmXXXX.tmp中,它的一個主要引出函式是GetProgramInfo,

作用是un-packing 和 un-encrypting ,還有就是檢查系統中有無偵錯程式。
      用WinDbg載入WealthLab.exe,因為Armadillo採用seh技術改變程式的流程,所以使用以下命

令“sxd *”就是告訴WinDbg捕獲異常後不要處理,有原程式處理,對應trw2000的命令是“faults

off”,另外Armadillo 呼叫IsDebuggerPresent來判斷系統中有無偵錯程式,如果用trw2000,當然檢

查不到,但用WinDbg時要下斷點“bp IsDebuggerPresent”,然後f5執行……中斷後來到這裡:

kernel32!IsDebuggerPresent:
77e52e92 64a118000000    mov    eax,fs:[00000018]
77e52e98 8b4030          mov    eax,[eax+0x30]
77e52e9b 0fb64002        movzx  eax,byte ptr [eax+0x2]
77e52e9f c3              ret    《==== eax=1表示系統有偵錯程式,所以將eax改為0
                                    (如果用trw2000,忽略這裡)

然後f5執行……中斷後來到這裡:
04064be 33854cfbffff    xor    eax,[ebp-0x4b4]
004064c4 8be8            mov    ebp,eax
004064c6 b804000000      mov    eax,0x4
004064cb cc              int    3  〈==== seh技術!
004064cc 8b0dbc574100    mov    ecx,[image00400000+0x157bc (004157bc)]

看一下seh鏈“dd fs:0”最後查到地址40a944。下命令“gn 40a944”(trw2000中要用 INT3HERE

OFF命令,並下斷點bpx 40a944)來到這裡:
0040a944 55              push    ebp
0040a945 8bec            mov    ebp,esp
0040a947 83ec08          sub    esp,0x8
0040a94a 53              push    ebx
0040a94b 56              push    esi
0040a94c 57              push    edi
0040a94d 55              push    ebp
0040a94e fc              cld
……
……

以上程式碼產生ArmXXXX.tmp,並獲得引出函式地址(在此忽略),執行到00406807  時將呼叫

GetProgramInfo

004067f6  mov dword ptr [ebp-0x134],0x4157dc
00406800  lea    ecx,[ebp-0x154]
00406806  push    ecx
00406807  call dword ptr [ebp-0x490]{ARM1!GetProgramInfo (1000b0c0)} 〈=進入
0040680d  add    esp,0x4
00406810  and    eax,0xff
00406815  test    eax,eax
00406817  jnz    image00400000+0x6823 (00406823)

進入ARM1!GetProgramInfo (1000b0c0)後來到這……

1000913c 8065d800        and    byte ptr [ebp-0x28],0x0
10009140 8365fc00        and    dword ptr [ebp-0x4],0x0
10009144 0f018dbcfdffff  sidt    [ebp-0x244]      〈==[ebp-0x244]中防入idt表
1000914b 8b85befdffff    mov    eax,[ebp-0x242]
10009151 83c008          add    eax,0x8      〈=== int2 handle的地址
10009154 8b18            mov    ebx,[eax]
10009156 83c010          add    eax,0x10      〈===int3 handle的地址
10009159 8b00            mov    eax,[eax]
1000915b 25ffff0000      and    eax,0xffff
10009160 81e3ffff0000    and    ebx,0xffff
10009166 2bc3            sub    eax,ebx      〈== int2,3 handle的地址相減
10009168 83f81e          cmp    eax,0x1e
1000916b 7547            jnz    ARM1!ReleaseHook+0x3c5b (100091b4)
1000916d 8b8548fcffff    mov    eax,[ebp-0x3b8]
10009173 050c010000      add    eax,0x10c

這是第二個anti-debug程式碼,判斷int2,3 handle地址的距離,正常的話小於0x1e。如果安裝了

int3 handle(某些偵錯程式會)的話,大於0x1e。奇怪的是WinDbg不能走完這段程式碼,在1000914b

處異常(trw2000就可以)。所以在10009144 處,我把eip改到10009168,並改eax小於0x1e。

f5繼續執行,其間在kernel32!IsDebuggerPresent又會中斷一次,將返回值改為0;來到這裡:

1000b82e push    ebx
1000b82f push    0x3
1000b831 push    ebx
1000b832 push    eax
1000b833 call    ARM1!GetProgramInfo+0x8c8 (1000b988)
1000b838 pop    ecx
1000b839 push    eax
1000b83a call    dword ptr [10013048]{kernel32!CreateFileA (77e5a837)}
1000b840 cmp    eax,0xffffffff
1000b843 jz      ARM1!GetProgramInfo+0x78e (1000b84e)
1000b845 push    eax
1000b846 call    dword ptr [ARM1!NukeNow+0x6da1 (10013044)]
1000b84c jmp    ARM1!GetProgramInfo+0x799 (1000b859)
1000b84e call    dword ptr [10013060]{ntdll!RtlGetLastWin32Error}
1000b854 cmp    eax,0x2
1000b857 jz      ARM1!GetProgramInfo+0x79d (1000b85d)
1000b859 mov    byte ptr [ebp-0x1],0x1

eax為"\\.\SICE", "\\.\NTICE", 和 "\\.\SIWDEBUG"  ,哈哈,檢測s-ice!如果它們存在或

RtlGetLastWin32Error(win98是GetLastError )不等於2,將設立DEBUG 標誌。注意這裡重複3次



以上是anti-debug的分析,對付它的方法大家都知道了吧!  :)

(二)脫殼

      WealthLab.exe執行後,在工作目錄下產生WEALTHLAB.TMP0檔案。這是個不能執行的脫殼檔案

。把改名為WEAL-NUPACK.exe。用peditor檢視,OEP是4086fc。用WinHex 10.2開啟,發現從0x400到

0x40400共256k的內容被添入0x58,即 pop eax的機器碼。只要找回這部分程式碼就是完整的脫殼檔案

了。但不能用procdump,系統會崩潰,幸好WinHex提供了類似功能。先用peditor的FLC計算器計算

0x400和0x40400的虛擬地址,分別是0x401000,0x441000。
    WealthLab.exe執行後,在執行WinHex ,按ALT+F9 開始記憶體編輯,選WEALTHLAB.TMP0程式,

來到偏移0x401000初,選0x401000到0x441000(共256k)的一塊記憶體,複製下來,並把它貼上到

WEAL-NUPACK.exe對應的地方(0x400到0x40400共256k)。
    到此已經把加殼檔案還原了,但還是不能執行!why?呵呵……因為OEP不對嘛!

(三)找OEP
    前面兩步還挺容易,找OEP卻把我難住了,足足花了3個小時才找到。因為外殼和原程式在兩個

不同的程式中,地址空間不一樣,debug起來有困難,幸好我們知道是外殼產生了原程式的程式,所

以斷點CreateProcessA是關鍵!
    從新載入WealthLab.exe,躲過anti-debug,繼續從第一步描述的地址開始,下斷點“bp

CreateProcessA”,來到這裡:

:00408908 6820594100              push 00415920
:0040890D 8D45B8                  lea eax, dword ptr [ebp-48]
:00408910 50                      push eax
:00408911 6A00                    push 00000000
:00408913 6A00                    push 00000000
:00408915 6A04                    push 00000004
:00408917 6A00                    push 00000000
:00408919 6A00                    push 00000000
:0040891B 6A00                    push 00000000

* Reference To: KERNEL32.GetCommandLineA, Ord:00CAh
                                  |
:0040891D FF15D4004100            Call dword ptr [004100D4]
:00408923 50                      push eax
:00408924 8B4D08                  mov ecx, dword ptr [ebp+08]
:00408927 51                      push ecx

* Reference To: KERNEL32.CreateProcessA, Ord:0044h
                                  |
:00408928 FF1544004100            Call dword ptr [00410044]
:0040892E 85C0                    test eax, eax
:00408930 7527                    jne 00408959
:00408932 C705E057410009000000    mov dword ptr [004157E0], 00000009

外殼在這裡產生WEALTHLAB.TMP0程式,在此前已經把環境變數和命令列引數設定好了。f10執行10多

行來到這裡:


* Reference To: KERNEL32.ResumeThread, Ord:022Ch
                                  |
:00406DC1 FF152C004100            Call dword ptr [0041002C]

……
……
……

* Reference To: KERNEL32.GetExitCodeProcess, Ord:010Bh
                                  |
:00406DE2 FF1540004100            Call dword ptr [00410040]
:00406DE8 81BD4CF2FFFF03010000    cmp dword ptr [ebp+FFFFF24C], 00000103
:00406DF2 7423                    je 00406E17
:00406DF4 C705E05741000A000000    mov dword ptr [004157E0], 0000000A

……
……
……

* Reference To: KERNEL32.Sleep, Ord:0296h
                                  |
:00406E19 FF1558004100            Call dword ptr [00410058]
:00406E1F 8B1524594100            mov edx, dword ptr [00415924]
:00406E25 52                      push edx

* Reference To: KERNEL32.SuspendThread, Ord:0298h
                                  |
:00406E26 FF1554014100            Call dword ptr [00410154]
:00406E2C 83F8FF                  cmp eax, FFFFFFFF
:00406E2F 7530                    jne 00406E61

……
……
……

* Reference To: KERNEL32.GetThreadContext, Ord:0167h
                                  |
:00406E6F FF1538004100            Call dword ptr [00410038]
:00406E75 85C0                    test eax, eax
:00406E77 7530                    jne 00406EA9
:00406E79 C705E05741000A000000    mov dword ptr [004157E0], 0000000A
:00406E83 A124594100              mov eax, dword ptr [00415924]

注意,實際上外殼自身作為偵錯程式來啟動WEALTHLAB.TMP0程式,把WEALTHLAB.TMP0程式掛起後呼叫

GetThreadContext獲得執行緒Context即“執行緒上下文”,如果不懂的話,請看看有關作業系統原理的

書吧。獲得執行緒Context有什麼用呢?等一下有說明……,繼續執行:

* Reference To: KERNEL32.ResumeThread, Ord:022Ch
                                  |
:00406ED2 FF152C004100            Call dword ptr [0041002C]
:00406ED8 E9EAFEFFFF              jmp 00406DC7

……
……
……

* Reference To: KERNEL32.VirtualProtectEx, Ord:02C4h
                                  |
:00406F08 FF1558014100            Call dword ptr [00410158]
:00406F0E 85C0                    test eax, eax
:00406F10 7523                    jne 00406F35
:00406F12 C705E05741000A000000    mov dword ptr [004157E0], 0000000A

……
……
……

:00406F35 8D8554F2FFFF            lea eax, dword ptr [ebp+FFFFF254]
:00406F3B 50                      push eax
:00406F3C 6A02                    push 00000002
:00406F3E 6894554100              push 00415594
:00406F43 8B0DCC224100            mov ecx, dword ptr [004122CC]
:00406F49 330DD8224100            xor ecx, dword ptr [004122D8]
:00406F4F 330DE0224100            xor ecx, dword ptr [004122E0]
:00406F55 330DF0224100            xor ecx, dword ptr [004122F0]
:00406F5B 51                      push ecx
:00406F5C 8B1520594100            mov edx, dword ptr [00415920]
:00406F62 52                      push edx

* Reference To: KERNEL32.WriteProcessMemory, Ord:02E9h
                                  |
:00406F63 FF1534004100            Call dword ptr [00410034]
:00406F69 85C0                    test eax, eax
:00406F6B 7523                    jne 00406F90

看看上面程式碼,呵呵…… 原來外殼將WEALTHLAB.TMP0程式的部分段(即內容是0x58的那部分)設為

類似不可讀寫或執行的屬性,來觸發自己解碼。前面說過外殼將作為偵錯程式而WEALTHLAB.TMP0正好

是被偵錯程式!具體可看看hying以前關於Armadillo 外殼的帖子,精華里有。
繼續執行:

:00407035 6A64                    push 00000064

* Reference To: KERNEL32.Sleep, Ord:0296h
                                  |
:00407037 FF1558004100            Call dword ptr [00410058]
:0040703D 8B0DD4224100            mov ecx, dword ptr [004122D4]
:00407043 330DF8224100            xor ecx, dword ptr [004122F8]
:00407049 330DE0224100            xor ecx, dword ptr [004122E0]
:0040704F 330DF0224100            xor ecx, dword ptr [004122F0]
:00407055 898D18F3FFFF            mov dword ptr [ebp+FFFFF318], ecx
:0040705B 8D9560F2FFFF            lea edx, dword ptr [ebp+FFFFF260]
:00407061 52                      push edx     
:00407062 A124594100              mov eax, dword ptr [00415924]
:00407067 50                      push eax

* Reference To: KERNEL32.SetThreadContext, Ord:0283h
                                  |
:00407068 FF158C004100            Call dword ptr [0041008C] 〈==關鍵啊!!!
:0040706E 85C0                    test eax, eax
:00407070 7523                    jne 00407095

外殼呼叫SetThreadContext來改變WEALTHLAB.TMP0程式的eip來達到改變EOP的目的!!!
關於SetThreadContext的說明請看MSDN文件。其中edx是Context指標,edx+b8是eip的值;
下命令“dd edx+b8"將看到OEP的地址是006a19c4。(有關Context結構請看附錄),用peditor將

WEAL-NUPACK.exe的OEP改為006a19c4,到此脫殼完成,脫殼檔案完美執行!!

(四)總結
    外殼作為偵錯程式來除錯加殼程式,並利用類似頁異常的處理方式來動態解碼,利用除錯函式

SetThreadContext來動態改變加殼程式的OEP,達到隱藏OEP的目的。
    其實相對來說,Armadillo 比aspr要容易多了,(如果你對window的除錯原理有了解的話)如

果再加上類似aspr的花指令和反反覆覆的seh,來加強反靜態分析,應該是個保護很強的外殼。

附錄:
  Context結構的宣告(來自winnt.h 註釋是我加的)
typedef struct _CONTEXT86 {
    DWORD ContextFlags;

    DWORD  Dr0;
    DWORD  Dr1;
    DWORD  Dr2;
    DWORD  Dr3;
    DWORD  Dr6;
    DWORD  Dr7;

    FLOATING_SAVE_AREA FloatSave;  //FLOATING_SAVE_AREA的大小是112byte
    DWORD  SegGs;
    DWORD  SegFs;
    DWORD  SegEs;
    DWORD  SegDs;

    DWORD  Edi;
    DWORD  Esi;
    DWORD  Ebx;
    DWORD  Edx;
    DWORD  Ecx;
    DWORD  Eax;

    DWORD  Ebp;              // 偏移是0xb8
    DWORD  Eip;
    DWORD  SegCs;              // MUST BE SANITIZED
    DWORD  EFlags;            // MUST BE SANITIZED
    DWORD  Esp;
    DWORD  SegSs;

} CONTEXT86;

相關文章