Lock98是国内第一套基于Windows
32位操作系统的软件加密工具,
自称可以:
可对抗任何静态分析
可对抗一切拷贝机
对抗所有的脱壳工具及拷贝工具
太吹牛了吧,不太相信,就拿它开刀。
--------------------------------------------------------------
说得这么好,也想来一份试一试。于是从他的主页上下载了一份。接下来就开始安装,在安装过程中,任意
输入了一些信息,程序开始复制文件。但很快提示“磁盘密钥信息校验失败”。
来到安装文件夹中,只有几个文件:
DEFAULT CFG
HDINST32 EXE
LOCK98
INI
LOCK98 HLP
没有主程序文件。
第一步,想办法得到主程序文件。
程序在安装中会提示“磁盘密钥信息校验失败”,很有可能是在校验失败后将主程序文件删除了,
再来一次,启动TRW2000,当安装程序复制文件完成,还没有提示出错时,按 CTRL+N 断下,按几次
F12回到程序领空,下命令 SUSPEND
让进程挂起。
回到安装文件夹中,果然看见了主程序lock98.exe ,马上把它复制一份。再按CTRL+N ,让安装程序继续,等
安装程序完成,lock98.exe 果然被删除了,不过我要的已经有了。运行 lock98.exe ,发现它也是用磁盘加
密的,没有加密盘,程序没法运行。
第二步,处理 lock98.exe 脱去它的磁盘加密的壳
运行lock98.exe后,提示 "加密盘错误",程序无法运行.
用peditor打开lock98.exe,它的 sections 如下:
CODE
DATA
BSS
.idata
.tls
.rdata
.reloc
.rsrc
_LOCK98_ <---看样子这就是通过LOCK98.EXE加密后增加的节,
查看它的引入表,各种引入的DLL及引入API都在,初步判断程序在加密过程中没有对引入表做手脚.
使用TRW2000进行跟踪,在加壳程序中,果然有很多的花指令,动态分析比较麻烦.
为了验证一下它有没有对代码段(即CODE段)进行加密,我找到代码段的一个地址,
对其下断点 BMP .....
运行后程序被断在_LOCK98_中的某处,它在一个循环中不断的对代码段的数据进行还原.可见,程序对原代码段进行的处理,代码段还原后,运行一段时间后开始读加密盘.因为没有加密盘,很快就提示出错.
于时,我就大胆猜想,即然程序没有对引入表做手脚,在读加密盘之前又对代码进行了还原.那么只要在提示出错时,将程序DUMP下来,找到原程序的入口就可以了.
所以马上用 peditor 将程序DUMP下来,保存在磁盘中,接下来就开始想办法找程序入口点.
在DUMP下来的程序中,使用peditor查看其引入表,发现其引入表已经在加载过程中已经被破坏(因为这是一个Borland
Delphi 3.0 编译的程序。TLINK32 所产生的 PE 对引入表中的Characteristics [也就是 hint-name array ]总是
0 ,而使 FirstThunk 指向引入函数的名称,而其它编译器会使它指向引入函数的名称。FirstThunk 在程序加载时,会被函数的实际地址填充而造成引入表被破坏),所以接下来就想办法修正引入表。
在原程序中,引入表的DLL和API都很完整,所以首先想到用原程序中的引入表,把它提取出来,放到DUMP下来的程序中。经查在原程序中,引表入在文件中的偏移在5E000处。
Characteristics
指向DLL名,在文件中实际为5E728
__________
__________
| |
|
|
0005E000 00 00 00 00 00 00 00 00 00 00 00 00
28 C7 06 00 ............(?.
FirstThunk 指向引入函数名的指针数组,在文件中实际指向5e140
__________
|
|
0005E010 40 C1 06 00 00 00 00 00 00 00 00 00 00 00 00
00 @?.............
0005E020 80 C9 06 00 D0 C1 06 00 00
00 00 00 00 00 00 00 ?.辛..........
0005E030 00 00 00 00 BA C9
06 00 E0 C1 06 00 00 00 00 00 ....荷..嗔......
0005E040 00
00 00 00 00 00 00 00 FA C9 06 00 F0 C1 06 00 ..........鹆..
...............
指向引入函数名
__________
|
|
0005E140 36 C7 06 00 4E C7 06 00 66 C7 06 00
7E C7 06 00 6?.N?.f?.~?.
0005E150 9A C7 06 00 A8 C7 06 00
B8 C7 06 00 C4 C7 06 00 毲..ㄇ..盖..那..
0005E160 D2 C7 06 00 E2
C7 06 00 F8 C7 06 00 0E C8 06 00 仪..馇.....?.
而在DUMP下来的程序中,引入表在文件中的偏移为6C000处,其它的信息都很完整,但其FirstThunk在加载时被破坏(都是Borland的小错误造成的,让我多花了很多时间):
0006C000 00 00 00 00 21 AF 32 37 00 00 F7 BF 28 C7
06 00 ....!?7..骺(?.
0006C010 40 C1 06 00 00 00 00 00 72
DA 38 37 00 00 F5 BF @?.....r?7..蹩
0006C020 80 C9 06 00 D0 C1
06 00 00 00 00 00 72 DA 38 37 ?.辛......r?7
0006C030 00
00 E8 BF BA C9 06 00 E0 C1 06 00 00 00 00 00 ..杩荷..嗔......
0006C040
76 26 48 39 00 00 E8 7F FA C9 06 00 F0 C1 06 00 v&H9..?..鹆..
0006C050 00 00 00 00 21 AF 32 37 00 00 F7 BF 8A CA 06 00
....!?7..骺娛..
0006C060 10 C2 06 00 00 00 00 00 72 DA 38 37 00
00 E8 BF .?.....r?7..杩
0006C070 EC CA 06 00 28 C2 06 00
00 00 00 00 21 AF 32 37 焓..(?.....!?7
0006C080 00 00 F7 BF 5C
CB 06 00 44 C2 06 00 00 00 00 00 ..骺\?.D?.....
0006C090
72 DA 38 37 00 00 E7 BF 78 CF 06 00 34 C3 06 00 r?7..缈x?.4?.
0006C0A0 00 00 00 00 38 74 5A 35 00 00 F2 BF C6 CF 06 00 ....8tZ5..蚩葡..
0006C0B0 44 C3 06 00 00 00 00 00 72 DA 38 37 00 00 F5 BF
D?.....r?7..蹩
0006C0C0 C0 D4 06 00 6C C4 06 00 00 00 00 00 CF
40 A0 3B 涝..l?.....螥?
0006C0D0 00 00 B7 BF 9E DD 06 00
A0 C6 06 00 00 00 00 00 ..房炤..犉......
0006C0E0 75 DA 38 37 00
00 E1 7F C8 DF 06 00 04 C7 06 00 u?7..?冗...?.
0006C0F0
00 00 00 00 75 DA 38 37 00 00 CB 7F FE DF 06 00 ....u?7..?..
0006C100 10 C7 06 00 00 00 00 00 21 AF 32 37 00 00 F7 BF .?.....!?7..骺
0006C110 1A E0 06 00 18 C7 06 00 00 00 00 00 73 DA 38 37
.?..?.....s?7
0006C120 00 00 DE BF 34 E0 06 00 20 C7 06 00 00
00 00 00 ..蘅4?. ?.....
0006C130 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 ................
本来就是指向引入函数名的,现在已经变为实际地址。
_________
| |
0006C140 06 AF F8 BF 82
BA F7 BF 5D BA F7 BF 8F 45 F8 BF .總瑚縘瑚繌E
0006C150 B6
44 F8 BF 5C 02 F8 BF 50 4A F7 BF 34 49 F7 BF 禗\.PJ骺4I骺
0006C160
C6 0E FA BF 1F 7E F7 BF 01 7E F7 BF 29 74 F7 BF ?.~骺.~骺)t骺
0006C170
78 73 F7 BF 54 DA 08 C0 15 F3 F9 BF 2D 78 F7 BF xs骺T??簌?x骺
我就将原程序中的引入表从 5E000 大小为 204E 的引入表,覆盖到DUMP下来的程序的6C000处,再使用peditor查看时,引入表已经显示正常。
找入口点前,先把DUMP下来的程序处理一下,使用 peditor 打开程序,查看程序的sections,看到 _LOCK98_ 这一个节在文件中的偏移在C7000开始,于是,先将节的总数由9改为8,用WINHEX打开,将C7000处开始的无用的数据删除。
找入口点真是件麻烦的事,没有加密盘,要分析它的的运行过程找入口点比较烦,于是就想到了另一种方法。
在很多的程序中,程序的开始处的代码都非常类似:如VC++编译的程序,
第一条语句一般是
PUSH EBP
很快就是一个KERNEL32的函数的调用
GetVersion
这是一个用 Borland Delphi 编译的程序,它也一定有类似的代码。于是找了一个其它的 Delphi 编译的程序,发现它的码代调用的特点是:
第一句 PUSH EBP
而且入口点基本是在代码段的结束的地方。
于是我先将 DUMP下来的程序用peditor 改入口点为 1000 ,再用 w32dasm 反编译,在结束处有以下代码:
:0045DB04 98384500 DWORD 00453898
:0045DB08 98564500 DWORD 00455698
:0045DB0C 60564500 DWORD 00455660
:0045DB10 306C4500 DWORD 00456C30
:0045DB14 006C4500 DWORD 00456C00
:0045DB18 B46F4500 DWORD 00456FB4
:0045DB1C 846F4500 DWORD 00456F84
:0045DB20 5C854500 DWORD 0045855C
:0045DB24 2C854500 DWORD 0045852C
:0045DB28 00000000 BYTE 4 DUP(0)
:0045DB2C 30D9 xor cl, bl
:0045DB2E 45 inc ebp
:0045DB2F 00 BYTE 0
:0045DB30 55 push ebp 〈---------找到一个 push ebp 的代码
:0045DB31 8BEC mov ebp, esp
:0045DB33 83C4F4 add esp, FFFFFFF4
:0045DB36 B858D94500 mov eax, 0045D958
:0045DB3B E8A87BFAFF call 004056E8
:0045DB40 A1A4ED4500 mov eax, dword ptr [0045EDA4]
:0045DB45 8B00 mov eax, dword ptr [eax]
:0045DB47 E8DC0FFDFF call 0042EB28
:0045DB4C A1A4ED4500 mov eax, dword ptr [0045EDA4]
:0045DB51 8B00 mov eax, dword ptr [eax]
:0045DB53 83C034 add eax, 00000034
* Possible StringData Ref from Code Obj ->"Lock98.hlp"
|
:0045DB56 BAB8DB4500 mov edx, 0045DBB8
:0045DB5B E8185EFAFF call 00403978
:0045DB60 8B0D30ED4500 mov ecx, dword ptr [0045ED30]
:0045DB66 A1A4ED4500 mov eax, dword ptr [0045EDA4]
:0045DB6B 8B00 mov eax, dword ptr [eax]
* Possible StringData Ref from Code Obj ->"勎@"
|
:0045DB6D 8B15BC6F4500 mov edx, dword ptr [00456FBC]
:0045DB73 E8C80FFDFF call 0042EB40
:0045DB78 8B0D5CEE4500 mov ecx, dword ptr [0045EE5C]
:0045DB7E A1A4ED4500 mov eax, dword ptr [0045EDA4]
:0045DB83 8B00 mov eax, dword ptr [eax]
* Possible StringData Ref from Code Obj ->"勎@"
|
:0045DB85 8B1508364500 mov edx, dword ptr [00453608]
:0045DB8B E8B00FFDFF call 0042EB40
:0045DB90 A15CEE4500 mov eax, dword ptr [0045EE5C]
:0045DB95 8B00 mov eax, dword ptr [eax]
:0045DB97 E880ECFCFF call 0042C81C
:0045DB9C A1A4ED4500 mov eax, dword ptr [0045EDA4]
:0045DBA1 8B00 mov eax, dword ptr [eax]
:0045DBA3 E82410FDFF call 0042EBCC
:0045DBA8 E8475CFAFF call 004037F4
:0045DBAD 000000 BYTE 3 DUP(0)
:0045DBB0 FFFFFFFF BYTE 4 DUP(0ffh)
:0045DBB4 0A00 or al, byte ptr [eax]
:0045DBB6 0000 add byte ptr [eax], al
:0045DBB8 4C dec esp
:0045DBB9 6F outsd
:0045DBBA 636B39 arpl dword ptr [ebx+39], ebp
:0045DBBD 382E cmp byte ptr [esi], ch
:0045DBBF 686C700000 push 0000706C 〈--------代码结束处
从代码结束处往上找,很快就找到一个 push ebp的指令,指令的地址是:0045DB30
去除基址是:5db30 就先试试它吧。用 peditor 再次打开 DUMP 下来的程序,填写入口点为 5db30
试运行,竟然一次运行成功,哈哈,程序就这样被脱壳了,运气不错。
一个吹大牛的东东就这样搞定了。
-------------------------------------------------------------
从这一次脱壳过程中,我的体会是:
lock98 的加密有很多地方做得不好,
1.在读加密盘以前就将所有的代码段进行的还原。
2.没有对原程序的引入表进行加密
3.没有对内存DUMP进行防范。
还有,找入口点还可以从某种编译程序的特点入手,从它的入口代码的特点去多次尝试,可能会有很大的收获。