首页
社区
课程
招聘
[原创]PELock v1.0x 脱壳之完美风暴
发表于: 2007-2-9 00:12 17164

[原创]PELock v1.0x 脱壳之完美风暴

2007-2-9 00:12
17164

【脱壳对象】:PELock v1.06 加壳的ACProtect v2.0自带 ACP_Feedback.exe

    之所以选择这个对象的原因在于:
   
    1. PELock已经不再升级,其加壳程序的兼容性也随着操作系统的升级而下降,因此现在用PELock加壳的程序也难觅其踪;
    2. ACP_Feedback.exe是一个Delphi程序,PELock对Delphi程序的一个地址表做了特殊处理,我们可以借此机会一觑这个特别的保护手段:)
   
【作者】:jdpack

【联系方式】:redoc at citiz.net
   
【脱壳工具】:OllyDbg,LordPE,ImportRec等等

【脱壳过程】:

    OllyDbg载入ACP_Feedback.exe,忽略除内存访问异常以外的所有异常,Shift+F9 12次以后程序启动。
    重新载入程序,这次我们Shift+F9 11次以后对代码段下内存访问断点,来到:
   
          CALL    @ACP_Feed_00401DB0
          JMP     @ACP_Feed_008806E9
          PUSH    0
          PUSH    0
          CALL    @ACP_Feed_00401E74
          MOV     EBX,EAX
          CALL    @ACP_Feed_00401E94
          JMP     @ACP_Feed_008806F4
          JNZ     @ACP_Feed_0040219A
          PUSH    EBX
          CALL    @ACP_Feed_00401E64
          JMP     @ACP_Feed_00880700
          JMP     @ACP_Feed_0088070B
          DB      0E8h
          DB      022h                       ; CHAR '"'
         
    很明显程序的开头几句指令被偷走了,不过这难不倒我们,随便找一个Delphi的程序就可以把入口补上:
   
          push ebp
          mov esp, ebp
          add esp, -10
          push ebx
          mov eax, XXXXXXXX
   
    Copyright by redoc (redoc at citiz.net)
   
    任意找一个API调用确定IAT的位置和大小:
   
@ACP_Feed_00401CEC:

          JMP     @ACP_Feed_00880500
          DB      00
          MOV     EAX,EAX
         
@ACP_Feed_00880500:

          JMP     DWORD PTR DS:[00405108]                        ; 定位IAT
          JMP     @ACP_Feed_00401CF2
          OUT     0FF,EAX

dd 000370000h, 00037009Dh, 0003700D1h, 00037011Dh, 000370163h, 00037020Fh, 000370228h, 0003702D1h
dd 0003702EFh, 000370321h, 0003703ADh, 00037045Ah, 000370508h, 000370564h, 0003705E5h, 0F80EEB0Fh
dd 00037062Eh, 00037068Eh, 0A9D5AE40h, 0003706BFh, 000370715h, 00037076Bh, 05CAAD636h, 00037079Eh
dd 0003707EDh, 000370826h, 0003708C8h, 07D2BBBE9h, 0003708F5h, 00037091Dh, 00037093Dh, 00037098Ah
dd 0BCBE2098h, 0003709D4h, 000370A07h, 000370A30h, 000370A64h, 000370A7Ah, 000370AA1h, 000370AF0h
dd 000370B3Ah

    这些00037XXXX的地址很容易识别,不过很显然它们被加密了。我们对其中任意一个地址下内存访问断点,就可以中断到外壳填写IAT的代码。
   
          MOV     DWORD PTR DS:[ECX],EBX
          JMP     @ACP_Feed_00354668
         
    幸运的是,当外壳执行到这句指令时,EAX恰好为正确的API地址,所以我们写一个类似下面的脚本就可以避开所有的IAT重定向:
   
          mov tmpreg, ebx
          mov ebx, eax
          sti
          mov ebx, tmpreg
   
    需要注意的是,由于外壳的CRC校验,我们不能直接修改这句指令为 "mov [ecx], eax" 或者在这句指令上下bp断点,唯一的方法是采用硬件断点。
   
    Copyright by redoc (redoc at citiz.net)
   
    OK,PELock对IAT的加密现在看了已经没有什么强度了,但是在当时看来还是比较出色的。下面才是PELock值得关注的重点――Code Redirection。
   
    PELock将原程序中的一些6字节指令重定向到外壳申请的空间去执行,这样脱壳以后程序就会出现访问异常。下面是PELock通常下毒手的一些指令:

第一组:

004011C6  JMP     00880187
004011CB  DB      00

00880187  CALL    DWORD PTR DS:[404008]                        ; call dword ptr [XXXXXXXX]
0088018D  JMP     ACP_Feed.004011CC

第二组:

0040190A  JMP     00880341
0040190F  CDQ

00880341  LEA     EBX,DWORD PTR DS:[C5FCE76]
00880347  LEA     EBX,DWORD PTR DS:[EBX+22CDBDF0]
0088034D  LEA     EBX,DWORD PTR DS:[EBX+459B7BE1]
00880353  LEA     EBX,DWORD PTR DS:[EBX+8B36F7C3]        ; mov ebx, XXXXXXXX
00880359  JMP     ACP_Feed.0040190F

    当然这里也有可能是eax, ecx, ebp ... whatever
   
第三组:

004010D8  JMP     008800FD
004010DD  PUSH    DWORD PTR FS:[EDX]

008800FD  PUSH    40119A                                ; push XXXXXXXX
00880102  JMP     ACP_Feed.004010DD

第四组:

00401293  JMP     008801B1
00401298  JE      SHORT 004012A1

008801B1  CMP     EAX,0D00                                ; cmp eax, XXXXXXXX
008801B7  JMP     ACP_Feed.00401298

    当然这里也有可能是ebx, ecx, ebp ... whatever
   
    Copyright by redoc (redoc at citiz.net)

    指令的变形并不复杂,甚至可以说几乎没有变化,但是给转储带来困难。一种比较取巧的方法是把这些区段补到脱壳后的文件中,但是要完美还原的话还是要写代码来修复。jingulong达人曾经给出一段修复代码,没能耐心看完,自己写了一个ODbgScript脚本做了一下类似反汇编器的工作如下:
   
        mov        TmpOpcode, [TmpRemoteJmp], 1
        cmp        TmpOpcode, FF                                // call/jmp dword ptr [XXXXXXXX]
        je        CallJmpDwordIns
        cmp        TmpOpcode, 8D                                // lea reg32, [XXXXXXXX]
        je        AddRegIns
        cmp        TmpOpcode, 68                                // push XXXXXXXX
        je        PushConstIns
        cmp        TmpOpcode, 81                                // cmp reg32, XXXXXXXX
        je        CmpRegIns
        msg        "new mode"                                // more transforming modes to be revealed:)
        ret
        
        …………………………
        
    完美还原所有的Redirected Code到原来的位置上,脚本比较长,不过理解了上述原理就很容易实现。
   
    最后剩下需要处理的就是Delphi的那张表了,我们注意一下程序运行到入口点时eax的值:00356C20

@ACP_Feed_00356C20:

dd 000000007h, 000356C28h, 000401E24h, 000401DF4h, 000401C64h, 000401C10h, 000401E5Ch, 000401E2Ch
                    |           |
                    ~~~~~~~~~~~~~
dd 000401F2Ch, 000401EFCh, 000401F64h, 000401F34h, 000401FA4h, 000401F74h, 000000000h, 0004020E8h

    这是一张地址表,既包含了数据又包含了代码指针,需要留意的是上面标出的这两个数据,第二个数据恰好是指向第三个数据的指针。
   
    现在我们把这个表搬到脱壳以后废弃的区段中,把入口点处的 "mov eax, XXXXXXXX" 中的 XXXXXXXX 设定为这个表的指针。
   
    今年的暖冬让人不禁战栗,也许在南太平洋的某块水域,一场完美的热带风暴正在紧张的酝酿之中……
   
    PELock, u r TermiNated...


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 7
支持
分享
最新回复 (31)
雪    币: 898
活跃值: (4039)
能力值: ( LV9,RANK:3410 )
在线值:
发帖
回帖
粉丝
2
不错
forgot的脱壳机效果也不错吧
2007-2-9 09:06
0
雪    币: 280
活跃值: (58)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
最初由 fly 发布
不错
forgot的脱壳机效果也不错吧

请我哪里有下载啊?
2007-2-9 11:27
0
雪    币: 224
活跃值: (147)
能力值: ( LV9,RANK:970 )
在线值:
发帖
回帖
粉丝
4
楼住提供目标程序下载吧

脱壳机早有耳闻
但是不曾见过
2007-2-9 12:38
0
雪    币: 207
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
forgot的脱壳机效果也不错吧
2007-2-9 12:39
0
雪    币: 207
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
最初由 wynney 发布
楼住提供目标程序下载吧
上传的附件:
2007-2-9 12:59
0
雪    币: 207
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
最初由 fly 发布
不错
forgot的脱壳机效果也不错吧

谢谢版主,原来已经有脱壳机了啊,呵呵
2007-2-9 13:02
0
雪    币: 304
活跃值: (82)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
8
原来这么多人不知道啊,
2007-2-9 14:16
0
雪    币: 146
活跃值: (33)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
9
哪里有pelock的脱机?
2007-2-9 17:59
0
雪    币: 264
活跃值: (30)
能力值: ( LV12,RANK:250 )
在线值:
发帖
回帖
粉丝
10
PELock, u r TermiNated...

这句话我喜欢,谢谢楼主的美文。
2007-2-10 22:38
0
雪    币: 6075
活跃值: (2236)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
11
支持。

以下是建议:
1. OEP 混乱只是插入了一些junkcode
2. Delphi那个表可以找到原始的位置放回去
3. 那些jmp好像有个特殊的cmp eax, xxx要处理,太老了我记不清楚了

贴一个unpacker脱的
上传的附件:
2007-2-11 22:32
0
雪    币: 370
活跃值: (15)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
12
原来还有脱壳机,07光盘里翻了下,硬盘里库存找了下,竟然都没收藏有,太不可思议了,谁有,就给个吧?
2007-2-12 02:16
0
雪    币: 405
活跃值: (10)
能力值: ( LV9,RANK:1130 )
在线值:
发帖
回帖
粉丝
13
脱壳机不要,要脱文好啊。
2007-2-12 09:37
0
雪    币: 207
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
14
最初由 forgot 发布
支持。

以下是建议:
1. OEP 混乱只是插入了一些junkcode
2. Delphi那个表可以找到原始的位置放回去
........


谢谢~
脱壳机版主果然细心:)
1. 对Delphi程序来说,OEP Stolen Code只有固定的5句,从堆栈和寄存器就可以恢复;
2. 同意,OEP位置确定了就可以还原初始化表到原来的位置;
3. cmp eax, XXXXXXXX 有2种机器码,变形以后的是6个字节,但是还原的时候要处理成5个字节,否则会覆盖下面的代码;
4. 上传的脱壳文件好像有误-。-
2007-2-12 12:54
0
雪    币: 207
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
很多  木马用这个加壳的  
建议 应该把 脱壳机 公布出来
网络也会安静些
2007-2-12 16:48
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
有脱壳机呀 郁闷
2007-2-27 13:52
0
雪    币: 105
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
  还是不要公布的好.... 虽然在私下公布了...

公布只是时间问题吧... 嘿嘿..forgot那天高兴..一下帖出来了...
2007-2-27 21:13
0
雪    币: 203
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
通俗易懂,顶!
2007-3-28 02:24
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
哪里有下载。
2007-5-6 11:46
0
雪    币: 173
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
8错.学习了
2007-5-12 09:16
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
谢谢版主,有脱壳机了
2007-5-13 02:05
0
雪    币: 224
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
支持的说~~
2007-5-13 03:11
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
脱壳机收藏了,呵呵
2007-5-13 07:57
0
雪    币: 213
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24


转发:一个牛人写的PELock 1.0x脱壳实例(离脱壳机不远了)

PELock 1.0x -> Bartosz Wojcik脱壳实例之CryptCD Professional version 4.0.exe
下载地址:http://bbs.hanzify.org/index.php?act=Attach&type=post&id=14354

1.借尸还魂--搞定iat表
用OD加载CryptCD Professional version 4.0.exe
OD的异常全部勾上,到壳的入口:
0049D05C >JMP SHORT CryptCD_.0049D060
0049D05E XCHG ESP,ESI
0049D060 CLC
0049D061 JNB SHORT CryptCD_.0049D064

Alt+E打开模块窗口,光标选择kernel32 (system) Ctrl+N打开函数窗口
找到LoadLibraryA这个函数,双击它:
77E5D8B4 >CMP DWORD PTR SS:[ESP+4],0
77E5D8B9 PUSH EBX
77E5D8BA PUSH ESI
77E5D8BB JE SHORT kernel32.77E5D8D6

在77E5D8B9 PUSH EBX 这行下内存访问中断,F9运行程序:
第二次中断在:
00374CD9 MOV EDX,DWORD PTR DS:[ECX+ESI]
00374CDC JMP SHORT 00374CE1
......
0037501D MOV EAX,DWORD PTR DS:[ESI]
0037501F MOV EDX,EAX
00375021 CMP AL,0CC
00375023 JNZ SHORT 00375054
......
00375B22 MOV AL,BYTE PTR DS:[ECX]
00375B24 INC ECX
00375B25 OR EDX,DWORD PTR DS:[EBX+EAX*4]
00375B28 TEST DL,8
00375B2B JNZ SHORT 00375B1F

壳会全程检查函数代码,所以int3中断不能使用。

Shift+F9继续运行,直到重新被中断在函数中
77E5D8B9 PUSH EBX
77E5D8BA PUSH ESI
77E5D8BB JE SHORT kernel32.77E5D8D6

堆栈中出现:
0012EA54 72F44841 返回到 72F44841 来自 kernel32.LoadLibraryA
0012EA58 72F1A978 ASCII "gdi32.dll"

回到壳代码中,F7运行到:
00374661 MOV DWORD PTR DS:[ECX],EBX
00374663 JMP SHORT 00374668

EAX 77D18ABC USER32.TranslateMessage
ECX 00426288 CryptCD_.00426288
EDX 77D10000 USER32.77D10000
EBX 003E193F
ESP 0012FFAC
EBP 0037062D
ESI 00380399
EDI 00380389 ASCII "USER32.dll"
EIP 00374661

分析代码发现,[ECX]是iat表中的地址,EBX是加密后的函数地址,再仔细分析发现,
这时候的EAX中就是未加密的函数,如果把:

00374661 MOV DWORD PTR DS:[ECX],EBX
修改成:
00374661 MOV DWORD PTR DS:[ECX],EAX

嘿嘿就可以得到正确的iat表了,修改看看
继续运行,出现Invalid version of library USER32.dll错误。是因为下面的CRC没有通过。

没有关系,用OllyScript来搞定它:
=======================================
//PELock 1.0x 脱壳实例之CryptCD Professional version 4.0
//获取iat表Script
//by fxyang 2005.5.20
//由于只想得到iat表,所以没有检查表的结束。
var index
#LOG

gpa "LoadLibraryA", "kernel32.dll"
bprm $RESULT,1
mov index,1
eob exp1
run

exp1:
cmp index,0
je exp2
dec index
esto

exp2:
bpmc
rtu
bprm 00374661,1
mov index,1
eob exp3
esto

exp3:
cmp index,0
je exp4
dec index
esto

exp4:
mov [ecx],eax
add eip,2
mov index,1
esto
=========================================

脚本完成后到iat表中看看:
00426000 D8 17 DA 77 9A 22 DA 77 ?趙?趙
00426008 68 6A DB 77 10 24 DA 77 hj踳$趙
00426010 00 00 00 00 A4 7F 33 77 ....?3w
......
00426370 CF 90 32 76 0F DA 33 76 蠍2v?v
00426378 00 00 00 00 59 58 F3 4F ....YX驩
00426380 0B 65 EE 4F 00 10 EF 4F e頞.颫
00426388 A8 12 EF 4F A6 F2 ED 4F ?颫︱鞳
00426390 00 00 00 00 00 00 00 00 ........

一个完整的表,用Import.Reconstructor得到完整的表,保存备用。

2.修复被壳转移的代码

重新OD加载程序,重新对LoadLibraryA函数的代码下内存访问中断。F9运行:
在第二次被中断在函数中时,Alt+M打开内存窗口,双击00401000段:
00401000 MOV EAX,DWORD PTR SS:[ESP+4]
00401004 MOV DWORD PTR DS:[440840],EAX
00401009 CALL CryptCD_.00401080
0040100E CALL CryptCD_.00401320
00401013 CALL CryptCD_.00403A20
00401018 TEST EAX,EAX
0040101A JNZ SHORT CryptCD_.00401033
0040101C PUSH 5BCD98
00401021 PUSH 6DE6CC
00401026 CALL CryptCD_.00403560

对0040101C PUSH 5BCD98下内存访问中断,Shift+F9运行:

003780F0 PUSHAD
003780F1 PUSH 4
003780F3 PUSH 3000
003780F8 PUSH 5D58
003780FD PUSH 0
003780FF CALL DWORD PTR SS:[EBP+4]
00378102 XCHG EAX,EDI
00378103 CALL 003780E9
00378108 LEA ESI,DWORD PTR SS:[EBP-5]
0037810B SUB ESI,0BAB
00378111 PUSH ESI
00378112 MOV EBX,101
00378117 XOR BYTE PTR DS:[EDI],DL
00378119 INC EDI
0037811A MOV EDX,DWORD PTR DS:[ESI]
0037811C ADD ESI,4
0037811F MOV BYTE PTR DS:[EDX],0E9
00378122 MOV EAX,EDI
00378124 SUB EAX,EDX
00378126 SUB EAX,5
00378129 MOV DWORD PTR DS:[EDX+1],EAX
0037812C MOV AL,BYTE PTR DS:[ESI]
0037812E INC ESI
0037812F MOVZX ECX,AL
00378132 AND EAX,3
00378135 SHR ECX,2
00378138 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
0037813A MOV ECX,EAX
0037813C REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
0037813E MOV AL,BYTE PTR DS:[ESI]
00378140 INC ESI
00378141 ADD EDX,EAX
00378143 MOV BYTE PTR DS:[EDI],0E9
00378146 SUB EDX,EDI
00378148 SUB EDX,5
0037814B MOV DWORD PTR DS:[EDI+1],EDX
0037814E ADD EDI,5
00378151 DEC EBX
00378152 JNZ SHORT 00378117
00378154 POP EDI
00378155 LEA ECX,DWORD PTR SS:[EBP+66]
00378158 SUB ECX,EDI
0037815A REP STOS BYTE PTR ES:[EDI]
0037815C POPAD
0037815D RETN

这段代码就是把程序的代码修改为调用到壳中转移的地址,分析这段代码

0037811F MOV BYTE PTR DS:[EDX],0E9
修改[EDX]指向的地址40101C为JMP

00378122 MOV EAX,EDI
00378124 SUB EAX,EDX
00378126 SUB EAX,5
00378129 MOV DWORD PTR DS:[EDX+1],EAX

计算跳转偏移,然后写入到程序中

0037812C MOV AL,BYTE PTR DS:[ESI]
0037812E INC ESI
0037812F MOVZX ECX,AL
00378132 AND EAX,3
00378135 SHR ECX,2
00378138 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
0037813A MOV ECX,EAX
0037813C REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]

到[ESI]指向的地址看看:

0037753E 1C 10 40 00 05 68 C4 82 @.h膫
00377546 42 00 05 21 10 40 00 05 B.!@.
0037754E 68 B8 82 42 00 05 38 10 h競B.8
00377556 40 00 05 68 18 82 42 00 @.h侭.
0037755E 05 3D 10 40 00 05 68 08 =@.h
00377566 82 42 00 05 53 10 40 00 侭.S@.
0037756E 05 68 F0 81 42 00 05 58 h饋B.X
00377576 10 40 00 05 68 E4 81 42 @.h鋪B
0037757E 00 05 81 10 40 00 12 8D .?@.

这个地址中其实是一个表,表的组成是:
1.)需要修改的程序地址 :1C 10 40 00
2.)需要复制到壳申请临时空间的代码长度:05
3.)需要转移的代码:68 C4 82 42 00
4.)写入jump回程序中地址的偏移:05

0037813E MOV AL,BYTE PTR DS:[ESI]
00378140 INC ESI
00378141 ADD EDX,EAX
00378143 MOV BYTE PTR DS:[EDI],0E9
00378146 SUB EDX,EDI
00378148 SUB EDX,5
0037814B MOV DWORD PTR DS:[EDI+1],EDX

计算jump回到程序中的偏移,写入jump回的代码

分析完了上面的流程和方法后,就可以利用这段代码把转移的代码写回到程序中:

修改:
003780F0 PUSHAD
003780F1 PUSH 4
003780F3 PUSH 3000
003780F8 PUSH 5D58
003780FD PUSH 0
003780FF CALL DWORD PTR SS:[EBP+4]
00378102 XCHG EAX,EDI
00378103 CALL 003780E9
00378108 LEA ESI,DWORD PTR SS:[EBP-5]
0037810B SUB ESI,0BAB
00378111 PUSH ESI
00378112 MOV EBX,101
00378117 NOP
00378118 NOP
00378119 INC EDI
0037811A MOV EDX,DWORD PTR DS:[ESI]
0037811C ADD ESI,4
0037811F MOV EDI,EDX //关键,把壳的地址替换为程序的地址
00378121 NOP
00378122 MOV EAX,EDI
00378124 SUB EAX,EDX
00378126 SUB EAX,5
00378129 NOP
0037812A NOP
0037812B NOP
0037812C MOV AL,BYTE PTR DS:[ESI]
0037812E INC ESI
0037812F MOVZX ECX,AL
00378132 AND EAX,3
00378135 SHR ECX,2
00378138 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
0037813A MOV ECX,EAX
0037813C REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
0037813E MOV AL,BYTE PTR DS:[ESI]
00378140 INC ESI
00378141 ADD EDX,EAX
00378143 NOP
00378144 NOP
00378145 NOP
00378146 SUB EDX,EDI
00378148 SUB EDX,5
0037814B NOP
0037814C NOP
0037814D NOP
0037814E ADD EDI,5
00378151 DEC EBX
00378152 JNZ SHORT 00378117
00378154 POP EDI
00378155 LEA ECX,DWORD PTR SS:[EBP+66]
00378158 SUB ECX,EDI
0037815A REP STOS BYTE PTR ES:[EDI]
0037815C POPAD
0037815D RETN

修改完成后,F4到0037815D RETN

Alt+M打开内存窗口,在00401000的代码段下内存访问中断,Shift+F9运行程序:

0041B9D4 CALL DWORD PTR DS:[426124]
0041B9DA XOR EDX,EDX
0041B9DC MOV DL,AH
0041B9DE MOV DWORD PTR DS:[441C04],EDX

这是程序的伪OEP,在堆栈中检查:

0012FFA8 0012FFB8 指针到下一个 SEH 记录
0012FFAC 0041F270 SE 句柄
0012FFB0 004263F0 CryptCD_.004263F0
0012FFB4 FFFFFFFF
0012FFB8 0012FFE0 指针到下一个 SEH 记录

这个程序是VC++编译的,根据堆栈中的数据恢复代码:

0041B9AE >PUSH EBP
0041B9AF MOV EBP,ESP
0041B9B1 PUSH -1
0041B9B3 PUSH CryptCD_.004263F0
0041B9B8 PUSH CryptCD_.0041F270 ; SE handler installation
0041B9BD MOV EAX,DWORD PTR FS:[0]
0041B9C3 PUSH EAX
0041B9C4 MOV DWORD PTR FS:[0],ESP
0041B9CB SUB ESP,58
0041B9CE PUSH EBX
0041B9CF PUSH ESI
0041B9D0 PUSH EDI
0041B9D1 MOV DWORD PTR SS:[EBP-18],ESP

用LordPE dump下程序,修改程序的OEP为0041B9AE,保存备用.

3.还原IAT表

用Import.Reconstructor加载保存的iat表,去掉增加新的区段的勾,然后在新的输入表信息的
RVA 中填入原iat表的RVA 00026000 在OEP中填入0001B9AE 后,修复上面保存的dump程序.
看似修复好了iat表,运行看看,程序不见了 看来还是有问题.有什么问题呢?

用OD 加载原程序,直接运行,运行成功后来到imports段00426000
再用OD加载修复后的dump程序,比较imports段和原来的有些什么不同,发现原imports表中多了些数据:

00426390 00 00 00 00 00 00 00 00 9A 99 99 99 99 99 E9 3F ........殭櫃櫃?
004263A0 00 00 00 00 00 00 50 3F 00 00 80 3A 00 00 00 00 ......P?..€:....
004263B0 00 00 00 00 00 00 E0 3F 9A 99 99 99 99 99 B9 3F ......?殭櫃櫃?
004263C0 80 09 F8 7B 32 BF 1A 10 8B BB 00 AA 00 30 0C AB €.?#123;2?嫽.?0.
......
00426AC0 64 61 79 00 4D 6F 6E 64 61 79 00 00 53 75 6E 64 day.Monday..Sund
00426AD0 61 79 00 00 53 61 74 00 46 72 69 00 54 68 75 00 ay..Sat.Fri.Thu.
00426AE0 57 65 64 00 54 75 65 00 4D 6F 6E 00 53 75 6E 00 Wed.Tue.Mon.Sun.
00426AF0 FF FF FF FF 27 42 42 00 2B 42 42 00 FF FF FF FF 'BB.+BB.
00426B00 84 42 42 00 88 42 42 00 FF FF FF FF D4 4B 42 00 凚B.圔B.訩B.
00426B10 D8 4B 42 00 FF FF FF FF 43 4C 42 00 47 4C 42 00 豄B.CLB.GLB.
00426B20 20 05 93 19 01 00 00 00 40 6B 42 00 00 00 00 00 ?...@kB.....
00426B30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

而在修复的程序中这些数据被iat表的其他结构覆盖掉了:

00426390 00 00 00 00 00 60 02 00 00 00 00 00 00 00 00 00 .....`.........
004263A0 5C 64 02 00 00 60 02 00 14 60 02 00 00 00 00 00 \d..`.`.....
004263B0 00 00 00 00 AC 64 02 00 14 60 02 00 58 60 02 00 ....琩.`.X`.
004263C0 00 00 00 00 00 00 00 00 1E 66 02 00 58 60 02 00 ........f.X`.
004263D0 CC 60 02 00 00 00 00 00 00 00 00 00 D0 67 02 00 蘞.........術.
004263E0 CC 60 02 00 5C 62 02 00 00 00 00 00 00 00 00 00 蘞.\b.........
004263F0 B6 6E 02 00 5C 62 02 00 68 62 02 00 00 00 00 00 秐.\b.hb.....
00426400 00 00 00 00 F6 6E 02 00 68 62 02 00 88 62 02 00 ....鰊.hb.坆.
00426410 00 00 00 00 00 00 00 00 7C 6F 02 00 88 62 02 00 ........|o.坆.
......
00426AE0 1F 02 49 73 42 61 64 52 65 61 64 50 74 72 00 00 IsBadReadPtr..
00426AF0 1C 02 49 73 42 61 64 43 6F 64 65 50 74 72 00 FF IsBadCodePtr.
00426B00 F1 00 47 65 74 41 43 50 00 FF 85 01 47 65 74 4F ?GetACP.?GetO
00426B10 45 4D 43 50 00 FF F8 02 53 65 74 45 6E 64 4F 66 EMCP.?SetEndOf
00426B20 46 69 6C 65 00 00 37 00 43 6F 6D 70 61 72 65 53 File..7.CompareS
00426B30 74 72 69 6E 67 41 00 00 38 00 43 6F 6D 70 61 72 tringA..8.Compar

现在知道了原因,来尝试修复它:
1.)先把iat表的IID和Name结构转移到数据的后面,就用OD的二进制复制功能把
00426390 00 60 02 00 00 00 00 00 00 00 00 00 58 64 02 00 .`.........Xd.
004263A0 00 60 02 00 14 60 02 00 00 00 00 00 00 00 00 00 .`.`.........
004263B0 A8 64 02 00 14 60 02 00 58 60 02 00 00 00 00 00 ╠.`.X`.....
004263C0 00 00 00 00 1A 66 02 00 58 60 02 00 CC 60 02 00 ....f.X`.蘞.
004263D0 00 00 00 00 00 00 00 00 CC 67 02 00 CC 60 02 00 ........蘥.蘞.
004263E0 5C 62 02 00 00 00 00 00 00 00 00 00 B2 6E 02 00 \b.........瞡.
004263F0 5C 62 02 00 68 62 02 00 00 00 00 00 00 00 00 00 \b.hb.........

到:

00427340 46 6F 6E 74 41 00 6F 6C 65 33 32 2E 64 6C 6C 00 FontA.ole32.dll.
00427350 A1 00 47 65 74 48 47 6C 6F 62 61 6C 46 72 6F 6D ?GetHGlobalFrom
00427360 53 74 72 65 61 6D 00 06 92 00 43 72 65 61 74 65 Stream.?Create
00427370 53 74 72 65 61 6D 4F 6E 48 47 6C 6F 62 61 6C 00 StreamOnHGlobal.
00427380 FD 00 4F 6C 65 49 6E 69 74 69 61 6C 69 7A 65 00 ?OleInitialize.
00427390 14 01 4F 6C 65 55 6E 69 6E 69 74 69 61 6C 69 7A OleUninitializ
004273A0 65 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e...............

的数据二进制复制到:

00426B90 00 60 02 00 00 00 00 00 .`.....
00426B98 00 00 00 00 58 64 02 00 ....Xd.
00426BA0 00 60 02 00 14 60 02 00 .`.`.
00426BA8 00 00 00 00 00 00 00 00 ........
00426BB0 A8 64 02 00 14 60 02 00 ╠.`.
00426BB8 58 60 02 00 00 00 00 00 X`.....
00426BC0 00 00 00 00 1A 66 02 00 ....f.
......
00427B70 53 74 72 65 61 6D 4F 6E StreamOn
00427B78 48 47 6C 6F 62 61 6C 00 HGlobal.
00427B80 FD 00 4F 6C 65 49 6E 69 ?OleIni
00427B88 74 69 61 6C 69 7A 65 00 tialize.
00427B90 14 01 4F 6C 65 55 6E 69 OleUni
00427B98 6E 69 74 69 61 6C 69 7A nitializ
00427BA0 65 00 00 00 00 00 00 00 e.......

然后从原程序中把需要的数据还原到:

00426390 00 00 00 00 00 00 00 00 9A 99 99 99 99 99 E9 3F ........殭櫃櫃?
004263A0 00 00 00 00 00 00 50 3F 00 00 80 3A 00 00 00 00 ......P?..€:....
004263B0 00 00 00 00 00 00 E0 3F 9A 99 99 99 99 99 B9 3F ......?殭櫃櫃?
004263C0 80 09 F8 7B 32 BF 1A 10 8B BB 00 AA 00 30 0C AB €.?#123;2?嫽.?0.
......
00426AC0 64 61 79 00 4D 6F 6E 64 61 79 00 00 53 75 6E 64 day.Monday..Sund
00426AD0 61 79 00 00 53 61 74 00 46 72 69 00 54 68 75 00 ay..Sat.Fri.Thu.
00426AE0 57 65 64 00 54 75 65 00 4D 6F 6E 00 53 75 6E 00 Wed.Tue.Mon.Sun.
00426AF0 FF FF FF FF 27 42 42 00 2B 42 42 00 FF FF FF FF 'BB.+BB.
00426B00 84 42 42 00 88 42 42 00 FF FF FF FF D4 4B 42 00 凚B.圔B.訩B.
00426B10 D8 4B 42 00 FF FF FF FF 43 4C 42 00 47 4C 42 00 豄B.CLB.GLB.
00426B20 20 05 93 19 01 00 00 00 40 6B 42 00 00 00 00 00 ?...@kB.....
00426B30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

再用二进制编辑器复制dump下修复的文件中iat表的FirstThunk结构替换iat表中的函数地址:
00426000 66 64 02 00 74 64 02 00 fd.td.
00426008 84 64 02 00 94 64 02 00 刣.攄.
00426010 00 00 00 00 B6 64 02 00 ....禿.
00426018 C6 64 02 00 DC 64 02 00 芼.躣.
00426020 F4 64 02 00 08 65 02 00 鬱.e.
00426028 22 65 02 00 3E 65 02 00 "e.>e.
00426030 50 65 02 00 66 65 02 00 Pe.fe.
......
00426360 DE 72 02 00 00 00 00 00 農.....
00426368 00 73 02 00 10 73 02 00 .s.s.
00426370 24 73 02 00 38 73 02 00 $s.8s.
00426378 00 00 00 00 50 73 02 00 ....Ps.
00426380 68 73 02 00 80 73 02 00 hs.€s.
00426388 90 73 02 00 00 00 00 00 恠.....

好了,到这里把需要的数据都恢复到原来的位置了,下面就是重新修正iat表结构中的偏移.

第一个要修正的偏移就是iat中的FirstThunk结构,即00426000开始的部分.
计算一下移动的偏移量=00426B90-00426390=800
现在来写代码修正:

0041B91E > B8 00604200 MOV EAX,00426000
0041B923 8100 00080000 ADD DWORD PTR DS:[EAX],800
0041B929 8D40 04 LEA EAX,DWORD PTR DS:[EAX+4]
0041B92C 8338 00 CMP DWORD PTR DS:[EAX],0
0041B92F ^ 74 F8 JE SHORT dumped-u.0041B929
0041B931 ^ EB F0 JMP SHORT dumped-u.0041B923

B8 00 60 42 00 81 00 00 08 00 00 8D 40 04 83 38 00 74 F8 EB F0

在00426390 00 00 00 00 下内存访问中断,到这里上面的修正就完成了。

修改后:
00426000 66 6C 02 00 74 6C 02 00 fl.tl.
00426008 84 6C 02 00 94 6C 02 00 刲.攍.
00426010 00 00 00 00 B6 6C 02 00 ....秎.
00426018 C6 6C 02 00 DC 6C 02 00 苐.躭.
00426020 F4 6C 02 00 08 6D 02 00 鬺.m.
......
00426360 DE 7A 02 00 00 00 00 00 込.....
00426368 00 7B 02 00 10 7B 02 00 .{.{.
00426370 24 7B 02 00 38 7B 02 00 ${.8{.
00426378 00 00 00 00 50 7B 02 00 ....P{.
00426380 68 7B 02 00 80 7B 02 00 h{.€{.
00426388 90 7B 02 00 00 00 00 00 ?#123;.....

第二个要修正的偏移是IID结构中的Name RVA

写代码修正它:
0041B9AE >MOV EAX,dumped-p.00426B9C
0041B9B3 ADD DWORD PTR DS:[EAX],800
0041B9B9 LEA EAX,DWORD PTR DS:[EAX+14]
0041B9BC CMP DWORD PTR DS:[EAX],0
0041B9BF JE SHORT dumped-p.0041B9B9
0041B9C1 JMP SHORT dumped-p.0041B9B3

B8 9C 6B 42 00 81 00 00 08 00 00 8D 40 14 83 38 00 74 F8 EB F0

没有几项,手动运行,完成后IID结构:

00426B90 00 60 02 00 00 00 00 00 00 00 00 00 58 6C 02 00 .`.........Xl.
00426BA0 00 60 02 00 14 60 02 00 00 00 00 00 00 00 00 00 .`.`.........
00426BB0 A8 6C 02 00 14 60 02 00 58 60 02 00 00 00 00 00 ╨.`.X`.....
00426BC0 00 00 00 00 1A 6E 02 00 58 60 02 00 CC 60 02 00 ....n.X`.蘞.
00426BD0 00 00 00 00 00 00 00 00 CC 6F 02 00 CC 60 02 00 ........蘯.蘞.
00426BE0 5C 62 02 00 00 00 00 00 00 00 00 00 B2 76 02 00 \b.........瞯.
00426BF0 5C 62 02 00 68 62 02 00 00 00 00 00 00 00 00 00 \b.hb.........
00426C00 F2 76 02 00 68 62 02 00 88 62 02 00 00 00 00 00 騰.hb.坆.....
00426C10 00 00 00 00 78 77 02 00 88 62 02 00 68 63 02 00 ....xw.坆.hc.
00426C20 00 00 00 00 00 00 00 00 F2 7A 02 00 68 63 02 00 ........騴.hc.
00426C30 7C 63 02 00 00 00 00 00 00 00 00 00 46 7B 02 00 |c.........F{.
00426C40 7C 63 02 00 00 00 00 00 00 00 00 00 00 00 00 00 |c.............

用OD的保存到 可执行文件 把修正后的文件保存成新的程序。

用LordPE修正 导入表 的RVA=00026B90 ,SIZE=000000B4 保存。

4.混乱器代码的修正

重新运行上面保存的程序,发现程序会异常,用OD加载调试跟踪发现:

00401740 LEA ECX,DWORD PTR DS:[5E1C0EDA]
00401746 LEA ECX,DWORD PTR DS:[ECX+A1E3F12A]
0040174C INC ESP
0040174D ADD CL,DL
0040174F CLC
00401750 ADD EAX,14
00401753 DEC EDX

发现发现这是PELock 1.0x的壳把原程序的部分代码经过混乱器混乱后变成的代码。
这个代码能完成功能,但是代码的长度比原程序长多了,这样经过自动恢复后会把
下面正确的代码覆盖掉,所以程序就异常了。

发现发现混乱器混乱的是有规律的,其实混乱器生成的代码功能就是:
MOV ECX,04

就是说壳会把程序中赋值常数代刖炻移骰炻摇?
现在来修复它:
修正00401740 LEA ECX,DWORD PTR DS:[5E1C0EDA]的代码为:

00401740 MOV ECX,4

然后再找回覆盖的代码--

用OD加载未脱壳的程序,运行。Ctrl+G 到00401740 地址,发现代码是:
00401740 JMP 009E0379
00401745 CDQ
00401746 SUB EAX,EDX
00401748 MOV EDX,DWORD PTR DS:[440ABC]
0040174E SAR EAX,1

光标停在00401740 JMP 009E0379 回车:
009E0379 LEA ECX,DWORD PTR DS:[5E1C0EDA]
009E037F LEA ECX,DWORD PTR DS:[ECX+A1E3F12A]
009E0385 JMP CryptCD_.00401745

可以看到返回地址是00401745,看看00401745的代码是:
00401745 CDQ
00401746 SUB EAX,EDX
00401748 MOV EDX,DWORD PTR DS:[440ABC]
0040174E SAR EAX,1
00401750 ADD EAX,14
00401753 DEC EDX
00401754 MOV DWORD PTR DS:[42CD80],ECX
0040175A MOV DWORD PTR DS:[42CD8C],ECX

用OD的二进制代码复制功能把被覆盖的代码,恢复如下:

00401740 MOV ECX,4
00401745 CDQ
00401746 SUB EAX,EDX
00401748 MOV EDX,DWORD PTR DS:[440ABC]
0040174E SAR EAX,1
00401750 ADD EAX,14
00401753 DEC EDX

我还没有发现自动恢复混乱器混乱代码的方法,只能这样跟踪,现在的麻烦就是不知道有多少这样的代码。

经过上面的修复,脱壳基本完成。我只能做到这样,或许大家有好的方法,不妨写出来共享。
我不善于写文章,写得很乱,看不明白不要骂我 :)

by fxyang
2005.5.24
上传的附件:
2007-5-13 14:27
0
雪    币: 213
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
转贴

[转帖]PELOCK手记(翻译)
翻译/发帖:小狐狸
说明:
前一篇由于篇幅的关系不完整,作废。
这篇是翻译,欢迎大家提出宝贵意见。

LOCK手册集摘
\\\***PELOCK(LOCK手册集摘)***///
***** ***** 作者:crUsAdEr ***** ****

这篇文章是为了介绍PELOCK1.06的保护。许多软件(作者)用此保护以免遭到破解
的厄运,我会介绍此保护的特点并最终脱壳。PELOCK是高度依赖操作系统的,由
于这篇文章是基于WIN2K系统介绍其特点,所以会与别的系统有些出入。
所需工具:
IDA4.15
SOFT ICE(基于WIN2K SP3操作系统)
LordPE Deluxe
Superbpm (基于NT/2k操作系统)
Imprec 1.4.2
目标: PELock 1.06
包括: PElock.idc

序言:
PELOCK是与SVKPROTECTOR具有许多相似之处的一种壳,尤其是在IAT REDIRECTION和
OEP脱壳方面。同时PELOCK还可以通过ICEDUMP对KERNEL32.DLL的漏洞侦测出WIN98操
作系统中的ICEDUMP(引用SPLAJ在FRAVIA公告版上的留言)。
尽管我用比较浅显的语言说明,但我想,大家都是拥有一定基础并能熟练运用工具的
高手(应该能听的懂)。PELOCK还具有通过检验OPCODES “CC”来侦测BPX的功能,所
以请不要下BPX 函数名 不然的话PELOCK会飘然离你而去。同时它具有反跟踪能力,因
此你就得用上SUPERBPM。
(我建议大家看看我的两篇介绍ASPROTECT1.2和ARMADILLO2.6的文章以获得一些基础
知识使介绍更加清楚明白)
1.玩转LOCK
OK,首先你保证已经下载了PELOCK,这时你发现SOFTICE不能载入。怎么办!PELOCK
的核心,许多的壳都是通过载入调试来脱壳,因此我们需要一个可以载入的工具。这
下用到了前面介绍的SUPERBPM工具,用它与SOFTICE合作,下“BPM VERTUALALLOC X”
载入。运行PELOCK,断下来后,你会停在这:
00431682    push 4
00431684    push 3000h
00431689    push 0F000h; loader 大小
0043168E    push ecx
0043168F    mov edx, [ebp+VirtualAlloc]
00431692    cmp byte ptr [edx], 0CCh  ; 检测 opcodes for bpx
00431695    setz cl; 如果发现bpx 那么
00431698    add edx, ecx              ; 改变edx的值并且发生错误
0043169A    call edx
0043169C    lea edx, [ebp+756h]  <===  你应该停在这
004316A2    push eax; 这里的eax是VirtualAlloc的地址
004316A3    push edx; 这儿main PELock loader将会decrypted to
004316A4    push eax
004316A5    pusha
<-----------省略------------->
<--loader 解密(decryption)程序----------->
00431768    mov [esp+1Ch], edi
0043176C    popa
0043176D    retn; 从这跳向PELock loader code

正如上面所看到的,这儿就是PELOCK解密的地方。尽管这段程序省略了,但是你可以
很容易的通过SOFTICE滚动条找到RET命令。我们要在43176D处下断点,并且DUMP这个
ELOCK LOADER,你可以在堆栈中找到新解密(DECRYPTED)的LOADER的基址。DUMP后
我们将进入IDA去分解学习。我相信,你一定会重建PE文件头,剪切粘贴DUMP下来的
到PELOCK.EXE中去并再次用IDA重新载入。
如果你照做了,你将会发现:
00401000    call near ptr PE_Lock_start+24h
00401005
00401005 GetAPIAddress proc near      ; CODE XREF: ...
00401005
00401005 var_4= byte ptr -4
00401005 arg_0= dword ptr  8
00401005 arg_4= dword ptr  0Ch
00401005 arg_8= dword ptr  10h
00401005 arg_C= byte ptr  14h
00401005 arg_104= dword ptr  10Ch
00401005
00401005    push ebp
00401006    mov ebp, esp
00401008    pusha
<---------继续---------->

摘录的部分是一些PELOCK LOADER的命令,开始处是一些GETPROCADDRESS程序,用来
读取系统DLL,PE文件头和从输出表找到API地址。API名称是杂乱的并与表相比较(
我们将讨论更多关于API REDIRECTION SECTION)。如果一个API地址被找到,那么
将核对首字节“CC”,假使发现BPX,那么执行FLOW的些微改变将会导致PELOCK的最
终结束。由于整个LOADER不完全解密(DECRYPTED),许多部分仍然是个“谜”,只有
在PELOCK执行他的主程序时才能完全解密,如果你发现IDA DISASSEMBLY与SOFTICE屏
幕有差别,那么你不得不重新DUMP这个LOADER并且把DUMP下来的解密部分覆盖在原来
的部分以使IDA分析时更准确,方便。
当你开始追踪PELOCK LOADER时,你会发现有许多无用但会阻碍分析的代码包含在LOA
DER中,使分析变的困难。下面就是这样的例子:

0040159F C1 F7 00              sal edi, 0
004015A2 C1 F2 00              sal edx, 0; 毫无用处
004015A5 EB 02                jmp short loc_4015A9; 跳过
004015A5                  ; -----------------------------------------------------------
004015A7 CD                    db 0CDh ; -
004015A8 20                    db  20h ;  
004015A9                  ; -----------------------------------------------------------
004015A9                  
004015A9                  loc_4015A9:                  ; CODE XREF: ...
004015A9 C1 F0 00              sal eax, 0
004015AC EB 02                jmp short loc_4015B0
004015AC                  ; -----------------------------------------------------------
004015AE C9                    db 0C9h ; +
004015AF 22                    db  22h ; "
004015B0                  ; -----------------------------------------------------------
004015B0                  
004015B0                  loc_4015B0:                  ; CODE XREF: ...
004015B0 E8 01 00 00 00        call loc_4015B6; 顺序读出 head
004015B0                  ; -----------------------------------------------------------
004015B5 42                    db  42h ; B
004015B6                  ; -----------------------------------------------------------
004015B6                  
004015B6                  loc_4015B6:                  ; CODE XREF: ...
004015B6 8D 64 24 04          lea esp, [esp+4];纠正堆栈
OR THIS
004015F1 E8 01 00 00 00        call loc_4015F7
004015F1                  ;
-----------------------------------------------------------
004015F6 3B                    db  3Bh ; ;
004015F7                  ; -----------------------------------------------------------
004015F7                  
004015F7                  loc_4015F7:                  ; CODE XREF: ...
004015F7 8F 44 24 FC          pop dword ptr [esp-4];纠正堆栈
OR THIS
004015D6 7A 03                jp  short loc_4015DB; 不要在意Eflags
004015D8 7B 01                jnp short loc_4015DB; 和将会跳到哪里
004015D8                  ;

-----------------------------------------------------------
004015DA 7E                    db  7Eh ; ~
004015DB                  ; -----------------------------------------------------------
004015DB                  
004015DB                  loc_4015DB:                  ; CODE XREF: ...
004015DB EB 02                jmp short loc_4015DF; 无用代码
004015DB                  ; -----------------------------------------------------------
004015DD 8B                    db  8Bh ; ?
004015DE 8D                    db  8Dh ; ?
004015DF                  ; -----------------------------------------------------------
004015DF                  
004015DF                  loc_4015DF:                  ; CODE XREF: ...
004015DF C1 F6 00              sal esi, 0

我们有必要把这些无用的,对于分析没有任何价值的代码清除,以便于我们看清
整个代码,同时也方便了IDA的分析。你可以用如MOW,IMHOTEP或者自创的工具来
修整这些代码。我自己就做了一个简单的IDC SCRIPT,他可以检查并把他们NOP掉
并单行排列显示。文章中FURTHER CODE的摘录就正象无用代码的清除一样。我已把
IDC SCRIPT(作者写的工具)帖了上来作为参考,你可以自己做一个比较顺手的。
2.SOFTICE 侦测
当你清除了无用代码后,你就可以放心的研究PELOCK了,但首先我们必须使PELOCK
运行在SOFTICE下。在WIN2K系统下,PELOCK对SOFTICE有很强的侦测性,如INT1侦测,
UNHANDLEDEXCEPTIONFILTER BYTE侦测。
即由于SOFTICE载入时,他会把KERNEL32!UNHANDLEDEXCEPTIONFILTER API的第一字节
“55”用“CC”来代替。
PELOCK就是这样侦测UNHANDLEDEXCEPTIONFILTER BYTE的,如下:
004010D4 80 38 CC              cmp byte ptr [eax], 0CCh ; '?
004010D7 75 0F                jnz short 跳则成功
004010D9 74 01                jz  short near ptr 跳则完蛋

因此,我们使用READ(R命令)修改ZERO FLAG的值来避开对UNHANDLEDEXCEPTIONFILTER
的校验。
BPM UNHANDLEDEXCEPTIONFILTER  R FL Z
当WIN2000系统运行在无SOFTICE状态时,登陆IDT的INT01描述符已经是DPL=0。因此,如
果RING-3级试图访问INT01 WITH DPL=0时,GP=INT0D的例外就会出现,同时WINDOWS INT0
D HANDLER 将会报告COOOOOO5(例外_过程_违反)作为“例外”代码,而EIP就作为“例外”
产生的地址。当两者同时运行时,登陆IDT的INT01描述符被设定为DPL=3,于是这时INT01便
可顺利的进入RING3级。如果INT01冲突,那么下一个命令地址便代替它保存在堆栈中,同时W
INDOWS将检验所有的断点,如果不存在,那么WINDOWS将报告80000004(例外_独立_进程)错
误。简言之,如果SOFTICE运行,当INT01冲突时,则“例外”代码是80000004,而出现“例外
”的地址便是EIP+2;反言之,如果SOFTICE不运行,“例外“代码和地址就变成了C0000005和
EIP。(更多细节,请参见INTEL MANUAL VOLUME 3)
在此感谢[YATES]的精彩解说!
当SOFTICE运行时,PELOCK根据校验的结果产生不同的错误地址。一个SEH HANDLER便
形成了!
004044A3 CD 01                int 1    ; 如果SOFTICE不运行的例外地址
004044A5 C3                    retn; 如果SOFTICE运行的例外地址
004044A6                  ; -----------------------------------------------------------
004044A6 EB 36                jmp short near sice_不_存在
004044A6                  ; -----------------------------------------------------------
004044A8 90 90 90 90 90 90+    Softice_侦测到 db 35h dup(90h)
004044DD                  ; -----------------------------------------------------------
004044DD C3                    retn
004044DD                  ; -----------------------------------------------------------
004044DE 90 90 90 90 90 90+sice_不_存在 db 40h dup(90h)
<--- Inside SEH handler --->
004040E4 83 A8 B8 00 00 00+    sub [eax+CONTEXT.Eip], 0FFFFFFFDh ; Eip+3
004040E4 FD                                    ; 如果SICE存在, int1 例外
004040E4                                        ; 将向前移2字节

因此,如果侦测到SOFTICE,例外FLOW将会改变,同时PELOCK也会结束。为了避免这
种情况的发生,我们需要修改登陆IDT的INT01描述符,使DPL=0。在WIN2K下运行IDT
;找到INTERRUPT DESCRIPTORS TABLE的地址
D <ADDRESS OF IDT>
用IDT修改从EE到8E的总共0DH字节  感谢SINTAX 和NIKOLATESLA 的帮助
具体细节,请参考ART OF ASSEMBLY ABOUT IDT FORMAT
3.输入表重建
当你完成了IDT的修改(重新运行SOFTICE时),(在这期间)UNHANDEDEXCEPTIONFILTER
将会中断两次,(你会发现)PELOCK已经可以正常运行了。我们下一步就是获取输入表。
运行PELOCK,中断在第一个UNHANDLEDEXCEPTIONFILTER,我们停在前文所说的GETPROCADD
RESS中,下“bpm EIP X”断点,再次中断时,按一次F12,我们就会进入IAT LOADING和RE
DIRECTING PROCESS的中心。
00405350 EB 2A                jmp short near ptr ImportLoadingStart
00405350                  ; ------------------------------------------------------
00405352 90 90 90 90 90 90+next_dll_entry db 27h dup(90h) ; CODE XREF: seg000:004059C3_j
00405379                  ; ------------------------------------------------------
00405379 8D 7E 01              lea edi, [esi+1]
00405379                  ; ------------------------------------------------------

0040537C 90 90 90 90 90 90+ImportLoadingStart db 24h dup(90h) ; CODE XREF: seg000:00405350_j
004053A0                  ; ------------------------------------------------------
004053A0 57                    push edi        ; edi -> DLL 名称
004053A1 FF 55 00              call [ebp+API.LoadLibrary]
004053A1                  ; ------------------------------------------------------
004053A4 90 90 90 90 90 90+    db 2Ch dup(90h)
004053D0                  ; ------------------------------------------------------
004053D0 85 C0                test eax, eax
004053D2 0F 84 13 1A 00 00    jz  Library_loading_fail
004053D2                  ; ------------------------------------------------------
004053D8 90 90 90 90 90 90+    db 26h dup(90h)
004053FE                  ; ------------------------------------------------------
004053FE 8B D0                mov edx, eax    ; edx = DLL 句柄
004053FE                  ; ------------------------------------------------------
00405400 90 90 90 90 90 90+    db 2Dh dup(90h)
0040542D                  ; ------------------------------------------------------
0040542D 8B F7                mov esi, edi    ; import data block pointer(指针)
0040542D                  ; ------------------------------------------------------
0040542F 90 90 90 90 90 90+    db 2Dh dup(90h)
0040545C                  ; ------------------------------------------------------
0040545C                  string_scan_loop:  ; CODE XREF: seg000:00405461_j
0040545C 8A 06                mov al, [esi]  ;通过校验空字节
0040545E 46                    inc esi        ;对Import Data block循环扫描
0040545F 84 C0                test al, al    ;并把指针放到DLL名称的末尾
00405461 75 F9                jnz short string_scan_loop
00405461                  ; ------------------------------------------------------
00405463 90 90 90 90 90 90+not_end_of_thunk db 4Eh dup(90h) ; CODE XREF: seg000:00405925_j
004054B1                  ; ------------------------------------------------------
004054B1 8B 46 01              mov eax, [esi+1] ; 从Import Data Block获得API name hash
004054B1                  ; ------------------------------------------------------
004054B4 90 90 90 90 90 90+    db 2Ah dup(90h)
004054DE                  ; ------------------------------------------------------
004054DE B9 B2 06 00 00        mov ecx, 6B2h  ; 为下面的CRC循环初始化数值
004054DE                  ; ------------------------------------------------------
004054E3 90 90 90 90 90 90+    db 23h dup(90h)
00405506                  ; ------------------------------------------------------
00405506                  CRC_import_decrypt_loop: ; CODE XREF: seg000:00405510_j
00405506 2B 84 8D 0A 3D 00+    sub eax, [ebp+ecx*4+3D0Ah] ; 通过对代码部分CRC校验
0040550D D3 C0                rol eax, cl    ; 循环解密API name hash
0040550F 49                    dec ecx        ; 阻止对数值的修改
00405510 75 F4                jnz short CRC_import_decrypt_loop
00405510                  ; ------------------------------------------------------
00405512 90 90 90 90 90 90+    db 28h dup(90h)
0040553A                  ; ------------------------------------------------------
0040553A 8A 0E                mov cl, [esi]  ; 获得API名称的第一个字符
0040553C 83 C6 05              add esi, 5
0040553C                  ; ------------------------------------------------------
0040553F 90 90 90 90 90 90+    db 23h dup(90h)
00405562                  ; -------------------------------------------------
00405562 51                    push ecx        ; 第一个字符入栈
00405562                  ; ------------------------------------------------------
00405563 90 90 90 90 90 90+    db 23h dup(90h)
00405586                  ; ------------------------------------------------------
00405586 50                    push eax        ; API name hash入栈
00405586                  ; ------------------------------------------------------
00405587 90 90 90 90 90 90+    db 32h dup(90h)
004055B9                  ; ------------------------------------------------------
004055B9 52                    push edx      ; DLL句柄入栈
004055B9                  ; ------------------------------------------------------
004055BA 90 90 90 90 90 90+    db 29h dup(90h)
004055E3                  ; ------------------------------------------------------
004055E3 FF 75 00              push dword ptr [ebp+0]

004055E3                  ; ------------------------------------------------------
004055E6 90 90 90 90 90 90+    db 33h dup(90h)
00405619                  ; ------------------------------------------------------
00405619 E8 E7 B9 FF FF        call GetAPIAddress ; The home-made GetProcAddress
00405619                  ; ------------------------------------------------------
0040561E 90 90 90 90 90 90+    db 30h dup(90h)  <=== 你应该到达这里
这句无用代码已用IDC SCRIPT清除
0040564E                  ; ------------------------------------------------------
0040564E 85 C0                test eax, eax  ; 校验找到的API地址
0040564E                  ; ------------------------------------------------------
00405650 90 90 90 90 90 90+    db 1Dh dup(90h)
0040566D                  ; ------------------------------------------------------
0040566D 0F 84 92 17 00 00    jz  API_add_not_found
0040566D                  ; ------------------------------------------------------
00405673 90 90 90 90 90 90+    db 5Bh dup(90h)
004056CE                  ; ------------------------------------------------------
004056CE F6 C1 80              test cl, 80h    ; 校验 redirection flag(标志位)
004056CE                                      ; 如果cl=80,API就改变(redirected)
004056CE                  ; ------------------------------------------------------
004056D1 90 90 90 90 90 90+    db 2Bh dup(90h)
004056FC                  ; ------------------------------------------------------
004056FC 8B 0E                mov ecx, [esi]
004056FC                  ; ------------------------------------------------------
004056FE 90 90 90 90 90 90+    db 47h dup(90h)
00405745                  ; ------------------------------------------------------
00405745 0F 84 87 00 00 00    jz  near ptr redirect_import+2Bh ;关键跳转
00405745                  ; ------------------------------------------------------
0040574B 90 90 90 90 90 90+    db 55h dup(90h)
004057A0                  ; ------------------------------------------------------
004057A0 89 01                mov [ecx], eax  ; 用原始的API地址

004057A2 E9 1F 01 00 00        jmp next_entry_in_thunk ; 更新 First Thunk of Import Directory

004057A2                  ; ------------------------------------------------------
004057A7 90 90 90 90 90 90+redirect_import db 8Bh dup(90h) ; CODE XREF: seg000:00405745_j
00405832                  ; ------------------------------------------------------
00405832 89 19                mov [ecx], ebx  ; 用更改的API 地址更新First Thunk
00405832                  ; ------------------------------------------------------
00405834 90 90 90 90 90 90+    db 5Dh dup(90h)
00405891                  ; ------------------------------------------------------
00405891 E8 51 01 00 00        call near ptr redirect_API+1Fh
00405891                  ; ------------------------------------------------------
00405896 90 90 90 90 90 90+    db 30h dup(90h)
004058C6                  ; ------------------------------------------------------
004058C6                  next_entry_in_thunk: ; CODE XREF: seg000:004057A2_j
004058C6 83 C6 04              add esi, 4
004058C6                  ; ------------------------------------------------------
004058C9 90 90 90 90 90 90+    db 30h dup(90h)
004058F9                  ; ------------------------------------------------------
004058F9 B1 FE                mov cl, 0FEh ; '? ; 校验stopping byte
004058F9                  ; ------------------------------------------------------
004058FB 90 90 90 90 90 90+    db 28h dup(90h)
00405923                  ; ------------------------------------------------------
00405923 38 0E                cmp [esi], cl  ; 如果存在则作为thunk的结束
00405925 0F 85 60 FB FF FF    jnz near ptr not_end_of_thunk+28h
00405925                  ; ------------------------------------------------------
0040592B 90 90 90 90 90 90+    db 32h dup(90h)
0040595D                  ; ------------------------------------------------------
0040595D 38 4E 01              cmp [esi+1], cl ; 再次校验stopping byte
0040595D                  ; ------------------------------------------------------
00405960 90 90 90 90 90 90+    db 28h dup(90h)
00405988                  ; ------------------------------------------------------
00405988 0F 84 FC 17 00 00    jz  import_done ; 如果2个stopping byte
都符合
00405988                                      ; 则作为Import Data的结束
00405988                  ; ------------------------------------------------------
0040598E 90 90 90 90 90 90+    db 35h dup(90h)
004059C3                  ; ------------------------------------------------------
004059C3 E9 8A F9 FF FF        jmp near ptr 下一个_dll_入口

希望我的注解大家都明白。基本上,IMPORT DATA被储存在一个块中,以“FE“作为STOPPING BYTE或MARKER。每个DLL IMPORT入口以连续的ASCII值组成的DLL名称开始,IMPORT ENTRIES跟随,以“FE“结束。TABLE每一个IMPORT入口由5个字节组成,首字节是API名称的第一个ASCII字符,下一个双字节是API NAME HASH。下面就是一个IMPORT DATA BLOCK的例子:
00407E21 6B 65 72 6E 65 6C+aKernel32_dll_0 db 'kernel32.dll',0
00407E2E 56                    db  56h ; V
00407E2F B5 F2 31 F6          dd 0F631F2B5h
00407E33 56                    db  56h ; V
00407E34 22 0A AC 32          dd 32AC0A22h
<-------------省略------------->
00407E60 47                    db  47h ; G
00407E61 E4 0D 06 DE          dd 0DE060DE4h
00407E65 FF                    db 0FEh ;  stopping byte
00407E66 75 73 65 72 33 32+aUser32_dll db 'user32.dll',0
00407E71 4D                    db  4Dh ; M
00407E72 71 A2 D2 A8          dd 0A8D2A271h
00407E76 FF                    db 0FEh ;stopping byte  
00407E77 FF                    db 0FEh ;  
上面的每一个IMPORT ENTRY都作为GETPROCADDRESS的参数(假想系统DLL EXPORT DIRECTORIES
是按字母分类的)分列于EXPORT DIRECTORY中并进行校验直到找出符合首字母的字符为止。接
下来将对每个EXPORT DIRECTORY的API名称执行HASH,直到找到符合条件的HASH为止,这个找到
的API地址会校验BPX并且返回EAX中去。明显的,在4056CE处的比较和在405745处的跳转是决定
API是否应该REDIRECTED(转向/改变)的关键。因为CRC对IMPORT HASH值的检验,使我们应该想
办法让PELOCK不改变(REDIRECT)任何API,而不是简单的修改跳转。解决方法有两个:修改GET
PROCADDRESS或者在405745处修改ZERO标志位的值,使其为0。我提倡第二种在SOFTICE中简单易行
的方法。
下BPM 405745 X 如果ZFL==1 下“R FL Z:X“
SOFTICE的屏幕由于无休止的中断而不停闪烁,大量的IMPORTS出现在
屏幕里,让你的眼睛暂时离开一会儿,几分钟之后,当你再看屏幕时,P
ELOCK已经安静的运行中了。再看看IMPREC,键入OEP=1000,很快的,
28000就是你的IAT。单击GET IMPORT,除了1个之外,IMPORT将会修整
所有的IMPORT。剩下的作为练习留给读者,目标是找到例外的API并找出
原因。
4.破解目标程序
当你修复了IAT REDIRECTION并获得了有效的IMPORT TREE(目录)后,
保存并暂时搁置。下面我们将破解主程序并完全脱壳。再次运行PELOCK,
打开LORDPE,选择PARTIAL DUMP(部分脱壳),键入DUMP SIZE作为P
ELOCK.EXE的IMAGE SIZE。通过IDA分析后,你会发现仍然有大量所需
的代码未能解密,下面就是这样一段未解密代码的例子:

00401ECA E8 33 5A EF FF        call near ptr 2F7902h  ; CALL Decryptor(解密调用)
00401ECA                  ; ----------------------------------------------------------
00401ECF E8 02 00 00          dd 2E8h; code block property(代码段属性)
00401ED3 66 7B 6B 3C 47 89+    db 66h, 7Bh, 6Bh, 3Ch, 47h, 89h, 0EDh, 39h, 53h, 0EFh, 83h,
00401ED3 ED 39 53 EF 83 00+    db 72h, 6Fh, 5Dh, 3Ch, 0EEh, 26h, 0B0h, 78h, 0AEh, 29h, 98h
00401ED3 B7 A1 AD 9D E8 B2+    db 0FBh, 0EBh, 0FAh, 0EBh, 0FCh, 0EBh, 6, 0CDh, 20h, 0EBh,
00401F40                  ; -------------------------------------------------------------
00401F40 E8 F1 61 EF FF        call near ptr 2F8136h  ;  CALL Re-encryptor(再加密)
00401F40                  ; -------------------------------------------------------------
00401F45 E8 02 00 00          dd 2E8h      ; code block property(代码段属性)
00401F49                  ; -------------------------------------------------------------

从上面可以看出,DUMP.EXE中有许多代码段,加解密地址并不在EXE程序的IMAGE中,而是在PE
LOADER MEMORY中。基本上,为了防止被脱壳和DUMPING,PELOCK把大量的加密段代码放入原始
程序中,当程序被执行时,解密段将首先被执行,它将代码块属性读出从而解密块,接着
从解密调用返回,执行代码块,在进入一般执行程序之前将调用再加密重新加密代码块。
通过对DUMP.EXE主程序的研究,发现它包含有三对不同地址,不同运算过程的加解密调用。
通过研究这些程序,我们发现注以“代码块属性“并且位于CALL下的双字(DWORD)其实是
加密代码块的长度值。尽管理解程序的解密算法对于我们正确DUMP目标程序意义不大,但是
却可以帮助我们了解它的工作原理。为了精简文章的长度,这儿就不赘述了。
为了正确DUMP程序,我们需要在到达OEP之前使PELOCK LOADER解密出所有的代码块。虽然可
以通过修改DECRYPTOR来解密,接着通过跳转避开加密的调用,但是我们怎样知道在到达OEP
之前是否已解开了所有的加密代码块呢?我是这样考虑的:由于LOADER OFFSET在不同的PC上
是不同的,所以PELOCK LOADER不仅需要知道哪里是用于DECRYPTOR和RE-ENCRYPTOR的调用,并
且需要计算出调用的距离以便进入目标程序中,同时猜想应该有一张存储相关信息的TABLE(表)
。事实正是如此。重新运行PELOCK,在原始的EXE IMAGE中下BPM <其中一个解密的调用>,SOFT
ICE将会恰好中断在主程序中。
0040DBA8                  insert_crypt_engine_3: ; CODE XREF: seg000:00407578_p
0040DBA8 60                    pusha
0040DBA9 E8 A3 FF FF FF        call mov_ebp_40DB56; 给EBP设置(必要的)基址
0040DBAE 8D 75 F7              lea esi, [ebp-9]
0040DBAE                  ; -----------------------------------------------------------
0040DBB1 90 90 90 90 90 90+    db 30h dup(90h)
0040DBE1                  ; -----------------------------------------------------------
0040DBE1 8D 95 6C 03 00 00    lea edx, [ebp+36Ch]; 获得Decryptor的地址
0040DBE1                  ; -----------------------------------------------------------
0040DBE7 90 90 90 90 90 90+    db 21h dup(90h)
0040DC08                  ; -----------------------------------------------------------
0040DC08 B9 03 00 00 00        mov ecx, 3      ; table(表)大小, 仅仅=3
-----------------------------------------------------------
0040DC0D 90 90 90 90 90 90+loop_inserting_all_engine_loc db 24h dup(90h) ;
0040DC31  
-----------------------------------------------------------
0040DC31 8B 06                mov eax, [esi]; 从table(表)中读取一个双字
0040DC31                  ; -------------------; 每个双字表明调用的地址
0040DC33 90 90 90 90 90 90+    db 25h dup(90h)  ;
0040DC58                  ; -----------------------------------------------------------
0040DC58 83 EE 04              sub esi, 4; 把表中的指针向后移
0040DC58                  ; -----------------------------------------------------------
0040DC5B 90 90 90 90 90 90+    db 2Dh dup(90h)
0040DC88                  ; -----------------------------------------------------------
0040DC88 C6 00 E8              mov byte ptr [eax], 0E8h ; 设置调用字节
0040DC88                  ; -----------------------------------------------------------
0040DC8B 90 90 90 90 90 90+    db 2Dh dup(90h)
0040DCB8                  ; -----------------------------------------------------------

  0040DCB8 40                    inc eax
0040DCB9 8B FA                mov edi, edx; edi -> Decryptor的地址
0040DCBB 2B F8                sub edi, eax; 计算调用的距离
0040DCBB                  ; -----------------------------------------------------------
0040DCBD 90 90 90 90 90 90+    db 2Bh dup(90h)
0040DCE8                  ; -----------------------------------------------------------
0040DCE8 83 EF 04              sub edi, 4; 减去指令长度
0040DCEB 89 38                mov [eax], edi  ; 在E8后插入调用距离
0040DCEB                  ; -----------------------------------------------------------
PATCHES made to force decrypt code(这里的修改为了代码的解密)
0040DCED 90 90 90 90 90 90+    db 28h dup(90h) ; push dword ptr [eax+4]
0040DCED 90 90 90 90 90 90+                    ; pushad
0040DCED 90 90 90 90 90 90+                    ; dec eax
0040DCED 90 90 90 90 90 90+                    ; call eax=>解密已修改的程序
0040DCED 90 90 90 90 90 90+                    ; popad
0040DCED 90 90 90 90 90 90+                    ; pop dword ptr [eax+4]
0040DCED 90 90 90 90                          ; mov word [eax-1], 7EB
0040DD15                  ; -----------------------------------------------------------
0040DD15 83 C0 08              add eax, 8;
0040DD18 03 40 FC              add eax, [eax-4]; 加上代码块长度
0040DD1B 83 C0 10              add eax, 10h; eax -> 调用Re-encryptor的地址
  should be placed
0040DD1B                  ; -----------------------------------------------------------
0040DD1E 90 90 90 90 90 90+    db 2Eh dup(90h); 这儿设置了另一个调用
0040DD4C                  ; -----------------------------------------------------------
;              ;
0040DD4C C6 00 E8              mov byte ptr [eax], 0E8h ; 'F' ; mov byte ptr [eax], E9
0040DD4C                  ; -----------------------------------------------------------
0040DD4F 90 90 90 90 90 90+    db 2Fh dup(90h);调用Re-encryptor
0040DD7E                  ; -----------------------------------------------------------
0040DD7E 40                    inc eax
0040DD7E                  ; -----------------------------------------------------------
0040DD7F 90 90 90 90 90 90+    db 2Ch dup(90h); edi -> Re-encryptor的地址
0040DDAB                  ; -----------------------------------------------------------
0040DDAB 8D BD 4E 07 00 00    lea edi, [ebp+74Eh] ; lea edi, [eax+8]
0040DDAB                  ; -----------------------------------------------------------
0040DDB1 90 90 90 90 90 90+    db 28h dup(90h)
0040DDD9                  ; ------------0040DDD9 2B F8                sub edi, eax; 计算调用的距离
0040DDD9                  ; -----------------------------------------------------------
0040DDDB 90 90 90 90 90 90+    db 23h dup(90h)
0040DDFE                  ; -----------------------------------------------------------
0040DDFE 83 EF 04              sub edi, 4; 减去指令长度
0040DDFE                  ; -----------------------------------------------------------
0040DE01 90 90 90 90 90 90+    db 25h dup(90h)
0040DE26                  ; -----------------------------------------------------------
0040DE26 89 38                mov [eax], edi; 插入调用距离
0040DE26                  ; -----------------------------------------------------------
0040DE28 90 90 90 90 90 90+    db 2Eh dup(90h); 结束调用
0040DE56                  ; -----------------------------------------------

0040DE56 49                    dec ecx; 计算表中数值减1
0040DE56                  ; -----------------------------------------------------------
0040DE57 90 90 90 90 90 90+    db 36h dup(90h); 如果不是表(TABLE)尾, 循环
0040DE8D                  ; -----------------------------------------------------------
0040DE8D 0F 85 7A FD FF FF    jnz near ptr loop_inserting_all_engine_loc
0040DE8D                  ; -----------------------------------------------------------
0040DE93 90 90 90 90 90 90+    db 23h dup(90h)
0040DEB6                  ; -----------------------------------------------------------
0040DEB6 8D 7D 52              lea edi, [ebp+52h]

0040DEB9 B9 16 03 00 00        mov ecx, 316h; 写入无用代码
0040DEBE F3 AA                repe stosb;    覆盖上述代码段
0040DEC0 61                    popa
0040DEC1 C3                    retn

注释说明的很清楚。每一部分加密代码的地址被读出并储存在TABLE(表)中,
通过解密代码块的长度计算RE-ENCRYPTOR的调用的区域,而E8XXXXXXXX的作用
是为了调用DECRYPTOR,。为了避免调用DECRYPTOR/ENCRYPTOR而使程序直接从
表(TABLE)上列出的地址解密出所有的的代码块,我们可以修改(PATCH)程
序.PATCH(修改)的必要过程已通过上述橘黄色注释标明。由于“CALL EAX”虚
拟了原始EXE的执行,因此它会触发用于解密某个地址下代码块的CALL DECRYP
TOR(的执行)。但是,我们的目的不是使程序返回执行到原始EXE中,而是通
过修改程序,使它返回执行到我们修改(PATCH)的循环中去。这是一件很容易
的事,只要纠正程序的堆栈即可。
我们要找到DECRYPTOR程序的结束语句,如:
RET  ;只返回执行到解密代码块
修改堆栈后是这样:
POP EAX  ;EAX-解密代码块的地址
RET  ;从“CALL EAX”返回我们修改的循环中
4.CRC 校验
上文已提到,程序中有3对不同的DECRYPTOR/ENCRYPTOR,而每一对又有一段
各自独立的程序,这段程序用于把(DECRYPTOR/ENCRYPTOR)调用程序插入目
标程序EXE中,通过EXE中的一张表(TABLE)放置调用的程序的地址。为了获得
正确的DUMP,我们可以通过修改这3对程序来解密目标程序EXE的整个代码部分。
然而,问题随之而来:其中一个调用插入程序(CALLS INSERTION ROUNTINE)
包含加密表(TABLE)地址,并通过CRC校验来解密TABLE ENTRIES。如果修改
将导致对TABLE ENTRIES解密错误情况的发生。下面就是这段程序:
00408368                  Insert_Crypt_Engine_1: ;
00408368 B9 4A 00 00 00        mov ecx, 4Ah ;  表(TABLE)的大小
00408368                  ; -----------------------------------------------------------
0040836D 90 90 90 90 90 90+    db 27h dup(90h)
00408394                  ; -----------------------------------------------------------
00408394 8D 75 E8              lea esi, [ebp-18h] ; 表的开端
00408394                  ; -----------------------------------------------------------
00408397 90 90 90 90 90 90+    db 27h dup(90h)
004083BE                  ;
-----------------------------------------------------------
004083BE 8D 95 68 0B 00 00    lea edx, [ebp+0B68h]; edx -> DECRYPTOR地址
004083BE                  ; -----------------------------------------------------------
004083C4 90 90 90 90 90 90+get_crypted_offset(得到_加/解密_偏移量) db 55h dup(90h) ; CODE XREF: ...
00408419                  ; -----------------------------------------------------------
00408419 8B 06                mov eax, [esi]; 从表中读取解密的ENTRY
0040841B EB 31                jmp short near ptr prepare_CRC_data_pointer(准备_CRC_数据_指针)
0040841B                  ; -----------------------------------------------------------
0040841D 90 90 90 90 90 90+set_no_of_loop(设置_no_of_循环) db 27h dup(90h) ; CODE XREF: ...
00408444                  ; -----------------------------------------------------------
00408444 BB 1F 01 00 00        mov ebx, 11Fh
00408449 E9 25 01 00 00        jmp near ptr calculate_insertion_address(计算_插入_地址)
00408449                  ; -----------------------------------------------------------
0040844E 90 90 90 90 90 90+prepare_CRC_data_pointer(同上) db 2Eh dup(90h) ; CODE XREF: ...
0040847C                  ; -----------------------------------------------------------
0040847C 83 EE 04              sub esi, 4
0040847F EB 9C                jmp short near ptr set_no_of_loop(同上)
0040847F                  ; -----------------------------------------------------------
00408481 90 90 90 90 90 90+get_call_distance(得到_调用_距离) db 2Fh dup(90h) ; CODE XREF: ...
004084B0                  ; -----------------------------------------------------------
004084B0 2B F8                sub edi, eax
004084B2 E9 84 00 00 00        jmp near ptr minus_length_4_bytes(减去_长度_4_字节)
004084B2                  ; -----------------------------------------------------------
004084B7 90 90 90 90 90 90+set_up_CALL_decrypt(设置_调用_解密) db 2Eh dup(90h) ; CODE XREF: ...
004084E5                  ; -----------------------------------------------------------
004084E5 C6 00 E8              mov byte ptr [eax], 0E8h ; 'F' ; 设置调用
004084E8 E9 09 01 00 00        jmp near ptr minus_1_byte(减去_1_字节)
004084E8                  ; -----------------------------------------------------------
004084ED 90 90 90 90 90 90+decrease_counter db 1Bh dup(90h) ; CODE XREF: ...
00408508                  ; -----------------------------------------------------------
00408508 49                    dec ecx; 通过计算减1来计算表尾
00408509 E9 C1 02 00 00        jmp 检验_结束_表?
00408509                  ; -----------------------------------------------------------
0040850E 90 90 90 90 90 90+get_CALL_to_address(得到_调用_地址) db 26h dup(90h) ; CODE XREF: ...
00408534                  ; -----------------------------------------------------------
00408534 8B FA                mov edi, edx
00408536 E9 46 FF FF FF        jmp near ptr get_call_distance(同上)
00408536                  ;
-----------------------------------------------------------
0040853B 90 90 90 90 90 90+minus_length_4_bytes(同上) db 30h dup(90h) ; CODE XREF: ...
0040856B                  ; -----------------------------------------------------------
0040856B 83 EF 04              sub edi, 4; TABLE ENTRIES指针减4
0040856E E9 F3 01 00 00        jmp near ptr store_jump_distance(保存_跳转_距离)
0040856E                  ; -----------------------------------------------------------
00408573 90 90 90 90 90 90+calculate_insertion_address (同上)db 27h dup(90h) ; CODE XREF: ...
0040859A                  ;
-----------------------------------------------------------
0040859A                  CRC_loop:          ; CODE XREF: ...
0040859A 03 84 9D F6 05 00+    add eax, [ebp+ebx*4+5F6h]
004085A1 D1 C8                ror eax, 1      ; 循环计算
004085A3 4B                    dec ebx        ; 解密代码的调用
004085A4 75 F4                jnz short CRC_loop ;插入的地址
004085A6 E9 0C FF FF FF        jmp near ptr set_up_CALL_decrypt(同上) ; <===  HERE do bpm
004085A6                  ; -----------------------------------------------------------
004085AB 90 90 90 90 90 90+get_distance_call db 44h dup(90h) ; CODE XREF: ...
004085EF                  ; -----------------------------------------------------------
004085EF 2B F8                sub edi, eax
004085F1 E9 E1 01 00 00        jmp near ptr byte_4087D7
004085F1                  ; -----------------------------------------------------------
004085F6 90 90 90 90 90 90+minus_1_byte(同上) db 29h dup(90h) ; CODE XREF: ...
0040861F                  ; -----------------------------------------------------------
0040861F 40                    inc eax
00408620 E9 E9 FE FF FF        jmp near ptr get_CALL_to_address(同上)
00408620                  ; -----------------------------------------------------------
00408625 90 90 90 90 90 90+byte_408625 db 24h dup(90h) ; CODE XREF: ...
00408649                  ; -----------------------------------------------------------
00408649 89 38                mov [eax], edi
0040864B E9 9D FE FF FF        jmp near ptr decrease_counter
0040864B                  ; -----------------------------------------------------------
00408650 90 90 90 90 90 90+calculate_block_length(计算_块_长度) db 1Eh dup(90h) ; CODE XREF: ...
0040866E                  ; -----------------------------------------------------------
0040866E 83 C0 08              add eax, 8      ; 解密代码的关键
00408671 EB 00                jmp short $+2
00408673 8B 58 FC              mov ebx, [eax-4] ; 得到加解密长度
00408673                  ; -----------------------------------------------------------
00408676 90 90 90 90 90 90+    db 2Eh dup(90h)
004086A4                  ; -----------------------------------------------------------
004086A4 C1 CB 03              ror ebx, 3; ror ebx, 3
004086A4                  ; -----------------------------------------------------------
004086A7 90 90 90 90 90 90+    db 28h dup(90h)
004086CF                  ; -----------------------------------------------------------
004086CF 03 C3                add eax, ebx; eax -> 插入调用Encryptor(的地址)
004086D1 E9 C1 00 00 00        jmp near ptr get_end_pointer(得到_结束_指针)
004086D1                  ; -----------------------------------------------------------
004086D6 90 90 90 90 90 90+get_reencrypt_call_address db 26h dup(90h) ; CODE XREF: ...
004086FC                  ; -----------------------------------------------------------
004086FC 8D BD 9C 13 00 00    lea edi, [ebp+139Ch]
00408702 E9 A4 FE FF FF        jmp near ptr get_distance_call
00408702                  ; -----------------------------------------------------------
00408707 90 90 90 90 90 90+insert_encrypt_call(插入_解密_调用)db 2Dh dup(90h) ; CODE
XREF: ...
00408734                  ; -----------------------------------------------------------
00408734 C6 00 E8              mov byte ptr [eax], 0E8h ; 'F'
00408737 EB 00                jmp short $+2
00408737                  ; -----------------------------------------------------------
00408739 90 90 90 90 90 90+    db 27h dup(90h)
00408760                  ; -----------------------------------------------------------
00408760 40                    inc eax
00408761 E9 70 FF FF FF        jmp near ptr get_reencrypt_call_address
00408761                  ; -----------------------------------------------------------
00408766 90 90 90 90 90 90+store_jump_distance(同上) db 2Ah dup(90h) ; CODE XREF: ...
00408790                  ; -----------------------------------------------------------
00408790 89 38                mov [eax], edi
00408792 E9 B9 FE FF FF        jmp near ptr calculate_block_length(无注释即同上)
00408792                  ; -----------------------------------------------------------
00408797 90 90 90 90 90 90+get_end_pointer db 30h dup(90h) ; CODE XREF: ...
004087C7                  ; -----------------------------------------------------------
004087C7 83 C0 10              add eax, 10h
004087CA E9 38 FF FF FF        jmp near ptr insert_encrypt_call
004087CF                  ; -----------------------------------------------------------
004087CF                  check_end_of_table?: ; CODE XREF: ...
004087CF 0F 85 1C FC FF FF    jnz near ptr get_crypted_offset+2Dh ;如果未结束则循环
004087D5 EB 3C                jmp short _done_ ;
004087D5                  ; -----------------------------------------------------------
004087D7 90 90 90 90 90 90+byte_4087D7 db 34h dup(90h) ; CODE XREF: ...
0040880B                  ; -----------------------------------------------------------
0040880B 83 EF 04              sub edi, 4
0040880E E9 12 FE FF FF        jmp near ptr byte_408625
00408813                  ; -----------------------------------------------------------
00408813                  _done_:            ; bpm here to wait till it finishes
                                                下BPM等待直到完成

这一段很难懂,因为代码FLOW(流程)不是一条直线,我希望通过色彩的标记使大家
能了解清楚一些。算法与先前摘录的代码算法类似,只不过代码块长度计算不同,而且
TABLE ENTRIES在解密方面未于利用。基本上TABLE ENTRIES对于加密代码段的解密
工作是通过40859A段的CRC循环完成的。如果你在4085A6上下断点,就会发现EAX就是
解密的地址。我有两个解决CRC校验的办法,一种是把整个原始代码块复制到另一区域,
然后修改CRC程序,在新的区域完成校验;还有一种就是再次利用SOFTICE断点。我赞成
后一种,因为它简单易行。
一般的,我会让PELOCK LOADER 继续对每个TABLE ENTRIES执行解密,然后在加密代
码块的前后插入适当的调用。但是,要想在4085A6处设断并把解密的地址存入表(TABLE)
中,我们至少得在结束时拥有一张解密表。
下BPM 408813 X  ;整个INSERTION循环结束时的地址断点
BPM 4085A6 X DO “DD ESI+4 EAX:X” ;把EAX中解密值存回表中
                                        ESI指针还原(-4->+4)
当SOFTICE到达最终的断点时,你会在数据窗口中发现新的包含加密代码块地址的解密表。
剩下的工作就是通过插入一些代码解密出最后的表中所有的代码块。
A EIP
mov ecx, 4A; 表大小
load_lodsd; 从表中读取1ENTRY
push dword ptr [eax+5];保存
pushad
call eax; 虚拟原始EXE执行程序
popad
pop dword ptr [eax+5]; 复原代码块属性值
mov word ptr [eax], 7EB;修改(PATCH)Call Decryptor JUMP OVER(跳过)
mov ebx, word ptr [eax+5] ; 读取
ror ebx, 3; 得到代码块长度
add eax, ebx
add eax, 19; 计算Call Encryptor地址
mov word ptr [eax], 7EB; 替换(REPLACE)Call Encryptor JUMP OVER(跳过)
dec ecx; 表大小减1
test ecx, ecx
jne load_; 如果未结束则获取下一个TABLE ENTRY
jmp eip; 大循环, 在此DUMP

接下来,在修改完成另两处INSERTION程序后,工作做完。因此当PELOCK重新运行并
进入大循环后, EXE中所有的加密代码块便完全被解密,并且DECRYPTORS和RE-ENCRY
PTORS调用也通过修改跳过。
5.找到OEP
既然我们已经正确DUMP程序并且解密、修正了所有的代码,接下来的工作就是运行IMPREC
并对IMPORT DIRECTORY进行纠正了。我们的目的当然是找到OEP。看一下DISASSEMBLY
和HEX显示,我们发现第一节(FIRST SECTION)以大量字符串开始,代码紧随其后,所以
猜想OEP一定在字符串的尾部。
另一类一般做法是PELOCK从哪里跳转到OEP。在前述的三对INSERTION程序完成执行后,
答案很快就揭晓了。
00407578 E8 2B 66 00 00        call insert_crypt_engine_3 ; insertion routine call
00407578                  ; -----------------------------------------------------------
0040757D 90 90 90 90 90 90+    db 28h dup(90h); that is responsible for inserting
004075A5                  ; -----------------------------------------------------------
004075A5 E8 0C 73 00 00        call insert_crypt_engine_2 ; those Call Decryptor/Encryptor
004075A5                  ; -----------------------------------------------------------
004075AA 90 90 90 90 90 90+    db 28h dup(90h)  ; 进入EXE主代码段004075D2                  ; -----------------------------------------------------------
004075D2 E8 C5 07 00 00        call unknown_import_stuff_and_insert_crypt_engine_1
004075D2                  ;
-----------------------------------------------------------
004075D7 90 90 90 90 90 90+    db 28h dup(90h); 代码仍然被加密
<-------------省略-------------->; 在到达前最后解密
00407AA1 5D                    pop ebp; OEP.
00407AA1                  ;
-----------------------------------------------------------
00407AA2 90 90 90 90 90 90+    db 124h dup(90h)
00407BC6                  ; -----------------------------------------------------------
00407BC6 68 1E 1D 40 00        push 401D1Eh    ; OEP!!!
00407BC6                  ; -----------------------------------------------------------
00407BCB 90 90 90 90 90 90+    db 23h dup(90h)
00407BEE                  ; -----------------------------------------------------------
00407BEE C3                    retn
因此,当找到任何INSERTION ROUTINE后,你可以检查堆栈从而发现从哪里调用,然后
下BPM。你应该DUMP LOADER并且看看IDA。尽管在到达OEP之前,会有一些循环和SHE等
给你的进程带来麻烦,但是IDA会帮你解决这一切并最终找到OEP。
6.总结
(省略了一些废话:-))
总的来说,PELOCK是一个很好的壳,破解的过程也充满刺激。假使PELOCK在加壳(PACK
ING)时就已经插入(INSERTING)了DECRYPTOR和ENCRYPTOR的调用,并且删去了包含解
密代码块地址的表的话,我想我们一定很难或者不能确定解密出所有的代码块。现在有
一个想法,那就?

找脱壳机的提供一线索
上传的附件:
2007-5-13 14:43
0
游客
登录 | 注册 方可回帖
返回
//