解密入门教学(七)-- 注册码是怎样炼成的

回复 星标
更多

解密入门教学(七)-- 注册码是怎样炼成的

你应该明白的是,并不是所有的软件作者都像你想象并希望的那笨  没有人愿意自己的软件被别人在调试器中用一条 d 指令就能找到正确的注册码...要是那样的话还出来搞什么?

前边儿我们讲的查找软件注册码的方法是有针对性的,必须保证的是该软件使用的是明码比较,这样的话,我们只需找对地方,一个d指令就成了。那既然有明码比较这个词,就不难猜出还有相应的非明码比较...非明码比较也比较容易理解,就是软件比较两个注册码的方法不同而以,并不是计算出正确的注册码后就与用户输入的进行比较,它可能会采用每计算出一位就与注码中的相应位比较一次,一但发现与用户输入的不同,就提示出错等等等等...

遇到这样的软件,我们其实也可以找到其相应的注册码,但有点儿惨,要一位一位的计下来...但是如果人家不给你面子,一但计算出某位不正确就跳走的话,那你怎么办?所以遇到这种软件,我们就只有对其算法进行分析,并做出注册机才是唯一的方法(如果你想写注册机的话)...

你要明白,就算我们能找到那些采用明码比较的软件的注册码,原因也仅仅是因为其采用的是明码比较,所以我们没有什么值的高兴的地方,我们真正要做的,并不是找到一个注册码而已...当然如果你刚入门,那对你的提高还是很有帮助的。我们Crack一个软件的最终目的,是对其进行相应的分析,搞懂它的注册算法并写出注册机,这样才算是成功的Crack了一个软件,成功后的心情是难以表达的!你可以想象一下,对一个软件进行仔细的分析,最后一下把它的算法给搞明白了,那种感觉...我深信不疑的认为有一天你也能体会的到,我等你。

相信你以前看过那些高人大虾的关于软件注册算法分析的文章,同时也相信你有过试图跟踪分析某软件的举动,虽然后来的结果另人不太满意。

其实分析一个软件的注册算法,这其中包括了一些技巧性方面的东西以及必要的经验,很难想象一个连调试器的使用都还没掌握的人试图去分析一个软件会是怎样一个场面。我是见过的,使用调试器并不难,但那并不意味着你就能去分析一个软件了,见 CALL 就追这样的举动可不是我一个人有过的经历,本章我尽量给你说明适当的分析方法。

相信大家都有不在父母陪同下独自使用调试器的能力以及看懂大部分汇编指令的能力了吧,那就够了!我们开始。

正式开始今天的正题,我来举两个例子,相信这两个例子都有足够的表达能力,最起码比我们家楼下那个卖油条的表达能力要强多了。

通过前边儿两章的讲解,我们已经把这个软件大体上给搞明白了,并且也追出了其相应的注册码。而我们今天的目的是对其注册算法进行分析,并写出注册机!这个软件的注册算法其实也比较简(并且存在Bug)用它来当例子,很能说明情况...

好的,我们开始,前边儿追注册码的时候我们就已经知道了其用于计算正确注册码的关键 CALL 的所在位置为 004f4dde。

先启动 ****ZIP,帮助--注册(所以我才说这个软件非常适合写教程用嘛,注册后仍然中以再次注册)输入注册名 Suunb[CCG],注册码 19870219。对地址 004f4dde下断点。

接着就按确定吧,呵呵,被 调试器 拦到了。通过前边两章的分析,我们以经知道了 004f4dde 处的这个 CALL 用于计算正确的注册码,所以我们直接跟进吧!注册码的算法,就包涵在这个 CALL 中,把它给分析透了,我们也就能弄明白软件的注册码是怎样生成的了。但是要怎么分析呢?这是一个比较严肃的问题,面对那一堆堆的指令,我不知道你是怎么想的,反正我第一次时是觉的找不着北,我怎么哪些重要哪些不重要呢?再说了,里面又包涵了那么多 CALL,我还要一个一个地追进去看看?

这就是我说的技巧所在了。其实也没什么可怕的,只要你汇编不是问题,就行了。我们首先可以先把这个计算注册码的 CALL 从头到尾执行一遍,搞明白其中大概的跳转以及其中某些 CALL 的作用,你可以执行过一个 CALL 后就看一下各个寄存器的变化情况,我们就可以看一下其包含的值是何类型,如是内存地址就用d指令看一下,如是数值就看一下是不是得到你输入注册名或注册码的位数等等,这样的话就可以淘汰下来一大部分的 CALL,因为有许多 CALL 的作用只是把注册名或注册码装入到内存中的某个地址或者得到注册名(注册码)的位数或注册码某一位的 ASCII 码,对与这些,我们不必深究。还是推荐你用 Ollydbg,执行过一条指令后很多信息都可以看到  好的,我接着说,按 F8 追入 CALL 之后先大概走一遍...我给出追入后的反汇编代码,并给出注释,相应的分析看后面...

508973

508973

508973

看了我加了注释后的代码是不是好理解多了?你也许会问,你怎么知道那些 CALL 是做什么的?我前边儿不是说过方法了吗?我们先大概地过上一遍,看一下各个跳转,然后再大大概的看一下各个 CALL 的作用...你以为上面这些注释是我过一遍之后就能写出来的?你多过几遍,心中就会有了个大概。

我尽量说的仔细一些:

其实很好理解的,我们追了进来,之后大概的看一下那些个 CALL,其中一些稍有经验的一看就知道是用来得到注册名或长度什么的,之后我们再从头跟一遍。跟到 004f4ff3 处,会发现其会对注册名的位数进行一个比较,看用户是否输入了注册名(也就是说如果edi中装的注册名的位数不大于0,即没输入就跳走,一会儿我会在后面说一下关于这点儿的Bug)而后在 004f4ffc 处我们会发现软件会得到注册名的内存地址,接下来的一条指令一看就知道是用来得到注册名中的各个字符的,见到这类指令,马上就向下看吧,找一下下边儿哪条指令会再跳回到 004f4ffc 处...我们会在 004f504f 处发现目标,好了,现在我们就知道了从 004f4ffc 到 004f504f 之间的那些个指令会对注册名中的每一个字符进行计算。

也就是说软件从 004f4ffc 处开始先是得到注册名中的第N位字符,然后进行一系列的运算,之后执行到了 004f504e 处时把先前先到的注册名的位数减去1然后看其是否为0,不为0就再跳到 004f4ffc 处,然后得以注册名的N+1位再来进行计算。此举的目的就是为了看注册名的各位是否都被计算过了,如果不为0就说明还没有计算完,很简单的道理嘛,edi中装的是注册名的位数,第计算过一位后就将其减1,减完了,注册名的各位也就都参加了运算...

好的,我们再来看具体的算法部分:

在 004f4ff5 的跳转,如果你输入了注册名,其就不会跳走...偶输入的是Suunb[CCG],好的,此时会继续执行到 004f4ff7 处,该指令对 ebx 进行初始化...给它付1,然后在 004f4ffc 处时会将 ebp-0c 中装的注册名的内存地址装入 eax中,接着的 004f4fff 处用于得到注册名的第一个字符,并将其装入al。想象一下,eax 中装的是注册名的内存地址,从该地址开始连续10个内存单元是我们输入的注册名S u u n b [ C C G ] 呵呵,明白了吗?eax中装的内存地址就是注册名在内存中的首地址,第一次执行到这里时ebx中装的是1,eax+ebx-01后得到的还是注册名的首地址,也就是S。而等到后面 004f504f 处的跳转指令跳转回来之前,会在 004f504d 处有一条inc指令会给 ebx 加1,这样的话再执行到这里时就会得到注册名中的第 2 个字符u了,嘿嘿,第三次来之前会再给ebx加上1,明白了吗?总知你可以把ebx中的值理解为当前参加运算的字符在注册名中的位数,即 ebx 是1就是得到注册名的第一位(S),如果 ebx 是2就是得到注册名的第2位(u)。

而后紧接着在 004f5003 处会有一个 CALL 等着我们,呵呵,这个 CALL 比较关键,注册码的一部份由它来决定,要发现它的重要性并不难,因为在 004f5003 处下面会有一个跳转,跳转之前会对al进行测试,嘿嘿,而 al 在 CALL 之前装入的是当前参与运算的字符...并且你用调试器过一下这个CALL就会发现其对 al  进行了修改,呵呵,这个 CALL 会对 al 做一些处理,而处理的结果直接影响了后面部分的流程,所以,对于它,我们一定要跟进...最好能派出两个人在边路对其进行防守,并找专门的后位对其盯梢...

我们待会儿再跟进它,现在还是要先搞明白软件大体上的算法。好的,我接着说,在 004f5008 处对al进行了测试之后会有一个跳转,即如果al中此时装的值为0就跳到 004f5031 处去...你可以理解为这个 CALL 会对字符进行一些运算,如果符合了要求,al就会被置0或1什么的,出来后的测试用来判断当前字符是否符合要求,如果符合就跳或不符合就跳...

继续,由于我输入的注册名的第一个字符是S,而S刚好能通过 004f5003 处的那个 CALL 的计算  所以就没有跳走,我继续按 F10 进行单步执行...接下来的004f500c、004f500f、004f5012这三条指令跟前边儿的得到注册码第N位字符的指令道理是一样的,你看注释好了...而后面从 004f5016 到 004f5029 处的这几条指令也没什么好讲的,对中间的两个 CALL 好奇的话可以进去大概看一下。得不到什么实质性的东西...而 004f502c 处的这个 CALL 嘛,就很重要了,呵呵,它的作用是什么呢?还记的我刚才说过的 004f5003 处的那个 CALL 吧,它执行过后会使 al 发生变化,它下面的跳转指令会根据al的值做相应跳转,即如果al为0,就跳到 004f5031 处,刚好就跳过了 004f502c 处的这个 CALL...而我输入的第一个字符是S,刚好符合了 004f5003 处那个 CALL 的要求,所以没有跳走,于是就执行到了这里,你可以追进去看一下,里面并不复杂,只是将当前参加运算的字符装入内存的 00D3B3C4 处(如果当前参加运算的字符在 004f5003 处没有通过,就不会执行到这里,明白过来了吧,这个 CALL 用于收集注册名中所有符合 004f5003 处那个 CALL 要求的字符)

现在我们已经明白了一半了,我们继续...

不管你是从 004f500a 处跳到 004f5031 处的,还是一步步执行到这里的,总知,不管你输入的注册名中参加当前运算的那一个字符符不符合 004f5003 处的那个 CALL 的要求,总知都会执行到这里...这条指令用来干什么呢?还记的ebx中装的是参加运算的字符在注册名中的相应的位数吗?cmp ebx,byte +01  就是用 ebx 减去1,该条指令的用途也就是看一下当前参加运算的字符是不是注册名中的第一个字符,如果是就跳到 004f5040 处,否则继续... 我们先看004f5040 处,当执行到此处时,ebp-0c中装的其实是注册名的内存地址(前边就已经说过了)在这里将其装入eax中,而后面 004f5043 处的指令的用途就是得到注册名的第一个字符...好了,我们再拐回来看 004f5036 处,如果当前参加运算的字符不是注册名中的第一个字符,就不会跳走,而执行到这里时同样将 ebp-0c 中装的注册名的内存地址放入 eax 中,而 004f5039 处的 eax,byte [eax+ebx-02]嘛,呵呵,很好理解,eax+ebx-01得到的是当前参加运算的字符的内存地址,而这里的eax+ebx-02得到的就是当前参加运算的字符的前面的那个字符,了解?

我们接着看004f5046处的那条指令吧,这个同样非常重要,它的作用是计算注册码的后半部分!

我相信你很容易就能理解它的意思了,当执行到这里时,eax中装的或者是注册码中的第一个字符,或者是当前参加运算的字符的前一个字符(注:字符在内存或寄存器中是以ASCII码来表示的,如S在eax中会显示为00000053,而S的ASCII码便是53,十进制为83)...我们第一次执行到这里时,esi中的值为0(即00000000)eax*4+a8的意思就是用当前参加运算的字符的ASCII码乘以4,再用积加上a8(也就是十进制数168,一路发?)再用这个和与esi相加,我已经说过了,第一次执行到这里时esi中的值为0...而当第二次执行到这里时,esi中装的便是注册名的第一个字符的ASCII码乘以4再加一路发的和...

你会问你为什么知道它是计算注册码的后半部分的?猜的!!呵呵,当然不是,我们可以看到,在004f5054处,程序会将前面计算的结果装用eax中,后边儿紧接着就是一个CALL,嘿嘿,光天化日之下,这也太明显了吧,我们追进去大概看一下就知道它的作用是将十六进制的数转换为十进制的...并将转换后的结果装入edx中装的内存地址处,在CALL之前我们会看到edx中的值以由004f5051处装入,即ebp-1c,CALL 过之后你用d ebp-1c看一下,就会看到你注册码的后半部分了...

而后程序会在 004f505b 将注册码后半部分装入 ecx 中,在 004f505e 处时会将一个内存地址 ebp-0c 装入 eax 处(它的作用就是起一个传递参数的作用,在待会儿的 CALL 中会用eax中装入的值来存放结果)之后的 004f5061 处会将ebp-10 装入 edx 中,ebp-10 处装的是什么呢?我们用 d ebp-10 指令看一下就会知道它的地址为 00D3B3C4。你的嗅觉敏感吗?不敏感的话我就再说一遍,还记的004f502c处的那个CALL吗?它的作用就是收集符合 004f5003 处的那个CALL 的要求的字符...

你明白过来了吗?

这个软件的注册算法是这样的

首先得到注册码的位数,看其是否大于0,不大于 0 就跳到 004f5051 处。

我们输入了 Suunb[CCG] 这个注册名,此时的注册码位数就是10,所以不会跳走,之后我们会来到 004f4fff 处,第一次执行到这里时会将注册名的第一个字符S装入al中,第二次来时会将注册名中的第二个字符(即u)装入al中,它的作用就是将当前参加运算的字符装入al中,之后紧接着就是一个CALL,这个CALL会对当前参加运算的字符进行计算...接着出来会有一个跳转,看al中装的是不是0,如果是就跳到 004f5031 处,如果不是非 0 值就说明当前这个字符符合了要求,那么就会执行到 004f502c 处,这里的 CALL 会将其存放置内存的00D3B3C4 处...而后到了004f5031处会有一个比较,作用是看当前参加运算的字符是不是注册名中的第一个字符,是的话就跳到 004f5040 处,在此将注册名的第一个字符装入 eax,用来参加 004f5046 处的计算。如果当前参加运算的不是注册名的第一个字符,那么就会在执行到 004f5039 处时得到当前参加运算的字符前面的那个字符,将其装入 eax 后就无条件跳到 004f5046 处来参加运算。

了解?也就是说你输入的注册名的第一个字符会参加两次计算,而最后一个字符不会参加计算(想想看,如果当前参加运算的字符是注册名中的第一个字符,它会参加计算,如果是第二个,就取前边的一个,即第一个又会参加一次计算,到了第三个的时候取第二个,到了第四个的时候取第三个...而当最后一个字符来到这里时会取前边的那个字符来参加运算,而这之后就循环就结束了,所以,最后一个不会被计算入内)等到注册名中的所有字符都参加过了运算,就会来到 004f5056 处,在这里将前面 004f5046 处的计算结果转换为十进制...而后会在后面的 004f5064 处的那个 CALL 里,将其与先前装入 00D3B3C4 处的所有符合 004f5003 处的 CALL 要求的字符合并到一起,这个结果,就是真正的注册码了!

也就是说,真正的注册码是由以下部分组成的:

你输入的注册名中的所有符合 004f5003 处的那个 CALL 要求的字符+((注册名中的第一个字符的ASCII码*4+168)*2+(除第一位和最后一位外的所有字符)*4+168的和的和)

现在我们也知道注册码是怎样炼成了的  那么我们要写注册机,就一定要把 004f5003 处的那个 CALL 给搞明白,这样的话,我们才能对注册名中的字符进行筛选...

重新调试程序,对 004f5003地点设断,看我给出的注释吧:

0167:004f4f60 push     ebp

0167:004f4f61 mov     ebp,esp

0167:004f4f63 push     ecx

0167:004f4f64 push     ebx

0167:004f4f65 push     esi

0167:004f4f66 mov      [ebp-01],al                       <--将字符装入内存ebp-01处

0167:004f4f69 mov     byte [ebp-03],02                  <--ebp-03处装入02

0167:004f4f6d mov     byte [ebp-02],01                  <--ebp-02处装入01

0167:004f4f71 mov     cl,[ebp-01]                       <--将参加运算的字符装入cl

0167:004f4f74 dec     ecx                               <--cl减1

0167:004f4f75 sub     cl,02                             <--cl再减去2

0167:004f4f78 jc       004f4fa4                          <--有进位就跳转,你不用担心,一般都不会跳走的啦

0167:004f4f7a inc     ecx                               <--ecx加1,也就是cl加1

0167:004f4f7b mov     bl,02                             <--bl装入02

0167:004f4f7d xor     eax,eax                           <--eax做异或运算,即将eax置0

0167:004f4f7f mov     al,[ebp-01]                       <--al装入参加运算的字符

0167:004f4f82 xor     edx,edx                           <--edx置0

0167:004f4f84 mov     dl,bl                             <--将bl中的值付给dl

0167:004f4f86 mov     esi,edx                           <--再付给esi

0167:004f4f88 xor     edx,edx                           <--edx置0

0167:004f4f8a div     esi                               <--用eax中装的参加运算的字符的ASCII码除以当前esi的值

0167:004f4f8c test     edx,edx                           <--测试edx,edx中装的是上刚才除法计算后的余数

0167:004f4f8e jnz     004f4f93                          <--不为0就跳到004f4f93处

0167:004f4f90 inc     byte [ebp-03]                     <--ebp-03处的值加1

0167:004f4f93 cmp     byte [ebp-03],02                  <--用ebp-03处的值减去02

0167:004f4f97 jna     004f4f9f                          <--不大于就跳到004f4f9f处

0167:004f4f99 mov     byte [ebp-02],00                  <--ebp-02装入00

0167:004f4f9d jmp     short 004f4fa4                    <--无条件跳转到004f4fa4处

0167:004f4f9f inc     ebx                               <--ebx加1

0167:004f4fa0 dec     cl                                <--cl减1

0167:004f4fa2 jnz     004f4f7d                          <--不为0就跳到004f4f7d处再来一遍

0167:004f4fa4 mov     al,[ebp-02]                       <--将ebp-02处的值装入al后返回

0167:004f4fa7 pop     esi

0167:004f4fa8 pop     ebx

0167:004f4fa9 pop     ecx

0167:004f4faa pop     ebp

不知道你看不看的明白,我大概给你说明一下,大体上就是这样的:

先得到这个字符的 ASCII 码,然后用其减去2,并将 bl 置值02...然后用 eax 中装的减了2的 ASCII 码除以 esi 中装的 bl 中的值,之后就看 edx 中装的余数是否为0,如果为0就将 ebp-03 处的值加1,你应该知道 ebp-03 处在初始化的时候被付值为2,如果其被加了1,那就大于了2,这样的话后面在004f4f97处就不会跳走,如果不跳走,在 004f4f99 处,ebp-02就会被装入00,之后就会无条件跳转到004f4fa4处,在那里就会把ebp-02的值,也就是 00 装入 al,明白过来了吧...但如果 edx 中装的余数不为 0,那么在 004f4f8e 处就会跳走,到了 004f4f93 处时由于 ebp-03 中装的是 02,所以条件就会成立,从而可以跳到 004f4f9f 处去继续执行程序...而到了 004f4f9f 处后 ebx(也就是bl)会被加上1,而cl在后面会被减去1,如果 cl 不为 0 的话就再跳到 004f4f7d 处,再来一遍,直到cl变为零为止...

上面这个过程在 Delphi 中可以这样表示:

(变量 S 中装的是当前参加运算的字符,Code 变量用来收集符合要求的字符,变量的声明我没有写明)

N:=Ord(S);

for i:=2 to N-1 do

begin

modz:=(N-i) mod i;

if modz=0 then Break;

end;

if modz<>0 then

Code:=Code+S;

这就起到了与前面汇编代码相同的作用了

下面我给你写一个函数,该函数可以用来得到注册码中的所有符合要求的字符:

function GetKeyChar(Name: String): String;

var

i,ASC,sh,modz:integer;

Code:String;

begin

for i:=1 to Length(Name) do

begin

ASC:=Ord(Name[i]);

for sh:=2 to ASC-1 do

begin

modz:=(ASC-sh) mod sh;

if modz=0 then Break;

end;

if modz<>0 then

Code:=Code+Name[i];

end;

Result:=Code;

end;

你可以这样来用:

var

S1,S2:String;

begin

S1:=Edit1.text;       //Edit1用来输入注册名;

S2:=GetKeyChar(S1);    //此时S2中得到的便是注册名中所有符合要求的字符了;

end;

你现在是不是很想知道都有哪些字符能符合要求?其实 CHINAZIP 自己就可以告诉我们,你只要在输入注册名的时候把所有的可用字符全填上,然后在用d指令看一下注册码的前半部(即非数字部分),就可以知道了,当然你也可以自己写一个程序来试试,用 Delphi 的话可以直接用我上面给的函数...告诉你好了,在所有的 ASCII 字符中,只有以下几个符合要求:

CGIOSYaegkmq5=%)+;/

不信的话你可以试试,正确的注册码中的字符只能是这几个...看来还是比较尊重我们CCG的嘛。

好了,我把完整的注册机给你贴出来吧,这并不难,只要加上后半部分注册码的计算就OK了:

你可以在 Delphi 中声明以下函数,便可直接使用:

function GetKey(Name: String): String;

var

N:String;

i,sh,ci:integer;

Si:integer;

ASC,modz:integer;

begin

i:=Length(Name);

if i=0 then Result:='请输入注册名...'

else

begin

for sh:=1 to i do

begin

ASC:=Ord(Name[sh]);

for ci:=2 to ASC-1 do

begin

modz:=(ASC-ci) mod ci;

if modz=0 then Break;

end;

if modz<>0 then N:=N+Name[sh]

end;

Si:=Ord(Name[1])*4+168;

for sh:=1 to i-1 do

begin

Si:=Si+Ord(Name[sh])*4+168;

end;

N:=UpperCase(N+inttostr(Si));

Result:=N;

end;

end;

最后顺便说一下,如果你稍微留意一下,就会发现,在刚开始的004f4ff3处会对注册名的位数进行测试,即如果位数为0就会在下一条指令时跳至004f5051处,如果你稍微有一点儿经验,就会发觉,通常如果程序发现注册名的位数为0,就会直接跳到失败处,而这个软件却没有!

004f5051 处是什么呢?居然是要得到注册码的后半部分,我们一到 00D3B3C4 处看看,会看到一堆0,你现在在想什么呢?既然我们没有输入注册名,那么就不可能有注册码的前半部分,而后边便会有 CALL 来合并前后两部分的注册码  你重新启动一下软件,注册名不填,把注册码填为 0 注册一下看看...这个粗心的作者啊,造成这样的原因很简单,软件根本就没有判断注名是否为空并在软件初始化的时候把用于计算注册码后半部分的 integer 变量付了初始值0,否则的话 00D3B3C4 处的内存应该为空值..(难不成到时连0都不用输就能注册?)

所以说,Crack 并不是一件坏事,像这种情况你完全可以告诉作者的嘛,到时不但交了一个朋友而且说不准还会得到个免费的注册码....(不知道有没有白帽子Cracker?嘿嘿,CCC刚好也可以是注册码的前半部分哦~~)我希望你明白,对于这种注册算法简单且存在Bug的软件(通常也说明其作者还没什么经验 ^_^),我们不应该为能提供它注册机而感到高兴,如果能帮助其作者改善算法或去掉Bug,又何尝不是一件好事呢?毕竟软件上面加的有中华两个字,你忍心???

我不知道上面给你讲的中华压缩注册分析你是否看懂了,我个人认为我讲的还是比较详细的了(几乎每条指令都加了注释且又再三在后面说明)但如果你仍然看不懂的话,请务必相信是本人写的文章不好,不要放弃啊哥们儿~~!

好了,我再来给你举另外一个例子...通过它来给你讲一下另外一种比较常见的注册码计算方法,即将运算的结果与一个表中的字符进行转换,也就是常说的密码表啦^_^

我们开始吧...

首先运算一下这个软件,其会自动生成机器码,在我这边儿是 xn2urkeUMwpNv5xZ。

我们用 Ollydbg 载入,在反汇编代码处(即左上方那个子窗口中)按Ctrl+G,输入0040432f,回车后便来到了这里。我们大概看看,有经验的话很容易就会看出此地便是了(这次运气比较好,一下就能找到软件计算注册码的地方 ^_^)

好吧,我们按 F2 在这里下断,接着按 F9 来运行程序,在注册处输入 CHINA Cracking Group Suunb 后按确定会被 Ollydbg 断下来,大概跑跑看看吧  我贴出反汇编代码:

508973

508973

508973

508973

508973

508973

508973

我的注释写的还算清楚吧 ^_^,我再大概给你讲解一下:

软件的注册码是这样计算出来的,机器码中的各个字符的 ASCII 码加上 1500 后除以 62 的余数在密码表中对应的字符,就是相应的注册码。

比如说我这里的机器码为 xn2urkeUMwpNv5xZ,x 的 ASCII 码为 78 (十进制120) 78+5DC 的值为 654(即1620) 接着用 1620 除以 3E(62) 得商 26 余 8,好的,我们从“密码表”0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ的开始处向后数8下,就会到了 8 这个字符,这就是x对应的注册码。

好的,我给出 Delphi 的注册机(我仍将其写为函数的形式):

508973

这一章就是最后一章了。
<本章完>

508973

中国年

看 雪 学 院


508973

2017-02-17 22:26:27更新过
此帖已被锁定,无法回复
新窗口打开 关闭