• 标 题:对PECompact.exe v1.34的手动脱壳 -----俺写的一篇臭文 (10千字)
  • 作 者:ljttt
  • 时 间:2000-7-26 7:16:08
  • 链 接:http://bbs.pediy.com

作者:      ljttt
写作日期:2000-07-25
目标程序:PECompact.exe 35,840  v1.34 脱壳
程序下载:http://www.cnvnet.com/download/d/pecsetup.exe
使用工具:SoftICE 4.00; ProcDump 1.6.2; IceDump 6.0.1.5; HexWorkShop;

(补充一句:以下Import table我称作输入表,可能说法不规范,不过我打习惯了,大家原谅一下)
(注:以下文中数字为十六进制)
分析:
运行ProcDump,点击Pe-Editor按钮,选中PECompact.exe,得到程序内存映象大小:Size of Image为 00031000和基地址Image Base为0040000。然后点击Sections,没有发现.idata这样容易识别的输入表Section,
看来找输入表的地址和大小也将成为重点了。

首先我们来抓取Import table
1、先装载IceDump。到IceDump目录中运行相应SoftICE版本的icedump.exe。(比如我用的SoftICE版本为4.00就到win9x/400目录下运行icedump.exe)
2、运行SoftICE的Loader,加载PECompact.exe。
3、Ctrl-D进行SoftICE,如下设置断点
    bpx loadlibrarya do "dd esp->4"
    (注:在没有明确的输入表地址的情况下,这不失为找输入表地址的一种好办法)
4、按F5回到Windows中,单击Loader中的Load按钮。程序中断在第一条指令处。进入了SoftICE。
    (看来PECompact.exe不象受Asprotect保护的程序那样。无法用SoftICE自动中断在第一条指令处)
5、按F5运行程序。又中断在我们设下的中断Loadlibrarya处。看看数据窗口。显示如下:

0030:004171AE 4E52454B  32334C45  4C4C442E  4C000000      KERNEL32.DLL...L
0030:004171BE 4C64616F  61726269  00417972  65470000      oadLibraryA...Ge
0030:004171CE 6F725074  64644163  73736572  56000000      tProcAddress...V
0030:004171DE 75747269  6C416C61  00636F6C  69560000      irtualAlloc...Vi
0030:004171EE 61757472  6572466C  00000065  74697845      rtualFree...Exit
0030:004171FE 636F7250  00737365  72460000  694C6565      Process...FreeLi
0030:0041720E 72617262  00000079  4D746547  6C75646F      brary...GetModul
0030:0041721E 6E614865  41656C64  47000000  6F4D7465      eHandleA...GetMo
0030:0041722E 656C7564  656C6946  656D614E  9D8B0041      duleFileNameA...
0030:0041723E 004020A6  20AA9D3B  01750040  0CB58BC3      . @.;.. @.u.....

6、根据经验,一般第一次是脱壳代码自己的函数库的加载。并不是我们需要的输入表。因此,按F5继续执行程序。程序又一次中断,这一次数据窗口显示如下:

0030:0040D4A0 4E52454B  32334C45  6C6C642E  49444700      KERNEL32.dll.GDI
0030:0040D4B0 642E3233  55006C6C  33524553  6C642E32      32.dll.USER32.dl
0030:0040D4C0 4441006C  49504156  642E3233  49006C6C      l.ADVAPI32.dll.I
0030:0040D4D0 4547414D  2E504C48  006C6C64  646D6F63      MAGEHLP.dll.comd
0030:0040D4E0 3233676C  6C6C642E  45485300  32334C4C      lg32.dll.SHELL32
0030:0040D4F0 6C6C642E  41434A00  2E31474C  006C6C64      .dll.JCALG1.dll.
0030:0040D500 696C7061  6C642E62  00F4006C  4C746547      aplib.dll...GetL
0030:0040D510 45747361  726F7272  01A70000  65766F4D      astError....Move
0030:0040D520 656C6946  01160041  50746547  41636F72      FileA...GetProcA
0030:0040D530 65726464  00007373  655201CB  6F436461      ddress....ReadCo

可以看到偏移地址为40D4A0,根据我们对输入表的知识,(具体原理说明可以看看雪教学中的"脱壳高级篇"中的文章,那里面比我说得清楚多了)我们可以搜索内存映象中的字节A0,D4,00,00。(即00D4A0=40D4A0-400000)来确定输入表的位置。下指令

s 30:400000 l ffffffff A0,D4,00,00

7、搜索的结果显示

Pattern found at 0030:0040D00C (0000D00C)

我们用如下指令来定位到输入表的起始位置(当然目前还不能确定就是输入表)

dd 0040D00C-C

如下为数据窗口的显示结果。

0030:0040D000 0000D0C8  00000000  00000000  0000D4A0      ................
0030:0040D010 0000D2B4  0000D1A0  00000000  00000000      ................
0030:0040D020 0000D4AD  0000D38C  0000D1CC  00000000      ................
0030:0040D030 00000000  0000D4B7  0000D3B8  0000D260      ............`...

现在我们来看看到底是否我们要找的输入表。下指令
dd 40D0C8

显示结果如下:
0030:0040D0C8 0000D50A  0000D51A  0000D526  0000D538      ........&...8...
0030:0040D0D8 0000D548  0000D556  0000D564  0000D57A      H...V...d...z...
0030:0040D0E8 0000D58C  0000D59E  0000D5B4  0000D5C2      ................
0030:0040D0F8 0000D5D2  0000D5DE  0000D5F4  0000D5FE      ................

继续追踪,下指令
db 40D50A

显示结果如下:
0030:0040D50A F4 00 47 65 74 4C 61 73-74 45 72 72 6F 72 00 00  ..GetLastError..
0030:0040D51A A7 01 4D 6F 76 65 46 69-6C 65 41 00 16 01 47 65  ..MoveFileA...Ge
0030:0040D52A 74 50 72 6F 63 41 64 64-72 65 73 73 00 00 CB 01  tProcAddress....
0030:0040D53A 52 65 61 64 43 6F 6E 73-6F 6C 65 41 00 00 31 00  ReadConsoleA..1.

(以上说这么多,只是为了说明找输入表的方法的具体实现过程,原理在各位高手的"脱壳高级篇"已经说得很详细了)
OK,符合输入表的规范。看来这就是我们要找的输入表了。我们还要确定一下输入表的长度,在数据窗口中向下翻页。直到发现大量的0x00字节。确定一下长度为C40字节。(这次就不Dump窗口看了,写到这里我开始怀疑我这篇又臭又长的文章不知道要写多长,大家还没有看着看着睡着了吧 :-) )

现在我们来保存输入表,下指令
PAGEIN D 40D000 C40 C:\TEMP\DUMP.BIN

现在再来确定程序真正的Entry Point

1、这里我为了省事,干脆
BD *
禁止所有中断,然后按F5运行程序,重新在SoftICE的Loader中单击Load按钮运行程序,(当然这种方法在对PECompact.exe的脱壳中可以用,其他的就不一定了)又中断在第一条指令。窗口显示如下:
015F:0041E800  EB06                JMP      0041E808                (JUMP )
015F:0041E802  6800E00100          PUSH      0001E000
015F:0041E807  C3                  RET
==> 0041E808  9C                  PUSHFD
015F:0041E809  60                  PUSHAD
015F:0041E80A  E802000000          CALL      0041E811  (注意了,这里可不是什么过程调用,只是JMP的变形而已)
015F:0041E80F  33C0                XOR      EAX,EAX
015F:0041E811  8BC4                MOV      EAX,ESP
015F:0041E813  83C004              ADD      EAX,04

注意,在Call 0041E811语句处我们要按F8键继续,不然就Over了。这里的Call语句可不是什么真正的过程调用。无非是变相的一种Jmp跳转语句而已。要识别它也不难。看看它跳转的地址是否就在附近。如果是就小心了。按F8键不要按F10键。
好了,转过了一道小小的弯,一路F10下来,还顺风顺水。直到如下
015F:0041E860  5F                  POP      EDI
015F:0041E861  8DB570804000        LEA      ESI,[EBP+00408070]
015F:0041E867  B99F030000          MOV      ECX,0000039F
015F:0041E86C  F3A5                REPZ MOVSD
015F:0041E86E  5F                  POP      EDI
015F:0041E86F  C3                  RET
到了第一个ret。看!它要跳转了。又一段旅程开始。按F8继续,这一段就要小心一点,直到。
015F:00420121  8BB53E854000        MOV      ESI,[EBP+0040853E]
015F:00420127  8BBD42854000        MOV      EDI,[EBP+00408542]
015F:0042012D  E82E0A0000          CALL      00420B60
015F:00420132  61                  POPAD
015F:00420133  9D                  POPFD
015F:00420134  50                  PUSH      EAX
015F:00420135  6800E04100          PUSH      0041E000
015F:0042013A  C20400              RET      0004
又要跳转到41E000了。瞧这里,又要小心了。不要在Call 0041E009语句处按F10哦
015F:0041E000  9C                  PUSHFD
015F:0041E001  60                  PUSHAD
015F:0041E002  E802000000          CALL      0041E009
015F:0041E007  33C0                XOR      EAX,EAX
015F:0041E009  8BC4                MOV      EAX,ESP
015F:0041E00B  83C004              ADD      EAX,04
一路下来到
015F:0041E092  FFD0                CALL      EAX
015F:0041E094  FFA59A214000        JMP      [EBP+0040219A]
015F:0041E09A  61                  POPAD
015F:0041E09B  9D                  POPFD
015F:0041E09C  6800704100          PUSH      00417000
015F:0041E0A1  C3                  RET
又要跳到417000了。这里又要小心Call 00417009语句了。
015F:00417000  9C                  PUSHFD
015F:00417001  60                  PUSHAD
015F:00417002  E802000000          CALL      00417009
015F:00417007  33C0                XOR      EAX,EAX
015F:00417009  8BC4                MOV      EAX,ESP
015F:0041700B  83C004              ADD      EAX,04
一路下来,终于到终点站了。万岁!
015F:00417090  6A00                PUSH      00
015F:00417092  FFD0                CALL      EAX
015F:00417094  FFA59A214000        JMP      [EBP+0040219A]
015F:0041709A  61                  POPAD
015F:0041709B  9D                  POPFD
015F:0041709C  6800104000          PUSH      00401000
015F:004170A1  C3                  RET
程序真正的入口到了。在ret指令处我们下指令:
PAGEIN D 400000 31000 C:\TEMP\DUMP.EXE

第三步。开始我们的后期修补工作。(Zzzzzz.....Zzzzzz......什么,大家都睡着了,没办法我总不能就此结束吧。咬牙继续写下去。)

1、启动HexWorkShop,打开Dump.exe。用菜单中的Go命令定位到0000D000处。用Select Block命令选择C40个字节,按Del键删除。

然后打到Dump.bin文件,用菜单中的Select Block命令选择C40个字节(用Select All一样)。用Copy命令复制到剪贴板。切换到Dump.exe窗口用菜单中的Paste命令粘贴过来。(注意当前位置是从D000开始的,不要贴错位置哦)

2、启动ProcDump。单击PE Editor打开Dump.exe文件。然后单击Sections,把每个Section中
PSize改成和VSize同样大小,Offset改成和RVA同样大小。按OK确定。

3、然后修改Entry Point为00001000。单击Directory按钮修改其中的Import Table的RVA为0000D000,Size为00000C40,然后保存。

4、该收工吧。看看成果。运行Dump.exe。屏幕显示 "找不到所需的.Dll文件 - JCALG1.DLL"不存在。 (险些晕倒)。
5、再看看,回到HexWorkShop窗口。用菜单中的Go命令定位到00D000处,找到如下位置:
0000D000 0000D0C8  00000000  00000000  0000D4A0      ................
0000D010 0000D2B4  0000D1A0  00000000  00000000      ................
0000D020 0000D4AD  0000D38C  0000D1CC  00000000      ................
0000D030 00000000  0000D4B7  0000D3B8  0000D260      ............`...
0000D040 00000000  00000000  0000D4C2  0000D44C      ............L...
0000D050 0000D284  00000000  00000000  0000D4CF      ................
0000D060 0000D470  0000D28C  00000000  00000000      p...............
0000D070 0000D4DC  0000D478  0000D294  00000000      ....x...........
0000D080 00000000  0000D4E9  0000D480  0000D2A4      ................
0000D090 00000000  00000000  0000D4F5  0000D490      ................
0000D0A0 0000D2AC  00000000  00000000  0000D500      ................
0000D0B0 0000D498  00000000  00000000  00000000      ................
0000D0C0 00000000  00000000  0000D50A  0000D51A      ................
0000D0D0 0000D526  0000D538  0000D548  0000D556      &...8...H...V...
6、把从0000D08C开始到0000D0B2之间的字节全部改为00。保存。为什么这样改?因为
这之间的字节分成两段,分别对应着输入表中对JCALG1.DLL和aplib.dll的引入。
0000D2A4  00000000  00000000  0000D4F5  0000D490
0000D2AC  00000000  00000000  0000D500  0000D498

7、运行,Ok!

附:
我写了个ProcDump的Script如下,可以省略大部分步骤。现在简要说明一下。
运行ProcDump。用Script脱壳出文件Dump.exe,然后按修补工作中的第3-5步修改即可。不过第5步时定位到0000C600字节处。因为ProcDump脱壳出的Dump.exe文件头只有600个字节,而内存中文件头要占1000个字节。所以可以得到VA=D000-1000-600=C600字节。

[5'My ONE PECompact v1.34]
L1=OBJR
L2=LOOK 60
L3=BP
L4=OBJR
L5=LOOK C3
L6=BP
L7=WALK
L8=OBJR
L9=LOOK EB,14
LA=BP
LB=WALK
LC=OBJR
LD=LOOK C2,04,00
LE=BP
LF=WALK
L10=OBJR
L11=LOOK C3
L12=BP
L13=WALK
L14=OBJR
L15=LOOK 68,??,??,??,??,C3
L16=BP
L17=STEP
OPTL1=00000000
OPTL2=01010001
OPTL3=01010001
OPTL4=00030000
OPTL5=00000000

脱壳时间没花多少时间,写这篇又臭又长的文章花了俺3个多小时。俺文字功底不好。该文章的结构参照看雪兄的"脱壳高级篇"中一文。俺实在写累了。睡觉去罗!

最后,还有一个问题问问各位,TRW中如何保存当前屏幕?(写作 用)还请赐教。

  • 标 题:另一种办法 (258字)
  • 作 者:冰毒
  • 时 间:2000-7-27 4:05:45

好长时间没来,今天上来看到帖子,以为1.34比1.32有了什么改变. 抓一个一试,还是老样子: 即在Push 417000下的RET处Prodump,而不是一般以为的Push 401000下的RET处(或者说最后一层壳是不用脱的). Dump后只需改动入口值即可. 运行正常,也可用W32dasm反汇编. 至少在我的机子上是这样.