(题外话: 选择话题里应该多一个灌水、胡扯之类的。。。,搞得我都没得选)

☆ 手工脱Armadillo

Milescan Web Security Auditor Trial 1.1 (auditor_1.1_trial.exe)

http://www.milescan.com

spider_trial.exe(4,313,088 bytes)
auditor_trial.exe(4,313,088 bytes)

Milescan是一款商用WWW扫描器,不是太出名,其主站为http://www.milescan.com。
支持SQL注入扫描。它有一个兄弟产品更出名些,Paros Proxy,这是一款开源软件,
可以当普通代理使用,但更重要的是作为WWW扫描器使用,从名字上可能看不出后一
点来,我实测过其扫描功能,相当不错,其主站为http://www.parosproxy.org/。既
然开源的不错,想必商用的更不错,这是我关注Milescan的最初原因。

Milescan在其主站提供试用版下载,需要免费注册进入下载页面,文件较大(24.7M)。
这个试用版本身包含了完整的功能性代码,但通过其它手段做了如下两个限制:

. 30天试用期
. 只能扫描如下IP:

10.0.0.0     10.255.255.255
172.16.0.0   172.31.255.255
192.168.0.0  192.168.255.255
127.0.0.0    127.255.255.255

第一个限制是通过Armadillo实现的,第二个限制是软件本身的。软件本身是用Java
开发的,最终编译成native code,没有保持在字节码状态。

运气不太好,终于碰上自己关心的程序需要脱壳,之前从未接触过脱壳。PEiD报
Armadillo壳,版本信息不太对,ArmaGUI.rar、mm_dillodie1.6.zip都可以成功脱掉
这个版本的壳。就本例而言,手脱虽然是个无意义的举动,但还是记录备忘一下手脱
过程,艺多不压身。

因为第一次接触手工脱壳,很多观点、操作相当稚嫩,贻笑大方。

用PEiD查origspider_trial.exe,报:

Armadillo 1.xx - 2.xx -> Silicon Realms Toolworks

用Armadillo find protected查origspider_trial.exe,报:

Debug-Blocker
Enable Strategic Code Splicing

Armadillo一般有类似这样的程序逻辑:

--------------------------------------------------------------------------
HANDLE  mutex;

mutex   = OpenMutex( ..., ..., "%X::DA%08X" );
if ( !mutex )
{
    /*
     * 父进程流程
     */
    CreateProcess();
    DebugActiveProcess();
    SuspendThread();
    while ( 1 )
    {
        WaitForDebugEvent( lpDebugEvent, ... );
        switch ( lpDebugEvent->dwDebugEventCode )
        {
        /*
         * 1
         */
        case EXCEPTION_DEBUG_EVENT:
            switch ( lpDebugEvent->u.Exception.ExceptionRecord.ExceptionCode )
            {
            /*
             * STATUS_GUARD_PAGE_VIOLATION,0x80000001
             */
            case EXCEPTION_GUARD_PAGE:
                ... ...
                WriteProcessMemory();
                ... ...
                break;
            /*
             * STATUS_ACCESS_VIOLATION,0xC0000005
             */
            case EXCEPTION_ACCESS_VIOLATION:
                ... ...
                break;
            /*
             * STATUS_BREAKPOINT,0x80000003
             */
            case EXCEPTION_BREAKPOINT:
                ... ...
                break;
            case ...
            }  /* end of switch */
            ... ...
            break;
        /*
         * 3
         */
        case CREATE_PROCESS_DEBUG_EVENT:
            ... ...
            /*
             * 确保子进程不会再走父进程流程,否则无限递归了
             */
            CreateMutex( ..., ..., "%X::DA%08X" );
            /*
             * 与DebugActiveProcess()之后的那个SuspendThread()配对
             */
            ResumeThread();
            ... ...
            break;
        case ...
        }  /* end of switch */
        ContinueDebugEvent( lpDebugEvent->dwProcessId, lpDebugEvent->dwThreadId, ... );
    }  /* end of while */
}
else
{
    /*
     * 子进程流程
     *
     * 子进程自己负责处理引入表
     */
}
--------------------------------------------------------------------------

单纯的Debug-Blocker只是靠DebugActiveProcess()确保无法用Ring 3调试器attach
子进程,从而无法调试子进程。

更复杂的则是子进程有意识地触发EXCEPTION_GUARD_PAGE、EXCEPTION_BREAKPOINT异
常,父进程捕获异常进行相应处理之后子进程再继续执行。

我碰上的这个是单纯的Debug-Blocker。

Armadillo在反调试器,一定要用HideOD隐藏OD。

就本例而言,只需对付IsDebuggerPresent()即可,下面是一个cdb.exe调试示例:

--------------------------------------------------------------------------
cdb.exe -hd -noinh origspider_trial.exe

g $exentry
ba e 1 IsDebuggerPresent "g poi(esp);r eax=0;g"
--------------------------------------------------------------------------

OD载入origspider_trial.exe,停在如下位置:

--------------------------------------------------------------------------
0092FBFB >/$  55            push    ebp
0092FBFC  |.  8BEC          mov     ebp, esp
0092FBFE  |.  6A FF         push    -1
0092FC00  |.  68 28829500   push    origspid.00958228
0092FC05  |.  68 70EF9200   push    origspid.0092EF70                ;  SE handler installation
0092FC0A  |.  64:A1 0000000>mov     eax, dword ptr fs:[0]
0092FC10  |.  50            push    eax
0092FC11  |.  64:8925 00000>mov     dword ptr fs:[0], esp
--------------------------------------------------------------------------

he OpenMutexA
F9

--------------------------------------------------------------------------
7C830607 >  8BFF            mov     edi, edi    // OpenMutexA入口
7C830609    55              push    ebp
--------------------------------------------------------------------------

断在OpenMutexA入口时查看stack:

--------------------------------------------------------------------------
0006F798   0091B205  /CALL to OpenMutexA from origspid.0091B1FF
0006F79C   001F0001  |Access = 1F0001
0006F7A0   00000000  |Inheritable = FALSE
0006F7A4   0006FDD8  \MutexName = "520::DAE5F88F3F" // 记下字符串指针0x0006FDD8
--------------------------------------------------------------------------

在IDA中看看相关汇编代码(没有实际意义,仅记录备忘):

--------------------------------------------------------------------------
0091B1CA A1 B8 23 95 00                          mov     eax, ds:dword_9523B8 ; 0xAF7D5A38
0091B1CF 33 05 D0 23 95 00                       xor     eax, ds:dword_9523D0 ; 0xAF7D5A38 ^ 0x4A85D507 = 0xE5F88F3F
0091B1D5 50                                      push    eax             ; 0xE5F88F3F
0091B1D6 FF 15 B8 21 95 00                       call    ds:GetCurrentProcessId
0091B1DC 50                                      push    eax             ; PID
0091B1DD 68 D4 26 95 00                          push    offset aXDa08x  ; "%X::DA%08X"
0091B1E2 8D 8D D8 FE FF FF                       lea     ecx, [ebp+Name]
0091B1E8 51                                      push    ecx             ; Dest
0091B1E9 E8 0C 3C 01 00                          call    _sprintf
0091B1EE 83 C4 10                                add     esp, 10h
0091B1F1 8D 95 D8 FE FF FF                       lea     edx, [ebp+Name]
0091B1F7 52                                      push    edx             ; lpName
0091B1F8 6A 00                                   push    0               ; bInheritHandle
0091B1FA 68 01 00 1F 00                          push    1F0001h         ; dwDesiredAccess
0091B1FF FF 15 A8 20 95 00                       call    ds:OpenMutexA
0091B205 85 C0                                   test    eax, eax
--------------------------------------------------------------------------
00921400 8B 15 B8 23 95 00                       mov     edx, ds:dword_9523B8
00921406 33 15 D0 23 95 00                       xor     edx, ds:dword_9523D0
0092140C 52                                      push    edx
0092140D A1 6C 97 95 00                          mov     eax, ds:child_pi
00921412 8B 48 08                                mov     ecx, [eax+8]
00921415 51                                      push    ecx
00921416 68 D4 26 95 00                          push    offset aXDa08x  ; "%X::DA%08X"
0092141B 8D 95 F8 FD FF FF                       lea     edx, [ebp-208h]
00921421 52                                      push    edx
00921422 E8 D3 D9 00 00                          call    _sprintf
00921427 83 C4 10                                add     esp, 10h
0092142A 8D 85 F8 FD FF FF                       lea     eax, [ebp-208h]
00921430 50                                      push    eax
00921431 6A 00                                   push    0
00921433 6A 00                                   push    0
00921435 FF 15 A4 20 95 00                       call    ds:CreateMutexA
--------------------------------------------------------------------------

我没有动态拦截CreateMutexA(),只是在IDA中查看"%X::DA%08X"的交叉引用关系定
位0x00921416的。

at 0x00401000

--------------------------------------------------------------------------
00401000    0000            add     byte ptr [eax], al
00401002    0000            add     byte ptr [eax], al
00401004    0000            add     byte ptr [eax], al
00401006    0000            add     byte ptr [eax], al
00401008    0000            add     byte ptr [eax], al
0040100A    0000            add     byte ptr [eax], al
0040100C    0000            add     byte ptr [eax], al
0040100E    0000            add     byte ptr [eax], al
00401010    0000            add     byte ptr [eax], al
00401012    0000            add     byte ptr [eax], al
00401014    0000            add     byte ptr [eax], al
00401016    0000            add     byte ptr [eax], al
--------------------------------------------------------------------------

这是一片空闲的有效内存(可以另找空闲区域),手工输入如下汇编代码:

--------------------------------------------------------------------------
00401000    60              pushad
00401001    9C              pushfd
00401002    68 D8FD0600     push    6FDD8                            ; ASCII "520::DAE5F88F3F"
00401007    33C0            xor     eax, eax
00401009    50              push    eax
0040100A    50              push    eax
0040100B    E8 9783427C     call    kernel32.CreateMutexA
00401010    9D              popfd
00401011    61              popad
00401012  - E9 F0F5427C     jmp     kernel32.OpenMutexA
--------------------------------------------------------------------------
60 9C 68 D8 FD 06 00 33 C0 50 50 E8 97 83 42 7C 9D 61 E9 F0 F5 42 7C
--------------------------------------------------------------------------

这段代码模拟0x00921435处的CreateMutexA(),以此欺骗0x0091B1FF处的OpenMutexA(),
强制走子进程流程。这次是单纯的Debug-Blocker,可以一开始就强制走子进程流程,
如果父进程需要处理EXCEPTION_GUARD_PAGE、EXCEPTION_BREAKPOINT异常,处理时机
要复杂些。

在理解这个壳的时候重点参考了两份文档:

Armadillo 2.52加壳原理分析和改进的脱壳方法 - leo_cyl
http://blog.csdn.net/compiler/articles/91123.aspx

试玩armadillo3.50a一点心得 - mysqladm [2004-03-06]
http://www.pediy.com/bbshtml/BBS6/pediy6837.htm

这两份文档解释了一些重要原理,适合对Win32本身较熟但对脱壳不熟的程序员参看。
其中mysqladm原创了欺骗OpenMutexA()的方案,致敬。

手工修改EIP指向0x00401000,F9之后再次断在OpenMutexA入口:

--------------------------------------------------------------------------
7C830607 >  8BFF            mov     edi, edi    // OpenMutexA入口
7C830609    55              push    ebp
--------------------------------------------------------------------------

先撤消0x00401000附近的所有修改。

hd OpenMutexA
Alt-M

--------------------------------------------------------------------------
00400000   00001000   origspid              PE header     Imag   R         RWE
00401000   00163000   origspid   .text                    Imag   R         RWE  // 在.text上设内存访问断点
00564000   00084000   origspid   .data                    Imag   R         RWE
005E8000   00004000   origspid   .bss                     Imag   R         RWE
005EC000   002D0000   origspid   .rdata                   Imag   R         RWE
008BC000   0000D000   origspid   .jidata    data          Imag   R         RWE
008C9000   00001000   origspid   .idata                   Imag   R         RWE
008CA000   00010000   origspid   .jedata                  Imag   R         RWE
008DA000   00017000   origspid   .reloc                   Imag   R         RWE
008F1000   00001000   origspid   .config                  Imag   R         RWE
008F2000   00050000   origspid   .text1     code          Imag   R         RWE
00942000   00010000   origspid   .adata     code          Imag   R         RWE
00952000   00010000   origspid   .data1     imports       Imag   R         RWE
00962000   00010000   origspid   .reloc1    relocations   Imag   R         RWE
00972000   000F0000   origspid   .pdata                   Imag   R         RWE
00A62000   00003000   origspid   .rsrc      resources     Imag   R         RWE
--------------------------------------------------------------------------

F9运行,中间如果有异常抛出(注意OD下方状态栏)就用Shift-F9继续,直至内存访问
断点命中:

--------------------------------------------------------------------------
00FDEBA7    0FBE00          movsx   eax, byte ptr [eax] // 此时EAX=00401000
--------------------------------------------------------------------------
00401000    6A 00           push    0
00401002    E8 43F91500     call    origspid.0056094A
00401007    50              push    eax
00401008    6A 01           push    1
0040100A    50              push    eax
0040100B    E8 24000000     call    origspid.00401034
00401010    85C0            test    eax, eax
00401012    74 16           je      short origspid.0040102A
00401014    68 00000000     push    0
--------------------------------------------------------------------------

TNND,都不知道什么时候0x00401000已被写了新数据(原来全零),OD的内存访问断点
还真是不可靠,太容易被VirtualProtect()系列函数干挠。若想定位向0x00401000写
入新数据的代码,可用硬件断点。

清除.text上的内存访问断点。

he 00401000
F9

断在0x00401000之后清除所有断点。

在.text上设内存访问断点,不知是谁的原创,我看到的文档都在引别人的,却没有
看到源头。其本意是假设OEP位于.text中,在.text上设内存访问断点可快速定位OEP。

0x00401000即本例的OEP。现在来对付"Enable Strategic Code Splicing"。运行
ArmInline v0.96 Final,在Processs里选中origspider_trial.exe,会自动获取如
下信息:

(Slave)Process ID
Start Of Target Code    0x401000
Length Of Target Code   0x163000
Start Of Spliced Code   0x3C60000
Lenght Of Spliced Code  0x1000

用Alt-M查看内存信息加深理解:

--------------------------------------------------------------------------
Memory map
Address    Size       Owner      Section    Contains      Type   Access    Initial   Mapped as
00400000   00001000   origspid                            Imag   R         RWE
00401000   00163000   origspid   .text                    Imag   R         RWE  // Target Code
00564000   00084000   origspid   .data                    Imag   R         RWE
005E8000   00004000   origspid   .bss                     Imag   R         RWE
005EC000   002D0000   origspid   .rdata                   Imag   R         RWE
008BC000   0000D000   origspid   .jidata    data          Imag   R         RWE
008C9000   00001000   origspid   .idata                   Imag   R         RWE
008CA000   00010000   origspid   .jedata                  Imag   R         RWE
008DA000   00017000   origspid   .reloc                   Imag   R         RWE
008F1000   00001000   origspid   .config                  Imag   R         RWE
008F2000   00050000   origspid   .text1     code          Imag   R         RWE
00942000   00010000   origspid   .adata     code          Imag   R         RWE
00952000   00010000   origspid   .data1     imports       Imag   R         RWE
00962000   00010000   origspid   .reloc1    relocations   Imag   R         RWE
00972000   000F0000   origspid   .pdata                   Imag   R         RWE
00A62000   00003000   origspid   .rsrc      resources     Imag   R         RWE
00A70000   00103000                                       Map    R         R
00B80000   000C8000                                       Map    R E       R E
00E80000   00001000                                       Priv   RW        RW
00E90000   00001000                                       Priv   RW        RW
00EA0000   0000E000                                       Priv   RW        RW
00EB0000   00001000                                       Priv   RW        RW
00FB0000   00056000                                       Priv   RW        RW
01010000   00002000                                       Map    R         R
01020000   00002000                                       Map    R         R
01030000   00005000                                       Priv   RW        RW
0116D000   00001000                                       Priv   RW  Guar  RW
0116E000   00002000                         stack of thr  Priv   RW  Guar  RW
01170000   00018000                                       Priv   RW        RW
01190000   000A4000                                       Priv   RW        RW
01253000   00002000                                       Priv   RW        RW
01270000   00007000                                       Priv   RW        RW
012B0000   0000C000                                       Priv   RW        RW
012C0000   00006000                                       Priv   RW        RW
012D0000   00004000                                       Priv   RW        RW
012E0000   00001000                                       Map    RW        RW
012F0000   00002000                                       Map    R         R
01300000   00003000                                       Priv   RW        RW
01340000   00001000                                       Map    RW        RW
01350000   00022000                                       Priv   RW        RW
01391000   00026000                                       Priv   RW        RW
01550000   00001000                                       Priv   RW        RW
01560000   0003E000                                       Priv   RW        RW
01660000   00001000                                       Priv   RW        RW
016E0000   00001000                                       Priv   RW        RW
0172D000   00001000                                       Priv   RW  Guar  RW
0172E000   00002000                         stack of thr  Priv   RW  Guar  RW
03C60000   00011000                                       Priv   R E       RWE  // Spliced Code
--------------------------------------------------------------------------

在ArmInline里点"Remove Splices",得到如下提示信息:

73 splices repaired.
Splice repairing complete. Patching process...
Patch succesful.

最后那个succesful不是我写错的,是它自己写错的。关于ArmInline参看:

Armadillo客户版Code Splicing+Import Table Elimination的简便修复方法 - fly [2005-09-28]
http://bbs.pediy.com/showthread.php?t=17253
(介绍了一些Armadillo保护机制的术语以及ArmInline的使用方法)

fly这里用的是个低版本的ArmInline,现在的0.96版用起来更省事。

后面的操作回头来看可能有些稚嫩,但我还是忠实地记录原始学习过程,见笑。

运行LordPE选中origspider_trial.exe,dump full,保存成dumped.exe。

运行ImportREC,选中origspider_trial.exe,将OEP改成00001000,注意,一定不要
直接写成00401000,这里减去的00400000即ImageBase。自动查找IAT,现在RVA变成
004C9078。

0x004C9078 + 0x00400000 = 0x008C9078

回到OD,查看相关信息:

--------------------------------------------------------------------------
00401000    6A 00           push    0
00401002    E8 43F91500     call    origspid.0056094A
--------------------------------------------------------------------------
00560908  - FF25 A8908C00   jmp     near dword ptr [8C90A8]          ; ADVAPI32.DeregisterEventSource
0056090E  - FF25 A4908C00   jmp     near dword ptr [8C90A4]          ; ADVAPI32.RegCloseKey
00560914  - FF25 A0908C00   jmp     near dword ptr [8C90A0]
0056091A  - FF25 9C908C00   jmp     near dword ptr [8C909C]          ; ADVAPI32.RegSetValueExA
00560920  - FF25 98908C00   jmp     near dword ptr [8C9098]          ; ADVAPI32.RegisterEventSourceA
00560926  - FF25 94908C00   jmp     near dword ptr [8C9094]          ; ADVAPI32.ReportEventA
0056092C  - FF25 18918C00   jmp     near dword ptr [8C9118]
00560932  - FF25 14918C00   jmp     near dword ptr [8C9114]          ; kernel32.FindClose
00560938  - FF25 10918C00   jmp     near dword ptr [8C9110]
0056093E  - FF25 0C918C00   jmp     near dword ptr [8C910C]
00560944  - FF25 08918C00   jmp     near dword ptr [8C9108]          ; kernel32.GetModuleFileNameA
0056094A  - FF25 04918C00   jmp     near dword ptr [8C9104]          ; from 0x00401002
00560950  - FF25 00918C00   jmp     near dword ptr [8C9100]
00560956  - FF25 FC908C00   jmp     near dword ptr [8C90FC]
0056095C  - FF25 F8908C00   jmp     near dword ptr [8C90F8]          ; kernel32.GetStdHandle
00560962  - FF25 F4908C00   jmp     near dword ptr [8C90F4]          ; ntdll.RtlAllocateHeap
00560968  - FF25 F0908C00   jmp     near dword ptr [8C90F0]          ; ntdll.RtlFreeHeap
0056096E  - FF25 EC908C00   jmp     near dword ptr [8C90EC]
00560974  - FF25 E8908C00   jmp     near dword ptr [8C90E8]
0056097A  - FF25 28918C00   jmp     near dword ptr [8C9128]          ; USER32.OemToCharA
... ...
0056367A    FF25 D4878C00   jmp     near dword ptr [8C87D4]
00563680    FF25 00888C00   jmp     near dword ptr [8C8800]
00563686    FF25 08888C00   jmp     near dword ptr [8C8808]
0056368C    FF25 10888C00   jmp     near dword ptr [8C8810]
00563692    FF25 18888C00   jmp     near dword ptr [8C8818]
00563698    FF25 20888C00   jmp     near dword ptr [8C8820]
0056369E    FF25 30888C00   jmp     near dword ptr [8C8830]
005636A4    FF25 38888C00   jmp     near dword ptr [8C8838]
005636AA    FF25 40888C00   jmp     near dword ptr [8C8840]
005636B0    FF25 48888C00   jmp     near dword ptr [8C8848]
--------------------------------------------------------------------------
008C9078  004C9192  origspid.004C9192
008C907C  004C917A  origspid.004C917A
008C9080  004C9168  origspid.004C9168
008C9084  004C9156  origspid.004C9156
008C9088  004C9148  origspid.004C9148
008C908C  004C9130  origspid.004C9130
008C9090  00000000
008C9094  77F3F9D8  ADVAPI32.ReportEventA
008C9098  77F3B857  ADVAPI32.RegisterEventSourceA
008C909C  77F3EBD7  ADVAPI32.RegSetValueExA
008C90A0  00FCB725
008C90A4  77F56CCE  ADVAPI32.RegCloseKey
008C90A8  77F3F160  ADVAPI32.DeregisterEventSource
008C90AC  00FC7E3F
008C90B0  004C926E  origspid.004C926E
008C90B4  004C925E  origspid.004C925E
008C90B8  004C9252  origspid.004C9252
008C90BC  004C9246  origspid.004C9246
008C90C0  004C9236  origspid.004C9236
008C90C4  004C9224  origspid.004C9224
008C90C8  004C9212  origspid.004C9212
008C90CC  004C91FE  origspid.004C91FE
008C90D0  004C91E8  origspid.004C91E8
008C90D4  004C91CE  origspid.004C91CE
008C90D8  004C91BC  origspid.004C91BC
008C90DC  004C91B0  origspid.004C91B0
008C90E0  004C91A2  origspid.004C91A2
008C90E4  00000000
008C90E8  00FC9931
008C90EC  00FC7E85
008C90F0  7C959E17  ntdll.RtlFreeHeap
008C90F4  7C959FD6  ntdll.RtlAllocateHeap
008C90F8  7C82B437  kernel32.GetStdHandle
008C90FC  00FCBD32
008C9100  00FC72A7
008C9104  00FCB213
008C9108  7C8245FF  kernel32.GetModuleFileNameA
008C910C  00FC86CD
008C9110  00FCACCD
008C9114  7C82BFB3  kernel32.FindClose
008C9118  00FC8BC6
008C911C  00FC7E49
008C9120  004C927A  origspid.004C927A
008C9124  00000000
008C9128  77E519CC  USER32.OemToCharA
008C912C  00FC7D67
--------------------------------------------------------------------------

获取输入表,显示无效函数,剪切指针,修复转存文件,这将生成一个dumped_.exe。

退出OD,双击执行dumped_.exe,出错信息表明0x00FCB213内存无效。这是Armadillo
的一种反dump机制,针对IAT中部分Entry进行特殊处理,使之指向更难理解、不易
dump的hook code,后者最终调用完成实际功能的Win32 API。前面dump时IAT已被处
理过,IAT中出现了指向hook code的Entry:

008C9104  00FCB213

而0x00FCB213处的代码不在dump范围内,所以执行dumped_.exe时出错。

--------------------------------------------------------------------------
00FCB213    55              push    ebp
00FCB214    8BEC            mov     ebp, esp
00FCB216    51              push    ecx
00FCB217    53              push    ebx
00FCB218    56              push    esi
00FCB219    57              push    edi
00FCB21A    E8 81270000     call    00FCD9A0
00FCB21F    FF75 08         push    dword ptr [ebp+8]
00FCB222    E8 61CAFFFF     call    00FC7C88
00FCB227    85C0            test    eax, eax
00FCB229    59              pop     ecx
00FCB22A    8945 FC         mov     dword ptr [ebp-4], eax
00FCB22D    75 2A           jnz     short 00FCB259
00FCB22F    60              pushad
00FCB230    8B15 18B7FF00   mov     edx, dword ptr [FFB718]          ; kernel32.7C82196D
00FCB236    83C2 64         add     edx, 64                          ; 注意这个加0x64
00FCB239    FFD2            call    near edx                         ; kernel32.GetTickCount
00FCB23B    8B15 B4B6FF00   mov     edx, dword ptr [FFB6B4]          ; kernel32.7C8246E6
00FCB241    83C2 64         add     edx, 64                          ; 注意这个加0x64
00FCB244    B9 05000000     mov     ecx, 5
00FCB249    803A CC         cmp     byte ptr [edx], 0CC
00FCB24C    74 07           je      short 00FCB255
00FCB24E  ^ E2 F9           loopd   short 00FCB249
00FCB250    FF75 08         push    dword ptr [ebp+8]
00FCB253    FFD2            call    near edx                         ; kernel32.GetModuleHandleA,这是完成实际功能的Win32 API
00FCB255    8945 FC         mov     dword ptr [ebp-4], eax
00FCB258    61              popad
00FCB259    8B45 FC         mov     eax, dword ptr [ebp-4]
00FCB25C    5F              pop     edi
00FCB25D    5E              pop     esi
00FCB25E    5B              pop     ebx
00FCB25F    C9              leave
00FCB260    C2 0400         retn    4
--------------------------------------------------------------------------

关于针对IAT中部分Entry进行特殊处理,参看:

Armadillo标准壳完全扫盲 - [2007-05-17]
http://hi.baidu.com/%CC%EC%CD%E2%C3%AB%B3%E6/blog/item/91313df5a0483e24bc3109b4.html
(解释了一些原理,适合新手,但有些术语并不正确,自己修正着理解吧)

重新用OD调试origspider_trial.exe,欺骗OpenMutexA()之后清除所有断点。

hw 008C912C
F9
dd 008C9078

--------------------------------------------------------------------------
008C9078  004C9192  origspid.004C9192
008C907C  004C917A  origspid.004C917A
008C9080  004C9168  origspid.004C9168
008C9084  004C9156  origspid.004C9156
008C9088  004C9148  origspid.004C9148
008C908C  004C9130  origspid.004C9130
008C9090  00000000
008C9094  004C9192  origspid.004C9192
008C9098  004C917A  origspid.004C917A
008C909C  004C9168  origspid.004C9168
008C90A0  004C9156  origspid.004C9156
008C90A4  004C9148  origspid.004C9148
008C90A8  004C9130  origspid.004C9130
008C90AC  00000000
008C90B0  004C926E  origspid.004C926E
008C90B4  004C925E  origspid.004C925E
008C90B8  004C9252  origspid.004C9252
008C90BC  004C9246  origspid.004C9246
008C90C0  004C9236  origspid.004C9236
008C90C4  004C9224  origspid.004C9224
008C90C8  004C9212  origspid.004C9212
008C90CC  004C91FE  origspid.004C91FE
008C90D0  004C91E8  origspid.004C91E8
008C90D4  004C91CE  origspid.004C91CE
008C90D8  004C91BC  origspid.004C91BC
008C90DC  004C91B0  origspid.004C91B0
008C90E0  004C91A2  origspid.004C91A2
008C90E4  00000000
008C90E8  004C926E  origspid.004C926E
008C90EC  004C925E  origspid.004C925E
008C90F0  004C9252  origspid.004C9252
008C90F4  004C9246  origspid.004C9246
008C90F8  004C9236  origspid.004C9236
008C90FC  004C9224  origspid.004C9224
008C9100  004C9212  origspid.004C9212
008C9104  004C91FE  origspid.004C91FE
008C9108  004C91E8  origspid.004C91E8
008C910C  004C91CE  origspid.004C91CE
008C9110  004C91BC  origspid.004C91BC
008C9114  004C91B0  origspid.004C91B0
008C9118  004C91A2  origspid.004C91A2
008C911C  00000000
008C9120  004C927A  origspid.004C927A
008C9124  00000000
008C9128  004C927A  origspid.004C927A
008C912C  00000000
--------------------------------------------------------------------------

清除所有断点。与处理后的IAT比较一下,设置如下数据断点:

hw 008C90A0

--------------------------------------------------------------------------
00FDDB8E    8908            mov     dword ptr [eax], ecx             ; 数据断点命中
00FDDB90    8B85 10D9FFFF   mov     eax, dword ptr [ebp-26F0]        ; origspid.008C90A0
00FDDB96    83C0 04         add     eax, 4
00FDDB99    8985 10D9FFFF   mov     dword ptr [ebp-26F0], eax
00FDDB9F  ^ E9 4DFCFFFF     jmp     00FDD7F1
--------------------------------------------------------------------------

单步跟踪这附近的代码执行情况,可以定位如下代码:

--------------------------------------------------------------------------
00FDD9D2    50              push    eax                              ; 比较Win32 API的名字
00FDD9D3    FF15 7873FE00   call    near dword ptr [FE7378]          ; msvcrt._stricmp
00FDD9D9    59              pop     ecx
00FDD9DA    59              pop     ecx
00FDD9DB    85C0            test    eax, eax
00FDD9DD   /75 11           jnz     short 00FDD9F0                   ; 改成jmp,否则开始针对IAT中部分Entry进行特殊处理
00FDD9DF    8B85 58C2FFFF   mov     eax, dword ptr [ebp-3DA8]
00FDD9E5    8B40 08         mov     eax, dword ptr [eax+8]
00FDD9E8    8985 68CAFFFF   mov     dword ptr [ebp-3598], eax
00FDD9EE    EB 02           jmp     short 00FDD9F2
00FDD9F0  ^ EB 9C           jmp     short 00FDD98E
--------------------------------------------------------------------------

0x00FDD9D3处进行名字比较,检查是否是需要特殊处理的Win32 API。0x00FDD9DD处
不跳转表明找到一个需要特殊处理的Win32 API,最终向IAT中写入hook code入口地
址。若不需要特殊处理则直接在IAT中写入Win32 API入口地址。

重新用OD调试origspider_trial.exe,欺骗OpenMutexA()之后清除所有断点。

hw 008C912C
F9
dd 008C9078

清除所有断点。

hw 008C9094
F9
at 00FDD9DD

将0x00FDD9DD处的jnz改成jmp。

hw 008C912C
F9
dd 008C9078

--------------------------------------------------------------------------
008C9078  004C9192  origspid.004C9192
008C907C  004C917A  origspid.004C917A
008C9080  004C9168  origspid.004C9168
008C9084  004C9156  origspid.004C9156
008C9088  004C9148  origspid.004C9148
008C908C  004C9130  origspid.004C9130
008C9090  00000000
008C9094  77F3F9D8  ADVAPI32.ReportEventA
008C9098  77F3B857  ADVAPI32.RegisterEventSourceA
008C909C  77F3EBD7  ADVAPI32.RegSetValueExA
008C90A0  77F46A17  ADVAPI32.RegCreateKeyExA
008C90A4  77F56CCE  ADVAPI32.RegCloseKey
008C90A8  77F3F160  ADVAPI32.DeregisterEventSource
008C90AC  00FC7E3F
008C90B0  004C926E  origspid.004C926E
008C90B4  004C925E  origspid.004C925E
008C90B8  004C9252  origspid.004C9252
008C90BC  004C9246  origspid.004C9246
008C90C0  004C9236  origspid.004C9236
008C90C4  004C9224  origspid.004C9224
008C90C8  004C9212  origspid.004C9212
008C90CC  004C91FE  origspid.004C91FE
008C90D0  004C91E8  origspid.004C91E8
008C90D4  004C91CE  origspid.004C91CE
008C90D8  004C91BC  origspid.004C91BC
008C90DC  004C91B0  origspid.004C91B0
008C90E0  004C91A2  origspid.004C91A2
008C90E4  00000000
008C90E8  7C825529  kernel32.WriteFile
008C90EC  7C801DC6  kernel32.LoadLibraryA
008C90F0  7C959E17  ntdll.RtlFreeHeap
008C90F4  7C959FD6  ntdll.RtlAllocateHeap
008C90F8  7C82B437  kernel32.GetStdHandle
008C90FC  7C823EC7  kernel32.GetProcessHeap
008C9100  7C823D7A  kernel32.GetProcAddress
008C9104  7C82474A  kernel32.GetModuleHandleA   // 对比前述0x00FCB253处的代码
008C9108  7C8245FF  kernel32.GetModuleFileNameA
008C910C  7C830BE4  kernel32.GetEnvironmentVariableA
008C9110  7C831FE1  kernel32.FindFirstFileA
008C9114  7C82BFB3  kernel32.FindClose
008C9118  7C8268F1  kernel32.ExitProcess
008C911C  00FC7E49
008C9120  004C927A  origspid.004C927A
008C9124  00000000
008C9128  77E519CC  USER32.OemToCharA
008C912C  00FC7D67
--------------------------------------------------------------------------

务必撤消0x00FDD9DD处的修改,将jmp改回成jnz,清除所有断点。

he 00401000
F9

断在0x00401000之后清除所有断点。现在我们有未被处理过的IAT。

运行ArmInline v0.96 Final,选中origspider_trial.exe,Remove Splices。

运行LordPE选中origspider_trial.exe,dump full,保存成dumped.exe。

运行ImportREC,选中origspider_trial.exe,将OEP改成00001000,自动查找IAT,
现在RVA变成004C9078。获取输入表,显示无效函数,剪切指针,修复转存文件,这
将生成一个dumped_.exe。

退出OD,双击执行dumped_.exe,搞定。脱壳后30天试用期自动去除。破解IP限制与
脱壳无关,与本文无关。注意origauditor_trial.exe也加了壳,手脱原理同上。

本来还有个脱壳后的优化,照着下文简单折腾了一下:

脱壳后软件减肥大法 - yesky1 [2003-11-20]
http://www.pediy.com/bbshtml/BBS6/pediy6313.htm

但未成功,不打算深究这一步了。前面有几次提到撤消某某处的修改,是为了躲避内
存中的自校验过程。

曾参看过一些手脱Armadillo壳的文章,不过不太适合我入门,主要原因是之前我完
全不了解该壳的一些保护机制,如果照着TA们的步骤做,会有很多细节对不上号,比
如那个所谓的"magic jmp",照葫芦画瓢只会让我更困惑,还好我一开始也没打算照
葫芦画瓢。由于这次的保护机制只用到两个:

Debug-Blocker
Enable Strategic Code Splicing

所以像我这样的脱壳新手还可以承受,再复杂的以后有需求时再学习吧。

没有技术含量的操作备忘录。

我把这次对我产生原理性的直接帮助的几篇文章列在正文中了。后面的参考资源则是
一个良莠不齐的总收集,亦可参看。

☆ 参考资源

[ 2] Armadillo壳相关文档

     脱壳入门初级教学
     http://bbs.pediy.com/showthread.php?t=20366

     Armadillo 2.52加壳原理分析和改进的脱壳方法 - leo_cyl
     http://blog.csdn.net/compiler/articles/91123.aspx
     (解释了一些重要原理,适合喜欢折腾的Win32程序员)

     试玩armadillo3.50a一点心得 - mysqladm [2004-03-06]
     http://www.pediy.com/bbshtml/BBS6/pediy6837.htm
     (解释了一些重要原理,适合喜欢折腾的Win32程序员)

     Armadillo标准壳完全扫盲 - [2007-05-17]
     http://hi.baidu.com/%CC%EC%CD%E2%C3%AB%B3%E6/blog/item/91313df5a0483e24bc3109b4.html
     (解释了一些原理,适合新手,但有些术语并不正确,自己修正着理解吧)

     Armadillo客户版Code Splicing+Import Table Elimination的简便修复方法 - fly [2005-09-28]
     http://bbs.pediy.com/showthread.php?t=17253
     (介绍了一些Armadillo保护机制的术语以及ArmInline的使用方法)

     Armadillo 3.6主程序脱壳 - tDasm [2004-03-13]
     http://www.pediy.com/bbshtml/BBS6/pediy6444.htm
     (一些原创脱壳技巧)

     Armadillo COPYMEMEII之DUMP的一个LOADPE小插件 - jwh51 [2004-03-15]
     http://www.pediy.com/bbshtml/BBS6/pediy6443.htm

     Armadillo 双进程标准壳快速脱壳 - fly [2004-03-16]
     http://www.pediy.com/bbshtml/BBS6/pediy6446.htm

     Blaze Media Pro5.05脱壳+基本修复CC(int3)+破解 - pyzpyz [2004-04-22]
     http://www.pediy.com/bbshtml/BBS6/pediy6499.htm
     (在tDasm文章基础上新增更多原创脱壳技巧)

     手动脱Armadillo CopyMem-ll +Debug-Blocker壳全过程 - [2004-05-04]
     http://www.cnblogs.com/f4ncy/archive/2005/01/22/95674.html
     http://www.pediy.com/bbshtml/BBS6/pediy6732.htm

     iRider.exe 2.20BETA主程序脱壳 - wxhing [2004-11-12]
     http://wxhing.blogcn.com/diary,204358883.shtml

     浅谈Armadillo V.3.75 与 V.3.78的保护 - C-pen [2004-12-06]
     http://www.pediy.com/bbshtml/BBS6/pediy6906.htm
     (不适合学习阶段的人看,基本是熟手的自我总结小片段,不成体系)

     Armadillo V4.0输入表乱序的简便修复方法 - fly [2004-12-31]
     http://bbs.pediy.com/showthread.php?t=9193
     (介绍了ImportREC)

     脱Armadillo 1.xx - 2.xx的壳教程 - [2005-01-20]
     http://bbs.pediy.com/showthread.php?t=10131

     用Ollydbg手脱Armadillo加壳的DLL - jameshero [2005-05-31]
     http://bbs.pediy.com/showthread.php?t=14098

     Armadillo几个版本脱壳总结 - wynney [2005-06-26]
     http://bbs.pediy.com/showthread.php?t=14753

     ArmadilloCopyMem-ll+Debug-Block - wynney [2005-06-27]
     http://bbs.pediy.com/showthread.php?t=14778

     Armadillo双进程标准壳快速脱壳 - KuNgBiM [2005-08-18]
     http://bbs.pediy.com/showthread.php?t=16342
     (这个基本照搬前文pediy6446.htm)

     Patch 修复 Armadillo 的IAT乱序 - [2005-12-07]
     http://bbs.pediy.com/showthread.php?t=19202
     (有创意,可参考)

     脱带KEY的ARM双进程的壳
     http://bbs.pediy.com/showthread.php?t=29177

     用OD手脱 Armadillo v4.40 DLL壳 - regkiller
     https://forum.eviloctal.com/read-htm-tid-19169.html

     Armadillo 1.xx - 2.xx脱壳 - layper
     http://www.juntuan.net/pjjs/pjjs/n/2005-07-22/6420.html

     老生常谈Armadillo CopyMem-II+Debug-Blocker 保护方式脱壳 - daxia2002
     http://www.hacker.com.cn/forum/view_120213.html

     脱壳后软件减肥大法 - yesky1 [2003-11-20]
     http://www.pediy.com/bbshtml/BBS6/pediy6313.htm

     浅谈程序脱壳后的优化 - CCDebuger [2006-07-03]
     http://bbs.pediy.com/showthread.php?t=28402

     Armadillo壳相关工具

     http://www.pediy.com/tools/unpacker.htm
     http://www.pediy.com/tools/unpack/Armadillo/armafp/armafp.zip
     http://www.pediy.com/tools/unpack/Armadillo/ArmaGUI/ArmaGUI.rar
     http://www.pediy.com/tools/unpack/Armadillo/ArmInline/ArmInlinev0.96f.zip
     http://www.pediy.com/tools/PE_tools/Lordpe/LPE-DLX.rar
     http://www.pediy.com/tools/PE_tools/Rebuilder/Import%20REC/ucfir16f.rar