我在《(之四)》一文中大言不惭地搞了一个“通用脱壳机”,当时我甚至不知道壳还有“压缩壳”和“加密壳”之分。在网友的提示下自觉汗颜,气不敢出,忙翻书充电。找了一个ASProtect 1.31版尝尝(不敢找更高的版本,因为我现在还不知道什么叫SDK)。果然ASProtect名不虚传,无数的“暗道机关”加“地雷”,初入者简直寸步难行。我花了约一个月的(业余)时间,居然摸出了一些门道,只用OD,无需任何其它工具,实现了“完美脱壳”。现把我的方法总结如下,和网友分享。
  
  一、认识ASProtect中的SEH
  1.ASProtect 1.31版中,共有29——31个SEH异常结构,对不同的程序加密方式可以不同,所以设置的SEH结构也稍有不同。大多数SEH我们无需理它,更不必了解它在干什么,不去招惹它可以相安无事。
  2.几乎所有的SEH都可以nop掉或jmp,不过一定要(从异常开始处)nop到或jmp到“pop dword ptr fs:[0]”。由于有花指令,“pop dword ptr fs:[0]”并不总是可以直接看到,但代码“67 64 8F 06 00 00”或“64 8F 05 00 00 00 00”就是pop dword ptr fs:[0]的位置。本来,pop dword ptr fs:[edi]等,只要edi=0,都是可以的,但ASProtect 1.31版中只有pop dword ptr fs:[0]一种形式。
  3.所有的SEH异常位置,最后4个字节是不变的。这样就很容易识别该SEH的功能。
  4.几个重要的SEH位置(只是位置,并不关心SEH干什么,加密等工作也不是SEH干的)
  xxxxDC77:  将压缩程序解压还原;
  xxxx578B:  加密IAT表;
  (xxxx58A9,xxxx4AAA,xxxx4AED)是更利害的加密IAT过程;(供不同程序使用,与前者不共用)
  xxxxEA2E:  只要知道了OEP,从这里跳出就可以dump了,后面近10个SEH都是为加密OEP设置的;
  5.程序中有几个必需经过的“文件校验”(查断点,修改,反Anti)结合具体程序再说。
  
  二、什么都不管,首先寻找OEP
  打开OD,去掉“忽略内存访问”的勾,让它每次中断在SEH。一律按“shift+F9”运行,大约中断29次后,来到“xxxxF145”中断。将该处代码“01 56 00”改为“EB 08”(即jmp xxxxF14F)。按F7不放(单步跟踪),注意观察OD的地址栏,有时在一个循环中久久不能跳出,可以尝试在可能跳出的位置设断,再按F9。当地址出现“xxx8xxxx”时要小心了(OEP入口,每次运行地址都不同,但前四字节xxx8是不变的)。将内存显示地址设置为“xxx81000”并可以尝试在内存栏中搜索代码“8D 04 18 5C FF E0”。若找到了,OEP就很快就得到了。由于有花指令,即使OEP入口就在眼前,你也很可能错过。入口处代码是(每次地址是不同的):

00983B2A    F4                      hlt
00983B2B    F4                      hlt
00983B2C    FB                      sti
00983B2D    39EB                    cmp ebx,ebp
00983B2F    019A 8D04185C           add dword ptr ds:[edx+5C18048D],ebx
00983B35    FFE0                    jmp eax
  
  由于在如下00983B03处多次回跳,可以先搜索“0F BF C1 E9 D5 FE FF FF”,入口转跳“jmp eax”就在它的下面几行处,那怕是到了最后几跳,jmp eax前面的代码还在不断地变化!(利害!)  
00983AFB    66:81DB 2B1F            sbb bx,1F2B
00983B00    0FBFC1                  movsx eax,cx
00983B03  ^ E9 D5FEFFFF             jmp 009839DD
  单步走到jmp eax,记下eax的值,这项工作就完成了。

  三、查找IAT表的加密过程
  重新运行OD,打开“notepade_asp.exe”(XP中的notepade.exe用ASProtect 1.31加密),18次按shift+F9来到0096578B异常,将该处代码改为“EB 00”,可以很快到达009655E4——0096572B,这就是加密IAT表的全部代码。花指令不算太多,只需将每个“EB 01”下面的一个字节改为90(nop)就可以去除了。
  不必关心它是怎样加密的,只需知道它有多少种加密方式。每加密一次,它都要将加密后的代码存入IAT表,因此查找存入IAT表地址的代码是关键。原来,它是用下面方法存入加密代码的:

00965707    8B55 0C                 mov edx,dword ptr ss:[ebp+C]
0096570A    8B12                    mov edx,dword ptr ds:[edx]
0096570C    8902                    mov dword ptr ds:[edx],eax
  其中,eax是加密后的代码,edx是IAT表的位置。本段代码中,供有5处相同的代码,说明它有5种加密方式,但对一个程序不是5种加密都用上的。为此,在每个mov dword ptr ds:[edx],eax处设断,按F9运行,每中断一次,释放一个断点,当程序走到下一个异常时,回头看看剩下几个断点。剩下的就是没有使用的加密方式。notepade_asp.exe只用了三种方式,它们是:

00965663    57                      push edi        ;API函数名
00965664    8B45 10                 mov eax,dword ptr ss:[ebp+10]
00965667    50                      push eax        ;库函数名
00965668    56                      push esi
00965669    E8 8AFDFFFF             call 009653F8      ;GetProcAddress
0096566E    8BD8                    mov ebx,eax
00965670    6A 00                   push 0
00965672    68 48489600             push 964848
00965677    8D4D FC                 lea ecx,dword ptr ss:[ebp-4]
0096567A    8BD3                    mov edx,ebx
0096567C    8BC6                    mov eax,esi
0096567E    E8 15F9FFFF             call 00964F98      ;加密
00965683    8B55 0C                 mov edx,dword ptr ss:[ebp+C]
00965686    8B12                    mov edx,dword ptr ds:[edx]
00965688    8902                    mov dword ptr ds:[edx],eax    ;存入IAT(加密)
0096568A    E9 90000000             jmp 0096571F

009656FC    57                      push edi        ;API函数名
009656FD    8B45 10                 mov eax,dword ptr ss:[ebp+10]
00965700    50                      push eax        ;库函数名
00965701    56                      push esi
00965702    E8 F1FCFFFF             call 009653F8      ;GetProcAddress
00965707    8B55 0C                 mov edx,dword ptr ss:[ebp+C]
0096570A    8B12                    mov edx,dword ptr ds:[edx]
0096570C    8902                    mov dword ptr ds:[edx],eax    ;存入IAT(未加密)
0096570E    EB 0F                   jmp short 0096571F

00965710    B8 08439600             mov eax,964308      ;964308干什么?
00965715    8B55 0C                 mov edx,dword ptr ss:[ebp+C]
00965718    8B12                    mov edx,dword ptr ds:[edx]
0096571A    8902                    mov dword ptr ds:[edx],eax    ;存入IAT(加密)
0096571C    EB 01                   jmp short 0096571F
  
  964308干什么的?它作为加密地址存入了IAT?跟踪进入964308,原来,它就是调用GetProcAddress,思路完全清楚了,且API都是明码出现,脱壳工作就显得简单多了!

  四、增加notepade_asp.exe的空字节,准备写入IID和INT(函数名表)
  不知什么原因,每个ASProtect加壳程序最后都有4096个0字节(但不一定在文件中),可按如下修改,并将装载尺寸改为32000,用16进制编辑器,在文件最后添加4096个0字节:

----------------------------------------------------------
节区名称  实际尺寸  内存地址  对齐尺寸  文件地址  节区属性
----------------------------------------------------------
          00008000  00001000  00004000  00000400  E0000040
          00002000  00009000  00000200  00004400  E0000040
.rsrc     00008000  0000B000  00000E00  00004600  E0000040
.data     0001E000  00013000  0001D400  00005400  E0000040
.adata    00001000  00031000  00001000  00022800  E0000040

  五、添加脱壳代码
  在文件最后1000h字节中添加如下代码:(全部的代码就这些)
00228000  00 00 00 00 B0 11 03 01 B0 01 03 01 00 00 00 00  
00228010  00 00 00 00 56 50 57 39 05 00 10 03 01 74 1F A3  
00228020  00 10 03 01 8B F0 E8 58 00 00 00 83 C0 10 89 38  
00228030  83 C0 04 A3 08 10 03 01 FF 05 10 10 03 01 5E 56  
00228040  E8 3E 00 00 00 83 EF 02 89 3D 0C 10 03 01 5F 58  
00228050  5E E8 A2 43 93 FF 8B 55 0C 8B 12 A1 0C 10 03 01  
00228060  89 02 83 3D 10 10 03 01 00 74 13 81 F2 00 00 00  
00228070  01 A1 08 10 03 01 89 10 FF 0D 10 10 03 01 E9 9C  
00228080  46 93 FF 8B 3D 04 10 03 01 57 AC 3C 00 74 03 AA  
00228090  EB F8 47 47 89 3D 04 10 03 01 5F 81 F7 00 00 00 
002280A0  01 A1 08 10 03 01 C3 90 00 00 00 00 00 00 00 00 
  228000——228014是作为寄存器使用,228004存入的是函数名表开始地址,228008存入的是IID表开始地址。程序从228014开始,反汇编是:

01031014    56                  push esi
01031015    50                  push eax
01031016    57                  push edi
01031017    3905 00100301       cmp dword ptr ds:[1031000],eax     ;将[1031000]作为新库识别标记
0103101D    74 1F               je short 0103103E                  ;新库开始则不跳
0103101F    A3 00100301         mov dword ptr ds:[1031000],eax
01031024    8BF0                mov esi,eax
01031026    E8 58000000         call 01031083                      ;取库函数名字串
0103102B    83C0 10             add eax,10
0103102E    8938                mov dword ptr ds:[eax],edi     ;库名地址存IID
01031030    83C0 04             add eax,4
01031033    A3 08100301         mov dword ptr ds:[1031008],eax
01031038    FF05 10100301       inc dword ptr ds:[1031010]         ;新库开始标记
0103103E    5E                  pop esi
0103103F    56                  push esi
01031040    E8 3E000000         call 01031083                      ;取API函数名字串
01031045    83EF 02             sub edi,2
01031048    893D 0C100301       mov dword ptr ds:[103100C],edi     ;API地址暂存
0103104E    5F                  pop edi
0103104F    58                  pop eax
01031050    5E                  pop esi
01031051    E8 A24393FF         call 009653F8         ;借用原程序取IAT表地址
01031056    8B55 0C             mov edx,dword ptr ss:[ebp+C]
01031059    8B12                mov edx,dword ptr ds:[edx]
0103105B    A1 0C100301         mov eax,dword ptr ds:[103100C]
01031060    8902                mov dword ptr ds:[edx],eax     ;复原后的API地址存入IAT
01031062    833D 10100301 00    cmp dword ptr ds:[1031010],0
01031069    74 13               je short 0103107E          ;若不是新库则跳             
0103106B    81F2 00000001       xor edx,1000000
01031071    A1 08100301         mov eax,dword ptr ds:[1031008]     
01031076    8910                mov dword ptr ds:[eax],edx     ;IAT表开始地址存入IID表
01031078    FF0D 10100301       dec dword ptr ds:[1031010]
0103107E  - E9 9C4693FF         jmp 0096571F         ;返回原程序,准备下次复原  
01031083    8B3D 04100301       mov edi,dword ptr ds:[1031004]     ;写INT函数名表子程序  
01031089    57                  push edi
0103108A    AC                  lods byte ptr ds:[esi]
0103108B    3C 00               cmp al,0
0103108D    74 03               je short 01031092                 
0103108F    AA                  stos byte ptr es:[edi]
01031090  ^ EB F8               jmp short 0103108A                 
01031092    47                  inc edi
01031093    47                  inc edi
01031094    893D 04100301       mov dword ptr ds:[1031004],edi
0103109A    5F                  pop edi
0103109B    81F7 00000001       xor edi,1000000
010310A1    A1 08100301         mov eax,dword ptr ds:[1031008]
010310A6    C3                  retn

  六、修改几个转跳
  打开OD,按shift+F9,18次后来到0096578B处,将该处异常代码改为“EB 00”。剩下的工作就不多了,将00965669处的“call 009653F8”改为“jmp 01031014”,将00965702处的“call 009653F8”改为“jmp 01031014”,最后将00965710作如下改动:

00965710    BF 009A9700             mov edi,979A00    ;在该处自已键入GetProcAddress字串
00965715    57                      push edi
00965716    8B45 10                 mov eax,dword ptr ss:[ebp+10]
00965719    50                      push eax
0096571A    56                      push esi
0096571B  ^ EB E5                   jmp short 00965702
0096571D    90                      nop

反复核对修改无误后,按F9,再按一次shift+F9来到0096EA2E,准备dump了。虽然后面设置了许多的陷阱,但我根本就不过去了,ASProtect看着我干着急^_^.

  七、Dump,修改最后的PE头的函数导入表
  到了0096EA2E后,修改该代码为“EB 0E”(即jmp 96EA3E),单步二次,将“add esp,4”改为“add esp,84”,继续汇编“push 100739D”(OEP地址),“ret”。然后单步存入100739D。或先修改为:

0096EA2E   /EB 0E                   jmp short 0096EA3E
0096EA30   |BA 886DC1E7             mov edx,E7C16D88
0096EA35   |C3                      retn
0096EA36   |FE                      ???                               
0096EA37   |AD                      lods dword ptr ds:[esi]
0096EA38   |221B                    and bl,byte ptr ds:[ebx]
0096EA3A   |F2:                     prefix repne:
0096EA3B   |90                      nop
0096EA3C   |90                      nop
0096EA3D   |90                      nop
0096EA3E   \67:64:8F06 0000         pop dword ptr fs:[0]
0096EA44    81C4 84000000           add esp,84
0096EA4A    68 9D730001             push 100739D
0096EA4F    C3                      retn

然后单步运行。进入100739D后。开始dump,去掉“重建输入表”的勾,dump存盘为notepad_new.exe,最后,用16进制编辑器将函数导入表地址改为:“00310B4”(B4 10 03 00),导入表长度可改可不改,或改为“000000F0”(F0 00 00 00)存盘。然后运行,怎么样,完美脱壳了吧!只不过脱壳程序中垃圾过多,显得很臃肿,可以减肥。

  这次就写到这里,下次准备说说xxxx58A9,xxxx4AAA,xxxx4AED更利害的加密IAT过程。