Asprotect V2.X 的脱壳与修复的总结及练习

作    者: blackeyes

关于Asprotect V2.X 的脱壳与修复, loveboom的《ASPROTECT 2.x 脱壳系列》已经非常的全面与经典. 

本人在此只是依葫芦画瓢, 并将有些地方再详细的解释一下, 给菜鸟们脱Asprotect V2.X时进行参考, 高手就不要看了.


先在理论上研讨一下, 由于编译器的不一样, C 与 Delphi 所编译的汇编结果有差别, Asprotect 加壳时处理的也不一样.

先假设有一程序:

OEP: 00401000
IAT: 00407000 - 00407FFF
在00401100 CALL DLL1.API1
在00401180 CALL DLL1.API2

Ospr 加壳后, 好多的API CALL 被改成 CALL 12000000

然后开始研究.

1. C/C++ Program

1.1) 未被加壳的程序

00401000    55              PUSH EBP                                  ; 程序 OEP
00401001    8BEC            MOV EBP, ESP
...
00401100    FF15 00704000   CALL DWORD PTR DS:[00407000]              ; DLL1.API1
00401106    ...
00401180    FF15 04704000   CALL DWORD PTR DS:[00407004]              ; DLL1.API2
00401186    ...


********* IAT 可能是这样的 ************
00407000    DD  7C571000    // DLL1.API1
00407004    DD  7C572000    // DLL1.API2
...
004070FC    DD  00000000

00407100    DD  7D00XXXX    // DLL2.API1
...
004071FC    DD  00000000

...

00407F00    DD  7F00XXXX    // DLLn.API1
...
00407FFC    DD  00000000



1.2) 被 Asprotect 加壳后的程序在OEP

00401000    55              PUSH EBP                                  ; 程序 OEP
00401001    8BEC            MOV EBP, ESP
...
00401100    E8   FBEEBF11   CALL 12000000                             ; 壳
00401105    ??
00401106    ...
00401180    E8   7BEEBF11   CALL 12000000                             ; 壳
00401185    ??
00401186    ...

*************IAT************************
00407000    DD  ????????    // 被加密的 DLL1.API1 信息
00407004    DD  7C572000    // 未加密的 DLL1.API2

ASPR 将许多的API CALL 都改成了统一的 CALL 12000000 
即改 CALL DWORD PTR DS:[xxxxx] 成 CALL 12000000


1.3) 加壳后的程序运行过API CALL 后

00401000    55              PUSH EBP                                  ; 程序 OEP
00401001    8BEC            MOV EBP, ESP
...
00401100    E8   00EFBF12   CALL 13000005                             ; 壳
00401105    ??
00401106    ...                                                       ; 假设运行到这
00401180    E8   7BEEBF11   CALL 12000000                             ; 壳
00401185    ??
00401186    ...

即 每经过一个CALL12000000 后, 这个 CALL12000000 就会被改成 CALL13000005 或者 其它的 CALL 14000005, 反正每个API 还不一样, 分别

对应一段壳的CODE, 等于相对应的API CODE

1.4) 脱壳与修复需要解决以下几个问题

A.) 找到每一个 CALL 12000000 所在的地址, 例如00401100, 00401180, ...

B.) 找到每一个 CALL 12000000 实际应该CALL到的 API 地址, 例如 7C571000, 7C572000, ...

C.) 如果IAT的地址未加密, 修复 CALL 12000000 成 CALL DWORD PTR DS:[0040xxxx]

  例如  00401180    E8   7BEEBF11   CALL 12000000 这句, 根据 API 地址 7C572000 可在 IAT 中找到, 位于00407004

  修复成  00401180    FF15 04704000   CALL DWORD PTR DS:[00407004]

  注意: CALL 12000000 是 5 个字节, 而 CALL DWORD PTR DS:[xxxxxxxx]  是 6 个字节

D.) 如果IAT的地址被加密, 可在IAT强行加上一项, 再修复 CALL 12000000 成 CALL DWORD PTR DS:[0040xxxx]

  例如  00401100    E8   FBEEBF11   CALL 12000000 这句, API 地址 7C571000 在 IAT 中找不到, 

  可在IAT最后加上一项, 最后的IAT可能会变成这样:
...
00407F00    DD  7D00XXXX    // DLLn.API1
...
00407FFC    DD  00000000    // 原来的 IAT 到这结束

00408000    DD  7C571000  // 新加的 IAT 项
00408004    DD  7C573000  // 新加的 IAT 项
...
00408080    DD  00000000  // 每个DLL的IAT必须以 00000000 结束, 不能与其它DLL 的IAT连成一片
00408084    DD  7D00xxxx  // 新加的 IAT 项, 另外的DLL的
...
........    DD  00000000    // 修复的 IAT 到这结束

  再把00401180 修复成  00401100    FF15 04704000   CALL DWORD PTR DS:[00408004]

  注意: 这里已经与未加壳的程序有一点不一样了, 即 API 地址在 IAT 的位置不一样, 但功能是一样的, 

1.5) 在到达OEP之前, 壳把程序中的API CALL 改成 CALL 12000000 的地方的CODE 是这样的:

012C5F7A    45              INC EBP                                  ; EBP 就是 CALL 12000000 处的地址, Pactch1
012C5F7B    8945 00         MOV DWORD PTR SS:[EBP],EAX
012C5F7E    6A 0A           PUSH 0A
012C5F80    E8 8FCEFEFF     CALL 012B2E14
012C5F85    8BC8            MOV ECX,EAX
012C5F87    038B E4000000   ADD ECX,DWORD PTR DS:[EBX+E4]
012C5F8D    8BD6            MOV EDX,ESI
012C5F8F    8BC3            MOV EAX,EBX
012C5F91    E8 9EE5FFFF     CALL 012C4534
012C5F96    FF0C24          DEC DWORD PTR SS:[ESP]
012C5F99    03B3 E4000000   ADD ESI,DWORD PTR DS:[EBX+E4]
012C5F9F    833C24 00       CMP DWORD PTR SS:[ESP],0
012C5FA3  ^ 0F87 55FEFFFF   JA 012C5DFE                              ; 循环
012C5FA9    53              PUSH EBX                                 ; 到这, 所有的 API CALL 改 CALL 12000000 都完成了

所以 在 012C5F7A 处, 要先到我们的修复 CODE1, 把 EBP 保存下来, 然后再回来

012C5F7A    E8 xxxxxxxx     JMP 1540100                              ;
012C5F7F    90              NOP

到 012C5FA9 后, 把 012C5F7A 处的CODE 还原, 再运行到 OEP

找这段CODE, 可在壳解压缩自身后, SEARCH
  inc ebp
  move [ebp], eax
  push 0A

1.6) 到达OEP之后, 如果跟踪 CALL 12000000 到壳中, 壳会根据CALL是从哪儿来的 解码 API 地址,

  API 地址 出现并且不会被壳检测的地方的CODE是这样的:

012A2544         85C0                         TEST EAX,EAX
012A2546         74 0A                        JE SHORT 012A2552
012A2548         FF15 18902C01                CALL DWORD PTR DS:[12C9018]    // 到这儿时, EDX=API 地址, Patch2
012A254E         09C0                         OR EAX,EAX
012A2550         74 01                        JE SHORT 012A2553
012A2552         C3                           RETN

然后壳把CALL 12000000 改成 相应的CALL 13000005

最后壳再跳到新改成的 CALL 13000005 处继续执行

015100A1         9D                           POPFD
015100A2         5C                           POP ESP
015100A3         FF6424 FC                    JMP DWORD PTR SS:[ESP-4]      // 这儿跳到00401100 处的 CALL 13000005     
015100A7         F2:                          PREFIX REPNE:                 ; Superfluous prefix
015100A8         EB 01                        JMP SHORT 015100AB
015100AA       - E9 94CA2E01                  JMP 027FCB43
015100AF         C3                           RETN

  在 OEP 处修复时,  就是用修复 CODE2 模拟的运行 每一处的 CALL 12000000, 取得 每一处的对应的API 地址, 
  
  然后在 JMP DWORD PTR SS:[ESP-4]  处截住, 这时当然还不能让它跑去执行真正的API CODE
  
  根据CALL 12000000 所在的地址与得到的 API 地址就可修复 IAT

  然后再模拟运行下一个 CALL 12000000


所以 在 012A2548 处, 要先到我们的修复 CODE3, 把 EDX 保存下来, 然后再回来
012A2548         FF15 xxxxxxxx                CALL DWORD PTR DS:[01540050]    

// 01540050 中是CODE3 的起始地址 015401F0

在 015100A3 处, 不能让它自由的跳, 要让它回到 修复 CODE2 去 修复 IAT

loveboom 说这儿的 CODE 不能改, 壳会检测出来, 我没试过. 反正可在这儿设硬件断点, 再靠script 强行跳到 CODE2 中.

只是每次运行时 JMP DWORD PTR SS:[ESP-4] 这一句所在的地址还都不一样, 真的挺烦人的

找这段CODE, 可 在OD 中 用 Ctrl+T 设 Run Trace 的暂停条件 command is POPFD, 再 Ctrl+F11, 

找到后去掉暂停条件,并设硬件断点, 否则 Script 运行会出错的.



2. 再讨论 Delph 程序

2.1) 未被加壳的程序

00401000    55              PUSH EBP                                  ; 程序 OEP
00401001    8BEC            MOV EBP, ESP
...
00401100    E8 FB4E0000     CALL 00406000                             ; DLL1.API1
00401105    ...
00401180    E8 7F4E0000     CALL 00406004                             ; DLL1.API2
00401185    ...


00406000    FF25 00704000   JMP DWORD PTR DS:[00407000]               ; DLL1.API1 
00406006    FF25 04704000   JMP DWORD PTR DS:[00407004]               ; DLL1.API2 

*************IAT************************
IAT 与 C/C++ 程序一样

2.2) 被 Asprotect 加壳后的程序在OEP

00401000    55              PUSH EBP                                  ; 程序 OEP
00401001    8BEC            MOV EBP, ESP
...
00401100    E8 FB4E0000     CALL 00406000                             ; DLL1.API1
00401105    ...
00401180    E8 7F4E0000     CALL 00406004                             ; DLL1.API2
00401185    ...


00406000    E8 FB9FBF11     CALL 12000000                             ; 壳
00406005    ??
00406006    E8 7B9FBF11     CALL 12000000                             ; 壳
0040600B    ??

注意: ASPR 修改的地方与C/C++程序是不一样的, 它是改 JMP DWORD PTR DS:[xxxxx] 成 CALL 12000000


2.3) 加壳后的程序运行过API CALL 后
00401000    55              PUSH EBP                                  ; 程序 OEP
00401001    8BEC            MOV EBP, ESP
...
00401100    E8 FB4E0000     CALL 00406000                             ; DLL1.API1
00401105    ...
00401180    E8 7F4E0000     CALL 00406004                             ; DLL1.API2
00401185    ...


00406000    E8 00A0BF12     CALL 13000005                             ; 壳
00406005    ??
00406006    E8 7B9FBF11     CALL 12000000                             ; 壳
0040600B    ??

同样是把CALL 12000000 改成 CALL 13000005

2.4) 修复与C程序类似, 只是要把 
    CALL 12000000 
  修复成 
    FF25  xxxxxxxx    JMP DWORD PTR DS:[xxxxxxxx]


3.0  理论上研讨完了, 再说实际修复

3.1) 与位置无关的修复CODE

需要OLLYDBG 的plugin: Memory Manage, 分配 一块内存

01540000    DD              012C5F7A                                  ; // Patch1 Address, 供 第 1 段CODE 用
01540010    DD              012A2548                                  ; // Patch2 Address, 供 第 2 段CODE 用
01540020    DD              0077A180                                  ; // IAT Start Address, 供 第 2 段CODE 用
01540024    DD              00001408                                  ; // Original IAT Size, 供 第 2 段CODE 用
01540040    DD              ??                                        ; // Patch1 后的 Address
01540050    DD              ??                                        ; // 第 3 段CODE 的 Address
01540054    DD              ??                                        ; // CurrAPIAddress
01540058    DD              ??                                        ; // CurrDLLBase
01540060    DD              ??                                        ; // IAT Start Address
01540064    DD              ??                                        ; // Current IAT Size
01540080    DD              ??                                        ; // 指向 CALL Address Struct 
01540084    DD              ??                                        ; // 指向 API  Address Struct 


// 01540100 - 01540165 为 第 1 段CODE, 保存 每一个 CALL 的地址 到我们的表中, 即EBP
// 需要 01540000 处为 Patch1 Address
01540100    60              PUSHAD
01540101    55              PUSH EBP                                 ; EBP 中为CALL 12000000 处的地址
01540102    90              NOP
01540103    E8 00000000     CALL 01540108
01540108    5D              POP EBP
01540109    8D55 5A         LEA EDX,DWORD PTR SS:[EBP+5A]            ; EDX = 01540162
0154010C    81E5 0000FFFF   AND EBP,FFFF0000                         ; EBP = 01540000, 我们的基址
01540112    90              NOP
01540113    83BD 80000000 0>CMP DWORD PTR SS:[EBP+80],0
0154011A    75 1B           JNZ SHORT 01540137
0154011C    8B45 00         MOV EAX,DWORD PTR SS:[EBP]               ; Patch1 Address
0154011F    83C0 05         ADD EAX,5
01540122    8945 40         MOV DWORD PTR SS:[EBP+40],EAX            ; Patch1 后的 返回Address
01540125    8D45 40         LEA EAX,DWORD PTR SS:[EBP+40]
01540128    8902            MOV DWORD PTR DS:[EDX],EAX               ; 自动修正 01540160 中的 JMP DWORD PTR: [xxxx]
0154012A    8D95 00030000   LEA EDX,DWORD PTR SS:[EBP+300]           ; CALL Address Struct @ 基址+300, 够了吧!
01540130    8995 80000000   MOV DWORD PTR SS:[EBP+80],EDX            ; 保存
01540136    90              NOP
01540137    8B8D 80000000   MOV ECX,DWORD PTR SS:[EBP+80]
0154013D    8379 04 00      CMP DWORD PTR DS:[ECX+4],0               ; CurrCallAddr
01540141    75 09           JNZ SHORT 0154014C
01540143    8D51 10         LEA EDX,DWORD PTR DS:[ECX+10]
01540146    8911            MOV DWORD PTR DS:[ECX],EDX
01540148    8951 04         MOV DWORD PTR DS:[ECX+4],EDX
0154014B    90              NOP
0154014C    8B79 04         MOV EDI,DWORD PTR DS:[ECX+4] 
0154014F    90              NOP
01540150    5D              POP EBP                                  ; EBP 中为CALL 12000000 处的地址
01540151    892F            MOV DWORD PTR DS:[EDI],EBP               ; 保存 CALL 地址 到我们的表中
01540153    83C7 04         ADD EDI,4
01540156    8979 04         MOV DWORD PTR DS:[ECX+4],EDI             ; CurrCallAddr 指向表中的下一处
01540159    61              POPAD
0154015A    45              INC EBP                                  ; 执行壳原来的代码
0154015B    8945 00         MOV DWORD PTR SS:[EBP],EAX               ; 执行壳原来的代码
0154015E    6A 0A           PUSH 0A                                  ; 执行壳原来的代码
01540160    FF25 40005401   JMP DWORD PTR DS:[1540040]               ; 回到壳中去
01540166    90              NOP
01540167    90              NOP
01540168    90              NOP
01540169    90              NOP
0154016A    90              NOP
0154016B    90              NOP
0154016C    90              NOP
0154016D    90              NOP
0154016E    90              NOP
0154016F    90              NOP
01540170    90              NOP
01540171    90              NOP
01540172    90              NOP
01540173    90              NOP
01540174    90              NOP
01540175    90              NOP
01540176    90              NOP
01540177    90              NOP
01540178    90              NOP
01540179    90              NOP
0154017A    90              NOP
0154017B    90              NOP
0154017C    90              NOP
0154017D    90              NOP
0154017E    90              NOP
0154017F    90              NOP

// 01540180 - 015402BF 为 第 2 段CODE, 修复每一个CALL, 及IAT
// 需要 01540010 处为 Patch2 Address, 01540020 处为IAT Address & Size
01540180    60              PUSHAD
01540181    E8 00000000     CALL 01540186
01540186    5D              POP EBP
01540187    8D55 7D         LEA EDX,DWORD PTR SS:[EBP+7D]            ; EDX = 01540203
0154018A    90              NOP
0154018B    90              NOP
0154018C    90              NOP
0154018D    81E5 0000FFFF   AND EBP,FFFF0000                         ; EBP = 01540000, 我们的基址
01540193    90              NOP
01540194    8B5D 10         MOV EBX,DWORD PTR SS:[EBP+10]            ; Patch2 Address
01540197    8B43 02         MOV EAX,DWORD PTR DS:[EBX+2]             ; 取 012A2548处 CALL [12C9018] 中的 12C9018
0154019A    8902            MOV DWORD PTR DS:[EDX],EAX               ; 改 01540201处 为 CALL DWORD PTR DS:[12C9018]
0154019C    8D42 ED         LEA EAX,DWORD PTR DS:[EDX-13]            ; 第 3 段 CODE 的 Address
0154019F    8945 50         MOV DWORD PTR SS:[EBP+50],EAX
015401A2    8D45 50         LEA EAX,DWORD PTR SS:[EBP+50]
015401A5    8943 02         MOV DWORD PTR DS:[EBX+2],EAX             ; 改 Patch2 处 为 CALL DWORD PTR DS:[1540050]
015401A8    90              NOP
015401A9    8BB5 80000000   MOV ESI,DWORD PTR SS:[EBP+80]            ; 指向 CALL Address Struct 
015401AF    8B46 04         MOV EAX,DWORD PTR DS:[ESI+4]             ;      CALL Address Struct 的结束地址
015401B2    05 00010000     ADD EAX,100
015401B7    25 00FFFFFF     AND EAX,FFFFFF00
015401BC    8985 84000000   MOV DWORD PTR SS:[EBP+84],EAX            ; 指向 API  Address Struct 
015401C2    90              NOP
015401C3    8D50 10         LEA EDX,DWORD PTR DS:[EAX+10]
015401C6    8910            MOV DWORD PTR DS:[EAX],EDX               ; FirstAPIAddr
015401C8    8950 04         MOV DWORD PTR DS:[EAX+4],EDX             ; CurrAPIAddr
015401CB    90              NOP
015401CC    8B85 80000000   MOV EAX,DWORD PTR SS:[EBP+80]
015401D2    8B30            MOV ESI,DWORD PTR DS:[EAX]               ; ESI=CallAddrs[]
015401D4    8B85 84000000   MOV EAX,DWORD PTR SS:[EBP+84]
015401DA    8B38            MOV EDI,DWORD PTR DS:[EAX]               ; EDI=APIAddrs[]
015401DC    90              NOP
015401DD    AD              LODS DWORD PTR DS:[ESI]                  ; CallAddr[i]
015401DE    83F8 00         CMP EAX,0
015401E1    74 3D           JE SHORT 01540220                        ; 所有的API 地址都得到了, 跳去IAT 修复
015401E3    FFE0            JMP EAX                                  ; 跳到 CallAddr[i] 去执行 CALL 12000000
015401E5    90              NOP
015401E6    90              NOP
015401E7    90              NOP
015401E8    90              NOP
015401E9    90              NOP
015401EA    90              NOP
015401EB    90              NOP
015401EC    90              NOP
015401ED    90              NOP
015401EE    90              NOP
015401EF    90              NOP
015401F0    60              PUSHAD                                   ; 015401F0 - 01540207 是 第 3 段CODE, 保存 EDX
015401F1    E8 00000000     CALL 015401F6
015401F6    5D              POP EBP
015401F7    81E5 0000FFFF   AND EBP,FFFF0000
015401FD    8955 54         MOV DWORD PTR SS:[EBP+54],EDX            ; 保存 API Address 到 [EBP+54], 01540054
01540200    61              POPAD
01540201    FF15 18902C01   CALL DWORD PTR DS:[12C9018]              ; 执行壳原来的代码
01540207    C3              RETN                                     ; 回到壳中去
01540208    90              NOP
01540209    90              NOP
0154020A    90              NOP
0154020B    90              NOP
0154020C    90              NOP
0154020D    90              NOP
0154020E    90              NOP
0154020F    90              NOP
01540210    8B45 54         MOV EAX,DWORD PTR SS:[EBP+54]            ; 由 Script 在 015100A3   JMP DWORD PTR SS:[ESP-4] 断点强行跳到这
01540213    AB              STOS DWORD PTR ES:[EDI]                  ; API Address ==> APIAddrs[i]
01540214  ^ EB C7           JMP SHORT 015401DD
01540216    90              NOP
01540217    90              NOP
01540218    90              NOP
01540219    90              NOP
0154021A    90              NOP
0154021B    90              NOP
0154021C    90              NOP
0154021D    90              NOP
0154021E    90              NOP
0154021F    90              NOP
01540220    8B45 20         MOV EAX,DWORD PTR SS:[EBP+20]            ; IAT address
01540223    8945 60         MOV DWORD PTR SS:[EBP+60],EAX            ; IAT address
01540226    8B45 24         MOV EAX,DWORD PTR SS:[EBP+24]            ; IAT size
01540229    8945 64         MOV DWORD PTR SS:[EBP+64],EAX            ; IAT size
0154022C    90              NOP
0154022D    90              NOP
0154022E    90              NOP
0154022F    90              NOP
01540230    8B85 80000000   MOV EAX,DWORD PTR SS:[EBP+80]
01540236    8B30            MOV ESI,DWORD PTR DS:[EAX]               ; ESI=CallAddrs[]
01540238    8B85 84000000   MOV EAX,DWORD PTR SS:[EBP+84]
0154023E    8B18            MOV EBX,DWORD PTR DS:[EAX]               ; EBX=APIAddrs[]
01540240    90              NOP
01540241    AD              LODS DWORD PTR DS:[ESI]                  ; EAX = Call Address
01540242    8B13            MOV EDX,DWORD PTR DS:[EBX]               ; EDX = API Address
01540244    83F8 00         CMP EAX,0
01540247    74 6C           JE SHORT 015402B5
01540249    50              PUSH EAX
0154024A    52              PUSH EDX
0154024B    90              NOP
0154024C    90              NOP
0154024D    90              NOP
0154024E    90              NOP
0154024F    8B7D 60         MOV EDI,DWORD PTR SS:[EBP+60]            ; IAT
01540252    8B4D 64         MOV ECX,DWORD PTR SS:[EBP+64]            ; IAT size
01540255    C1E9 02         SHR ECX,2
01540258    8BC2            MOV EAX,EDX
0154025A    F2:AF           REPNE SCAS DWORD PTR ES:[EDI]            ; 在IAT中找 API Address
0154025C    90              NOP
0154025D    83F9 00         CMP ECX,0
01540260    75 3D           JNZ SHORT 0154029F                       ; 找到就跳
01540262    90              NOP                                      ; 没找到, 要加进去
01540263    A9 000000C0     TEST EAX,C0000000                        ; 是系统API 地址吗?
01540268    74 17           JE SHORT 01540281                        ; 不是的, 就把API Address 当 DLL 基址好了
0154026A    25 0000FFFF     AND EAX,FFFF0000                         ; 这5行是从ASPR抄过来的, 根据地址求 DLL 基址
0154026F    05 00000100     ADD EAX,10000                            ; 
01540274    2D 00000100     SUB EAX,10000                            ; 
01540279    66:8138 4D5A    CMP WORD PTR DS:[EAX],5A4D
0154027E  ^ 75 F4           JNZ SHORT 01540274
01540280    90              NOP
01540281    837D 58 00      CMP DWORD PTR SS:[EBP+58],0              ; 以前的 DLL 基址有值吗?
01540285    74 10           JE SHORT 01540297                        ; 没有, 跳去保存 DLL 基址, 并加 IAT
01540287    3B45 58         CMP EAX,DWORD PTR SS:[EBP+58]            ; 基址 == 以前的 DLL 基址 ?
0154028A    74 0B           JE SHORT 01540297                        ; 是, 相同的DLL
0154028C    90              NOP                                      ; 不同的DLL
0154028D    8945 58         MOV DWORD PTR SS:[EBP+58],EAX            ; 保存 DLL 基址
01540290    33C0            XOR EAX,EAX
01540292    AB              STOS DWORD PTR ES:[EDI]                  ; 加 00000000 到 IAT, 表示上一个DLL的IAT结束
01540293    8345 64 04      ADD DWORD PTR SS:[EBP+64],4              ; size + = 4
01540297    8945 58         MOV DWORD PTR SS:[EBP+58],EAX            ; 保存 DLL 基址
0154029A    8BC2            MOV EAX,EDX
0154029C    AB              STOS DWORD PTR ES:[EDI]                  ; 加 API 地址 到 IAT
0154029D    8345 64 04      ADD DWORD PTR SS:[EBP+64],4              ; size += 4
015402A1    90              NOP
015402A2    83EF 04         SUB EDI,4                                ; EDI 指向 IAT 中的 API
015402A5    5A              POP EDX
015402A6    58              POP EAX
015402A7    66:C700 FF25    MOV WORD PTR DS:[EAX],25FF               ; 改 CALL 12000000 成 JMP DWORD PTR DS:[xxxx], (Delphi)
如果是C的话, 就应该是 MOV WORD PTR DS:[EAX],15FF, 即改成 CALL DWORD PTR DS:[xxxx]
015402AC    8978 02         MOV DWORD PTR DS:[EAX+2],EDI
015402AF    83C3 04         ADD EBX,4
015402B2  ^ EB 8D           JMP SHORT 01540241                       ; 循环
015402B4    90              NOP
015402B5    33C0            XOR EAX,EAX                              ; 最后再往IAT中加一个00000000
015402B7    83C7 04         ADD EDI,4
015402BA    AB              STOS DWORD PTR ES:[EDI]
015402BB    8345 58 04      ADD DWORD PTR SS:[EBP+58],4              ; 用ImportRec修复时, 用[EBP+58]做IAT size
015402BF    61              POPAD                                    ; 
015402C0  - EB FE           JMP SHORT 015402C0                       ; 死循环, 万一忘了在这设断点, 修复代码就跑飞了
015402C2    90              NOP                                      ; 修复完毕, 回OEP DUMP & 修复
015402C3    90              NOP
015402C4    90              NOP


01540300    DD  FirstCallAddr         // 01540310
01540304    DD  CurrCallAddr

01540310    DD  CallAddrs[]

0154xx00    DD  FirstAPIAddr
0154xx04    DD  CurrAPIAddr

0154xx10    DD  APIAddrs[]

Binary Code@1540100
60 55 90 E8 00 00 00 00 5D 8D 55 5A 81 E5 00 00 FF FF 90 83 BD 80 00 00 00 00 75 1B 8B 45 00 83
C0 05 89 45 40 8D 45 40 89 02 8D 95 00 03 00 00 89 95 80 00 00 00 90 8B 8D 80 00 00 00 83 79 04
00 75 09 8D 51 10 89 11 89 51 04 90 8B 79 04 90 5D 89 2F 83 C7 04 89 79 04 61 45 89 45 00 6A 0A
FF 25 40 00 54 01 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
60 E8 00 00 00 00 5D 8D 55 7D 90 90 90 81 E5 00 00 FF FF 90 8B 5D 10 8B 43 02 89 02 8D 42 ED 89
45 50 8D 45 50 89 43 02 90 8B B5 80 00 00 00 8B 46 04 05 00 01 00 00 25 00 FF FF FF 89 85 84 00
00 00 90 8D 50 10 89 10 89 50 04 90 8B 85 80 00 00 00 8B 30 8B 85 84 00 00 00 8B 38 90 AD 83 F8
00 74 3D FF E0 90 90 90 90 90 90 90 90 90 90 90 60 E8 00 00 00 00 5D 81 E5 00 00 FF FF 89 55 54
61 FF 15 18 90 2C 01 C3 90 90 90 90 90 90 90 90 8B 45 54 AB EB C7 90 90 90 90 90 90 90 90 90 90
8B 45 20 89 45 60 8B 45 24 89 45 64 90 90 90 90 8B 85 80 00 00 00 8B 30 8B 85 84 00 00 00 8B 18
90 AD 8B 13 83 F8 00 74 6C 50 52 90 90 90 90 8B 7D 60 8B 4D 64 C1 E9 02 8B C2 F2 AF 90 83 F9 00
75 3D 90 A9 00 00 00 C0 74 17 25 00 00 FF FF 05 00 00 01 00 2D 00 00 01 00 66 81 38 4D 5A 75 F4
90 83 7D 58 00 74 10 3B 45 58 74 0B 90 89 45 58 33 C0 AB 83 45 64 04 89 45 58 8B C2 AB 83 45 64
04 90 83 EF 04 5A 58 66 C7 00 FF 25 89 78 02 83 C3 04 EB 8D 90 33 C0 83 C7 04 AB 83 45 58 04 61
EB FE 90 90 90

3.2) 修复用的 Script

// filename: ospr.osc
var addr                  // 定义变量

start:
  eob label_break         // 中断发生时,跳到 label_break 
  run                     //  == F9

label_break:
  cmp eip,015100A3        // 每次运行这个值都要重新修改
  jne label_exit
  mov addr,esp
  sub addr,4
  mov [addr],1540210      // 回到CODE2中去
  jmp start

label_exit:
  ret                     // 退出 script

3.3) 例子下载:

http://www.crystaloffice.com/download.html


4.0  心得与感受

4.1) 感觉现在用 ASPR V2.X 加密的软件越来越多了.

4.2) 有些版本的 ASPR 会在INT 3 的 SEH 中 清 调试寄存器, 所以在INT 3 之前设的硬件断点会失效.

4.3) 有些版本的 ASPR 在 CALL 12000000 后, 并不改写 CALL 12000000, 并且出现API Address 的CODE 也与本文中的

     Patch2 处的CODE 不一样; 但用 Ctrl+F11 跟到POPF 处, 在 Run Trace 中总有个地方 API Address 会出现在某

   一寄存器中.

4.4) ASPR V2.X 真的好难脱, 即使没有 Stolen code