【破文标题】diablo2oo2's Crackme 1 分析 v1.2
【对 象】和我一样菜的新手 :)
【破解工具】flyODBG + PEiD
【保护方式】序列号
【任 务】分析算法 + 写出注册机
【作 者】David
【破解声明】初学破解,发现入门的破文很少,所以写此篇与大家分享.
【邮 箱】DavidNes [AT] GMail.com
【Blog网址】http://www.5DBlog.com/user1/Davidz
【破解过程】
在网上找呀找,终于找到个给我点自信心的Crackme.它是dUP的作者写的,总共有8个,今天这个是最简单的1. ^-^
1.检查文件
打开PEiD检查文件,结果显示MASM32 / TASM32.说明文件未加壳,且用汇编语言编写. :)
2.载入到flyODBG
打开flyODBG,加载后,我们停在了这里:
00401000 > 6A 00 push 0 <----------- HoHo,Here,Here
00401002 E8 35040000 call <jmp.&KERNEL32.GetModuleHandleA>
00401007 A3 30314000 mov dword ptr ds:[403130],eax
0040100C 6A 0A push 0A
0040100E 6A 00 push 0
00401010 6A 00 push 0
..............................
是不是看不出什么呢? 没关系,现在我们通过下断来获取我们所需的东西.点击"插件"菜单,选择进入"命令行"插件.如果是英文则是Plugin -->
Commandline.由于我们的目的是分析算法,所以这里设断点: bp GetDlgItemTextA,此函数的作用是获取文本框的内容.
现在,我们运行文件(F9),随便输入Name和Serial.(注:之前如出现暂停,可用Shift + F9略过,不影响结果).按下"Try"键,程序遇到断点,停在了
77D3274F这个地址.看看flyODBG的窗体标题,得知我们处在USER32的领空.点击"调试"-->"执行到用户代码",然后我们回到了这里:
00401246 33C0 xor eax,eax //eax=0
00401248 6A 28 push 28
0040124A 68 8C314000 push d2k2_cra.0040318C
0040124F 6A 02 push 2
00401251 FF75 08 push dword ptr ss:[ebp+8]
00401254 E8 8F010000 call <jmp.&USER32.GetDlgItemTextA> //调用GetDlgItemTextA函数,获取输入的Name
00401259 84C0 test al,al <---- 我们在这儿呢 :) //al为Name的长度,单位Byte
0040125B 0F84 06010000 je d2k2_cra.00401367 //为0则跳,MessageBoxA --> "Enter Name!"
00401261 3C 20 cmp al,20
00401263 0F8F 13010000 jg d2k2_cra.0040137C //大于20H则跳,MessageBoxA --> "Name can be max 32 Chars long!"
00401269 3C 05 cmp al,5
0040126B 0F8C 20010000 jl d2k2_cra.00401391 //小于5H则跳,MessageBoxA --> "Name must be min 5 Chars long!"
..............................
看看我们所停地址的上面,是一个完整的GetDlgItemTextA调用. 分析0040124A这一行我们知道,我们输入的Name被保存在了0040318C
这个地址.后面的几行代码请看相关的注释,均为判断Name是否合法的语句.我们继续往下看:
00401271 8D1D 8C314000 lea ebx,dword ptr ds:[40318C] //ebx=40318C,这个地址就是储存Name的地址
00401277 33C9 xor ecx,ecx //ecx=0
00401279 B0 05 mov al,5 //al=5
0040127B 33D2 xor edx,edx //edx=0
0040127D 8A0C1A mov cl,byte ptr ds:[edx+ebx] //依次取Name的前5位至cl,5位之后不参与计算
00401280 80F1 29 xor cl,29 //cl=cl xor 29
00401283 02C8 add cl,al //cl=cl+al
00401285 80F9 41 cmp cl,41
00401288 7C 1C jl short d2k2_cra.004012A6 //cl小于41则跳到004012A6
0040128A 80F9 5A cmp cl,5A
0040128D 7F 17 jg short d2k2_cra.004012A6 //cl大于5A则跳到004012A6 (If cl < 41 or cl > 5A Then jmp short d2k2_cra.004012A6)
0040128F 888A 3C314000 mov byte ptr ds:[edx+40313C],cl //byte ptr ds:[edx+40313C]=cl 注:这里依次将运算后的注册码存入40313C-403140
00401295 C682 3D314000 mov byte ptr ds:[edx+40313D],0 //byte ptr ds:[edx+40313D]=0 的空间中
0040129C FEC2 inc dl //dl=dl+1
0040129E FEC8 dec al //al=al-1
004012A0 3C 00 cmp al,0
004012A2 74 08 je short d2k2_cra.004012AC //al等于0则跳到004012AC
004012A4 ^ EB D7 jmp short d2k2_cra.0040127D //跳回0040127D
004012A6 B1 52 mov cl,52 //cl=52
004012A8 02C8 add cl,al //cl=cl+al
004012AA ^ EB E3 jmp short d2k2_cra.0040128F //跳回0040128F
.............................
上面这段代码我们可以看出程序算出了前5位注册码,并将其存入40313C-403140这部分空间中.还可以发现注册码只与Name的前5位有关(PS:怪不得最小要求5位).
用VB语言表达上面的算法如下:
//Name(0 To 4)表示Name的前5位;Serial()表示计算出的注册码
For al = 5 To 1 Step -1
cl = Name(5 - al) //依次取Name的前5位至cl
cl = cl Xor &H29
cl = cl + al
If (cl < &H41) Or (cl > &H5A) Then
cl = &H52
cl = cl + al
End If
Serial(5 - al) = cl
Next al
继续跟下去:
004012AC 33D2 xor edx,edx //edx=0
004012AE B8 05000000 mov eax,5 //eax=5
004012B3 8A0C1A mov cl,byte ptr ds:[edx+ebx] //依次取Name的前5位至cl,5位之后不参与计算
004012B6 80F1 27 xor cl,27 //cl=cl xor 27
004012B9 02C8 add cl,al //cl=cl+al
004012BB 80C1 01 add cl,1 //cl=cl+1
004012BE 80F9 41 cmp cl,41
004012C1 7C 1C jl short d2k2_cra.004012DF //cl小于41则跳到004012DF
004012C3 80F9 5A cmp cl,5A
004012C6 7F 17 jg short d2k2_cra.004012DF //cl大于5A则跳到004012DF (If cl < 41H or cl > 5AH then jmp short d2k2_cra.004012DF)
004012C8 888A 41314000 mov byte ptr ds:[edx+403141],cl //byte ptr ds:[edx+403141]=cl 注:这里依次将运算后的注册码存入403141-403145
004012CE C682 42314000 mov byte ptr ds:[edx+403142],0 //byte ptr ds:[edx+403142]=0 的空间中
004012D5 FEC2 inc dl //dl=dl+1
004012D7 FEC8 dec al //al=al-1
004012D9 3C 00 cmp al,0
004012DB 74 08 je short d2k2_cra.004012E5 //al等于0则跳到004012E5
004012DD ^ EB D4 jmp short d2k2_cra.004012B3 //跳回004012B3
004012DF B1 4D mov cl,4D //cl=4D
004012E1 02C8 add cl,al //cl=cl+al
004012E3 ^ EB E3 jmp short d2k2_cra.004012C8 //跳回004012C8
.............................
上面这段代码是程序根据Name的前5位经不同的运算又算出了后5位的注册码,并存入了403141-403145这部分空间中.
用VB语言表达上面的算法如下:
//Name(0 To 4)表示Name前5位;Serial(0 To 9)表示计算出的注册码
For al = 5 To 1 Step -1
cl = Name(5 - al)
cl = cl Xor &H27
cl = cl + al + 1
If (cl < &H41) Or (cl > &H5A) Then
cl = &H4D
cl = cl + al
End If
Serial(5+(5 - al)) = cl
Next al
让我们继续往下看:
004012E5 33C0 xor eax,eax //eax=0
004012E7 6A 28 push 28
004012E9 68 B4314000 push d2k2_cra.004031B4 //这里可以看出程序把输入的Serial存入004031B4这里的空间中
004012EE 6A 04 push 4
004012F0 FF75 08 push dword ptr ss:[ebp+8]
004012F3 E8 F0000000 call <jmp.&USER32.GetDlgItemTextA> //调用GetDlgItemTextA函数,获得输入的Serial
004012F8 66:85C0 test ax,ax //ax为Serial长度,单位Byte
004012FB 74 55 je short d2k2_cra.00401352 //ax为零则跳,MessageBoxA --> "Wrong Code!Try Again!"
004012FD 66:83F8 0A cmp ax,0A
00401301 7F 4F jg short d2k2_cra.00401352 //ax不为0A则跳,MessageBoxA --> "Wrong Code!Try Again!".说明Serial必须是10位.
00401303 7C 4D jl short d2k2_cra.00401352 //(If ax <> 0AH Then jmp short d2k2_cra.00401352)
00401305 33C0 xor eax,eax //eax=0
00401307 33DB xor ebx,ebx //ebx=0
00401309 33C9 xor ecx,ecx //ecx=0
0040130B 33D2 xor edx,edx //edx=0
0040130D 8D05 B4314000 lea eax,dword ptr ds:[4031B4] //eax=4013B4
00401313 8A1C01 mov bl,byte ptr ds:[ecx+eax] //依次取Serial到bl
00401316 8A91 3C314000 mov dl,byte ptr ds:[ecx+40313C] //依次取byte ptr ds:[ecx+40313C]到dl,即刚才算出来的10位Serial(注:这里循环取10次)
0040131C 80FB 00 cmp bl,0
0040131F 0F84 81000000 je d2k2_cra.004013A6 //bl为0则跳,MessageBoxA --> "Serial is correct!..."
00401325 80C2 05 add dl,5 //dl=dl+5
00401328 80FA 5A cmp dl,5A
0040132B 7F 14 jg short d2k2_cra.00401341 //dl大于5A则跳到00401341
0040132D 80F2 0C xor dl,0C //dl=dl XOR 0CH
00401330 80FA 41 cmp dl,41
00401333 7C 11 jl short d2k2_cra.00401346 //dl小于41则跳到00401346
00401335 80FA 5A cmp dl,5A
00401338 7F 12 jg short d2k2_cra.0040134C //dl大于5A则跳到0040134C
0040133A 41 inc ecx //ecx=ecx + 1
0040133B 38DA cmp dl,bl
0040133D ^ 74 D4 je short d2k2_cra.00401313 //若dl=bl,则比较下一位
0040133F EB 11 jmp short d2k2_cra.00401352 //不相等则跳,MessageBoxA --> "Wrong Code!Try Again!"
00401341 80EA 0D sub dl,0D //dl=dl-0D
00401344 ^ EB E7 jmp short d2k2_cra.0040132D //跳回0040132D
00401346 B2 4B mov dl,4B //dl=4B
00401348 02D1 add dl,cl //dl=dl+cl
0040134A ^ EB EE jmp short d2k2_cra.0040133A //跳回0040133A
0040134C B2 4B mov dl,4B //dl=4B
0040134E 2AD1 sub dl,cl //dl=dl-cl
00401350 ^ EB E8 jmp short d2k2_cra.0040133A //跳回0040133A
..............................
这段代码相当于对上面算出来的Serial做一番修正后,与输入的Serial进行比较.可以看到0040133B这条是比较语句,因此这里的dl就相当于正确的Serial了,相关的计算也到此结束.它的计算用VB代码表示就是下面这样:
//Name(0 To 4)表示Name前5位;Serial(0 To 9)表示计算出的注册码
For ecx = 0 To 9
dl = Serial(ecx) //依次取出已算出的Serial
dl = dl + 5
If dl > &H5A Then dl = dl - &HD
dl = dl Xor &HC
If dl < &H41 Then
dl = &H4B
dl = dl + ecx
Else
If dl > &H5A Then
dl = &H4B
dl = dl - ecx
End If
End If
Serial(ecx) = dl //一番计算后重新赋值
Next ecx
经过这3步,Serial就计算出来了.至于写注册机,那么只要把上面的3段VB代码结合起来就可以写出来啦. ^-^
【F. A. Q.】
问:可以看出第一段00401271-004012AA中al控制了循环次数,但在0040127D mov cl,byte ptr ds:[edx+ebx]处,循环体内没有改变edx或ebx值的
代码,即每次都取Name[0].那edx或ebx值是何时发生了改变?
答:ebx的值未改变,一直为40318C.
edx的值改变注意这里:0040129C FEC2 inc dl //dl=dl+1
dl的改变(8位) ---> dx的改变(16位) ---> edx的改变(32位).
【更 新】
2005-08-20:第一版发布,同时也是第一次发表破文 :)
2005-09-10:v1.1发布,更新了注释.
2005-10-01:v1.2发布,修改了部分文字,增加了FAQ.
【下 载】
大家可以在这里下载到相关的资源(diablo2oo2's Crackme 1 + 注册机及源代码 + 这篇文章):
http://free.ys168.com/?davidz 里的"Crackme相关"栏目
【小 结】
第一次写这样的文章,还请大家多多指教! 继续偶的diablo2oo2's Crackme之路....
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)