大家好!花了足足3个多钟头,终于搞定了这个程序。这个程序的确可以说是crackme中的经典,而且很有趣味,又佩服作者设计它时的用心良苦和精巧构思。相信很多人都可以轻易的crack掉这个东东,但是我是新手,虽然有心做一名cracker,但是学艺不精,因而花了很长的时间才得以解决,实在是惭愧。在此,只是写出我的一点想法和感受,和大家探讨一下,希望各位大虾多多指教!
言归正传,开始看这个程序。程序的check按钮是检查该目录下一个key文件,并显示是否注册成功。按照我平时的习惯,先用W32Dasm查看一番(可惜我用的W32Dasm版本中文支持都不好,:(,不知道哪里有没有好用的版本),发现有如下的字符串:Congratulations!
Mail me (KwazyWebbit@hotmail",呵呵,多少和我们的目标有些关系的,双击看到如下内容:
* Reference
To: GDI32.MoveToEx, Ord:0147h
|
:0040113D
E812070000 Call 00401854
:00401142 FF7518
push [ebp+18]
:00401145 FF7514
push [ebp+14]
:00401148 FF7508
push [ebp+08]
* Reference To: GDI32.LineTo,
Ord:0144h
|
:0040114B E8FE060000
Call 0040184E
:00401150 C9
leave
:00401151 C21400
ret 0014
:00401154 33D2
xor edx, edx
:00401156 B82E522E55
mov eax, 552E522E
:0040115B B9454D414C
mov ecx, 4C414D45
:00401160 33C1
xor eax, ecx
:00401162 0553494854 add eax,
54484953
:00401167 B941205349
mov ecx, 49532041
:0040116C 0BC1
or eax, ecx
:0040116E 2D454B4146
sub eax, 46414B45
:00401173 23D0
and edx, eax
:00401175 81FADEC0AD0B cmp edx, 0BADC0DE
:0040117B 7513
jne 00401190
:0040117D 6A00
push 00000000
* Possible StringData Ref
from Data Obj ->"Success.."
|
:0040117F
6859334000 push 00403359
* Possible StringData Ref from Data Obj ->"Congratulations! Mail me
(KwazyWebbit@hotmail"
->".com) how you did it. Dont forget "
->"to include your keyfile! =]"
|
:00401184 68EC324000
push 004032EC
:00401189 6A00
push 00000000
* Reference To: USER32.MessageBoxA,
Ord:01BBh ;显示一个对话框,告诉你已经成功了!!!!!!!!
|
:0040118B E81C060000
Call 004017AC
* Referenced by a (U)nconditional
or (C)onditional Jump at Address:
|:0040117B(C)
|
:00401190 C3
ret
分析一下,该字符串是用作参数,由系统调用MessageBoxA向你报告你已经成功破解了!然后就是返回。向前看,觉得实在不着边际(我水平有限),于是放弃用W32Dasm静态分析,不得不请出元老级工具SoftIce。在这之前先用File
Monitor跟踪分析一下,
2 20:11:42 Pacme
Open C:\WINDOWS\DESKTOP\KWAZYWEB.BIT NOTFOUND
OPENEXISTING READONLY DENYNONE
发现启动或者按check按钮时,要打开一个名为kwazyweb.bit的文件,于是生成一个空文件,命名为kwazyweb.bit,继续分析
380 20:12:32 Pacme Open
C:\WINDOWS\DESKTOP\KWAZYWEB.BIT SUCCESS OPENEXISTING
READONLY DENYNONE
381 20:12:32
Pacme Read C:\WINDOWS\DESKTOP\KWAZYWEB.BIT
SUCCESS Offset: 0 Length: 1
382 20:12:32 Pacme Close
C:\WINDOWS\DESKTOP\KWAZYWEB.BIT SUCCESS CLOSE_FINAL
程序从文件头读取一个字节,往文件中写入一个字节‘1’,continue、、、
1183 20:13:00 Pacme Open
C:\WINDOWS\DESKTOP\KWAZYWEB.BIT SUCCESS OPENEXISTING
READONLY DENYNONE
1184 20:13:00
Pacme Read C:\WINDOWS\DESKTOP\KWAZYWEB.BIT
SUCCESS Offset: 0 Length: 1
1185 20:13:00 Pacme Read
C:\WINDOWS\DESKTOP\KWAZYWEB.BIT SUCCESS Offset:
1 Length: 49
1186 20:13:00 Pacme
Read C:\WINDOWS\DESKTOP\KWAZYWEB.BIT SUCCESS
Offset: 1 Length: 18
1187 20:13:00
Pacme Close C:\WINDOWS\DESKTOP\KWAZYWEB.BIT
SUCCESS CLOSE_FINAL
发现程序读取文件的次数由原来的一次变到了三次,研究一下,发现第二次读取数据的长度49刚好是'1'的ASCII值,好,这只是猜测。到W32Dasm中证实一下,发现ReadFile调用的次数刚好是3次,而且,第二次读取的长度为eax,就是第一次读取的数值,这不是巧合!!!第三次读的长度为0x12,刚好是十进制18,与File
Monitor分析结果完全吻合。
:004016EA 6848344000
push 00403448
:004016EF 6A01
push 00000001
:004016F1 68FA344000
push 004034FA
:004016F6 FF3544344000
push dword ptr [00403444]
* Reference
To: KERNEL32.ReadFile, Ord:01FDh
|
:004016FC E811010000 Call
00401812 ;读取文件的第一个字节,保存在[004034FA]中
:00401701 0FB605FA344000 movzx eax, byte
ptr [004034FA]
:00401708 85C0
test eax, eax
:0040170A 743B
je 00401747
:0040170C 6A00
push 00000000
:0040170E 6848344000 push 00403448
:00401713 50
push eax
:00401714 6888324000
push 00403288
;i am here,hehe
:00401719 FF3544344000
push dword ptr [00403444]
* Reference To: KERNEL32.ReadFile,
Ord:01FDh
|
:0040171F E8EE000000
Call 00401812
;从第二个字节开始读取数据,长度由eax,即第一个字
;的大小决定
:00401724 E8D7F8FFFF call
00401000 ;计算一个关键数据,一定要进去看看,:)
:00401729 6A00
push 00000000
:0040172B 6848344000
push 00403448
:00401730 6A12
push 00000012
:00401732 68E8344000
push 004034E8
:00401737 FF3544344000
push dword ptr [00403444]
* Reference
To: KERNEL32.ReadFile, Ord:01FDh
|
:0040173D E8D0000000 Call
00401812 ;第三次读取数据,长度为0x12(18)
:00401742 E882F9FFFF call
004010C9 ;关键所在!!!F8进去
在SoftICe中下断点bpx readfile,可以来到上面的位置,除了文件读取ReadFile是否成功的必要检查之外,基本上没有什么跳转,而且call也不多,这使我感到非常幸运。首先F8到00401724处的call
00401000,来到下面的地方:
:00401000 33C0
xor eax, eax
:00401002 33D2
xor edx, edx
:00401004 33C9
xor ecx, ecx
:00401006 8A0DFA344000 mov cl, byte ptr
[004034FA] ;还记得这个值吗,看看上面,就是文件第一个字节的值
:0040100C BE88324000 mov esi,
00403288 ;在上面看看,是第二次读取数据的缓冲区
:00401011 AC
lodsb
;读取缓冲区内容到AL中
:00401012 03D0
add edx, eax
;将AL加到EDX中
:00401014 E2FB
loop 00401011
;不断重复,结果是将第二次所有数据累加起来
:00401016 8815FB344000 mov byte
ptr [004034FB], dl ;放到[004034FB],注意,以后要用的
:0040101C C3
ret
继续跟踪第二个call,即00401742处的call 004010C9,如下:
:004010C9 55
push ebp
:004010CA 8BEC
mov ebp, esp
:004010CC 83C4FC
add esp, FFFFFFFC
*
Possible StringData Ref from Data Obj ->"****************C*......*...****.*.****...*..."
;这里的数据非常有趣,慢慢看吧
->".*.*..**********.*..*....*...*...**.****.*.*.."
->".****.*....*.*******..*.***..*.....*.*..***.**"
->".***.*...****....*X..*****************"
|
:004010CF 6865334000
push 00403365
* Possible StringData
Ref from Data Obj ->"****************C*......*...****.*.****...*..."
->".*.*..**********.*..*....*...*...**.****.*.*.."
->".****.*....*.*******..*.***..*.....*.*..***.**"
->".***.*...****....*X..*****************"
|
:004010D4 68BC314000
push 004031BC
* Reference To: KERNEL32.lstrcpyA,
Ord:02DCh
|
:004010D9 E83A070000
Call 00401818
;简单的拷贝,不必理它。
:004010DE
C70584314000CC314000 mov dword ptr [00403184], 004031CC ;注意一下这个变量,指向上面的图案中的第二行,为了
;方便后面的描述,记为
offsetOfPrettyPicture
:004010E8 E830FFFFFF
call 0040101D
;处理第三次读取的18个数据
:004010ED C645FE00
mov [ebp-02], 00
:004010F1 33C0
xor eax, eax
:004010F3
33C9 xor
ecx, ecx
* Referenced by a (U)nconditional or (C)onditional Jump at
Address:
|:00401128(C)
|
:004010F5 C645FF08
mov [ebp-01], 08
;大循环开始
* Referenced by a (U)nconditional or (C)onditional
Jump at Address:
|:0040111F(C)
|
:004010F9 806DFF02
sub byte ptr [ebp-01], 02
;小循环开始
:004010FD 0FB64DFE
movzx ecx, byte ptr [ebp-02]
:00401101 81C1E8344000
add ecx, 004034E8
:00401107 8A01
mov al, byte ptr [ecx]
:00401109 8A4DFF
mov cl, byte ptr [ebp-01]
:0040110C D2E8
shr al, cl
:0040110E 2403
and al, 03
:00401110
E81EFFFFFF call 00401033
;注册在该call中实现!!!!!!!!!!!!
:00401115 85C0
test eax, eax
:00401117 7411
je 0040112A
;!!!!!!!!这里是关键所在,如果跳转则退出该函数,不
;产生任何变化
:00401119 0FB655FF
movzx edx, byte ptr [ebp-01]
:0040111D 85D2
test edx, edx
:0040111F 75D8
jne 004010F9
;小循环的循环次数为4次,[ebp-01]从8减为0
:00401121 FE45FE
inc [ebp-02]
:00401124
807DFE12 cmp byte ptr [ebp-02],
12
:00401128 75CB
jne 004010F5
;大循环的循环次数为0x12(18),为数据缓冲区的大小
* Referenced by a (U)nconditional
or (C)onditional Jump at Address:
|:00401117(C)
|
:0040112A C9
leave
:0040112B C3
ret
看一看,call也不多,呵呵,真幸运!
首先看看004010E8处的call
0040101D:
:0040101D 8A15FB344000
mov dl, byte ptr [004034FB] ;还记得吧上面提醒你注意的数据,就是第二次数据的和
:00401023 B912000000 mov
ecx, 00000012
:00401028 B8E8344000
mov eax, 004034E8
:0040102D 3010
xor byte ptr [eax], dl
:0040102F 40
inc eax
:00401030 E2FB
loop 0040102D
:00401032 C3
ret
该函数的功能就是把第三次读取的18个数据与前面所得的和进行异或。看到这里,为了避免必要的麻烦,我修改了kwazyweb.bit文件,使得第一个字节为0x01,记住,要用16进制工具编辑,第二个字节为0x00,这样,所得的和为0,任何数和0异或时不变,利用这一点,省去了不少麻烦。后面接着随便输入18个字节。该文件大小一共是20个字节。
好,继续我们的分析。该函数里面有两个byte类型的局部变量,分别是[ebp-01]和[ebp-02] (还有一个word类型的变量[ebp-04],用于保存一个数据,下面会提到的)。有两个循环,大循环是为了处理18个数据,小循环对每一个数据进行4次处理,处理过程是右移6,4,2,0位之后,取出低2位,即若数据为0x12345678,则分别处理0x12,0x34,0x56,0x78。作为参数进入00401110
处的call 00401033。返回值决定是否退出当前函数。(多次试探知道注册在00401110 处的call 00401033里面实现)。让我们一起来看看这个关键的函数到底做了写什么?
:00401033 55
push ebp
:00401034 8BEC
mov ebp, esp
:00401036 83C4F8
add esp, FFFFFFF8
:00401039
8B1584314000 mov edx, dword ptr [00403184]
:0040103F 8955FC
mov dword ptr [ebp-04], edx ;将offsetOfPrettyPicture值保存在局部变量[ebp-04]中
:00401042 0AC0
or al, al
:00401044 7509
jne 0040104F
;AL=0?
:00401046 832D8431400010
sub dword ptr [00403184], 00000010 ;AL=0,offsetOfPrettyPicture减0x10(向上移)
:0040104D EB1F
jmp 0040106E
* Referenced by a (U)nconditional or (C)onditional Jump
at Address:
|:00401044(C)
|
:0040104F 3C01
cmp al, 01
;AL=1?
:00401051 7508
jne 0040105B
:00401053
FF0584314000 inc dword ptr [00403184]
;AL=1,offsetOfPrettyPicture加0x01(向右移)
:00401059
EB13 jmp
0040106E
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401051(C)
|
:0040105B 3C02
cmp al, 02
;AL=2?
:0040105D 7509
jne 00401068
:0040105F 83058431400010
add dword ptr [00403184], 00000010 ;AL=2,offsetOfPrettyPicture加0x10(向下移)
:00401066 EB06
jmp 0040106E
* Referenced by a (U)nconditional or (C)onditional
Jump at Address:
|:0040105D(C)
|
:00401068 FF0D84314000
dec dword ptr [00403184]
;AL=3,offsetOfPrettyPicture减0x01(向左移)
* Referenced by a
(U)nconditional or (C)onditional Jump at Addresses:
|:0040104D(U), :00401059(U),
:00401066(U)
|
:0040106E 8B1584314000
mov edx, dword ptr [00403184]
:00401074 8A02
mov al, byte ptr [edx]
;看offsetOfPrettyPicture处的值
:00401076 3C2A
cmp al, 2A
;为0x2A?'*'
:00401078 7506
jne 00401080
:0040107A 33C0
xor eax, eax ;'*',则返回0,上一级的函数退出,永远没有机会注册
:0040107C C9
leave
:0040107D C3
ret
:0040107E EB33
jmp 004010B3
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401078(C)
|
:00401080 3C58
cmp al, 58
;为0x58?'X'
:00401082 752F
jne 004010B3
:00401084 6A00
push 00000000
;'X',注册成功。在上面的图案中只有一个值是X(Exit)
:00401086 8D1559334000 lea edx,
dword ptr [00403359] ;"Sucess..."
:0040108C
52
push edx
:0040108D 8D15EC324000
lea edx, dword ptr [004032EC] ;"Congratulations"....
:00401093 52
push edx
:00401094 6A00
push 00000000
:00401096 8D15AC174000
lea edx, dword ptr [004017AC]
;[User32.MessageBoxA];在SoftIce中可以看到这个提示
:0040109C FFD2
call edx
;恭喜你,到这里就会显示成功信息!
:0040109E 8D157B324000 lea edx,
dword ptr [0040327B]
:004010A4 52
push edx
:004010A5 FF3520344000
push dword ptr [00403420]
:004010AB 8D15DC174000
lea edx, dword ptr [004017DC]
;[User32.SetWindowTextA];同上
:004010B1 FFD2
call edx
* Referenced
by a (U)nconditional or (C)onditional Jump at Addresses:
|:0040107E(U), :00401082(C)
|
:004010B3 8B1584314000 mov
edx, dword ptr [00403184]
:004010B9
C60243 mov byte
ptr [edx], 43 ;将offsetOfPrettyPicture处的值改为0x43,即'C'
;(Current),代表当前位置
:004010BC 8B55FC
mov edx, dword ptr [ebp-04] ;则将以前的offsetOfPrettyPicture值调出
:004010BF C60220
mov byte ptr [edx], 20 ;将上一个经历过的offsetOfPrettyPicture处值设为
;0x20,即空格,表示已经走过的路
:004010C2 B801000000 mov
eax, 00000001
:004010C7 C9
leave
:004010C8 C3
ret
经过这一番分析之后,才发现作者原来是在教我们玩一个小游戏。大致思路是这样,一共走18次,每次可以走4步(18次大循环和4次小循环),碰到'*'就game
over,其他的就可以continue,直到遇见'X',游戏就算过关了。在图案(事先我只是感到这些数据有些古怪,到了分析清楚算法之后,才发现这实际上是一个迷宫,我们的任务就是正确的从迷宫中闯出来!)里面,有一个唯一的'X',我就想,必须得到达这里才行,但是又不能碰到'*',刚开始试了好久,没想到合适的方法,直到我将数据区全部显示出来(将data区弄大了些),才发现这是一幅多么美妙的图片,到这里我不得不佩服作者的艺术天分!!!我把图片dump了出来,如下:
****************
C*......*...****
.*.****...*....*
.*..**********.*
..*....*...*...*
*.****.*.*...***
*.*....*.*******
..*.***..*.....*
.*..***.**.***.*
...****....*X..*
****************
看到了吗?不就是一个标准的迷宫?从C开始,到达X结束!而且路线已经非常清楚了,顺着'.'走就行了,连岔道都没有,呵呵,从来没有见到如此容易的迷宫,大概是作者对我们劳动的一种慰问吧。下面的就容易了,想大家都很清楚,按照上面的程序分析,'0'代表↑,'1'代表→,'2'代表↓,'3'代表←,看着图片一步步向前进,就可以得到一系列数据:
↓↓↓→ ↓↓↓← ↓↓→→ ↑→↑↑ →→→↑ ↑←←← ↑←↑↑
→→→→ →↓→→
2 2 2 1
2 2 2 3 2 2 1 1 0 1 0 0 1 1 1 0
0 3 3 3 0 3 0 0 1 1 1 1 1 2 1 1
↑→→↓
→→→↓ ↓←←↓ ←←↑← ←↓↓↓ ←↓↓→ →→↑↑ →→→→ ↓↓←←
0 1
1 2 1 1 1 2 2 3 3 2 3 3 0 3
3 2 2 2 3 2 2 1 1 1 0 0 1 1 1 1
2 2 3 3
看看我走的对不对。上面是所谓的4进制数,转换成16进制为
A9 AB A5 10 54 3F 30
55 65 16 56 BE F3 EA E9 50 55 AF ;KWAZYWEB.BIT
成功之后的图案变成了下面的样子:
****************
*
* ****
* **** * *
* ********** *
* * * *
* **** * * ***
* *
* *******
* *** * *
* *** **
*** *
**** *C *
****************
走过的地方都一贫如洗,呵呵
这就是KWAZYWEB.BIT的全部内容。到此为止,基本上已经结束了所有的工作。在原程序中点check,就弹出对话框告诉你注册成功,感觉真是爽啊!学crack就是欣赏这种感觉。不过,还有不足的就是文本框中的还是“Cracked
by : ”,没有把我自己的名字给弄进去,本来想在弄一下的,可是实在太累了,还是算了吧。留给哪位大虾来解决这个不是问题的问题,呵呵、、、
还有一点,我想全部用W32Dasm处理这个问题可能效率更高一些,可是我说过,我学艺不精的,呵呵,要是谁全部用W32Dasm破解,希望能指点一二,不胜感激。这是我的第一篇文章,写的不好,望各位海涵。我现于大学就读,明年毕业,不知要飘向何方、、、我对计算机有着浓厚的兴趣,特别是喜欢编程,只可惜起步晚,加上时间不足,至今也只能算是个初级水平,唉,太差了,不谈。一直以来,很羡慕那些有着高水平的cracker和hacker,也很欣赏成功那一刻的感觉,所以想多学习和了解有关方面的知识,有志同道合者,还望多多交流。
我的email是littlejackyonline@263.net,如果对这篇文章有什么疑问或者赐教,请发email。另外qq:22892341,但是别攻击我啊,:),bye------
---------------- jackyspy
于2001.8.8夜