【文章标题】: ASPack脱壳详细分析
【文章作者】: index09
【作者邮箱】: cradiator@gmail.com
【作者主页】: http://hi.baidu.com/index09
【软件名称】: OD + ASPack
--------------------------------------------------------------------------------
【详细过程】
  上次简单分析了UPX的解压过程。这次我们再分析一个简单的压缩壳ASPack。
  使用ASPack压缩记事本,然后调试开始。
  
  用OD载入后
  01013001 >  60              PUSHAD                                             ; ////////////////////////////////////
  01013002    E8 03000000     CALL NOTEPAD.0101300A
  01013007  - E9 EB045D45     JMP 465E34F7
  0101300C    55              PUSH EBP                                           ; 花指令 在1013015设置断点就可以通过
  0101300D    C3              RETN                                               ; 隐含了call和pop 代码重定位指令
  0101300E    E8 01000000     CALL NOTEPAD.01013014
  01013013    EB 5D           JMP SHORT NOTEPAD.01013072
  01013015    BB EDFFFFFF     MOV EBX,-13
  0101301A    03DD            ADD EBX,EBP                                        ; ebx是用于代码重定位的指针
  0101301C    81EB 00300100   SUB EBX,13000                                      ; ......................................
  这段代码中的JMP是一个花指令,不过使用F7 F8就很容易了解其中隐含的指令。
  这段指令的作用是使用 CALL和POP获得重定位指针。
  
  
  01013022    83BD 7D040000 0>CMP DWORD PTR SS:[EBP+47D],0
  01013029    899D 7D040000   MOV DWORD PTR SS:[EBP+47D],EBX                     ; [ebp+47D] = 重定位指针
  0101302F    0F85 C0030000   JNZ NOTEPAD.010133F5                               ; never jmp
  01013035    8D85 89040000   LEA EAX,DWORD PTR SS:[EBP+489]                     ; eax = kernel32.dll
  0101303B    50              PUSH EAX
  0101303C    FF95 090F0000   CALL DWORD PTR SS:[EBP+F09]                        ; getmodulehandle
  01013042    8985 81040000   MOV DWORD PTR SS:[EBP+481],EAX                     ; [ebp+481] = kernel32 handle
  01013048    8BF0            MOV ESI,EAX                                        ; esi = kernel32 handle
  0101304A    8D7D 51         LEA EDI,DWORD PTR SS:[EBP+51]                      ; ////////////////////////////////////
  这段代码提供了Kernel32.dll的句柄,并存入[ebp+418]中
  
  


  0101304D    57              PUSH EDI                                           ; function name
  0101304E    56              PUSH ESI                                           ; kernel32 handle
  0101304F    FF95 050F0000   CALL DWORD PTR SS:[EBP+F05]                        ; getprocaddress
  01013055    AB              STOS DWORD PTR ES:[EDI]                            ; [ebp+51] = virtualalloc
  01013056    B0 00           MOV AL,0
  01013058    AE              SCAS BYTE PTR ES:[EDI]                             ; 这个循环,获取了VirtualAlloc  VirtualFree
  01013059  ^ 75 FD           JNZ SHORT NOTEPAD.01013058                         ; VirtualProtect三个函数的地址
  0101305B    3807            CMP BYTE PTR DS:[EDI],AL                           ; 分别放在[ebp+51] [ebp+5E] [ebp+6A]
  0101305D  ^ 75 EE           JNZ SHORT NOTEPAD.0101304D                         ; ....................................
  到达这里代码使用GetProcAddress函数,获得了VirtualAlloc、VirtualFree和VirtualProtect三个函数地址。
  并且把它们分别存放在[ebp+51]、[ebp+5E]、[ebp+6A]中。
  这三个函数是解压缩时必须用到的。
  
  
  0101305D  ^\75 EE           JNZ SHORT NOTEPAD.0101304D                         ; ....................................
  0101305F    8D45 7A         LEA EAX,DWORD PTR SS:[EBP+7A]
  01013062    FFE0            JMP EAX                                            ; 跳到下面~~~~~~~~~~~~
  01013064  ^ E1 9A           LOOPDE SHORT NOTEPAD.01013000
  01013066    807C75 61 6C    CMP BYTE PTR SS:[EBP+ESI*2+61],6C
  0101306B    41              INC ECX
  0101306C    6C              INS BYTE PTR ES:[EDI],DX                           ; I/O 命令
  0101306D    6C              INS BYTE PTR ES:[EDI],DX                           ; I/O 命令
  0101306E    6F              OUTS DX,DWORD PTR ES:[EDI]                         ; I/O 命令
  0101306F    6300            ARPL WORD PTR DS:[EAX],AX
  01013071  ^ 74 9B           JE SHORT NOTEPAD.0101300E
  01013073    807C75 61 6C    CMP BYTE PTR SS:[EBP+ESI*2+61],6C
  01013078    46              INC ESI
  01013079    72 65           JB SHORT NOTEPAD.010130E0
  0101307B    65:00D4         ADD AH,DL                                          ; 多余的前缀
  0101307E    1A80 7C75616C   SBB AL,BYTE PTR DS:[EAX+6C61757C]
  01013084    50              PUSH EAX
  01013085    72 6F           JB SHORT NOTEPAD.010130F6
  01013087    74 65           JE SHORT NOTEPAD.010130EE
  01013089    637400 00       ARPL WORD PTR DS:[EAX+EAX],SI
  这是一大段花指令,不过貌似没做好。完全没有达到混淆汇编器的效果。通过JMP EAX直接跳到下面的解压代码。
  
  
  0101308D    8B9D 8D050000   MOV EBX,DWORD PTR SS:[EBP+58D]                     ; 跳到这里~~~~~~~~~~~~~  ebx = [ebp+58D] ???
  01013093    0BDB            OR EBX,EBX
  01013095    74 0A           JE SHORT NOTEPAD.010130A1                          ; always jmp
  01013097    8B03            MOV EAX,DWORD PTR DS:[EBX]
  01013099    8785 91050000   XCHG DWORD PTR SS:[EBP+591],EAX
  0101309F    8903            MOV DWORD PTR DS:[EBX],EAX
  010130A1    8DB5 BD050000   LEA ESI,DWORD PTR SS:[EBP+5BD]                     ; esi = ebp+5BD  point to RAV   RAWSIZE
  010130A7    833E 00         CMP DWORD PTR DS:[ESI],0
  010130AA    0F84 15010000   JE NOTEPAD.010131C5                                ; never jmp
  010130B0    6A 04           PUSH 4                                             ; PAGE_READWRITE
  010130B2    68 00100000     PUSH 1000                                          ; MEM_COMMIT
  010130B7    68 00180000     PUSH 1800                                          ; size
  010130BC    6A 00           PUSH 0                                             ; addr
  010130BE    FF55 51         CALL DWORD PTR SS:[EBP+51]                         ; virtualalloc
  
  .......
  
  0101325C   /74 11           JE SHORT NOTEPAD.0101326F
  0101325E   |03F2            ADD ESI,EDX
  01013260   |AD              LODS DWORD PTR DS:[ESI]
  01013261   |0BC0            OR EAX,EAX
  01013263   |74 0A           JE SHORT NOTEPAD.0101326F
  01013265   |03C2            ADD EAX,EDX
  01013267   |8BF8            MOV EDI,EAX
  01013269   |66:AD           LODS WORD PTR DS:[ESI]
  0101326B   |66:AB           STOS WORD PTR ES:[EDI]
  0101326D  ^|EB F1           JMP SHORT NOTEPAD.01013260
  这便是ASPack的解压指令了。
  这里 [ebp+5BD] 是一个很关键的内存区域,这里面存储了一个结构数组
  struct{
      DWORD dwSecRav;          //原程序段的RVA
      DWORD dwSecRawSize;      //源程序各段的RawSize
  }
  解压过程大致如下:
  1. 然后程序分配了0x1800的解压缓冲空间。
  2. 并且为每个段分配dwSecRawSize大的缓冲空间。
  3. 010130F6    E8 2D050000     CALL NOTEPAD.01013628   将部分数据释放到为每个段分配的缓冲中
  4. 使用缓冲中的数据与对应的段进行运算完成解压过程
  
  
  继续往下面看便是填充IAT的代码了
  0101326F    BE 04760000     MOV ESI,7604                                       ; /////////////////////填写IAT//////////////////
  01013274    8B95 7D040000   MOV EDX,DWORD PTR SS:[EBP+47D]                     ; edx = 重定位指针
  0101327A    03F2            ADD ESI,EDX                                        ; esi 指向结构 IMAGE_IMPORT_DIRECTORY
  0101327C    8B46 0C         MOV EAX,DWORD PTR DS:[ESI+C]
  0101327F    85C0            TEST EAX,EAX
  01013281    0F84 0D010000   JE NOTEPAD.01013394                                ; 这里跳走说明IAT全部填写完成
  01013287    03C2            ADD EAX,EDX
  01013289    8BD8            MOV EBX,EAX
  0101328B    50              PUSH EAX                                           ; eax = lib name
  0101328C    FF95 090F0000   CALL DWORD PTR SS:[EBP+F09]                        ; GetModuleHandle
  01013292    85C0            TEST EAX,EAX
  01013294    75 07           JNZ SHORT NOTEPAD.0101329D
  01013296    53              PUSH EBX
  01013297    FF95 0D0F0000   CALL DWORD PTR SS:[EBP+F0D]                        ; LoadLibrary??
  0101329D    8985 A1050000   MOV DWORD PTR SS:[EBP+5A1],EAX                     ; [ebp+5A1] = lib handle
  
  .....
  
  01013379  ^\E9 2FFFFFFF     JMP NOTEPAD.010132AD                               ; 跳到读取下一个函数
  0101337E    8906            MOV DWORD PTR DS:[ESI],EAX
  01013380    8946 0C         MOV DWORD PTR DS:[ESI+C],EAX
  01013383    8946 10         MOV DWORD PTR DS:[ESI+10],EAX
  01013386    83C6 14         ADD ESI,14
  01013389    8B95 7D040000   MOV EDX,DWORD PTR SS:[EBP+47D]
  0101338F  ^ E9 E8FEFFFF     JMP NOTEPAD.0101327C                               ; ...............................................
  步骤基本如下:
  1. esi指向源程序的 IMAGE_IMPORT_DIRECTORY
  2. 使用GetModuleHandle或LoadLibrary获得DLL的handle
  3. 最后程序破坏了IMAGE_IMPORT_DIRECTORY的OriginFirstThunk等信息,应该是用来防止Dump的。不过这基本没有用处。
  
  
  之后是对文件头的一些修改,因为牵扯到DOS头部,有些东西还没弄明白。
  然后便是
  0101340A    61              POPAD
  0101340B    75 08           JNZ SHORT NOTEPAD.01013415                         ; jmp to OEP
  这两句恢复了寄存器数据,并且JMP到OEP
  
  至此解压完毕,从刚才的过程中我们可以获得IAT的首地址等信息来修复IAT。
  
 老规矩,放上加壳和未加壳的Notepad,UDD调试文件。
 简单说明一下UDD的使用方法。
 用WinHex等32位编辑器打开UDD文件,可以看到里面包含了调试文件的路径。
 把它们改成你调试的Notepad的路径,然后把UDD文件考到OD的UDD目录就OK啦~~
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2009年08月31日 23:52:52