己有2年时间没怎么研究壳了,为了不至于成为壳肓,准备花点时间学一下脱壳。将第一个目标锁定在Obsidium上,这款壳相对来说容易些。
首先声明一下,这篇文章没什么新的技术,Obsidium壳的分析论坛己有许多文章讨论了,写这篇文章主要是记录一下自己的学习过程,方便以
后查找。同时也将这篇文章送给刚入门的脱壳菜鸟们,所以文章中我将详细记录操作过程,同时尽可能说明为什么这么做,希望能通过这篇文
章让新手们举一反三。
  

第1篇  Obsidium 1.0.0.69学习手记  

   用Obsidium 1.0.0.69建个项目,选中Encrypt resoures,Debugger checks,compression,Remove bytes at OEP,Runtime patching,
Runtime tracing等选项。
   为了降低难度,目标程序TraceMe1.exe没有重定位表,因此Obsidium加壳时没有重定位映像。
   实例下载:obsidiumtest1.0.rar 


一.寻找OEP及Stole code 

思路:现在外壳程序都大量了调用异常(SEH)产生非正常的跳转,用以干扰调试,正是外壳这些连续的SEH也给脱壳者指明了一条道路。
OD对异常处理相当灵活,因此在跟踪外壳的过程中,可以利用合适的异常做为一个路标来指导跟踪。用OD加载程序,除了“整数除以0异
常外”(为什么选这个异常?一个个异常试出来的),忽略其他所有异常。然后用OD隐藏插件将OD隐藏,这个版的Anti比较弱。同时按
Alt+B查看一个断点窗口,清除所有断点,有些断点Obsidium会发现被跟踪)
按F9运行程序,遇到异常按Shift+F9通过,5次异常后程序就运行起来了。

第1次异常:
009005EE    F7F0            div     eax
009005F0    EB 02           jmp     short 009005F4
009005F2    68 41EB01A2     push    A201EB41

第2次异常:
0090145E    F7F0            div     eax
00901460    EB 04           jmp     short 00901466

第3次异常:
009018C6    F7F0            div     eax
009018C8    EB 02           jmp     short 009018CC

第4次异常:
00901A62    F7F0            div     eax
00901A64    EB 04           jmp     short 00901A6A

第5次异常:
    
0040B5BC          F7F0               div     eax   //停在这里
0040B5BE          EB 04              jmp     short 0040B5C4

当第5次异常执行后,程序就运行起来了。因此重新加载程序,第5次异常后准备单步跟踪下去。
当第5次异常,停在0040B5BC时,查看堆栈窗口,数据如下:

0012FF98   0012FFE0  Pointer to next SEH record
0012FF9C   0040B5ED  SE handler//关键,如果程序异常后,系统将跳到此处执行

注:有关如何跟踪异常的技巧请参考加密与解密二版菜鸟学习笔记(2) - SEH 结构化异常处理 


因此在命令行设断:bp 0040B5ED
0040B5ED         /EB 04              jmp     short 0040B5F3  //在这句设断点
0040B5EF         |6965 26 F7C80000   imul    esp, [ebp+26], 0C8F7

设好断点后,按Shift+F9让程序通过异常,就会在0040B5ED地址处中断。
中断后,别忘了取消断点。也不要忘了按Shift,而直接按F9了,在Obsidium1.3这样操作后,外壳后面可能会异常出错.此时会看到一堆代码很乱,因为这是有花指令的原故。
此时用花指令去除器,将花指令去除。

 

得到干净的代码如下:
0040B5ED          90                 nop
0040B5EE          90                 nop
0040B5EF          90                 nop
0040B5F0          90                 nop
0040B5F1          90                 nop
0040B5F2          90                 nop
0040B5F3          C8 000000          enter   0, 0
0040B5F7          8B45 08            mov     eax, [ebp+8]
0040B5FA          90                 nop
0040B5FB          90                 nop
0040B5FC          90                 nop
0040B5FD          90                 nop
0040B5FE          8B00               mov     eax, [eax]
0040B600          90                 nop
……
0040B660          8B45 10            mov     eax, [ebp+10]
0040B663          81EA F5C0B200      sub     edx, 0B2C0F5
0040B669          90                 nop
0040B66A          90                 nop
0040B66B          90                 nop
0040B66C          90                 nop
0040B66D          90                 nop
0040B66E          8D940A EFAB0CFF    lea     edx, [edx+ecx+FF0CABEF]
0040B675          90                 nop
0040B676          90                 nop
0040B677          90                 nop
0040B678          90                 nop
0040B679          90                 nop
0040B67A          8990 B8000000      mov     [eax+B8], edx             ; CONTEXT.EIP,此时EDX的值0040BAEA

向下跟踪,其中0040B67A 这句比较熟悉,也是SEH的一个处理形式,eax是指向CONTEXT结构,其偏移B8就是新的EIP,异常后,程序会
跳到这个EIP执行,因此对EDX(此时值0040BAEA)设断:

bp edx或bp0040BAEA 

中断后来到这里:(中断后,别忘了取消断点。 )
0040BAEA          E8 AF000000        call    0040BB9E                  ; 这是一个加密CALL,按F7进去
  {
            //为了阅读方便,仅将相关代码重新整理排版列出,这段代码按F7走出即可
            //这段代码是SMC方法修改0040BAEA地址一段代码

           0040BB9E          60                 pushad
           0040BBA9          836C24 20 05       sub     dword ptr [esp+20], 5   //esp+20初始值为0xEF
           0040BBAE          8B4C24 20          mov     ecx, [esp+20]
           0040BBB7          C601 0E            mov     byte ptr [ecx], 0E      //此时ECX的值就是0040BAEA
           0040BBBE          C741 01 454EB4E7   mov     dword ptr [ecx+1], E7B44E45
           0040BBC5          BE B4000000        mov     esi, 0B4
           0040BBCA          F9                 stc
           0040BBCB          72 02              jb      short 0040BBCF
           0040BBCF          BB 14F9DC24        mov     ebx, 24DCF914
           0040BBD4          8131 E5445F5F      xor     dword ptr [ecx], 5F5F44E5
           0040BBDA          F9                 stc
           0040BBDB          72 04              jb      short 0040BBE1
           0040BBE1          83C1 04            add     ecx, 4
           0040BBE4          F9                 stc
           0040BBE5          72 06              jb      short 0040BBED
           0040BBED          C1C3 45            rol     ebx, 45
           0040BBF0          83EE 04            sub     esi, 4
           0040BBF3        ^ 0F85 DBFFFFFF      jnz     0040BBD4
           0040BBF9          F8                 clc
           0040BBFA          73 06              jnb     short 0040BC02
           0040BBFC          CE                 into
           0040BBFD          7E 12              jle     short 0040BC11
           0040BBFF          4F                 dec     edi
           0040BC00          AB                 stos    dword ptr es:[edi]
           0040BC01          58                 pop     eax
           0040BC02          61                 popad          //在这按F4,再按F8走出这个CALL
           0040BC03          C3                 retn
          }


走出上面的代码,重新来到0040BAEA 这个地址处:
0040BAEA         /EB 01              jmp     short 0040BAED            ; 加密CALL
0040BAEC         |11EB               adc     ebx, ebp
0040BAEE          0230               add     dh, [eax]

小技巧:你也可以一开始就在0040BAEA 这行,按一下F4,即可中断到这里 

按F8单步走,这段有花指令,不要去除,会有自检验,来到如下代码:
0040BB7F          FFE7               jmp     edi                            ;  //关键!  跟进
0040BB81          EB 01              jmp     short 0040BB84
0040BB83          6A EB              push    -15
0040BB85          030C10             add     ecx, [eax+edx]

来到(己去除花指令):
00905AE4          E8 00000000        call    00905AE9
00905AE9          90                 nop
00905AEA          90                 nop
00905AEB          90                 nop
00905AEC          90                 nop
00905AED          90                 nop
00905AEE          5D                 pop     ebp
00905AEF          90                 nop
00905AF0          90                 nop
00905AF1          90                 nop
00905AF2          90                 nop
00905AF3          90                 nop
00905AF4          81ED A0C1B200      sub     ebp, 0B2C1A0
00905AFA          90                 nop
00905AFB          90                 nop
00905AFC          90                 nop
00905AFD          90                 nop
00905AFE          90                 nop
00905AFF          64:67:8F06 0000    pop     dword ptr fs:[0]
……
00905EA9          61                 popad
00905EAA          90                 nop
00905EAB          90                 nop
00905EAC          90                 nop
00905EAD          90                 nop
00905EAE          90                 nop
00905EAF          9D                 popfd
00905EB0          90                 nop
00905EB1          90                 nop
00905EB2          90                 nop
00905EB3          90                 nop
00905EB4          90                 nop
00905EB5          90                 nop
00905EB6          90                 nop
00905EB7          90                 nop
00905EB8          90                 nop
00905EB9          90                 nop
00905EBA          55                 push    ebp           //Stole code第1句
00905EBB          90                 nop
00905EBC          90                 nop
00905EBD          90                 nop
00905EBE          90                 nop
00905EBF          8BEC               mov     ebp, esp      //Stole code第2句
00905EC1          90                 nop
00905EC2          90                 nop
00905EC3          90                 nop
00905EC4          90                 nop
00905EC5          90                 nop
00905EC6          6A FF              push    -1            //Stole code第3句
00905EC8          90                 nop
00905EC9          90                 nop
00905ECA          90                 nop
00905ECB          68 D0404000        push    4040D0        //Stole code第4句
00905ED0          90                 nop
00905ED1          90                 nop
00905ED2          90                 nop
00905ED3          90                 nop
00905ED4          68 D41E4000        push    401ED4        //Stole code第5句                  
00905ED9          90                 nop
00905EDA          90                 nop
00905EDB          90                 nop
00905EDC          64:A1 00000000     mov     eax, fs:[0]   //Stole code第6句
00905EE2          90                 nop
00905EE3          90                 nop
00905EE4          90                 nop
00905EE5          90                 nop
00905EE6          90                 nop
00905EE7          90                 nop
00905EE8          50                 push    eax           //Stole code第7句
00905EE9          90                 nop
00905EEA          90                 nop
00905EEB          90                 nop
00905EEC          90                 nop
00905EED          64:8925 00000000   mov     fs:[0], esp   //Stole code第8句
00905EF4          90                 nop
00905EF5          90                 nop
00905EF6          90                 nop
00905EF7          90                 nop
00905EF8          90                 nop
00905EF9          90                 nop
00905EFA          83EC 58            sub     esp, 58       //Stole code第9句
00905EFD          90                 nop
00905EFE          90                 nop
00905EFF          90                 nop
00905F00          90                 nop
00905F01          90                 nop
00905F02          53                 push    ebx           //Stole code第10句
00905F03          90                 nop
00905F04          90                 nop
00905F05          90                 nop
00905F06          90                 nop
00905F07          90                 nop
00905F08          90                 nop
00905F09          56                 push    esi           //Stole code第11句
00905F0A          90                 nop 
00905F0B          90                 nop
00905F0C          90                 nop
00905F0D          90                 nop
00905F0E          57                 push    edi           //Stole code第12句
00905F0F          90                 nop
00905F10          90                 nop
00905F11          90                 nop
00905F12          90                 nop
00905F13          90                 nop
00905F14          8965 E8            mov     [ebp-18], esp //Stole code第13句
00905F17          90                 nop
00905F18          90                 nop
00905F19          90                 nop
00905F1A          90                 nop
00905F1B        - E9 A6B4AFFF        jmp     TraceMe_.004013C6  //跳到伪OEP


伪OEP处的代码如下:
004013BF      A9            db      A9
004013C0      8B            db      8B
004013C1      A4            db      A4
004013C2      7F            db      7F
004013C3      6D            db      6D                               ;  CHAR 'm'
004013C4      E4            db      E4
004013C5      EE            db      EE
004013C6      FF            db      FF          //跳到此处,伪OEP
004013C7      15            db      15
004013C8      44            db      44                               ;  CHAR 'D'
004013C9      40            db      40                               ;  CHAR '@'
004013CA      40            db      40                               ;  CHAR '@'
004013CB      00            db      00


此时按一下Ctrl+A让OD重新分析一下代码:
004013A0      7A            db      7A                               ;  CHAR 'z'
004013A1      4A            db      4A                               ;  CHAR 'J'
004013A2      1D            db      1D
004013A3      66            db      66                               ;  CHAR 'f'
004013A4      83            db      83
004013A5      29            db      29                               ;  CHAR ')'
004013A6   .  C3            retn
004013A7      DA            db      DA
004013A8      05            db      05
004013A9      50            db      50                               ;  CHAR 'P'
004013AA      F3            db      F3
004013AB      7F            db      7F
004013AC      52            db      52                               ;  CHAR 'R'
004013AD      83            db      83
004013AE      59            db      59                               ;  CHAR 'Y'
004013AF      07            db      07
004013B0      65            db      65                               ;  CHAR 'e'
004013B1      39            db      39                               ;  CHAR '9'
004013B2      C4            db      C4
004013B3      50            db      50                               ;  CHAR 'P'
004013B4      18            db      18
004013B5      18            db      18
004013B6      5E            db      5E                               ;  CHAR '^'
004013B7      24            db      24                               ;  CHAR '$'
004013B8      5D            db      5D                               ;  CHAR ']'
004013B9      0F            db      0F
004013BA   .  8DAC4B 375AA9>lea     ebp, [ebx+ecx*2+8BA95A37]
004013C1   .  A4            movs    byte ptr es:[edi], byte ptr [esi>
004013C2   .  7F 6D         jg      short 00401431
004013C4   .  E4 EE         in      al, 0EE
004013C6   .  FF15 44404000 call    [404044]  //伪OEP
004013CC   .  33D2          xor     edx, edx
004013CE   .  8AD4          mov     dl, ah
004013D0   .  8915 28554000 mov     [405528], ed


将被抽去的OEP代码填上去,正确的代码如下:
004013A0      55            push    ebp
004013A1      8BEC          mov     ebp, esp
004013A3      6A FF         push    -1
004013A5      68 D0404000   push    004040D0
004013AA      68 D41E4000   push    00401ED4                        
004013AF      64:A1 0000000>mov     eax, fs:[0]
004013B5      50            push    eax
004013B6      64:8925 00000>mov     fs:[0], esp
004013BD      83EC 58       sub     esp, 58
004013C0      53            push    ebx
004013C1      56            push    esi
004013C2      57            push    edi
004013C3      8965 E8       mov     [ebp-18], esp
004013C6   .  FF15 44404000 call    [404044]

修补OEP代码后,就可以用LordPE将内存境像full dump另存为dumped.exe。
另外,Obsidium 的Stole code处理非常弱的,没有一点变形,平时只要根据程序的语言特征即可恢复。例如本例跟到伪OEP后,
记下堆栈数据:

0012FF4C   7C930738  ntdll.7C930738
0012FF50   FFFFFFFF
0012FF54   7FFD4000
0012FF58   A4653690
…………………………
0012FFA4   FFFFFFFF
0012FFA8   0012FF4C
0012FFAC   0012FFC0
0012FFB0   0012FFE0  Pointer to next SEH record  
0012FFB4   00401ED4  SE handler                   //这个值记下来
0012FFB8   004040D0  TraceMe_.004040D0           //这个值记下来

再找一个VC的程序,将OEP的代码复制过来,将push    4040D0 ,push    401ED4  填上即可。


二.IAT的处理 

   继续跟踪程序,“004013C6  call    [404044]”这句就是调用系统的某个API,其中404044就是IAT中的某个地址。
在数据窗口下命令:d 404044

00403FF4  00 00 00 00 00 00 00 00 00 00 00 00 DC 4B 90 00  ............躃?
00404004  E8 4B 90 00 F4 4B 90 00 00 4C 90 00 0C 4C 90 00  鐺?鬕?.L?.L?
00404014  18 4C 90 00 24 4C 90 00 30 4C 90 00 3C 4C 90 00  L?$L?0L?<L?
00404024  48 4C 90 00 54 4C 90 00 60 4C 90 00 6C 4C 90 00  HL?TL?`L?lL?
00404034  78 4C 90 00 84 4C 90 00 90 4C 90 00 DC 4D 90 00  xL?凩?怢?躆?
00404044  E6 4D 90 00 B4 4C 90 00 C0 4C 90 00 F0 4D 90 00  鍹?碙?繪?餗?
00404054  D8 4C 90 00 E4 4C 90 00 F0 4C 90 00 FC 4C 90 00  豅?銵?餖?麹?
00404064  08 4D 90 00 14 4D 90 00 20 4D 90 00 2C 4D 90 00  M?M? M?,M?
00404074  38 4D 90 00 44 4D 90 00 50 4D 90 00 5C 4D 90 00  8M?DM?PM?\M?
00404084  68 4D 90 00 74 4D 90 00 80 4D 90 00 8C 4D 90 00  hM?tM?M?孧?
00404094  98 4D 90 00 A4 4D 90 00 FA 4D 90 00 06 4E 90 00  楳??鶰?N?
004040A4  12 4E 90 00 1E 4E 90 00 2A 4E 90 00 36 4E 90 00  N?N?*N?6N?
004040B4  42 4E 90 00 4E 4E 90 00 5A 4E 90 00 66 4E 90 00  BN?NN?ZN?fN?
004040C4  72 4E 90 00 7E 4E 90 00 8A 4E 90 00
 FF FF FF FF  rN?~N?奛?

现在每个DWORD值都是0090xxxx的形式,其指向外壳代码里,程序调用API函数都由外壳程序代为处理。
为了让大家理解清楚,请看这张图:

 

   这张图就是输入表的结构,程序加载内存后,只需要IAT部分,其他部分就不需要了,IAT中的每项都指向一个函数。没加壳的程序,IAT部分
是Windows系统来填充的。加壳程序情况不同了,外壳程序自己模拟Windows系统来填充IAT表(图中红圈,输入表其他部分在外壳里是没有的)
,在填充过程中,外壳可填充HOOK-API代码的地址,这样可间接地获得程序的控制权。
   我们平时讨论的输入表重建工具ImpREC,就是通过这个IAT,重建整个输入表结构。由于Obsidium外壳己将IAT指向外壳代码里,用ImpREC
是不能获得正确的函数名了。我们必须想办法将各函数的真实地址填进IAT表里。例如:

 

   图中的地址7C81485F 就是 kernel32 中的FreeEnvironmentStringsW 函数,地址7C81EE79就是kernel32中的lstrcmpA函数。
这些IAT的地址与你当前的操作系统版本有关,等得到这些真实函数地址后,用ImpREC重建一张完整的输入表。
   再来确定IAT表的大小范围,IAT这张表是一个连续的表,其起始及结束应为0000 0000.上图的红色范围就是IAT表大小,起始地址就
404000,结束地址是4040D0。4040D0后面的地址虽然有数据,但其己不是00904xxx形式了,因此是其他数据。

    IAT的地址:0x404000~0x4040D0  大小0xD0

    获得IAT的范围后,下面就是想办法让Obsidium向这张表里填充正确的API函数地址。关闭程序,重新调试,加载程序,在命令行:
d 404000,此时的数据区全是0.按F9运行程序,当第5次异常后,会发现数据窗口己重新填充数据了:

00404000  DC 4B 90 00 E8 4B 90 00 F4 4B 90 00 00 4C 90 00  躃?鐺?鬕?.L?
00404010  0C 4C 90 00 18 4C 90 00 24 4C 90 00 30 4C 90 00  .L?L?$L?0L?

因此再重新加载程序来过,当第4次整除异常时:
00901A62    F7F0            div     eax//第4次异常
00901A64    EB 04           jmp     short 00901A6A

对00404000 下内在断点,在数据窗口00404000,右键/断点/内存写入。按Shit+F9跳过异常继续执行,会中断如下:
00904808    893E            mov     [esi], edi
0090480A    EB 01           jmp     short 0090480D

别忘记删除内存断点,此时单步跟踪即可。下面代码己将花指令去除,并重新排版整理过:
009047B6    C607 60         mov     byte ptr [edi], 60               ;  pushad
009047BD    66:895F 01      mov     [edi+1], bx
009047C4    66:894F 03      mov     [edi+3], cx                      ; mov bp,cx
009047CC    C1CB 10         ror     ebx, 10                          ; 取bd指定(即mov)
009047D3    8857 06         mov     [edi+6], dl
009047D9    885F 05         mov     [edi+5], bl                      ;  B3 00  mov     bl, 0
009047E2    C647 07 E9      mov     byte ptr [edi+7], 0E9            ; jmp
009047EB    C1CB 10         ror     ebx, 10                          ; ebx=00B3BD66
009047F1    8B45 14         mov     eax, [ebp+14]
009047F4    2BC7            sub     eax, edi
009047F9    83E8 0C         sub     eax, 0C
00904800    8947 08         mov     [edi+8], eax                     ;  jmp     [ebp+14]
00904808    893E            mov     [esi], edi                       ; 写IAT (开始就中断此行)
0090480D    83C7 0C         add     edi, 0C                          ; EDI->buff{0}
00904814    83C6 04         add     esi, 4                           ; IAT+4
0090481A    41              inc     ecx                              ; 计数器
0090481E    3B4D 0C         cmp     ecx, [ebp+C]                     ; =26
00904826  ^ 72 8B           jb      short 009047B3


上面这段代码就是将己加密的API入口地址填进IAT中(上面这段代码不必看懂,只要知道其运行结果是构造如下的代码就行),各API入口代
码的形式:
xxxxxxxx    60              pushad
xxxxxxxx    66:BD 0200      mov     bp, cx (CX是计数器,循环一次加1)
xxxxxxxx    B3 00           mov     bl, 0
xxxxxxxx  ^ E9 56F0FFFF     jmp     [ebp+14]


过完00904826 一行,Ctrl+G,查看EDI处的汇编代码。如下
00904BDC    60              pushad
00904BDD    66:BD 0000      mov     bp, 0
00904BE1    B3 00           mov     bl, 0
00904BE3  ^ E9 6EF0FFFF     jmp     00903C56
00904BE8    60              pushad
00904BE9    66:BD 0100      mov     bp, 1
00904BED    B3 00           mov     bl, 0
00904BEF  ^ E9 62F0FFFF     jmp     00903C56

下面还有一些语句继续处理:
00904830    833E 00         cmp     dword ptr [esi], 0
00904837    75 64           jnz     short 0090489D
0090483C    893E            mov     [esi], edi                       ; 存进IAT
00904843    C607 60         mov     byte ptr [edi], 60               ; pushad
0090484C    66:C747 01 66B8 mov     word ptr [edi+1], 0B866          ;  mov     ax, 0
00904858    66:894F 03      mov     [edi+3], cx                      ; mov     ax,cx(初值是 26)
00904862    C647 05 B2      mov     byte ptr [edi+5], 0B2            ;  mov     dl, 0
0090486C    8857 06         mov     [edi+6], dl
00904874    C647 07 E9      mov     byte ptr [edi+7], 0E9            ; jmp
0090487C    8B45 14         mov     eax, [ebp+14]
0090487F    2BC7            sub     eax, edi
00904886    83E8 0C         sub     eax, 0C
0090488F    8947 08         mov     [edi+8], eax                     ; jmp     00903C56
00904896    90              nop
00904897    83C7 0C         add     edi, 0C
009048A2    8BC7            mov     eax, edi
009048A7    2B45 18         sub     eax, [ebp+18]
009048AF    5F              pop     edi
009048B0    5E              pop     esi
009048B1    5B              pop     ebx
009048B2    C9              leave
009048B3    C2 1400         retn    14


上面这段代码构造当前DLL中的最后一个IAT中的值:
00904DA4    60              pushad
00904DA5    66:B8 2600      mov     ax, 26
00904DA9    B2 00           mov     dl, 0
00904DAB  ^ E9 A6EEFFFF     jmp     00903C56

走出上面的CALL,来到如下(己去除花指令):
00904485    E8 FA020000     call    00904784                         //我们从这返回
0090448F    0143 04         add     [ebx+4], eax
0090449E    8B46 14         mov     eax, [esi+14]                   
009044A6    8B56 10         mov     edx, [esi+10]                  
009044AE    0303            add     eax, [ebx]                       
009044B3    0353 28         add     edx, [ebx+28]
009044BB    FF76 04         push    dword ptr [esi+4]                ; DLL基址
009044BE    53              push    ebx                              ; 
009044BF    52              push    edx                              ; 
009044C0    50              push    eax                              ; IAT地址
009044C1    FF76 0C         push    dword ptr [esi+C]                ; API函数个数
009044C4    E8 6D010000     call    00904636                         //对普通函数解密
009044CC    85C0            test    eax, eax
009044D1    0F84 BB000000   je      00904592
009044DF    837D F0 00      cmp     dword ptr [ebp-10], 0
009044E9    74 37           je      short 00904522
009044F2    8B46 14         mov     eax, [esi+14]                   
009044FB    8B56 10         mov     edx, [esi+10]                   
00904504    0303            add     eax, [ebx]                     
0090450C    0353 28         add     edx, [ebx+28]                    ; 
00904514    FF75 F4         push    dword ptr [ebp-C]                ; 
00904517    53              push    ebx                              ; 
00904518    52              push    edx                              ; 
00904519    50              push    eax                              ; IAT
0090451A    FF76 0C         push    dword ptr [esi+C]                ; API函数个数
0090451D    E8 92010000     call    009046B4                         //对特殊函数解密
00904528    83C6 18         add     esi, 18
00904531    FF45 F8         inc     dword ptr [ebp-8]
00904538    FF4D FC         dec     dword ptr [ebp-4]
00904541  ^ 0F85 1CFDFFFF   jnz     00904263                         //向上跳就继续处理下一个DLL的API函数
0090454A    33C0            xor     eax, eax
00904552    5F              pop     edi
00904553    5E              pop     esi
00904554    5B              pop     ebx
00904555    C9              leave
00904556    C3              retn

   上面这段代码,是Obsidium外壳为防止某些函数指针不能加密,而进行解密的函数。因此下面要做的就是将其修改一下,
让其成所有函数指针的解密器。
 
   下面的修改方法来自辉仔Yock的文章(好像是他的文章首先提出这种修改方法)。

1.先来看看如何对普通函数解密的

009044C4    E8 6D010000     call    00904636 //按F7跟进
{
     00904636    C8 000000       enter   0, 0
     0090463A    53              push    ebx
     0090463B    56              push    esi
     0090463C    57              push    edi
     0090463D    8B5D 14         mov     ebx, [ebp+14]
     00904640    8B75 10         mov     esi, [ebp+10]
     00904643    8B7D 0C         mov     edi, [ebp+C]
     00904646    8B5B 04         mov     ebx, [ebx+4]
     00904649    66:F706 2000    test    word ptr [esi], 20   //这里改成test    [esi], 8 
     0090464E    74 46           je      short 00904696       //这里改成jne      short 00904696 
     00904650    66:F706 0200    test    word ptr [esi], 2
     00904655    75 1F           jnz     short 00904676
     00904657    66:C706 0400    mov     word ptr [esi], 4
     0090465C    8B45 14         mov     eax, [ebp+14]
     0090465F    6A 01           push    1
     00904661    6A 00           push    0
     00904663    FF76 04         push    dword ptr [esi+4]
     00904666    6A 00           push    0
     00904668    FF75 18         push    dword ptr [ebp+18]
     0090466B    FF50 40         call    [eax+40]             //解密函数
     0090466E    85C0            test    eax, eax
     00904670    74 39           je      short 009046AB       //这里改成 je      short 00904696 
     00904672    8907            mov     [edi], eax
     00904674    EB 20           jmp     short 00904696
     00904676    66:C706 0400    mov     word ptr [esi], 4
     0090467B    8B45 14         mov     eax, [ebp+14]
     0090467E    0FB756 02       movzx   edx, word ptr [esi+2]
     00904682    6A 01           push    1
     00904684    52              push    edx
     00904685    6A 00           push    0
     00904687    FF76 04         push    dword ptr [esi+4]
     0090468A    FF75 18         push    dword ptr [ebp+18]
     0090468D    FF50 40         call    [eax+40]             //解密函数
     00904690    85C0            test    eax, eax
     00904692    74 17           je      short 009046AB       //这里改成 je      short 00904696 
     00904694    8907            mov     [edi], eax
     00904696    83C6 08         add     esi, 8
     00904699    83C7 04         add     edi, 4
     0090469C    FF4D 08         dec     dword ptr [ebp+8]
     0090469F  ^ 75 A8           jnz     short 00904649
     009046A1    33C0            xor     eax, eax
     009046A3    40              inc     eax
     009046A4    5F              pop     edi
     009046A5    5E              pop     esi
     009046A6    5B              pop     ebx
     009046A7    C9              leave
     009046A8    C2 1400         retn    14


分析一下,当执行到00904649 test    word ptr [esi], 20 这句时,查看ESI指向的数据:
00900B10  02 00 46 00 3A 8E 2F 1C 02 00 6C 00 3D 54 3F 6B  .F.:?.l.=T?k
00900B20  02 00 47 00 4F 2E B8 88 02 00 57 00 89 E5 80 9A  .G.O.笀.W.夊
00900B30  02 00 47 00 96 84 F8 62 02 00 47 00 C7 31 2C 96  .G.杽鴅.G.?,?
00900B40  02 00 4C 00 51 A3 52 57 02 00 4C 00 00 16 86 A3  .L.QW.L..啠
00900B50  02 00 4D 00 39 1E F1 72 02 00 4C 00 8D BD C1 3F  .M.9駌.L.嵔?
00900B60  02 00 47 00 FF 1F 7C C9 02 00 48 00 FE A8 89 58  .G.|?.H.塜
00900B70  02 00 56 00 4A 0D CE 09 02 00 48 00 72 1D DB 5E  .V.J.?.H.r踍
00900B80  02 00 47 00 70 65 86 B1 02 00 47 00 95 CB E2 AD  .G.pe啽.G.曀猸
00900B90  08 00  00 00 03 00 00 00 08 00  00 00 00 00 00 00  .............
00900BA0  02 00 45 00 CC 97 10 25 02 00 54 00 8D BF 40 AB  .E.虠%.T.嵖@?
 
  其中红色的“08”是特殊函数标志,其他的02是普通函数标志。将00904649 这句改成test    [esi], 8,
目的是比较当前是否为特殊函数,如是特殊函数不处理。
  经过上面的修改,就能将普通的API函数解码出来,并填充到IAT中去。执行完009046A1    xor     eax, eax一句后,
下命令:d 404000 就能查看出己被恢复的IAT数据。

(在数据窗口右键/长型/地址的方式查看)
00404000  7C81485F  kernel32.FreeEnvironmentStringsW   <------这些是解密的函数,函数的地址7C81485F己填充到IAT中
00404004  7C81EE79  kernel32.lstrcmpA
00404008  7C81CC23  kernel32.GetEnvironmentStringsA
0040400C  7C80A0C7  kernel32.WideCharToMultiByte
00404010  7C80A480  kernel32.GetStringTypeW
00404014  7C838CB9  kernel32.GetStringTypeA
00404018  7C80CEC4  kernel32.LCMapStringW
0040401C  7C832E2B  kernel32.LCMapStringA
00404020  7C809CAD  kernel32.MultiByteToWideChar
00404024  7C801D77  kernel32.LoadLibraryA
00404028  7C80AC28  kernel32.GetProcAddress
0040402C  7C9379FD  ntdll.RtlReAllocateHeap
00404030  7C809A81  kernel32.VirtualAlloc
00404034  7C9305D4  ntdll.RtlAllocateHeap
00404038  7C80B529  kernel32.GetModuleHandleA
0040403C  7C801EEE  kernel32.GetStartupInfoA
00404040  00904C9C                                      <------ 这2个是特殊函数                   
00404044  00904CA8
…………


2.再来看看如何对特殊函数解密的
0090451D    E8 92010000     call    009046B4
{
     009046B4    C8 000000       enter   0, 0
     009046B8    53              push    ebx
     009046B9    56              push    esi
     009046BA    57              push    edi
     009046BB    8B5D 14         mov     ebx, [ebp+14]
     009046BE    8B75 10         mov     esi, [ebp+10]
     009046C1    8B7D 0C         mov     edi, [ebp+C]
     009046C4    8B5B 04         mov     ebx, [ebx+4]
     009046C7    66:833E 08      cmp     word ptr [esi], 8
     009046CB    0F85 97000000   jnz     00904768
     009046D1    8B46 04         mov     eax, [esi+4]
     009046D4    83F8 00         cmp     eax, 0
     009046D7    74 44           je      short 0090471D
     009046D9    83F8 01         cmp     eax, 1
     009046DC    74 4E           je      short 0090472C
     009046DE    83F8 02         cmp     eax, 2
     009046E1    74 58           je      short 0090473B
     009046E3    83F8 03         cmp     eax, 3
     009046E6    74 11           je      short 009046F9
     009046E8    83F8 04         cmp     eax, 4
     009046EB    75 7B           jnz     short 00904768
     009046ED    8B45 18         mov     eax, [ebp+18]
     009046F0    05 EE1DB300     add     eax, 0B31DEE
     009046F5    8907            mov     [edi], eax
     009046F7    EB 6F           jmp     short 00904768
     009046F9    8B45 14         mov     eax, [ebp+14]
     009046FC    68 C5B1662D     push    2D66B1C5
     00904701    6A 00           push    0
     00904703    FF50 20         call    [eax+20]  //关键,解密函数,跟进
     {    //己去除花指令并重新整理
          004134F2    60              pushad
          004134F7    E8 00000000     call    004134FC
          00413500    5B              pop     ebx
          00413507    81EB C85AB300   sub     ebx, 0B35AC8
          00413511    8B9B B45AB300   mov     ebx, [ebx+B35AB4]
          0041351F    8B4424 24       mov     eax, [esp+24]
          0041352D    33C9            xor     ecx, ecx
          00413535    8B4483 48       mov     eax, [ebx+eax*4+48]
          0041353E    8B5424 28       mov     edx, [esp+28]
          00413547    51              push    ecx
          00413548    51              push    ecx
          00413549    51              push    ecx
          0041354A    52              push    edx
          0041354B    50              push    eax
          0041354C    FF53 40         call    [ebx+40]  //其实这个CALL是Obsidium自己实现的GetProcAddress 
          00413554    85C0            test    eax, eax  //解密后,EAX保存的就是函数地址
          0041355A   /0F84 DE010000   je      0041373E
          00413560    90              nop               //在这句加上:mov     [edi], eax 
          00413561    90              nop               //将正确的指针放进去IAT中
          00413562    90              nop
          00413563    90              nop
          00413564    90              nop
          00413565    90              nop
          00413566    8BF0            mov     esi, eax
          ……
     }
     00904706    50              push    eax
     00904707    53              push    ebx
     00904708    E8 D7030000     call    00904AE4
     ……
     0090475A    2BD0            sub     edx, eax
     0090475C    C643 05 E9      mov     byte ptr [ebx+5], 0E9
     00904760    8953 06         mov     [ebx+6], edx
     00904763    891F            mov     [edi], ebx   //必须将这句NOP掉,不然会将IAT中原来正确API地址覆盖
     00904765    83C3 0A         add     ebx, 0A
     00904768    83C6 08         add     esi, 8
     0090476B    83C7 04         add     edi, 4
     0090476E    8B45 14         mov     eax, [ebp+14]
     00904771    8958 04         mov     [eax+4], ebx    
     00904774    FF4D 08         dec     dword ptr [ebp+8]
     00904777  ^ 0F85 4AFFFFFF   jnz     009046C7
     0090477D    5F              pop     edi
     0090477E    5E              pop     esi
     0090477F    5B              pop     ebx
     00904780    C9              leave
     00904781    C2 1400         retn    14
}


经过修改后,就能得到正确的IAT了。

00904541  ^ 0F85 1CFDFFFF   jnz     00904263                         //向上跳就继续处理下一个DLL的API函数
0090454A    33C0            xor     eax, eax                          //将断点设在,走出IAT处理过程
00904552    5F              pop     edi
00904553    5E              pop     esi
00904554    5B              pop     ebx
00904555    C9              leave
00904556    C3              retn


此时就可以用ImportREC重建输入表,将IAT的地址RVA:4000,大小D0填进,Get Imports后就能得到正确的输入表。

 

上图明显一个地方出错,因为各DLL之间的数据应为0,因此此处kernel32.dll与user32.dll之间那项,点击右键Cut thunks将这项删除。
另外,再将user32.dll中的40cc此处的项Cut thunks。再将OEP的RVA 13A0填上,Fix Dump即可得到修复后的程序。


三.暗桩修复 

  但是执行脱壳的程序,会出错,用OD加载脱壳后的程序,禁止各种异常。
执行后,会出现“不知如何回避位地地址00901FFC的命令,……”错误,按Shift+F9通过这个异常,会中断如下:
0901FFC    FFFF            ???                                      ; 未知命令
00901FFE    FFFF            ???                                      ; 未知命令

此时查看堆栈:
0012FED4   004026FA  返回到 dumped_.004026FA 来自 009016C3               //在这一行按回车键 
0012FED8   004026A2  返回到 dumped_.004026A2 来自 dumped_.004026BE

会来到出错的代码行:
004026EC   .  FF15 34404000 call    [<&kernel32.HeapAlloc>]          ; \HeapAlloc
004026F2   >  5E            pop     esi
004026F3   .  EB FF         jmp     short 004026F4  //去除
004026F5   .  15 B6804000   adc     eax, 004080B6   //去除
004026FA   .  C3            retn

修改后再运行,再次出错:
00402581   .  FF15 84404000 call    [<&kernel32.HeapFree>]           ; \HeapFree
00402587   >  5E            pop     esi
00402588   .  EB FF         jmp     short 00402589  //nop
0040258A   .  15 B2804000   adc     eax, 004080B2   //nop
0040258F   .  C3            retn

退出时再次出错:
0040131C   .  FF15 B8404000 call    [<&user32.DestroyWindow>]        ; \DestroyWindow
00401322   .  5F            pop     edi
00401323   .  B8 01000000   mov     eax, 1
00401328   .  5E            pop     esi
00401329   .  81C4 F4000000 add     esp, 0F4
0040132F   .  EB FF         jmp     short 00401330   //nop
00401331   .  15 B6804000   adc     eax, 004080B6    //nop
00401336   .  C2 1000       retn    10


原来外壳将垃圾代码加进来了,可能程序其他地方还有这些代码,在OD反汇编窗口搜索命令“adc     eax, 004080B6”
00403B82   .  5F            pop     edi
00403B83   .  C9            leave
00403B84   .  EB FF         jmp     short 00403B85    //nop
00403B86   .  15 B6804000   adc     eax, 004080B6     //nop
00403B8B   .  C3            retn

kanxue
看雪技术论坛 www.pediy.com
2005.12.18