软件破解教程4

呵呵,看了我加了注释后的代码是不是好理解多了也许会问,你怎么知道那些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 ] 呵呵,明白了吗ax中装的内存地址就是注册名在内存中的首地址,第一次执行到这里时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要求的字符)
HOHOHO~~(请模仿周星星式的笑声…)现在我们已经明白了一半了…好的,我们继续…
不管你是从004f500a处跳到004f5031处的,还是一步步执行到这里的,总知,不管你输入的注册名中参加当前运算的那一个字符符不符合004f5003处的那个CALL的要求,总知都会执行到这里…这条指令用来干什么呢记的ebx中装的是参加运算的字符在注册名中的相应的位数吗mp 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得到的就是当前参加运算的字符的前面的那个字符,了解br>我们接着看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的要求的字符…
嘿嘿,你明白过来了吗br>这个软件的注册算法是这样的:首先得到注册码的位数,看其是否大于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给搞明白,这样的话,我们才能对注册名中的字符进行筛选…
好的,我们再来:
Ctrl+N呼出TRW2000,下bpx 004f5003,F5退出点确定被拦后便按F8跟进,呵呵,这个就没什么好说的了,看我给出的注释吧:
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码加上1500后除以62的余数在密码表中对应的字符,就是相应的注册码。
比如说我这里的机器码为xn2urkeUMwpNv5xZ,x的ASCII码为78(十进制120) 78+5DC的值为654(即1620) 接着用1620除以3E(62)得商26余8,好的,我们从“密码表”0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ的开始处向后数8下,就会到了8这个字符,嘿嘿,这就是x对应的注册码。
好的,我给出Delphi的注册机(我仍将其写为函数的形式):
function KeyGen(Name: String): String;
var
S:String[16];
P:String;
Key:String;
i,N,Z:integer;
begin
P:=’0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ’;
if Length(Name)<16 then
Result:=’机器码必须为16位…’
else
begin
S:=Name;
for i:=1 to 16 do
begin
N:=Ord(S);
N:=N+1500;
Z:= N mod 62;
Z:=Z+1;
Key:=Key+P[Z];
end;
Result:=Key;
end;
end;

呵呵,这一章就是最后一章了,现在也写完了…. 打个Kiss~~
(摘自 http://www.enjoykorea.net/27/viewspace-20672.html) 文章知识点与官方知识档案匹配,可进一步学习相关知识C技能树首页概览113219 人正在系统学习中

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2008年2月10日
下一篇 2008年2月11日

相关推荐