【文章标题】: th666西风 脱壳详解 + 技术分析
【文章作者】: index09
【作者邮箱】: cradiator@gmail.com
【作者主页】: http://hi.baidu.com/index09
【软件名称】: OD
【加壳方式】: th666西风
--------------------------------------------------------------------------------
【详细过程】
  最近在学习脱壳,恰巧看见th666大大在看雪上发表的新壳。
  http://bbs.pediy.com/showthread.php?t=96997
  
  只给出了加壳的notepad,我作为一个脱壳新手正好学习一下。
  分析后发现这个壳的思路十分清晰,用的技术也很实用,真的是学习的好材料。发上来一些心得与大家分享。
  
  用OD载入后
  0101988A >  E8 00000000     CALL NOTEPAD.0101988F
  0101988F    5B              POP EBX
  01019890    81EB 05000000   SUB EBX,5                                         ; ebx 是自定位指针
  01019896    8B93 9F080000   MOV EDX,DWORD PTR DS:[EBX+89F]                    ; edx = size
  0101989C    53              PUSH EBX
  0101989D    6A 40           PUSH 40                                           ; PAGE_EXECUTE_READWRITE
  0101989F    68 00100000     PUSH 1000                                         ; MEM_COMMIT
  010198A4    52              PUSH EDX                                          ; size
  010198A5    6A 00           PUSH 0
  010198A7    FF93 32080000   CALL DWORD PTR DS:[EBX+832]                       ; virtualalloc 生成[ebx+89F]的空间
  010198AD    5B              POP EBX
  010198AE    8BF0            MOV ESI,EAX                                       ; esi = room
  010198B0    8BBB 9B080000   MOV EDI,DWORD PTR DS:[EBX+89B]
  010198B6    03FB            ADD EDI,EBX                                       ; edi = some data
  010198B8    56              PUSH ESI                                          ; room
  010198B9    57              PUSH EDI                                          ; data
  010198BA    E8 86080000     CALL <NOTEPAD.extract>                            ; extract data in edi to buffer in esi (decompress code)
  010198BF    83C4 08         ADD ESP,8
  010198C2    8D93 BB080000   LEA EDX,DWORD PTR DS:[EBX+8BB]
  010198C8    52              PUSH EDX                                          ; ???
  010198C9    53              PUSH EBX                                          ; base
  010198CA    56              PUSH ESI                                          ; room
  010198CB    C3              RETN                                              ; jmp to esi (刚刚解压的代码)
  这段代码很容易看明白。
  首先 用CALL和POP获得自定位指针。
  然后VirtualAlloc一段内存空间。
  010198BA    E8 86080000     CALL <NOTEPAD.extract>                            ; extract data in edi to buffer in esi (decompress code)
  这一个CALL进去以后看就是我们熟悉的APLib解压过程了。
  
  这个壳很有趣,解压代码同样是经过压缩的,所以壳的头部申请空间,并且将壳的解压代码释放到空间里。
  然后使用 PUSH ESI和RETN来实现JMP的功能,跳转到真正的解压代码中。
  
  然后我们来到这里(这里的地址由于是刚刚申请的,所以每次运行都会改变)
  00930000    E8 00000000     CALL 00930005
  00930005    5B              POP EBX
  00930006    81EB 05000000   SUB EBX,5
  0093000C    8B7424 08       MOV ESI,DWORD PTR SS:[ESP+8]                      ; esi是程序入口时的栈顶
  00930010    E8 B7020000     CALL 009302CC                                     ; //////////////获得了许多函数地址///////////////////////
  00930015    8983 BF040000   MOV DWORD PTR DS:[EBX+4BF],EAX                    ; eax是 kernel32 module base
  0093001B    8B83 BF040000   MOV EAX,DWORD PTR DS:[EBX+4BF]
  00930021    8DB3 12050000   LEA ESI,DWORD PTR DS:[EBX+512]                    ; esi = 函数名地址
  00930027    E8 CE020000     CALL <MyGetProcAddress>
  外壳首先过得了kernel32的基地址,然后通过让eax = kernel32 handle  esi = function name并调用MyGetProcAddress来实现函数的获取。
  这里并没有使用windows提供的GetProcAddress例程,作者自己实现了MyGetProcAddress,十分有趣,所以我们跟进去看看。
  
  F7后来到这里
  009302FA >  55              PUSH EBP
  009302FB    8BEC            MOV EBP,ESP
  009302FD    83C4 EC         ADD ESP,-14
  00930300    895D EC         MOV DWORD PTR SS:[EBP-14],EBX
  00930303    C745 FC 0000000>MOV DWORD PTR SS:[EBP-4],0
  0093030A    8945 F4         MOV DWORD PTR SS:[EBP-C],EAX
  0093030D    8975 F0         MOV DWORD PTR SS:[EBP-10],ESI
  00930310    8BD6            MOV EDX,ESI
  00930312    803E 00         CMP BYTE PTR DS:[ESI],0
  00930315    74 03           JE SHORT 0093031A
  00930317    46              INC ESI
  00930318  ^ EB F8           JMP SHORT 00930312
  0093031A    46              INC ESI
  0093031B    2BF2            SUB ESI,EDX                                       ; esi = 函数长度
  0093031D    8975 F8         MOV DWORD PTR SS:[EBP-8],ESI
  00930320    8B75 F4         MOV ESI,DWORD PTR SS:[EBP-C]                      ; esi = kernel32 base
  00930323    0376 3C         ADD ESI,DWORD PTR DS:[ESI+3C]                     ; esi = kernel32 pe header
  00930326    8B76 78         MOV ESI,DWORD PTR DS:[ESI+78]
  00930329    0375 F4         ADD ESI,DWORD PTR SS:[EBP-C]                      ; esi = export directory
  0093032C    8B5E 20         MOV EBX,DWORD PTR DS:[ESI+20]
  0093032F    035D F4         ADD EBX,DWORD PTR SS:[EBP-C]                      ; ebx= address of names
  00930332    33D2            XOR EDX,EDX                                       ; ////////////////////////////////////////
  00930334    56              PUSH ESI                                          ; push data directory
  00930335    8B3B            MOV EDI,DWORD PTR DS:[EBX]
  00930337    037D F4         ADD EDI,DWORD PTR SS:[EBP-C]                      ; edi = function name
  0093033A    8B75 F0         MOV ESI,DWORD PTR SS:[EBP-10]
  0093033D    8B4D F8         MOV ECX,DWORD PTR SS:[EBP-8]
  00930340    FC              CLD                                               ; 这个循环从kernel32的导出表中寻找目标函数的索引
  00930341    F3:A6           REPE CMPS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]     ; edx 为索引
  00930343    75 03           JNZ SHORT 00930348
  00930345    5E              POP ESI
  00930346    EB 0C           JMP SHORT 00930354
  00930348    5E              POP ESI                                           ; esi = export directory
  00930349    83C3 04         ADD EBX,4                                         ; next export function
  0093034C    42              INC EDX                                           ; edx = index
  0093034D    3B56 18         CMP EDX,DWORD PTR DS:[ESI+18]                     ; [esi + 18] = number of names
  00930350  ^ 72 E2           JB SHORT 00930334                                 ; ...........................................
  00930352    EB 22           JMP SHORT 00930376
  00930354    2B5E 20         SUB EBX,DWORD PTR DS:[ESI+20]
  00930357    2B5D F4         SUB EBX,DWORD PTR SS:[EBP-C]
  0093035A    D1EB            SHR EBX,1                                         ; ebx = (ebx - address of names - image base) / 2
  0093035C    035E 24         ADD EBX,DWORD PTR DS:[ESI+24]
  0093035F    035D F4         ADD EBX,DWORD PTR SS:[EBP-C]                      ; ebx = 函数入口项
  00930362    0FB703          MOVZX EAX,WORD PTR DS:[EBX]                       ; eax = 函数入口序号
  00930365    C1E0 02         SHL EAX,2                                         
  00930368    0346 1C         ADD EAX,DWORD PTR DS:[ESI+1C]
  0093036B    0345 F4         ADD EAX,DWORD PTR SS:[EBP-C]
  0093036E    8B00            MOV EAX,DWORD PTR DS:[EAX]
  00930370    0345 F4         ADD EAX,DWORD PTR SS:[EBP-C]                      ; eax = function address
  00930373    8945 FC         MOV DWORD PTR SS:[EBP-4],EAX
  00930376    8B45 FC         MOV EAX,DWORD PTR SS:[EBP-4]
  00930379    8B5D EC         MOV EBX,DWORD PTR SS:[EBP-14]
  0093037C    C9              LEAVE
  0093037D    C3              RETN
  这里的我做的注释已经很清楚了。
  作者的思路是这样的,因为我们传入的kernel32的句柄实际上就是它的基地址。所以它指向kernel32.dll的DOS头部。
  通过头部找到PE头,然后又索引到kernel32.dll的输出表。
  程序遍历输出表,寻找esi指向的函数地址,然后通过eax返回。
  等于作者自己实现了windows提供的GetProcAddress。
  
  
  好了我们离开这里函数,重新回到解压缩的代码中。
  0093000C    8B7424 08       MOV ESI,DWORD PTR SS:[ESP+8]                      ; esi是程序入口时的栈顶
  00930010    E8 B7020000     CALL 009302CC                                     ; //////////////获得了许多函数地址///////////////////////
  00930015    8983 BF040000   MOV DWORD PTR DS:[EBX+4BF],EAX                    ; eax是 kernel32 module base
  0093001B    8B83 BF040000   MOV EAX,DWORD PTR DS:[EBX+4BF]
  00930021    8DB3 12050000   LEA ESI,DWORD PTR DS:[EBX+512]                    ; esi = 函数名地址
  00930027    E8 CE020000     CALL <MyGetProcAddress>
  0093002C    8983 22050000   MOV DWORD PTR DS:[EBX+522],EAX                    ; eax = GetMuduleHandleA
  00930032    8B83 BF040000   MOV EAX,DWORD PTR DS:[EBX+4BF]
  00930038    8DB3 DE040000   LEA ESI,DWORD PTR DS:[EBX+4DE]
  0093003E    E8 B7020000     CALL <MyGetProcAddress>
  00930043    8983 EB040000   MOV DWORD PTR DS:[EBX+4EB],EAX                    ; loadlibrarya
  00930049    8B83 BF040000   MOV EAX,DWORD PTR DS:[EBX+4BF]
  0093004F    8DB3 EF040000   LEA ESI,DWORD PTR DS:[EBX+4EF]
  00930055    E8 A0020000     CALL <MyGetProcAddress>
  0093005A    8983 FE040000   MOV DWORD PTR DS:[EBX+4FE],EAX                    ; getprocaddress
  00930060    8B83 BF040000   MOV EAX,DWORD PTR DS:[EBX+4BF]
  00930066    8DB3 26050000   LEA ESI,DWORD PTR DS:[EBX+526]
  0093006C    E8 89020000     CALL <MyGetProcAddress>
  00930071    8983 34050000   MOV DWORD PTR DS:[EBX+534],EAX                    ; virtualprotect
  00930077    8D93 C3040000   LEA EDX,DWORD PTR DS:[EBX+4C3]
  0093007D    52              PUSH EDX                                          ; user32.dll
  0093007E    FF93 EB040000   CALL DWORD PTR DS:[EBX+4EB]                       ; loadlibrary
  00930084    8983 62050000   MOV DWORD PTR DS:[EBX+562],EAX
  0093008A    8D93 02050000   LEA EDX,DWORD PTR DS:[EBX+502]
  00930090    52              PUSH EDX
  00930091    50              PUSH EAX                                          ; messageboxa
  00930092    FF93 FE040000   CALL DWORD PTR DS:[EBX+4FE]                       ; getprocaddress
  00930098    8983 0E050000   MOV DWORD PTR DS:[EBX+50E],EAX
  0093009E    8D93 52050000   LEA EDX,DWORD PTR DS:[EBX+552]
  009300A4    52              PUSH EDX
  009300A5    FFB3 62050000   PUSH DWORD PTR DS:[EBX+562]                       ; IsIconic
  009300AB    FF93 FE040000   CALL DWORD PTR DS:[EBX+4FE]                       ; getprocaddress
  009300B1    8983 5A050000   MOV DWORD PTR DS:[EBX+55A],EAX                    ; ...........................................
  009300B7    BA 1C060000     MOV EDX,61C                                       ; /////////////////////////////////////////
  作者使用刚刚介绍的函数,获得了几个重要函数后。
  
  
  009300B7    BA 1C060000     MOV EDX,61C                                       ; /////////////////////////////////////////
  009300BC    03D3            ADD EDX,EBX
  009300BE    52              PUSH EDX
  009300BF    6A 04           PUSH 4
  009300C1    6A 04           PUSH 4                                            ; 这一段将kernel32的e_lfanew设置为本段代码的基地址
  009300C3    8B93 BF040000   MOV EDX,DWORD PTR DS:[EBX+4BF]
  009300C9    83C2 1C         ADD EDX,1C
  009300CC    52              PUSH EDX
  009300CD    FF93 34050000   CALL DWORD PTR DS:[EBX+534]                       ; VirtualProtect   set kernel32 -> e_lfanew PAGE_READWRITE
  009300D3    8B93 BF040000   MOV EDX,DWORD PTR DS:[EBX+4BF]
  009300D9    83C2 1C         ADD EDX,1C
  009300DC    891A            MOV DWORD PTR DS:[EDX],EBX                        ; kernel32->e_lfanew = decoder base
  009300DE    BA 20060000     MOV EDX,620
  009300E3    03D3            ADD EDX,EBX
  009300E5    52              PUSH EDX
  009300E6    FFB3 1C060000   PUSH DWORD PTR DS:[EBX+61C]
  009300EC    6A 04           PUSH 4
  009300EE    8B93 BF040000   MOV EDX,DWORD PTR DS:[EBX+4BF]
  009300F4    83C2 1C         ADD EDX,1C
  009300F7    52              PUSH EDX
  009300F8    FF93 34050000   CALL DWORD PTR DS:[EBX+534]                       ; VirtualProtect.............................
  这就是作者的帖子中所说的改了kernel32内存中的一些数据。
  也就是e_lfanew指针。但暂时还不知道他的用意是什么。
  哪位大侠指点一下??
  
  然后来到了西风同学弹出版权对话框的地方
  009300FE    8B83 66050000   MOV EAX,DWORD PTR DS:[EBX+566]                    ; eax = 控制版权对话框的数字
  00930104    A9 02000000     TEST EAX,2
  00930109    74 04           JE SHORT 0093010F
  0093010B    EB 04           JMP SHORT 00930111
  0093010D    EB 02           JMP SHORT 00930111
  0093010F    EB 18           JMP SHORT 00930129
  00930111    6A 40           PUSH 40
  00930113    8D93 9D030000   LEA EDX,DWORD PTR DS:[EBX+39D]
  00930119    52              PUSH EDX
  0093011A    8D93 BF030000   LEA EDX,DWORD PTR DS:[EBX+3BF]
  00930120    52              PUSH EDX
  00930121    6A 00           PUSH 0
  00930123    FF93 0E050000   CALL DWORD PTR DS:[EBX+50E]                       ; messageboxa    弹出版权对话框
  00930129    5D              POP EBP                                           ; ebp = 程序入口点
  0093012A    8F83 82050000   POP DWORD PTR DS:[EBX+582]                        ; [ebx+582] = ?????
  00930130    B9 05000000     MOV ECX,5                                         ; ////////////////////////////////
  关键在这里
  009300FE    8B83 66050000   MOV EAX,DWORD PTR DS:[EBX+566]                    ; eax = 控制版权对话框的数字
  当ebx+566指向的数据为2时就会弹出对话框,不想看到他的话改一下:)
  
  继续往下
  0093012A    8F83 82050000   POP DWORD PTR DS:[EBX+582]                        ; [ebx+582] = ?????
  00930130    B9 05000000     MOV ECX,5                                         ; ////////////////////////////////
  00930135    8DB5 26080000   LEA ESI,DWORD PTR SS:[EBP+826]                    ; esi = IAT
  0093013B    8DBB 6A050000   LEA EDI,DWORD PTR DS:[EBX+56A]
  00930141    FF36            PUSH DWORD PTR DS:[ESI]                           ; 这个循环把程序本身的IAT复制到[ebx+56A]中
  00930143    8F07            POP DWORD PTR DS:[EDI]
  00930145    83C6 04         ADD ESI,4
  00930148    83C7 04         ADD EDI,4
  0093014B    49              DEC ECX
  0093014C  ^ 75 F3           JNZ SHORT 00930141                                ; ................................
  没什么好说的壳把程序本身的IAT,复制到另外一片空间中。
  以后大部分系统调用GetProcAddress之类的,都是从ebx+56A指向的函数数组中找啦~~~:)
  
  
  然后来这里
  00930158    8983 7E050000   MOV DWORD PTR DS:[EBX+57E],EAX                    ; ///////////这段代码将ebx+586指向的结构释放,共有[ebx+5fe]个结构///////////////////////
  0093015E    0FB78B FE050000 MOVZX ECX,WORD PTR DS:[EBX+5FE]                   ; ecx = [ebx+5fe] = ???? = 2
  00930165    8D93 86050000   LEA EDX,DWORD PTR DS:[EBX+586]
  0093016B    EB 4B           JMP SHORT 009301B8
  0093016D    51              PUSH ECX
  0093016E    52              PUSH EDX
  0093016F    53              PUSH EBX
  00930170    6A 04           PUSH 4                                            ; PAGE_READWRITE
  00930172    68 00100000     PUSH 1000                                         ; MEM_COMMIT
  00930177    FF32            PUSH DWORD PTR DS:[EDX]                           ; size
  00930179    6A 00           PUSH 0
  0093017B    FF93 76050000   CALL DWORD PTR DS:[EBX+576]                       ; VirtualAlloc
  00930181    8BF0            MOV ESI,EAX
  00930183    5B              POP EBX
  00930184    5A              POP EDX
  00930185    8B83 7E050000   MOV EAX,DWORD PTR DS:[EBX+57E]                    ; eax = image base
  0093018B    0342 04         ADD EAX,DWORD PTR DS:[EDX+4]
  0093018E    8BF8            MOV EDI,EAX
  00930190    56              PUSH ESI                                          ; esi是刚才申请的空间
  00930191    57              PUSH EDI                                          ; 压缩的数据
  00930192    FF93 82050000   CALL DWORD PTR DS:[EBX+582]                       ; 解压
  00930198    83C4 08         ADD ESP,8
  0093019B    56              PUSH ESI
  0093019C    8B0A            MOV ECX,DWORD PTR DS:[EDX]
  0093019E    F3:A4           REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]      ; 解压完复制回原地址
  009301A0    5E              POP ESI
  009301A1    52              PUSH EDX
  009301A2    53              PUSH EBX
  009301A3    68 00800000     PUSH 8000
  009301A8    FF32            PUSH DWORD PTR DS:[EDX]
  009301AA    56              PUSH ESI
  009301AB    FF93 7A050000   CALL DWORD PTR DS:[EBX+57A]                       ; VirtualFree  释放了刚才申请的空间
  009301B1    5B              POP EBX
  009301B2    5A              POP EDX
  009301B3    59              POP ECX
  009301B4    83C2 0C         ADD EDX,0C
  009301B7    49              DEC ECX
  009301B8    0BC9            OR ECX,ECX
  009301BA  ^ 75 B1           JNZ SHORT 0093016D                                ; ..............................................
  这是壳中比较重要的部分,释放程序资源和代码。
  在这里有几个比较重要的参数和结构。
  MOVZX ECX,WORD PTR DS:[EBX+5FE] 这里的ecx是一个技术器。
  LEA EDX,DWORD PTR DS:[EBX+586]  edx指向一个很重要的结构。
  如下
  struct{
       DWORD size;     //这个是一个大小,以后申请的缓冲就按照这个大小
       DWORD rva;      //这个事压缩资源的rva,加上基地址之后就可以找到压缩资源了
       DWORD unused;   //这个东西没有用到
  }
  首先作者读取size申请空间。再通过rva得到压缩后的代码地址。
  同样CALL ApLib的解压代码把解压后的代码释放到刚刚申请的缓冲中。
  再将缓冲中的数据复制回之前的压缩代码的位置。
  通过循环完成ecx个结构的解码,想这ecx应该就是源程序的段了。
  
  解码完毕后我们该修复IAT了。
  009301BC    8BB3 00060000   MOV ESI,DWORD PTR DS:[EBX+600]
  009301C2    03B3 7E050000   ADD ESI,DWORD PTR DS:[EBX+57E]                    ; esi = import directory
  009301C8    E9 E7000000     JMP 009302B4                                      ; ////////////////////////////////////////////////
  009301CD    53              PUSH EBX
  009301CE    56              PUSH ESI
  009301CF    8B46 0C         MOV EAX,DWORD PTR DS:[ESI+C]
  009301D2    0383 7E050000   ADD EAX,DWORD PTR DS:[EBX+57E]                    ; eax = dll name
  009301D8    50              PUSH EAX
  009301D9    FF93 72050000   CALL DWORD PTR DS:[EBX+572]                       ; getmodulehandlea
  009301DF    0BC0            OR EAX,EAX
  009301E1    75 14           JNZ SHORT 009301F7
  009301E3    5E              POP ESI
  009301E4    5B              POP EBX
  009301E5    53              PUSH EBX
  009301E6    56              PUSH ESI
  009301E7    8B46 0C         MOV EAX,DWORD PTR DS:[ESI+C]
  009301EA    0383 7E050000   ADD EAX,DWORD PTR DS:[EBX+57E]
  009301F0    50              PUSH EAX
  009301F1    FF93 6E050000   CALL DWORD PTR DS:[EBX+56E]                       ; loadlibrary
  009301F7    5E              POP ESI
  009301F8    5B              POP EBX
  009301F9    56              PUSH ESI
  009301FA    8BD0            MOV EDX,EAX                                       ; edx = module handle
  009301FC    8B06            MOV EAX,DWORD PTR DS:[ESI]                        ; eax = Original First Thunk RVA
  009301FE    8B7E 10         MOV EDI,DWORD PTR DS:[ESI+10]
  00930201    03BB 7E050000   ADD EDI,DWORD PTR DS:[EBX+57E]                    ; edi = First Thunk
  00930207    0BC0            OR EAX,EAX
  00930209    75 03           JNZ SHORT 0093020E
  0093020B    8B46 10         MOV EAX,DWORD PTR DS:[ESI+10]
  0093020E    8BF0            MOV ESI,EAX
  00930210    03B3 7E050000   ADD ESI,DWORD PTR DS:[EBX+57E]                    ; esi = Original First Thunk
  00930216    E9 8C000000     JMP 009302A7                                      ; ////////////////////////////////
  0093021B    F706 00000080   TEST DWORD PTR DS:[ESI],80000000
  00930221    74 1B           JE SHORT 0093023E
  00930223    8B06            MOV EAX,DWORD PTR DS:[ESI]
  00930225    25 FFFFFF7F     AND EAX,7FFFFFFF
  0093022A    53              PUSH EBX
  0093022B    52              PUSH EDX
  0093022C    56              PUSH ESI
  0093022D    57              PUSH EDI
  0093022E    50              PUSH EAX
  0093022F    52              PUSH EDX
  00930230    FF93 6A050000   CALL DWORD PTR DS:[EBX+56A]
  00930236    5F              POP EDI
  00930237    8907            MOV DWORD PTR DS:[EDI],EAX
  00930239    5E              POP ESI
  0093023A    5A              POP EDX
  0093023B    5B              POP EBX
  0093023C    EB 63           JMP SHORT 009302A1
  0093023E    8B06            MOV EAX,DWORD PTR DS:[ESI]
  00930240    83C0 02         ADD EAX,2                                         ; eax + 2是怎么回事
  00930243    53              PUSH EBX
  00930244    52              PUSH EDX
  00930245    56              PUSH ESI
  00930246    57              PUSH EDI
  00930247    0383 7E050000   ADD EAX,DWORD PTR DS:[EBX+57E]
  0093024D    50              PUSH EAX
  0093024E    52              PUSH EDX
  0093024F    FF93 6A050000   CALL DWORD PTR DS:[EBX+56A]                       ; getprocaddress
  00930255    5F              POP EDI
  00930256    8B93 66050000   MOV EDX,DWORD PTR DS:[EBX+566]
  0093025C    A9 01000000     TEST EAX,1                                        ; eax非零??? 这么判断???
  00930261    74 39           JE SHORT 0093029C
  00930263    3B83 22050000   CMP EAX,DWORD PTR DS:[EBX+522]                    ; eax 和 getmodulehandlea比较
  00930269    75 21           JNZ SHORT 0093028C
  0093026B    8983 18060000   MOV DWORD PTR DS:[EBX+618],EAX                    ; 如果是getmodulehandlea函数
  00930271    8D93 3B060000   LEA EDX,DWORD PTR DS:[EBX+63B]
  00930277    C602 A1         MOV BYTE PTR DS:[EDX],0A1
  0093027A    B8 18060000     MOV EAX,618
  0093027F    03C3            ADD EAX,EBX
  00930281    8942 01         MOV DWORD PTR DS:[EDX+1],EAX
  00930284    66:C742 05 FFE0 MOV WORD PTR DS:[EDX+5],0E0FF
  0093028A    8BC2            MOV EAX,EDX
  0093028C    3B83 5A050000   CMP EAX,DWORD PTR DS:[EBX+55A]                    ; eax 和 IsIconic 比较
  00930292    75 08           JNZ SHORT 0093029C
  00930294    8D93 47060000   LEA EDX,DWORD PTR DS:[EBX+647]                    ; 如果是 IsIconic 则替换为 ebx+647
  0093029A    8BC2            MOV EAX,EDX
  0093029C    8907            MOV DWORD PTR DS:[EDI],EAX
  0093029E    5E              POP ESI
  0093029F    5A              POP EDX
  009302A0    5B              POP EBX
  009302A1    83C6 04         ADD ESI,4
  009302A4    83C7 04         ADD EDI,4
  009302A7    833E 00         CMP DWORD PTR DS:[ESI],0                          ; original first thunk指向0 则跳出
  009302AA  ^ 0F85 6BFFFFFF   JNZ 0093021B                                      ; ....................................
  009302B0    5E              POP ESI
  009302B1    83C6 14         ADD ESI,14
  009302B4    837E 0C 00      CMP DWORD PTR DS:[ESI+C],0                        ; dll name == null??
  009302B8  ^ 0F85 0FFFFFFF   JNZ 009301CD                                      ; ................................................
  这一段代码十分标准,但有两个特别的地方。
  就是遇到GetModuleHandleA和IsIconic则替换为壳中自带的两个对应函数。
  这就给工具修复IAT带来了一定麻烦,好在解决方法很简单。
  00930269    75 21           JNZ SHORT 0093028C
  00930292    75 08           JNZ SHORT 0093029C
  把这两句直接改成JMP就可以啦。
  这里还有一个很有趣的判断
  0093025C    A9 01000000     TEST EAX,1                   
  00930269    75 21           JNZ SHORT 0093028C
  eax是GetProcAddress返回的函数地址。
  如果尾数不等于1才做替换工作,何解???望作者或各位大侠指点。
  
  009302BE    8B83 04060000   MOV EAX,DWORD PTR DS:[EBX+604]
  009302C4    0383 7E050000   ADD EAX,DWORD PTR DS:[EBX+57E]
  009302CA    50              PUSH EAX
  009302CB    C3              RETN
  然后RETN就来到OEP了,如果刚才改了JMP,OllyDump一下就可以了。
  
  
  最后提一下IsIconic的替换后函数。
  00930647    3BF6            CMP ESI,ESI
  00930649    64:A1 30000000  MOV EAX,DWORD PTR FS:[30]      ;获得peb
  0093064F    8B40 0C         MOV EAX,DWORD PTR DS:[EAX+C]    ;PEB_LDR_DATA
  00930652    8B40 1C         MOV EAX,DWORD PTR DS:[EAX+1C]    
  00930655    8B00            MOV EAX,DWORD PTR DS:[EAX]
  00930657    8B50 08         MOV EDX,DWORD PTR DS:[EAX+8]
  0093065A    8B42 1C         MOV EAX,DWORD PTR DS:[EDX+1C]
  0093065D    8BD0            MOV EDX,EAX
  0093065F    FFA2 5A050000   JMP DWORD PTR DS:[EDX+55A]
  最后一句挑到真正的IsIconic函数。
  前面作者获得了PEB中的一些数据,这个结构很复杂,不多说。
  有一篇文章希望有用
  http://bbs.pediy.com/showthread.php?t=52398
  
--------------------------------------------------------------------------------
【经验总结】
  这个壳虽然不难,但逻辑十分清晰,是不可多得的教材呀。希望看到作者更棒的作品
  还有导出表找函数那里写得十分精彩。
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2009年09月05日 22:19:37