【文章标题】: crackme破解分析
【文章作者】: 逍遥风
【下载地址】: 本地下载
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
----------------------------------------------------------------------
【详细过程】
一个典型的应用MD5算法的实例,在分析时要注意,有的信息是迷惑我们的。要多加注意
用OD载入这个CRACKME,根据注册提示信息或者命令行下断点
BP GetDlgItemTextA
都可以使程序中断,很容易找到关键代码处
来到这里:
00401A94 . 68 2C010000 push 12C ; /Count = 12C (300.)
00401A99 . 68 80334000 push 00403380 ; |Buffer = kgme_#1.00403380
00401A9E . 6A 64 push 64 ; |ControlID = 64 (100.)
00401AA0 . FF75 08 push dword ptr [ebp+8] ; |hWnd
00401AA3 . E8 68010000 call <jmp.&USER32.GetDlgItemTextA>; \GetDlgItemTextA
00401AA8 . 0BC0 or eax, eax ; 取注册名的位数
00401AAA . 75 19 jnz short 00401AC5 ; 输入了注册名就不出现错误提示
00401AAC . 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
00401AAE . 68 E0324000 push 004032E0 ; |oooh input error
00401AB3 . 68 F1324000 push 004032F1 ; |your name please !!!
00401AB8 . 6A 00 push 0 ; |hOwner = NULL
00401ABA . E8 5D010000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
00401ABF . C9 leave
00401AC0 . C2 1000 retn 10
00401AC3 . EB 33 jmp short 00401AF8
00401AC5 > 50 push eax ; 准备取注册码
00401AC6 . 68 2C010000 push 12C ; /Count = 12C (300.)
00401ACB . 68 F89C4000 push 00409CF8 ; |Buffer = kgme_#1.00409CF8
00401AD0 . 68 C8000000 push 0C8 ; |ControlID = C8 (200.)
00401AD5 . FF75 08 push dword ptr [ebp+8] ; |hWnd
00401AD8 . E8 33010000 call <jmp.&USER32.GetDlgItemTextA>; \GetDlgItemTextA
00401ADD . 0BC0 or eax, eax ; 取注册码的位数
00401ADF . 75 17 jnz short 00401AF8 ; 输入了注册码就不出现错误提示
00401AE1 . 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
00401AE3 . 68 E0324000 push 004032E0 ; |oooh input error
00401AE8 . 68 06334000 push 00403306 ; |where is da serial dude ?
00401AED . 6A 00 push 0 ; |hOwner = NULL
00401AEF . E8 28010000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
00401AF4 . C9 leave
00401AF5 . C2 1000 retn 10
00401AF8 > 68 34334000 push 00403334 ; /byteptr [e!]
00401AFD . 68 80334000 push 00403380 ; |ConcatString = "lovetc",80,""
00401B02 . E8 EB000000 call <jmp.&KERNEL32.lstrcatA> ; \lstrcatA
00401B07 . 58 pop eax ; 将注册名与固定字符串byteptr [e!]合并,设为字符串A
这个字符串是干什么用的呢?本以为后的算法与这个字符串有关,但是经过分析
后面的算法依然只是对注册名进行计算。与这个字符串无关。所以
这个字符串是没有什么意义的。
00401B08 . 68 A8564000 push 004056A8 ; /Arg3 = 004056A8
00401B0D . 50 push eax ; |Arg2
00401B0E . 68 80334000 push 00403380 ; |Arg1 = 00403380
00401B13 . E8 E8F4FFFF call 00401000 ; \将注册名进行MD5运算
00401B18 . E8 5C000000 call 00401B79 ; 对MD5计算结果进行处理
将注册名进行MD5计算(如何知道是MD5只需跟进这个CALL,就会发现4个MD5常数)。
那后面如何对MD5的计算结果进行处理呢?
跟进对MD5计算结果处理的那个CALL,看看。
来到这里 :
00401B79 /$ 60 pushad
00401B7A |. BF D0794000 mov edi, 004079D0 ; 用于存放最后的结果
00401B7F |. BE A8564000 mov esi, 004056A8 ; 将MD5(注册名)放进ESI中
00401B84 |. 8B06 mov eax, [esi] ; 取第一组数据放进EAX中
00401B86 |. 8B5E 04 mov ebx, [esi+4] ; 取第二组数据放进EBX中
00401B89 |. 33C3 xor eax, ebx ; XOR(第一组数据,第二组数据),结果设为A
00401B8B |. 50 push eax ; /将结果A作为注册码的第一部分
00401B8C |. 68 5C334000 push 0040335C ; |Format = "%.8X"
00401B91 |. 57 push edi ; |s => kgme_#1.004079D0
00401B92 |. E8 67000000 call <jmp.&USER32.wsprint>; \wsprintfA
00401B97 |. 83C4 0C add esp, 0C
00401B9A |. 83C7 08 add edi, 8
00401B9D |. 81F3 9900BD0F xor ebx, 0FBD0099 ; XOR(第二组数据,0xFBD0099),结果设为B
00401BA3 |. 53 push ebx ; /将结果B作为注册码的第二部分
00401BA4 |. 68 5C334000 push 0040335C ; |Format = "%.8X"
00401BA9 |. 57 push edi ; |s
00401BAA |. E8 4F000000 call <jmp.&USER32.wsprint>; \wsprintfA
00401BAF |. 83C4 0C add esp, 0C
00401BB2 |. 8B4E 08 mov ecx, [esi+8] ; 取第三组数据放进ECX中
00401BB5 |. 33CB xor ecx, ebx ; XOR(第三组数据,结果B),计算结果设为C
00401BB7 |. 83C7 08 add edi, 8
00401BBA |. 51 push ecx ; /<%.8X>
00401BBB |. 68 5C334000 push 0040335C ; |将结果C作为注册码的第三部分
00401BC0 |. 57 push edi ; |s
00401BC1 |. E8 38000000 call <jmp.&USER32.wsprint>; \wsprintfA
00401BC6 |. 83C4 0C add esp, 0C
00401BC9 |. 81F3 090A0C0B xor ebx, 0B0C0A09 ; XOR(结果C,0xB0C0A09),结果设为D
00401BCF |. 8B56 0C mov edx, [esi+C] ; 取第四组数据
00401BD2 |. 83C7 08 add edi, 8
00401BD5 |. 52 push edx ; /第四组数据作为注册码的最后一部分
00401BD6 |. 68 5C334000 push 0040335C ; |Format = "%.8X"
00401BDB |. 57 push edi ; |s
00401BDC |. E8 1D000000 call <jmp.&USER32.wsprint>; \wsprintfA
00401BE1 |. 83C4 0C add esp, 0C ; 结果D没有作用
00401BE4 |. 61 popad
00401BE5 \. C3 retn
还是以实例说明吧。以注册名:lovetc为例
MD5(lovetc) = 3A76C4E1F2907B1C35319657E278EA05
需要注意的是,现在就要对这个计算结果进行分组:
将MD5计算结果以8位为一组进行分组:
3A76C4E1 F2907B1C 35319657 E278EA05
| | | |
第一组数据 第二组数据 第三组数据 第四组数据
现在开始计算:
1)
XOR(第一组数据,第二组数据),结果设为A
即:
XOR (3A76C4E1,F2907B1C) = C8E6BFFD ------- 结果A
2)
XOR(第二组数据,0xFBD0099),结果设为B
即:
XOR (F2907B1C,0xFBD0099) = FD2D7B85 ------- 结果B
3)
XOR(第三组数据,结果B),结果设为C
即:
XOR(35319657,FD2D7B85)= C81CEDD2 ------- 结果C
4)
XOR(结果C,0xB0C0A09),结果设为D
既:
XOR(FD2D7B85,0xB0C0A09)= F621718C ------ 结果D
现在得到4个计算结果 ,但是最后一个计算结果(结果D)是没有参与组成注册码的。所以这个结果D也是没有意义的。
注册码的最后一部分,是由MD5(注册名)的最后一组数据(第4组数据)组成的。
所以最后的注册码为:(将3个计算结果与第四组数据合并)
合并: C8E6BFFD FD2D7B85 C81CEDD2 E278EA05
00401B1D . 68 D0794000 push 004079D0 ; /String2 = "C8E6BFFDFD2D7B85C81CEDD2E278EA05"
00401B22 . 68 F89C4000 push 00409CF8 ; |String1 = "1234567"
00401B27 . E8 CC000000 call <jmp.&KERNEL32.lstrcmpA> ; \lstrcmpA
最后真正的注册码与输入的注册码进行比较。
所以:
注册名:lovetc
注册码:C8E6BFFDFD2D7B85C81CEDD2E278EA05
----------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2006年08月31日 17:45:33
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课