一个判断F(UserName) == G(SN)的软件注册算法分析
这是一个典型的判断F(UserName) == G(SN)的软件,由于该软件属于商业软件,所以出于可以理解的
原因,这里省略掉该软件的名称等信息,我们只是分析和学习他的注册算法。
[Cracker] : prince
[时间] : 2005.03.27
[声明] : 只做技术交流,不做商业用途,如果你手头宽裕并喜欢这个软件的话请支持正版软件。
[E-mail] : Cracker_prince@163.com
[软件信息]
[软件说明] : 该软件是一套基于数据库的客户管理软件,就像每个公司的销售人员手中的名片夹一样,它
可以管理整个公司的客户的所有信息,方便查看,编辑等,所以它是面向企业级的商业软件。
[保护方式] : 序列号保护
[限制方式] : 时间限制
[外壳保护] : ASPack 2.12 -> Alexey Solodovnikov
[编译器/语言] : Borland Delphi 6.0 - 7.0 / PASCAL
用AspackDie搞定ASPack,无自校验,脱壳后可直接运行。启动时要求你输入用户名和注册码,我们输
入用户名:prince,注册码:98765432101234567890a,对于注册码的格式后面的跟踪会分析。点确定后有错误
提示,根据错误提示很容易定位关键部分代码,下断点在006056AB,重新注册,确定被断下:
---------------------------------------------------------------------------------
006056AB |.>push eax
006056AC |.>lea edx,dword ptr ss:[ebp-1C]
006056AF |.>mov eax,dword ptr ds:[ebx+30C]
006056B5 |.>call Unpacked.00472010 ; 取假码“98765432101234567890a”
006056BA |.>mov eax,dword ptr ss:[ebp-1C] ; 假码送EAX
006056BD |.>lea ecx,dword ptr ss:[ebp-6]
006056C0 |.>lea edx,dword ptr ss:[ebp-5]
006056C3 |.>call Unpacked.006051FC ; 根据假码计算比较中间码1
006056C8 |.>lea edx,dword ptr ss:[ebp-24]
006056CB |.>mov eax,dword ptr ds:[ebx+304]
006056D1 |.>call Unpacked.00472010 ; 取用户名“prince”
006056D6 |.>mov eax,dword ptr ss:[ebp-24] ; 用户名送EAX
006056D9 |.>lea edx,dword ptr ss:[ebp-20]
006056DC |.>call Unpacked.006050B4 ; 根据用户名计算比较中间码2
006056E1 |.>mov eax,dword ptr ss:[ebp-20] ; 根据用户名计算出的比较中间码3
006056E4 |.>mov edx,dword ptr ss:[ebp-4] ; 根据假码计算出的比较中间码4
006056E7 |.>call Unpacked.0040499C ; 比较中间码3和中间码4
006056EC |.>jnz Unpacked.006057B2 ; 不等则跳到失败提示
006056F2 |.>call Unpacked.0040BBA0 ; 取当前时间计算一个浮点值
006056F7 |.>add esp,-8 ; /
006056FA |.>fstp qword ptr ss:[esp] ; |Arg1 (8-byte)
006056FD |.>wait ; |
006056FE |.>mov eax,1 ; |
00605703 |.>call Unpacked.00494A44 ; \Unpacked.00494A44
00605708 |.>fcomp qword ptr ss:[ebp-18] ; 跟上面计算过的值进行比较,大于则提示过期
0060570B |.>fstsw ax
0060570D |.>sahf
0060570E |.>jbe short Unpacked.0060571F ; 跳到成功提示画面,否则提示过期
00605710 |.>mov eax,Unpacked.00605814
00605715 |.>call Unpacked.0043B148
0060571A |.>jmp Unpacked.006057CA
-----------------------------------------------------------------------------------------------
我们可以看出注册判断分为两步:1,把用户输入的假码经过计算得到一个中间码,再把用户名也经过
某种运算得到一个中间码,然后进行判断是否相等。如果不等,直接跳到错误提示,反之继续第2步。2,将用
户输入的注册码经过某种计算得到一个值,再获取当前时间计算一个值,二者进行比较,如果前一个值小于后
面的值,则提示注册码过期,反之则注册成功!
这里我们着重分析G(SN)函数,即要找到一个SN,使之满足F(UserName)==G(SN),这样也就达到了解密
的目的,所以对于F(UserName)函数,这里不做分析,我们只要知道经过变换之后的关键比较中间码3就可以了
入手分析了。
先看根据注册码计算中间码4的过程,006056C3处跟进:
------------------------------------------------------------------------------------------------
006051FC /$>push ebp
006051FD |.>mov ebp,esp
006051FF |.>push ecx
00605200 |.>mov ecx,5 ; ECX=5
00605205 |>>/push 0
00605207 |.>|push 0
00605209 |.>|dec ecx
0060520A |.>\jnz short Unpacked.00605205 ; 向堆栈里压10个0
0060520C |.>xchg dword ptr ss:[ebp-4],ecx
0060520F |.>push ebx
00605210 |.>push esi
00605211 |.>push edi
00605212 |.>mov esi,ecx
00605214 |.>mov ebx,edx
00605216 |.>mov dword ptr ss:[ebp-4],eax ; 取假码写入堆栈[EBP-4]
00605219 |.>mov edi,dword ptr ss:[ebp+14]
0060521C |.>xor eax,eax ; EAX清零
0060521E |.>push ebp
0060521F |.>push Unpacked.0060538B
00605224 |.>push dword ptr fs:[eax]
00605227 |.>mov dword ptr fs:[eax],esp
0060522A |.>mov eax,dword ptr ss:[ebp+8]
0060522D |.>call Unpacked.00404590 ; 未知,无关函数
00605232 |.>mov byte ptr ds:[ebx],0
00605235 |.>mov byte ptr ds:[esi],0
00605238 |.>mov byte ptr ds:[edi],1
0060523B |.>mov eax,dword ptr ss:[ebp+10]
0060523E |.>mov dword ptr ds:[eax],1
00605244 |.>mov cx,1 ; CX=1
00605248 |.>mov dx,1 ; DX=1
0060524C |.>mov ax,7D0 ; AX=7D0(十进制2000)
00605250 |.>call Unpacked.0040B92C ; 根据日期2000计算出一个浮点值
36526.000000000000存ST0
00605255 |.>mov eax,dword ptr ss:[ebp+C]
00605258 |.>fstp qword ptr ds:[eax] ; 该浮点值写入内存
0060525A |.>wait
0060525B |.>lea edx,dword ptr ss:[ebp-8]
0060525E |.>mov eax,dword ptr ss:[ebp-4] ; 取假码(98765432101234567890a)
00605261 |.>call Unpacked.006043F0 ; 关键计算,跟进
----------------------------------------------------------------------------------------------
00605261处为关键计算,跟进:
----------------------------------------------------------------------------------------------
006043F0 /$>push ebp
006043F1 |.>mov ebp,esp
006043F3 |.>add esp,-20
006043F6 |.>push ebx
006043F7 |.>push esi
006043F8 |.>push edi
006043F9 |.>xor ecx,ecx ; ECX清零
006043FB |.>mov dword ptr ss:[ebp-20],ecx
006043FE |.>mov dword ptr ss:[ebp-1C],ecx
00604401 |.>mov dword ptr ss:[ebp-18],ecx
00604404 |.>mov dword ptr ss:[ebp-14],ecx ; 以上都是堆栈清零
00604407 |.>mov edi,edx
00604409 |.>mov dword ptr ss:[ebp-4],eax ; 假码写入[EBP-4]
0060440C |.>xor eax,eax
0060440E |.>push ebp
0060440F |.>push Unpacked.0060450A
00604414 |.>push dword ptr fs:[eax]
00604417 |.>mov dword ptr fs:[eax],esp
0060441A |.>mov eax,edi
0060441C |.>call Unpacked.00404590 ; 未知,无关函数
00604421 |.>mov eax,dword ptr ss:[ebp-4] ; 取假码送EAX
00604424 |.>call Unpacked.00404850 ; 取假码个数送EAX
00604429 |.>dec eax ; 假码个数-1
0060442A |.>jl Unpacked.006044EF
00604430 |.>mov dword ptr ss:[ebp-C],3E
00604437 |.>lea eax,dword ptr ss:[ebp-14]
0060443A |.>mov edx,dword ptr ss:[ebp-4] ; 取假码
0060443D |.>mov dl,byte ptr ds:[edx] ; 取假码第1个字符
0060443F |.>call Unpacked.00404778 ; 未知,无关函数
00604444 |.>mov eax,dword ptr ss:[ebp-14] ; 下面是一张表
00604447 |.>mov edx,Unpacked.00604520 ; ASCII
"UlzCVB78typa3nmQWE4ASDsd90qwexcvIOPuioJ12FGH56TYKLZXrfghjkbRNM"
0060444C |.>call Unpacked.00404B94 ; 查表求出假码的第1个字符在表中的位置
00604451 |.>mov dword ptr ss:[ebp-8],eax ; 保存该字符在表中的位置
00604454 |.>cmp dword ptr ss:[ebp-8],0 ; 判断该字符是否在表中
00604458 |.>je Unpacked.006044EF
0060445E |.>mov eax,dword ptr ss:[ebp-4] ; 再取假码
00604461 |.>call Unpacked.00404850 ; 再求假码个数
00604466 |.>sub eax,2 ; 个数-2
00604469 |.>jl Unpacked.006044EF
0060446F |.>inc eax ; 再+1
00604470 |.>mov dword ptr ss:[ebp-10],eax ; 保存该值
00604473 |.>mov esi,2 ; ESI=2
00604478 |>>/lea eax,dword ptr ss:[ebp-18]
0060447B |.>|mov edx,dword ptr ss:[ebp-4] ; 取假码送EDX
0060447E |.>|mov dl,byte ptr ds:[edx+esi-1] ; 循环取假码的第i个字符(i=2;i<22;i++)
00604482 |.>|call Unpacked.00404778
00604487 |.>|mov eax,dword ptr ss:[ebp-18]
0060448A |.>|mov edx,Unpacked.00604520 ; ASCII
"UlzCVB78typa3nmQWE4ASDsd90qwexcvIOPuioJ12FGH56TYKLZXrfghjkbRNM"
0060448F |.>|call Unpacked.00404B94 ; 取出该字符在表中的位置
00604494 |.>|mov ebx,eax ; 该字符在表中的位置送EBX
00604496 |.>|test ebx,ebx
00604498 |.>|jle short Unpacked.006044D0
0060449A |.>|sub ebx,dword ptr ss:[ebp-8] ; 该字符在表中的位置-[EBP-8]这个变量
0060449D |.>|dec ebx ; 结果再-1
0060449E |.>|test ebx,ebx ; 判断结果是否为正
006044A0 |.>|jg short Unpacked.006044A9 ; 为正则跳过下面的循环
006044A2 |>>|/add ebx,dword ptr ss:[ebp-C] ; 否则+3E(十进制62)
006044A5 |.>||test ebx,ebx ; 继续检测结果是否为正
006044A7 |.>|\jle short Unpacked.006044A2 ; 如果不为正则继续加3E
006044A9 |>>|lea eax,dword ptr ss:[ebp-1C]
006044AC |.>|mov edx,Unpacked.00604520 ; ASCII
"UlzCVB78typa3nmQWE4ASDsd90qwexcvIOPuioJ12FGH56TYKLZXrfghjkbRNM"
006044B1 |.>|mov dl,byte ptr ds:[edx+ebx-1] ; 根据上面的结果查表求字符
006044B5 |.>|call Unpacked.00404778
006044BA |.>|mov edx,dword ptr ss:[ebp-1C]
006044BD |.>|mov eax,edi
006044BF |.>|call Unpacked.00404858
006044C4 |.>|mov eax,dword ptr ds:[edi]
006044C6 |.>|movzx eax,byte ptr ds:[eax+esi-2] ; 该字符为本次循环的最终结果
006044CB |.>|add dword ptr ss:[ebp-8],eax ; 将该字符的ASCII码加到[EBP-8],准备下一次循
环
006044CE |.>|jmp short Unpacked.006044E9
006044D0 |>>|lea eax,dword ptr ss:[ebp-20]
006044D3 |.>|mov edx,dword ptr ss:[ebp-4]
006044D6 |.>|mov dl,byte ptr ds:[edx+esi-1]
006044DA |.>|call Unpacked.00404778
006044DF |.>|mov edx,dword ptr ss:[ebp-20]
006044E2 |.>|mov eax,edi
006044E4 |.>|call Unpacked.00404858
006044E9 |>>|inc esi ; i++
006044EA |.>|dec dword ptr ss:[ebp-10]
006044ED |.>\jnz short Unpacked.00604478 ; 继续循环
006044EF |>>xor eax,eax
006044F1 |.>pop edx
006044F2 |.>pop ecx
006044F3 |.>pop ecx
-------------------------------------------------------------------------------------------------
以上即为关键算法,总结一下:
取假码第1个字符,查表UlzCVB78typa3nmQWE4ASDsd90qwexcvIOPuioJ12FGH56TYKLZXrfghjkbRNM,将其
在表中的位置保存至[EBP-8],然后循环取假玛接下来的字符,查表求其在表中的位置,用该值减去[EBP-8],
再减1,判断结果是否为正,如果为负数则循环加62,直到该值为正数为止,也就是使最后这个值落在1-62之间
,然后按该值作为表的索引查找目标字符,比如最后结果为3,那么就把表的第3个字符z作为最后结果保存,接
下来[EBP-8]+最后求出的字符的ASCII码,继续下次循环,直到取完所有假码。
将以上过程抽象成数学表达式,设第i个字符在表中的位置为x,[EBP-8]为y,最后求出的结果为z,那
么整个过程为:(x-y-1+n*62)%62=z,这里要求n要足够大,以保证(x-y-1+n*62)>0。记下这个表达式,我们继
续分析。返回到00605266:
------------------------------------------------------------------------------------------------
00605266 |.>mov eax,dword ptr ss:[ebp-8] ; 求出的中间码1("HINH88FI8wOZeQRU28mQ")送EAX
00605269 |.>call Unpacked.00404850 ; 求该中间码个数
0060526E |.>cmp eax,14 ; 个数同0x14比较,
00605271 |.>jnz Unpacked.00605370 ; 不等就失败了,所以通过这里我们知道注册码应
该是21个
00605277 |.>mov eax,dword ptr ss:[ebp+8]
0060527A |.>push eax
0060527B |.>mov ecx,0A
00605280 |.>mov edx,0B
00605285 |.>mov eax,dword ptr ss:[ebp-8]
00605288 |.>call Unpacked.00404AB0
0060528D |.>lea eax,dword ptr ss:[ebp-C]
00605290 |.>push eax
00605291 |.>mov ecx,1
00605296 |.>mov edx,1
0060529B |.>mov eax,dword ptr ss:[ebp-8]
0060529E |.>call Unpacked.00404AB0
006052A3 |.>mov eax,dword ptr ss:[ebp-C]
006052A6 |.>mov edx,Unpacked.006053A4
006052AB |.>call Unpacked.0040499C
006052B0 |.>sete byte ptr ds:[ebx]
006052B3 |.>lea eax,dword ptr ss:[ebp-10]
006052B6 |.>push eax
006052B7 |.>mov ecx,1
006052BC |.>mov edx,2
006052C1 |.>mov eax,dword ptr ss:[ebp-8]
006052C4 |.>call Unpacked.00404AB0
006052C9 |.>mov eax,dword ptr ss:[ebp-10]
006052CC |.>mov edx,Unpacked.006053A4
006052D1 |.>call Unpacked.0040499C
006052D6 |.>sete byte ptr ds:[esi]
006052D9 |.>lea eax,dword ptr ss:[ebp-14]
006052DC |.>push eax
006052DD |.>mov ecx,4
006052E2 |.>mov edx,3
006052E7 |.>mov eax,dword ptr ss:[ebp-8] ; 取中间码1
006052EA |.>call Unpacked.00404AB0 ; 截取第3到第6个字符
006052EF |.>mov eax,dword ptr ss:[ebp-14] ; 保存EAX
006052F2 |.>mov edx,Unpacked.006053B0 ; 常量ASCII"9999"
006052F7 |.>call Unpacked.0040499C ; 这里便是判断是否为VIP用户的关键
006052FC |.>setne byte ptr ds:[edi] ; 设置标志
006052FF |.>lea eax,dword ptr ss:[ebp-18]
00605302 |.>push eax
00605303 |.>mov ecx,4
00605308 |.>mov edx,7
0060530D |.>mov eax,dword ptr ss:[ebp-8]
00605310 |.>call Unpacked.00404AB0
00605315 |.>mov eax,dword ptr ss:[ebp-18]
00605318 |.>mov edx,1
0060531D |.>call Unpacked.00409B24
00605322 |.>mov edx,dword ptr ss:[ebp+10]
00605325 |.>mov dword ptr ds:[edx],eax
00605327 |.>lea eax,dword ptr ss:[ebp-1C]
0060532A |.>push eax
0060532B |.>mov ecx,4
00605330 |.>mov edx,3
00605335 |.>mov eax,dword ptr ss:[ebp-8] ; 中间码1
00605338 |.>call Unpacked.00404AB0 ; 仍然截取第3到第6个字符
0060533D >mov eax,dword ptr ss:[ebp-1C] ; 送EAX
00605340 |.>mov edx,1
00605345 |.>call Unpacked.00409B24 ; 根据该字符串计算软件的日期限制
0060534A |.>mov dword ptr ss:[ebp-20],eax
0060534D |.>fild dword ptr ss:[ebp-20] ; 计算结果送ST0
00605350 |.>fstp tbyte ptr ss:[ebp-2C] ; 保存至堆栈
00605353 |.>wait
00605354 |.>mov cx,1
00605358 |.>mov dx,1
0060535C |.>mov ax,7D0
00605360 |.>call Unpacked.0040B92C ; 以2000为参数计算浮点值
00605365 |.>fld tbyte ptr ss:[ebp-2C] ; 取上面的计算结果
00605368 |.>faddp st(1),st ; 2者相加,得数作为软件日期限制的标准
0060536A |.>mov eax,dword ptr ss:[ebp+C]
0060536D |.>fstp qword ptr ds:[eax] ; 结果保存,后面要用到
0060536F |.>wait
00605370 |>>xor eax,eax
00605372 |.>pop edx
00605373 |.>pop ecx
00605374 |.>pop ecx
------------------------------------------------------------------------------------------------
006052F7处的函数对计算出的中间码1的第3到第6位同“9999”比较,经过后面的分析可知如果中间码
的第3到第6位==9999的话,软件就认为改序列号为VIP序列号,不再有日期限制。
00605345处就是根据中间码1的第3到第6位计算软件的到期日期。后面的00605360函数就是以2000年为
基数计算一个基本值,然后跟00605345处计算出的浮点值相加,保存这个值,该值就是后面比较是否过期的标
准。接下来我们一路返回:
------------------------------------------------------------------------------------------------
006056AC |.>lea edx,dword ptr ss:[ebp-1C]
006056AF |.>mov eax,dword ptr ds:[ebx+30C]
006056B5 |.>call Unpacked.00472010 ; 取假码“98765432101234567890a”
006056BA |.>mov eax,dword ptr ss:[ebp-1C] ; 假码送EAX
006056BD |.>lea ecx,dword ptr ss:[ebp-6]
006056C0 |.>lea edx,dword ptr ss:[ebp-5]
006056C3 |.>call Unpacked.006051FC ; 根据假码计算比较中间码1
006056C8 |.>lea edx,dword ptr ss:[ebp-24] ; <---- 返回到这里
006056CB |.>mov eax,dword ptr ds:[ebx+304]
006056D1 |.>call Unpacked.00472010 ; 取用户名“prince”
006056D6 |.>mov eax,dword ptr ss:[ebp-24] ; 用户名送EAX
006056D9 |.>lea edx,dword ptr ss:[ebp-20]
006056DC |.>call Unpacked.006050B4 ; 根据用户名计算比较中间码2
006056E1 |.>mov eax,dword ptr ss:[ebp-20] ; 根据用户名计算出的比较中间码3("07aa94e7b5")
006056E4 |.>mov edx,dword ptr ss:[ebp-4] ; 根据假码计算出的比较中间码4("OZeQRU28mQ")
006056E7 |.>call Unpacked.0040499C ; 比较中间码3和中间码4
006056EC |.>jnz Unpacked.006057B2 ; 不等则跳到失败提示
006056F2 |.>call Unpacked.0040BBA0 ; 取当前时间计算一个浮点值
006056F7 |.>add esp,-8 ; /
006056FA |.>fstp qword ptr ss:[esp] ; |Arg1 (8-byte)
006056FD |.>wait ; |
006056FE |.>mov eax,1 ; |
00605703 |.>call Unpacked.00494A44 ; \Unpacked.00494A44
00605708 |.>fcomp qword ptr ss:[ebp-18] ; 跟上面计算过的值进行比较,大于则提示过期
0060570B |.>fstsw ax
0060570D |.>sahf
0060570E |.>jbe short Unpacked.0060571F ; 跳到成功提示画面,否则提示过期
00605710 |.>mov eax,Unpacked.00605814
---------------------------------------------------------------------------------------------
接下来取用户名“prince”开始计算中间码2,再对中间码2经过查表,截取等操作计算出中间码3:
07aa94e7b5,因为我们重点讨论如何解密G(SN)函数,所以这里不对计算用户名的过程进行分析;对于中间码4
的计算则很简单,就是中间码1("HINH88FI8wOZeQRU28mQ")的后10位。
006056E7函数对中间码3和中间码4进行了比较,不等就OVER。
哈哈,过程清楚了,现在我们要成功的话首先要满足第1个条件:使中间码3和中间码4相等,也就是说
,我们要构造一个注册码,使其经过计算后得到中间码为后10位为07aa94e7b5才能满足第1个条件。第2个条件
我们也分析过了,要想成为VIP,就要中间码1的第3到第6位为“9999”,至于其他没有限制的字符我们先不管
。好,先构造一个符合条件的中间码出来,不妨设中间码为:019999001007aa94e7b5,即
G(SN)==019999001007aa94e7b5, 那么现在我们的任务就是求G的逆函数G', 通过G'(019999001007aa94e7b5)求
出SN。看一下上面我们分析过的计算中间码1的过程表达式:(x-y-1+n*62)%62=z,整理一下,x就是我们要求的
SN的单个字符在表UlzCVB78typa3nmQWE4ASDsd90qwexcvIOPuioJ12FGH56TYKLZXrfghjkbRNM中的位置,y是这样一
个变量:第一次循环y==SN的第1个字符在表中的位置,以后每次循环y+=第i个中间码的ASCII码,n为指定好的
一个整数,不妨设100000,z为中间码1的第i个字符在表中的位置。哈,发现了吗?要求出SN的第i位x,只需要
确定一个值,其他都是已知量,这个值就是SN的第1个字符在表
UlzCVB78typa3nmQWE4ASDsd90qwexcvIOPuioJ12FGH56TYKLZXrfghjkbRNM中的位置!也就是说,SN的第1个字符可
以是这表中的62个字符中的任意一个,每个字符打头都可以算出一个唯一的中间码,说明同一个用户名可以有
62个注册码!说起来罗哩罗嗦的,我自己都晕了!用笔算,太累,还是让程序帮忙吧:
----------------------------------------------------------------------------------------------
#include "stdafx.h"
#include "stdio.h"
char chTable1[62] = {'U', 'l', 'z', 'C', 'V', 'B', '7', '8', 't', 'y',
'p', 'a', '3', 'n', 'm', 'Q', 'W', 'E', '4', 'A',
'S', 'D', 's', 'd', '9', '0', 'q', 'w', 'e', 'x',
'c', 'v', 'I', 'O', 'P', 'u', 'i', 'o', 'J', '1',
'2', 'F', 'G', 'H', '5', '6', 'T', 'Y', 'K', 'L',
'Z', 'X', 'r', 'f', 'g', 'h', 'j', 'k', 'b', 'R',
'N', 'M'};
char chEncryptKey[20] = {'0', '1', '9', '9', '9', '9', '0', '0', '1', '0',
'0', '7', 'a', 'a', '9', '4', 'e', '7', 'b',
'5'}; // 为了简便,这里指定了要解密的字符串,实际可以动态输入
int nEncryptKey[20] = {48, 49, 57, 57, 57, 57, 48, 48, 49, 48, 48, 55, 97, 97, 57, 52, 101, 55,
98, 53}; // 为了简便,这里指定了要解密的字符串,实际可以动态输入
int GetPos(char m)
{
int i;
for (i = 0; i < 62; i++)
{
if (chTable1[i] == m)
{
return i;
}
}
return -1;
}
int main(int argc, char* argv[])
{
char chKey[21] = {0};
int nTemp = 0;
// 简单起见我们指定首字符为a,实际可以让程序随机生成
chKey[0] = 'a';
nTemp = GetPos(chKey[0]) + 1;
if (0 == nTemp)
{
printf("Internal Error!\n");
return -1;
}
else
{
int nPos;
int nKeyPos = 0;
for (int i = 0; i < 20; i++)
{
nPos = GetPos(chEncryptKey[i]) + 1;
nKeyPos = nTemp + 1 + nPos;
if (62 < nKeyPos)
{
nKeyPos = (nTemp + 1 + nPos) % 62;
}
chKey[i + 1] = chTable1[nKeyPos - 1];
nTemp += nEncryptKey[i];
}
printf("My key is: \n");
for (i = 0; i < 21; i++)
{
printf("%c", chKey[i]);
}
printf("\n");
}
return 0;
}
---------------------------------------------------------------------------------------------
以上程序在VC++6.0和WIN2K平台上测试通过,输出结果:aJJpBUkf113Nw0NTuuruk
将用户名“prince”和注册码aJJpBUkf113Nw0NTuuruk输入程序,注册成功,没有日期限制,VIP版!
你也可以将中间码1的第3到第6位改为其他数字,比如5000,这样算出来的注册码也是合法的,但有日期限制,
到2013年过期,呵呵,也够用了吧?
总结:
一个可用的注册码:
User Name: prince
Key aJJpBUkf113Nw0NTuuruk
菜鸟写菜文,有失误还请大侠指教!
prince
2005.03.27