boonz's Keygenme #1 初学者分析笔记 (头一次发破文)
该Crackme来自pediy crackme 2007
在Crackme 2007中破解作者ainafeng已经给出了大概的破解流程。不过太简要了,作为初学者,我花了3天的功夫重新研究了一下这个crackme,下面是我自己的Crack过程,因为我是新手中的新手,OH NO!,所以只能挑这种最简单的Crame破解。这也是我第一次写破文,请各位多多指点校正,谢谢各位!
和我一样级别的朋友请仔细看注解,如果一点都看不懂请先看王爽著的"汇编语言",我就是看了两天才开始Crack的,欢迎大家一起研究讨论。
这个crackme是明码比较的,现在已经非常稀少了,断一下就可以直接看到真的注册码了,但是对于我而言直接看码只是爆破的水平,而且也没什么意义,研究算法才真的有意义!
不费话了,正式开始了!
使用工具:PEID,OD
先打开Crackme
第一个文本框输入yangchunyu
第二个文本框输入1234567890
点check,提示Hello, Mr. Badboy!
用PEID看无壳,OD载入,串参考,看到Hello, Mr. Badboy!
别急,慢慢向上看,一直到这里,下断,F9运行再次输入,第一个文本框输入yangchunyu,第二个文本框输入1234567890,点check,断下了,然后F8往下走,如下:
00401208 /$ 68 F8DC4000 push 0040DCF8 ; /String = "yangchunyu"
0040120D |. E8 80010000 call <jmp.&kernel32.lstrlenA> ; \lstrlenA
跟进这个call看看取得用户名长度的代码
跟到这里
7C80BDC2 8B45 08 mov eax, dword ptr [ebp+8] ; keygenme.0040DCF8
7C80BDC5 85C0 test eax, eax
7C80BDC7 0F84 68A80200 je 7C836635
7C80BDCD 8365 FC 00 and dword ptr [ebp-4], 0
7C80BDD1 8D50 01 lea edx, dword ptr [eax+1]
7C80BDD4 8A08 mov cl, byte ptr [eax] | 把"yangchunyu"中的字符挨个送过去
7C80BDD6 40 inc eax | 下一个
7C80BDD7 84C9 test cl, cl
7C80BDD9 ^ 75 F9 jnz short 7C80BDD4 | 没干完活继续回到7C80BDD4继续干
7C80BDDB 2BC2 sub eax, edx
7C80BDDD 834D FC FF or dword ptr [ebp-4], FFFFFFFF
7C80BDE1 E8 1B67FFFF call 7C802501
7C80BDE6 C2 0400 retn 4
从call返回到这里了
00401212 |. A3 86DC4000 mov dword ptr [40DC86], eax | EAX里是用户名长度,这里是A
00401217 |. 833D 86DC4000>cmp dword ptr [40DC86], 4 | 看看用户名长度是否小于4
0040121E |. 0F8C 29010000 jl 0040134D | 小于则跳走
00401224 |. 833D 86DC4000>cmp dword ptr [40DC86], 32 | 再看看用户名长度是否大于50
0040122B |. 0F8F 1C010000 jg 0040134D | 大于也不行
00401231 |. 33C0 xor eax, eax \
00401233 |. 33DB xor ebx, ebx | 都满足了开始寄存器清零
00401235 |. 33C9 xor ecx, ecx /
00401237 |. BF F8DC4000 mov edi, 0040DCF8 ; ASCII "yangchunyu" 把用户名的地址赋给edi留着后面用
0040123C |. 8B15 86DC4000 mov edx, dword ptr [40DC86] / 把用户名长度放到edx里
00401242 |> 0FB60439 /movzx eax, byte ptr [ecx+edi] | 把用户名第一个字节字符送到eax里
00401246 |. 83E8 19 |sub eax, 19 | EAX=EAX-19=79-19=60
00401249 |. 2BD8 |sub ebx, eax | EBX=EBX-EAX=0-79("y")
0040124B |. 41 |inc ecx | 下一个字符
0040124C |. 3BCA |cmp ecx, edx | 看看到没到最后一个字符
0040124E |.^ 75 F2 \jnz short 00401242 \ 没干完活跳回去继续干!!
把我用的注册名yangchunyu按字符挨个走一遍
"y"=79,"a"=61,"n"=6E,"g"=67,"c"=63,"h"=68,"u"=75,"n"=6E,"y"=79,"u"=75
于是有EBX=0-("y"-19)-("a"-19)-("n"-19)-("g"-19)-("c"-19)-("h"-19)-("u"-19)-("n"-19)-("y"-19)-("u"-19)=FFFFFCAF
00401250 |. 53 push ebx ; /<%lX> = FFFFFCAF 把这个中间值1压栈保存
00401251 |. 68 F8DB4000 push 0040DBF8 ; |%lx
00401256 |. 68 F8E04000 push 0040E0F8 ; |s = keygenme.0040E0F8
0040125B |. E8 38010000 call <jmp.&user32.wsprintfA> ; \wsprintfA
00401260 |. 83C4 0C add esp, 0C
00401263 |. 33C0 xor eax, eax \
00401265 |. 33D2 xor edx, edx |-寄存器清零开始下一段(注意没有清EBX)
00401267 |. 33C9 xor ecx, ecx /
00401269 |. 03C3 add eax, ebx | EAX=EAX+EBX=FFFFFCAF
0040126B |. 0FAFC3 imul eax, ebx | EAX=EAX*EBX=FFFFFCAF*FFFFFCAF=000AFFA1
0040126E |. 03C8 add ecx, eax | ECX=ECX+EAX=0+000AFFA1=000AFFA1
00401270 |. 2BD3 sub edx, ebx | EDX=EDX-EBX=0-FFFFFCAF=00000351
00401272 |. 33D0 xor edx, eax | EDX=EDX XOR EAX=00000351 XOR 000AFFA1=000AFCF0
00401274 |. 0FAFD8 imul ebx, eax | EBX=EBX*EAX=FFFFFCAF*000AFFA1=DB863B0F(结果为AFFA0DB863B0F只能存32位)
00401277 |. 53 push ebx ; /<%lX> 把中间值2压栈保存
00401278 |. 68 F8DB4000 push 0040DBF8 ; |%lx
0040127D |. 68 F8E14000 push 0040E1F8 ; |s = keygenme.0040E1F8
00401282 |. E8 11010000 call <jmp.&user32.wsprintfA> ; \wsprintfA
00401287 |. 83C4 0C add esp, 0C
0040128A |. 33C0 xor eax, eax /
0040128C |. 33DB xor ebx, ebx | 寄存器清零
0040128E |. 33D2 xor edx, edx |
00401290 |. 33C9 xor ecx, ecx \
00401292 |. B8 F8E04000 mov eax, 0040E0F8 ; ASCII "FFFFFCAF"(注意这次是把装FFFFFCAF的内存地址当作数值赋给了EAX)
00401297 |. 03D8 add ebx, eax | EBX=EBX+EAX=0+EAX=0040E0F8
00401299 |. 33CB xor ecx, ebx | ECX=ECX XOR EBX=0 XOR EBX =EBX=0040E0F8
0040129B |. 0FAFCB imul ecx, ebx | ECX=ECX*EBX=0040E0F8*0040E0F8=3DA4E100
0040129E |. 2BC8 sub ecx, eax | ECX=ECX-EAX=41B2F040-0040E0F8=41720F48
004012A0 |. 51 push ecx ; /<%lX> ECX=41720F48 把这个中间值3压栈保存
004012A1 |. 68 F8DB4000 push 0040DBF8 ; |%lx
004012A6 |. 68 F8E24000 push 0040E2F8 ; |s = keygenme.0040E2F8
004012AB |. E8 E8000000 call <jmp.&user32.wsprintfA> ; \wsprintfA
004012B0 |. 83C4 0C add esp, 0C
004012B3 |. 68 FCDB4000 push 0040DBFC ; /bon- 把内存0040DBFC中的字符也就是"bon-"压栈保存
004012B8 |. 68 F8DD4000 push 0040DDF8 ; |s = keygenme.0040DDF8
004012BD |. E8 D6000000 call <jmp.&user32.wsprintfA> ; \wsprintfA
004012C2 |. 83C4 08 add esp, 8
004012C5 |. 68 F8E04000 push 0040E0F8 ; /StringToAdd = "FFFFFCAF"
004012CA |. 68 F8DD4000 push 0040DDF8 ; |ConcatString = "Bon-"
004012CF |. E8 B2000000 call <jmp.&kernel32.lstrcatA> ; \lstrcatA
跟进这个call,代码如下
7C834D41 > 6A 08 push 8
7C834D43 68 904D837C push 7C834D90
7C834D48 E8 79D7FCFF call 7C8024C6
7C834D4D 8365 FC 00 and dword ptr [ebp-4], 0
7C834D51 8B45 0C mov eax, dword ptr [ebp+C]
7C834D54 8BD0 mov edx, eax
7C834D56 8A08 mov cl, byte ptr [eax]
7C834D58 40 inc eax
7C834D59 84C9 test cl, cl
7C834D5B ^ 75 F9 jnz short 7C834D56
7C834D5D 2BC2 sub eax, edx
7C834D5F 8BF2 mov esi, edx | 注意这里:把"FFFFFCAF"的地址0040E0F8赋给了esi
7C834D61 8BD0 mov edx, eax
7C834D63 8B7D 08 mov edi, dword ptr [ebp+8] | 把Bon_字符串所在内存的首地址0040DDF8赋给edi
7C834D66 4F dec edi | 往前数一个地址
7C834D67 8D47 01 lea eax, dword ptr [edi+1] | 再加回来
7C834D6A 8A4F 01 mov cl, byte ptr [edi+1] | "Bon_"挨个走一遍
7C834D6D 47 inc edi | 走一遍加一个数
7C834D6E 84C9 test cl, cl | 看看走没走完
7C834D70 ^ 75 F8 jnz short 7C834D6A | 没走完接着走
7C834D72 8BCA mov ecx, edx | 现在edi=edi+len("bon_")=edi+4也就是定位到了"Bon_"内存地址的后面第一个地址
7C834D74 C1E9 02 shr ecx, 2
7C834D77 F3:A5 rep movs dword ptr es:[edi], dword ptr [esi] |关键语句!:把FFFFFCAF放到Bon_后边,注意:edi,esi也同时变化
7C834D79 8BCA mov ecx, edx
7C834D7B 83E1 03 and ecx, 3
7C834D7E F3:A4 rep movs byte ptr es:[edi], byte ptr [esi] |此时[esi]内容为空
7C834D80 834D FC FF or dword ptr [ebp-4], FFFFFFFF
7C834D84 E8 78D7FCFF call 7C802501
7C834D89 C2 0800 retn 8
返回了此时0040DDF8地址开始的字符串已经是"Bon-FFFFFCAF"了
004012D4 |. 68 01DC4000 push 0040DC01 ; /-
004012D9 |. 68 F8DD4000 push 0040DDF8 ; |ConcatString = "Bon-FFFFFCAF-"
004012DE |. E8 A3000000 call <jmp.&kernel32.lstrcatA> 算法见上边call ; \lstrcatA
004012E3 |. 68 F8E14000 push 0040E1F8 ; /StringToAdd = "DB863B0F"
004012E8 |. 68 F8DD4000 push 0040DDF8 ; |ConcatString = "Bon-FFFFFCAF-DB863B0F"
004012ED |. E8 94000000 call <jmp.&kernel32.lstrcatA> 算法见上边call ; \lstrcatA
004012F2 |. 68 01DC4000 push 0040DC01 ; /-
004012F7 |. 68 F8DD4000 push 0040DDF8 ; |ConcatString = "Bon-FFFFFCAF-DB863B0F-"
004012FC |. E8 85000000 call <jmp.&kernel32.lstrcatA> 算法见上边call ; \lstrcatA
00401301 |. 68 F8E24000 push 0040E2F8 ; /StringToAdd = "41720F48"
00401306 |. 68 F8DD4000 push 0040DDF8 ; |ConcatString = "Bon-FFFFFCAF-DB863B0F-41720F48"
0040130B |. E8 76000000 call <jmp.&kernel32.lstrcatA> 算法见上边call ; \lstrcatA
00401310 |. B8 F8DD4000 mov eax, 0040DDF8 ; ASCII "Bon-FFFFFCAF-DB863B0F-41720F48"
00401315 |. BB F8DE4000 mov ebx, 0040DEF8 ; ASCII "1234567890"
0040131A |. 53 push ebx ; /String2 => "1234567890"
0040131B |. 50 push eax ; |String1 = "Bon-FFFFFCAF-DB863B0F-41720F48"
把假码和真码都压栈了
开始最后的比较了
0040131C |. E8 6B000000 call <jmp.&kernel32.lstrcmpA> ; \lstrcmpA 一看就是串比较 关键Call!
00401321 |. 74 15 je short 00401338 | 相等就hello, mr. goodboy! 不等就hello, mr. badboy!
00401323 |. 68 17DC4000 push 0040DC17 ; /hello, mr. badboy!
00401328 |. 68 F8DF4000 push 0040DFF8 ; |s = keygenme.0040DFF8
0040132D |. E8 66000000 call <jmp.&user32.wsprintfA> ; \wsprintfA
00401332 |. 83C4 08 add esp, 8
00401335 |. 33C0 xor eax, eax
00401337 |. C3 retn
00401338 |> 68 03DC4000 push 0040DC03 ; /hello, mr. goodboy!
0040133D |. 68 F8DF4000 push 0040DFF8 ; |s = keygenme.0040DFF8 算法总结
1.注册名长度应该介于4-50之间
2.中间数值1=19*(用户名长度)再依次减用户名的ASCII值
3.中间数值2=中间数值1的立方;
4.中间数值3=40E0F8*40E0F8-40E0F8=41720F48为常数
5.注册名格式="Bon-"中间数值1-中间数值2-中间数值3
我的注册码:
注册名: yangchunyu
序列号: Bon-FFFFFCAF-DB863B0F-41720F48
下面是用vb写的注册机
Private Sub Command1_Click()
If Len(Text1.Text) < 4 Or Len(Text1.Text) > 50 Then
MsgBox ("用户名长度应该介于4到50之间,请重新输入")
Text1.Text = ""
Text2.Text = ""
Exit Sub
End If
Dim UsrName As String
UsrName = Text1.Text
Dim NameAscCount As Integer
NameAscCount = 0
For i = 1 To Len(UsrName)
NameAscCount = NameAscCount + Asc(Mid(UsrName, i, 1))
Next i
Dim Sn1, Sn2, Sn3, Sn As String
Sn1 = Hex(25 * Len(UsrName) - NameAscCount)
Sn2 = Hex((25 * Len(UsrName) - NameAscCount) ^ 3)
Sn3 = "41720F48"
Sn = "Bon-" & Sn1 & "-" & Sn2 & "-" & Sn3
Text2.Text = Sn
End Sub
终于完活了,我的Crack功力增长1,经验增长1。:) 谢谢阅读!
Hmm..鼻子好难受,明天去医院抽血化验过敏原。
杨春雨于2009年8月13日晚20:32
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
上传的附件: