破解crackme5.1(逆推注册码)
【破文标题】破解crackme5.1(逆推注册码)
【对 象】新手
【破解工具】SoftICE
【保护方式】序列号
【任 务】分析算法,写出注册机
【破文作者】gdszmai
【破解过程】
1 用PEiD查壳,无壳。
2 运行程序,输入注册码"12345",点击“OK”,弹出对话框"Wrong S/N#"。
3 用W32Dasm 反汇编,参考→串式数据参考→查找"Wrong S/N#"→双击来到代码处→向上查找发现是从[004011B1]处跳转来的,此为关键跳转,再往上0040119E:Call 004012A3为关键Call,要追进。
4 运行crackme5.1,输入注册码"12345"
5 Ctrl+D,调出SoftICE,下断点bpx getwindowtexta,F5退出SoftICE
6 点击“OK”,马上被SoftICE中断,来到下面: *************************************************************************
* Reference To: USER32.GetWindowTextA, Ord:0000h
|
:0040119E E800010000 Call 004012A3 ;取注册码位数
:004011A3 FF3544204000 push dword ptr [00402044]
:004011A9 E85D000000 call 0040120B ;关键 call,跟进
:004011AE 83F801 cmp eax, 00000001 ;关键比较
:004011B1 751E jne 004011D1 ;跳就出错,如改为9090,就可暴破
:004011B3 6A30 push 00000030
* Possible StringData Ref from Data Obj ->"Good For U!"
|
:004011B5 6881204000 push 00402081
* Possible StringData Ref from Data Obj ->"U Did It!!" ;成功标志
|
:004011BA 688D204000 push 0040208D
:004011BF FF3548204000 push dword ptr [00402048]
* Reference To: USER32.MessageBoxA, Ord:0000h
|
:004011C5 E8EB000000 Call 004012B5
:004011CA 5E pop esi
:004011CB 5F pop edi
:004011CC 5B pop ebx
:004011CD C9 leave
:004011CE C21000 ret 0010 * Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004011B1(C)
|
:004011D1 6A30 push 00000030
* Possible StringData Ref from Data Obj ->"=)"
|
:004011D3 6898204000 push 00402098
* Possible StringData Ref from Data Obj ->"Wrong S/N#" ;失败标志
|
:004011D8 689B204000 push 0040209B
:004011DD FF3548204000 push dword ptr [00402048]
* Reference To: USER32.MessageBoxA, Ord:0000h
|
:004011E3 E8CD000000 Call 004012B5
:004011E8 5E pop esi
:004011E9 5F pop edi
:004011EA 5B pop ebx
:004011EB C9 leave
:004011EC C21000 ret 0010 *************************************************************************
* Referenced by a CALL at Address: ;从上面的关键call跳来
|:004011A9
|
:0040120B C8000000 enter 0000, 00
:0040120F 53 push ebx
:00401210 52 push edx
:00401211 33C0 xor eax, eax
:00401213 B8A6204000 mov eax, 004020A6 ;把注册码的地址送eax
:00401218 803800 cmp byte ptr [eax], 00 ;检查是否输入了注册码
:0040121B 7460 je 0040127D ;如无输入就跳到出错处
:0040121D 33DB xor ebx, ebx ;ebx 清零
:0040121F 33D2 xor edx, edx ;edx 清零
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040122C(C)
|
:00401221 8A18 mov bl, byte ptr [eax] ;取注册码的第一位数给bl
:00401223 C1C308 rol ebx, 08 ;ebx循环左移8位
:00401226 03D3 add edx, ebx ;循环左移8位后,累加到edx
:00401228 40 inc eax ;eax加一,准备取注册码的下一位数
:00401229 803800 cmp byte ptr [eax], 00 ;是否取完?
:0040122C 75F3 jne 00401221 ; 如没取完,继续取下一位数
:0040122E 52 push edx ;累加完后,数值压栈
* Possible StringData Ref from Data Obj ->"%lX"
|
:0040122F 6854204000 push 00402054
:00401234 68BF204000 push 004020BF
* Reference To: USER32.wsprintfA, Ord:0000h
|
:00401239 E88F000000 Call 004012CD
:0040123E BBBF204000 mov ebx, 004020BF ;累加后数值的地址送ebx
:00401243 803B38 cmp byte ptr [ebx], 38 ;比较第一位是否为‘8’
:00401246 7535 jne 0040127D ;不相等就出错
:00401248 807B0144 cmp byte ptr [ebx+01], 44 ;比较第二位是否为‘D’
:0040124C 752F jne 0040127D ;不相等就出错
:0040124E 807B0243 cmp byte ptr [ebx+02], 43 ;比较第三位是否为‘C’
:00401252 7529 jne 0040127D ;不相等就出错
:00401254 807B0341 cmp byte ptr [ebx+03], 41 ;比较第四位是否为‘A’
:00401258 7523 jne 0040127D ;不相等就出错
:0040125A 807B0446 cmp byte ptr [ebx+04], 46 ;比较第五位是否为‘F’
:0040125E 751D jne 0040127D ;不相等就出错
:00401260 807B0533 cmp byte ptr [ebx+05], 33 ;比较第六位是否为‘3’
:00401264 7517 jne 0040127D ;不相等就出错
:00401266 807B0636 cmp byte ptr [ebx+06], 36 ;比较第七位是否为‘6’
:0040126A 7511 jne 0040127D ;不相等就出错
:0040126C 807B0738 cmp byte ptr [ebx+07], 38 ;比较第八位是否为‘8’
:00401270 750B jne 0040127D ;不相等就出错
:00401272 B801000000 mov eax, 00000001 ;比较通过后,1送eax,成功标志
:00401277 5A pop edx
:00401278 5B pop ebx
:00401279 C9 leave
:0040127A C20400 ret 0004
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0040121B(C), :00401246(C), :0040124C(C), :00401252(C), :00401258(C)
|:0040125E(C), :00401264(C), :0040126A(C), :00401270(C)
|
:0040127D 33C0 xor eax, eax ;eax 清零,失败标志
:0040127F 5A pop edx
:00401280 5B pop ebx
:00401281 C9 leave
:00401282 C20400 ret 0004 *************************************************************************
7 分析算法:
此算法比较简单,就是把注册码的每一位数逐一送入bl,循环左移8位,累加到EDX,其结果为8DCAF368则注册成功。虽然算法简单,但如果从输入数找正确的注册码却困难,那么我们来逆推注册码:(下面数字均为十六进制)
①先假设注册码为最少的4位数: a1 a2 a3 a4
ebx 循环左移8位后的ebx edx=edx+ebx
00 00 00 a1 循环左移8位→ 00 00 a1 00 00 00 a1 00
00 00 a1 a2 00 a1 a2 00 + 00 a1 a2 00
00 a1 a2 a3 a1 a2 a3 00 + a1 a2 a3 00
a1 a2 a3 a4 a2 a3 a4 a1 + a2 a3 a4 a1
______________________
8D CA F3 68
从上面可以看出:a1=68 字符: "h"
a2=8D-a1=8D-68=25 字符: "%"
a3=CA-a1-a2=CA-68-25=3D 字符: "="
a4=F3-a1-a2-a3=F3-68-25-3D=29 字符: ")"
所以4位的注册码为“h%=)” ②假设注册码为5位数:a1 a2 a3 a4 a5
ebx 循环左移8位后的ebx edx=edx+ebx
00 00 00 a1 循环左移8位→ 00 00 a1 00 00 00 a1 00
00 00 a1 a2 00 a1 a2 00 + 00 a1 a2 00
00 a1 a2 a3 a1 a2 a3 00 + a1 a2 a3 00
a1 a2 a3 a4 a2 a3 a4 a1 + a2 a3 a4 a1
a2 a3 a4 a5 a3 a4 a5 a2 + a3 a4 a5 a2
__________________
8D CA F3 68 从上面可以看出:a2=68-a1
a3=8D-a1-a2
a4=CA-a1-a2-a3
a5=F3-a1-a2-a3-a4
所以如果设a1=32,字符为: "2" ,则可计算出5位的注册码为"26%=)" ③假设注册码为6位数:a1 a2 a3 a4 a5 a6 ebx 循环左移8位后的ebx edx=edx+ebx
00 00 00 a1 循环左移8位为 00 00 a1 00 00 00 a1 00
00 00 a1 a2 00 a1 a2 00 + 00 a1 a2 00
00 a1 a2 a3 a1 a2 a3 00 + a1 a2 a3 00
a1 a2 a3 a4 a2 a3 a4 a1 + a2 a3 a4 a1
a2 a3 a4 a5 a3 a4 a5 a2 + a3 a4 a5 a2
a3 a4 a5 a6 a4 a5 a6 a3 + a4 a5 a6 a3
___________________
8D CA F3 68
所以从上面可以看出,如果设: a1=21 !
a2=23 # 则: a3=68-a2-a1=24 $
a4=8D-a3-a2-a1=25 %
a5=CA-a4-a3-a2-a1=3D =
a6=F3-a5-a4-a3-a2-a1=29 ) 即6位数的注册码为 "!#$%=)" ④假设注册码为7位数: a1 a2 a3 a4 a5 a6 a7 ebx 循环左移8位后的ebx edx=edx+ebx
00 00 00 a1 循环左移8位为 00 00 a1 00 00 00 a1 00
00 00 a1 a2 00 a1 a2 00 + 00 a1 a2 00
00 a1 a2 a3 a1 a2 a3 00 + a1 a2 a3 00
a1 a2 a3 a4 a2 a3 a4 a1 + a2 a3 a4 a1
a2 a3 a4 a5 a3 a4 a5 a2 + a3 a4 a5 a2
a3 a4 a5 a6 a4 a5 a6 a3 + a4 a5 a6 a3
a4 a5 a6 a7 a5 a6 a7 a4 + a5 a6 a7 a4
__________________________
8D CA F3 68
从上面可以看出:a4=68-a1-a2-a3 ,然而键盘上可输入的字符是!(ASCII码为21)到 ~(ASCII码为7E),假设a1,a2,a3都为!(ASCII码最少),则a4=68-21-21-21=5,而ASCII码为5的,并不是可输入的字符,所以正确的注册码不可能为7位。 8 经过上面的分析,可以写出注册机(4-6位数):下面是C语言源程序
*************************************************************************
#include <stdio.h>
#include <math.h>
main()
{
int a1,a2,a3,a4,a5,a6;
printf("*******************************\n");
printf("crackme5.1 Key Generator\n");
printf("*******************************\n\n\n");
/*4位数的注册码*/
a1=0x68;
a2=0x8d-a1;
a3=0xca-a1-a2;
a4=0xf3-a1-a2-a3;
printf("%c%c%c%c\t",a1,a2,a3,a4);
printf("\n\n"); /*5位数的注册码*/
for(a1=0x21;a1<=0x7e;a1++) {
a2=0x68-a1; if(a2<0x21||a2>0x7e) continue;
a3=0x8d-a1-a2; if(a3<0x21||a3>0x7e) continue;
a4=0xca-a1-a2-a3; if(a4<0x21||a4>0x7e) continue;
a5=0xf3-a1-a2-a3-a4; if(a5<0x21||a5>0x7e) continue;
printf("%c%c%c%c%c\t",a1,a2,a3,a4,a5); }
/*6位数的注册码*/
printf("\n\n");
for(a1=0x21;a1<=0x7e;a1++) {
for(a2=0x21;a2<=0x7e;a2++) {
a3=0x68-a1-a2;if(a3<0x21||a3>0x7e) continue;
a4=0x8d-a1-a2-a3; if(a4<0x21||a4>0x7e) continue;
a5=0xca-a1-a2-a3-a4; if(a5<0x21||a5>0x7e) continue;
a6=0xf3-a1-a2-a3-a4-a5; if(a6<0x21||a6>0x7e) continue;
printf("%c%c%c%c%c%c\t",a1,a2,a3,a4,a5,a6); } }
printf("\n\n"); } *************************************************************************
C源程序经编译后,运行,算出4-6位所有的注册码为: h%=)
!G%=) "F%=) #E%=) $D%=) %C%=) &B%=) 'A%=) (@%=) )?%=) *>%=)
+=%=) ,<%=) -;%=) .:%=) /9%=) 08%=) 17%=) 26%=) 35%=) 44%=)
53%=) 62%=) 71%=) 80%=) 9/%=) :.%=) ;-%=) <,%=) =+%=) >*%=)
?)%=) @(%=) A'%=) B&%=) C%%=) D$%=) E#%=) F"%=) G!%=)
!!&%=) !"%%=) !#$%=) !$#%=) !%"%=) !&!%=) "!%%=) ""$%=) "##%=) "$"%=)
"%!%=) #!$%=) #"#%=) ##"%=) #$!%=) $!#%=) $""%=) $#!%=) %!"%=) %"!%=)
&!!%=) 把它输入后,点击“OK”,弹出"U Did It!!",成功破解了!唉,真累!
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
上传的附件: