第六章--爆破软件
爆破其实很简单,最起码比你能一下把你家的牙膏给全挤出来要容易多了。你只要先到大街上买几根雷管,然后放到你的显示器上再点着就OK了(不难吧,记的点着后跑远点儿)
爆破的原理我也说过了,相信你很容易就能理解了。我们今天就具体讲一下如何找到那个关键跳转以及如何才能买到即便宜又好用的雷管...
爆破一个软件一般只需要很少的几个步骤,首先先看一下其有无加壳,有的话是用何工具加的壳,知道了以后用相应的工具将其脱掉或进行手工脱壳,参考以有教程。接着我们就可以对脱过壳之后的软件来开刀了。你有两种选择,用W32Dasm或调试器,一般如果你遇上的是那种很菜的软件的话,用W32Dasm就可以搞定了。如果遇上的不是那种比较菜的,就买股票吧,因为股票是你如胶似漆的妻子!当!快醒醒啊...哦,一般如果你遇上的不是那种很菜的软件的话,就用调试器吧。先来说W32Dasm:我们首先用W32Dasm来进行反汇编(废话!)之后在串式参考中找到错误提示信息或可能是正确的提示信息双击鼠标左键来到相应的地址处。在W32Dasm的主窗口中分析相应汇编代码,找出关键跳转和关键call。绿色光条停在关键跳转,在W32Dasm主窗口底部找到关键跳转的偏移地址(实际修改地址)。用ultraedit找到偏移地址(实际修改地址)修改机器码(或放上一根雷管),保存(点火)!而用调试器也同样简单,等会儿会详细说明。
道理废话了那么多,来实例动手说明吧:
首先讲解用W32Dasm来进行爆破:
【软件名称】中华压缩(*p)
【软件版本】7.0
【文件大小】1041KB
【适用平台】Win9x/Me/NT/2000
【软件简介】*p(中华压缩)是一款压缩、解压各种压缩文档的工具软件,它支持包括ZIP格式文件在内的各种常见压缩格式如:ARJ、CAB、GZIP、JAR、LHA、TAR、ZOO、ARC、LZH、Pak等等。
软件的出处是电脑报2001年的合订本配套光盘,7.0时的保护做的很那个,目前最新版应该好多了...
好的,我们开始吧,首先第一步是你得把它装上(引来野狼N头),之后先随便找个字符串填上去注册一下,会看到一个错误对话框,提示"注册码不正确,无法注册"。接着我们用FI来看一下它用的是什么壳。ASPack 2.001,caspr出场。脱过壳后我们用W32Dasm花上半分钟或半小时的时间来对它进行反汇编。我们以经反汇编完毕。之后在串式参考中(字符串数据参考)中找刚才你看到的那个错误提示,找到之后双击几次,发现其只有一处调用。我们会来到004F0E64处,我把具体代码给贴上(请你从代码的最下边开始看):
:004F4DD1 E84EE1F3FF call 00432F24
:004F4DD6 8B55F0 mov edx, dword ptr [ebp-10]
:004F4DD9 8D4DF4 lea ecx, dword ptr [ebp-0C]
:004F4DDC 8BC3 mov eax, ebx
:004F4DDE E8C9010000 call 004F4FAC
:004F4DE3 8B55F4 mov edx, dword ptr [ebp-0C]
:004F4DE6 58 pop eax
:004F4DE7 E830F3F0FF call 0040411C
:004F4DEC 7576 jne 004F4E64 <--这个就是传说中的男人,Stop!这个就是传说中的关键跳转
:004F4DEE B201 mov dl, 01
:004F4DF0 A158254500 mov eax, dword ptr [00452558]
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004F4D86(C)
|
:004F4DF5 E85ED8F5FF call 00452658
:004F4DFA 8945FC mov dword ptr [ebp-04], eax
:004F4DFD 33C0 xor eax, eax
:004F4DFF 55 push ebp
:004F4E00 685D4E4F00 push 004F4E5D
:004F4E05 64FF30 push dword ptr fs:[eax]
:004F4E08 648920 mov dword ptr fs:[eax], esp
:004F4E0B B101 mov cl, 01
* Possible StringData Ref from Code Obj ->"Software\XDZHAN\*p"
|
:004F4E0D BAA84E4F00 mov edx, 004F4EA8
:004F4E12 8B45FC mov eax, dword ptr [ebp-04]
:004F4E15 E822DAF5FF call 0045283C
* Possible StringData Ref from Code Obj ->"Real Programmers Use Pascal!"
|
:004F4E1A B9CC4E4F00 mov ecx, 004F4ECC
* Possible StringData Ref from Code Obj ->"Key"
|
:004F4E1F BAF44E4F00 mov edx, 004F4EF4
:004F4E24 8B45FC mov eax, dword ptr [ebp-04]
:004F4E27 E854DEF5FF call 00452C80
* Possible StringData Ref from Code Obj ->"软件注册成功,谢谢您的支持!" <--我们向上看会在这里发现注册成功后的正确信息。正确信息处向上找第一个跳转就是我们要找的关键跳转。
|
:004F4E2C B8004F4F00 mov eax, 004F4F00
:004F4E31 E8563DF6FF call 00458B8C
:004F4E36 A16C305000 mov eax, dword ptr [0050306C]
:004F4E3B 8B00 mov eax, dword ptr [eax]
* Possible StringData Ref from Code Obj ->"中华压缩(*p)-注册版"
|
:004F4E3D BA244F4F00 mov edx, 004F4F24
:004F4E42 E80DE1F3FF call 00432F54
:004F4E47 33C0 xor eax, eax
:004F4E49 5A pop edx
:004F4E4A 59 pop ecx
:004F4E4B 59 pop ecx
:004F4E4C 648910 mov dword ptr fs:[eax], edx
:004F4E4F 686E4E4F00 push 004F4E6E
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004F4E62(U)
|
:004F4E54 8B45FC mov eax, dword ptr [ebp-04]
:004F4E57 E868E2F0FF call 004030C4
:004F4E5C C3 ret
:004F4E5D E9C2E9F0FF jmp 00403824
:004F4E62 EBF0 jmp 004F4E54
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004F4DEC(C)
|
* Possible StringData Ref from Code Obj ->"注册码不正确,无法注册!" <--这个就是出错的信息了,那正确信息也就在附近,上下看看。
|
:004F4E64 B8484F4F00 mov eax, 004F4F48 <--双击来到这里
:004F4E69 E81E3DF6FF call 00458B8C
:004F4E6E 33C0 xor eax, eax
:004F4E70 5A pop edx
:004F4E71 59 pop ecx
:004F4E72 59 pop ecx
:004F4E73 648910 mov dword ptr fs:[eax], edx
:004F4E76 689B4E4F00 push 004F4E9B
你可能有点不明白,为什么我说它就是关键跳转呢?还记的在破解原理中我举的例子吗?
我再给你讲一遍好了,通常我们会遇到两种关键跳转,我分别举例说明:
(1)
je (jne,jz,jnz) 19870219
........ XXXXXXXXXX
........ XXXXXXXXXX
........ 软件注册正确的相关信息
...
...
19870219 软件的出错信息
.......
.......
也就是说这第一种情况是先判断注册码是否正确,如果不正确就跳到19870219处,正确的话就不跳转,一直执行下去,直至注册正确处。
对于这种情况,我们要找的关键跳转,就是正确信息上面的第一个跳转。我们可能对其作相应修改或将其给nop掉就万事OK了。
(2)
je (jne,jz,jnz) 19870219
........ XXXXXXXXXX
........ XXXXXXXXXX
........ 软件的出错信息
...
...
19870219 软件注册正确的相关信息
.......
.......
而这第二种情况就是先判断注册码正确与否,如果正确就跳到19870219处,不正确的话就不跳转,一直执行下去,直至出错处。
对于这种情况,我们要找的关键跳转就是出错信息上面的第一个跳转。将其做相应修改或改为jmp后我们就可以为所欲为了
呵呵,道理也都给你讲明白了,我们来改一下试试吧。我们在W32Dasm中选中关键跳转,在右下角的状态栏中看到相应的偏移地址为000F41EC。好的,我们用UltraEdit来打开它。Ctrl+G,接着输入0xF41EC,回车后便会跳到相应的位置。相应的机器码是75(jne),我们将其改为74(jz)后存盘退出。
好了,运行一下看看,我们来随便输入一个注册码注册一下试试。呵呵,注册成功!
用W32Dasm我们就讲到这里,呵呵,很简单的,你下去之后自己找些保护简单的软件上上手吧。
我们接着来讲用调试器来进行爆破。
如果你真的试图用W32Dasm去爆破几个软件的话,用不了多少时间你就会发现一些问题。比如说有的软件你用W32Dasm反汇编后串式参考根本就不能用。或者串式参考中没有出错或正确的信息。还有就是有的软件就算你通过串式参考来到了相应的地方,刚想去找关键跳转你就会发现眼前的东西比你想像中的要乱的多...虽然你有可能通过认真仔细地找,仍会找到,但我不认为那是一件聪明的事情。毕竟,有一些动静是只有在程序执行期间才能看出来的。好的,如果你用W32Dasm遇到了找不到关键跳转的软件,就去用调试器吧!(你用调试器前可先用W32Dasm打开一遍看个先,如果很容易就让你找到了。那就没必要了)
在开始之前我们有必要讲一下用调试器来爆破的步骤(我知道你一定会用调试器的 ):首先,我们当然还是要把你要Crack的软件给装上(我挡我挡我挡,不要乱丢东西嘛!)然后来到输入注册码的地方,仍旧随便输入一个,接着不要按确定,待我们把调试器叫出来先。还记的我前面跟你讲的API的事情吗?软件要得到你输入的注册码,就一定会调用某个API函数来达到目的。我们就在调试器中用相应的API来做断点,这样的话,只要一有程序调用这个API,就会被调试器给拦截下来。
GetDlgItemInt、GetDlgItemText、GetDlgItemTextA这三个函数可能会有用。但是如果你用的是98,那为什么不用hmemcpy呢?那真的是一个不错的主意。当我们下完断点后就返回到你要注册的那个软件中,点确定这类的按钮。如果被调试器给断了下来,就说明你刚才下的断点有用,如果没有被断下来,就换个断点试试。接下来我们在调试器中来取消刚才你下的那个断点,我们以TRW2000为例(SoftICE与其操作大体相同)取消断点用bc *指令。然后我们就输入pmodule指令来返回到程序的领空(而在SoftICE中由于没有相应指令,呵呵,狂按F12吧)。现在我们把话题岔开一下,什么是领空呢?举个例子吧,你的程序要得到你输入的那个注册码,就会去调用相应的函数。比如调用GetDlgItemTextA,而GetDlgItemTextA本身又会去调用Hmemcpy这个函数,而这些函数都是存在于系统中的某个DLL文件中的。那么当这个程序调用相应的API函数的话,程序的领空就会转到这个相应的DLL文件中去执行这个API函数。(你就这样理解就行了)我前边也说过了,Hmemcpy这个函数应用程序本身并不直接调用,而是由其它的API函数来调用。那么,你就可以理解为你的程序调用了一个API函数,调用的同时程序的领空会转到这个API所在的DLL文件里,而这个API又调用了Hmemcpy函数,那么此时领空就会又转到了Hmemcpy所在的DLL文件中,之后当Hmemcpy执行完毕,就会返回到调用它的API的领空中去,而当这个API执行完毕的后就会返回到调用它的应用程序的领空中去。比如说我们用Hmemcpy这个函数来当断点,当我们输入完注册码按确定后,程序就会去调用某个API来得到你输入的那些数据,而这“某个API”又会去调用Hmemcpy,所以程序就被断到了。当然此时程序的领空也就不会在应用程序中了,但是当我们输入过pmodule指令之后我们就可以反回到应用程序本身的领空中去了。这样的话你看到的就是应用程序自身的代码了,而不是API的!好了,我接着刚才的说(到哪儿了来着?)当我们返回到程序自身的领空中去后就一直狂按F12吧,F12的作用是一直执行程序,直到遇上ret等指令。也就是一大坨一大坨地来执行程序^_^你一直按F12,直到程序出现注册错误对话框。然后记下刚才你按的次数,接着从头做起,这一次按F12的次数是你刚才按的次数-1,也就是说比上一次要少按一次。而后按键由F12换至F10(怎么没有F4?),还是一路狂按,直到软件提示出错,这次记下你按F10的次数。好的,再从头来一遍,我们再次按F10的时候,要一步一步慢慢来,一般你按F10的次数离你上次按的次数相差五六步的时候,一般就会看见一个CALL,接着是一个跳转指令。你一步一步地来,看过了这个跳转指令之后会不会跳走,如果跳走了,那一般你不会再过两三步就应该出错了。当然也有可能是你没有跳走,而过了两三步就出错了。这个应该不难理解,因为基本上它和我前边跟你介绍过的是一个道理。然而另外一种情况是你一路按F10下来,到了最后会发现根本没什么跳转指令,呵呵,别害怕,这个很常见的。遇上这种情况,我们只要把F10的次数变换为上次按F10的次数-1,这样的话你一般就会停在一个CALL处,而这个CALL,就是程序中的关键CALL,我们之后要吃点儿苦,要按F8追进去分析它,程序注册的成功与失败,就在这个CALL中,也就是说我们要修改的关键跳转,也在这个CALL中。呵呵,其实也很好理解的,就是把我上边说的那些个判断什么地放到了一个CALL里面。我们按F8追进去之后便仍旧按F10来一步一步执行,过不了多长时间你就会发现关键跳转了,找关键跳转的方法跟我前边说的一样,即按F10的次数跟上一次差五六步的时候慢下来,就会看到了。
你应该明白,程序是很灵活的东西,并没有那么多公式化的东西在里边,大概的分析方法就是这个样子,一切都要靠你自己去掌握,别人跟你讲,也只是讲一个分析的方法而以,我相信随着你以后经验的提高,你慢慢地就能应付各种情况了。
现在,我们再用调试器来对*P这个软件进行分析,希望你能够掌握这个并不难的方法。
首先,你要把刚才爆破过了的再改回来,或直接重装一遍。之后我们打开它,任意输入注册码,接着按Ctrl+N呼出TRW,下断点hmemcpy。下过后按F5退出(它就是不用F4,我也没办法^_^)然后我们点击确定。好的,程序被断了下来:
KERNEL?HMEMCPY
0147:9e62 push bp
0147:9e63 mov bp,sp
0147:9e65 push ds
0147:9e66 push edi
0147:9e68 push esi
0147:9e6a cld
0147:9e6b mov ecx,[bp+06]
0147:9e6f jcxz 9ee9
……以下N多代码省略……
我们输入bc *来取消断点,然后用pmodule来返回到程序的领空:
0167:00436d13 mov [ebx+0c],eax
0167:00436d16 mov eax,[ebx]
0167:00436d18 cmp eax,byte +0c
0167:00436d1b jnz 00436d38
0167:00436d1d mov edx,[ebx+08]
0167:00436d20 push edx
0167:00436d21 mov ecx,[ebx+04]
0167:00436d24 mov edx,eax
0167:00436d26 mov eax,esi
0167:00436d28 call 00432b24
……N多代码仍旧省略……
按7下F12另加1下F10来到0167:004f4dc4处,我们接着一下一下来按F10,大概按了10多下,就可以看到004f4dec处有一个跳转,我们执行到004f4dec处后果然跳走了。会跳到004f4e64处,我们跳过去之后按不了三下,程序就提示出错了。呵呵,明白过来了吧,004f4dec处的那个跳转jnz 004f4e64就是关键跳转,嘿嘿,找到了之后不用我说了吧
0167:004f4dc4 mov eax,[ebp-08]
0167:004f4dc7 push eax
0167:004f4dc8 lea edx,[ebp-10]
0167:004f4dcb mov eax,[ebx+02e0]
0167:004f4dd1 call 00432f24
0167:004f4dd6 mov edx,[ebp-10]
0167:004f4dd9 lea ecx,[ebp-0c]
0167:004f4ddc mov eax,ebx
0167:004f4dde call 004f4fac
0167:004f4de3 mov edx,[ebp-0c]
0167:004f4de6 pop eax
0167:004f4de7 call 0040411c
0167:004f4dec jnz 004f4e64 <--关键跳转!!
0167:004f4dee mov dl,01
0167:004f4df0 mov eax,[00452558]
0167:004f4df5 call 00452658
0167:004f4dfa mov [ebp-04],eax
0167:004f4dfd xor eax,eax
0167:004f4dff push ebp
0167:004f4e00 push dword 004f4e5d
0167:004f4e05 push dword [fs:eax]
0167:004f4e08 mov [fs:eax],esp
0167:004f4e0b mov cl,01
0167:004f4e0d mov edx,004f4ea8
0167:004f4e12 mov eax,[ebp-04]
0167:004f4e15 call 0045283c
0167:004f4e1a mov ecx,004f4ecc
0167:004f4e1f mov edx,004f4ef4
0167:004f4e24 mov eax,[ebp-04]
0167:004f4e27 call 00452c80
0167:004f4e2c mov eax,004f4f00
0167:004f4e31 call 00458b8c
0167:004f4e36 mov eax,[0050306c]
0167:004f4e3b mov eax,[eax]
0167:004f4e3d mov edx,004f4f24
0167:004f4e42 call 00432f54
0167:004f4e47 xor eax,eax
0167:004f4e49 pop edx
0167:004f4e4a pop ecx
0167:004f4e4b pop ecx
0167:004f4e4c mov [fs:eax],edx
0167:004f4e4f push dword 004f4e6e
0167:004f4e54 mov eax,[ebp-04]
0167:004f4e57 call 004030c4
0167:004f4e5c ret
0167:004f4e5d jmp 00403824
0167:004f4e62 jmp short 004f4e54
0167:004f4e64 mov eax,004f4f48 <---由上面的0167:004f4dec处跳来,出错!;
0167:004f4e69 call 00458b8c
0167:004f4e6e xor eax,eax
再来给你举另一个例子:
【软件名称】天网防火墙
【软件版本】2.46 Beta
【文件大小】1289KB
【适用平台】Win9x/Me/NT/2000
【软件简介】天网防火墙个人版是一套给个人电脑使用的网络安全程序,它可以帮你抵挡网络入侵和攻击,防止信息泄露,并可与我们的网站相配合,根据可疑的攻击信息,来找到攻击者。同时天网防火墙个人版把网络分为本地网和互联网,可以针对来自不同网络的信息,来设置不同的安全方案,它适合于在拨号上网的用户,也适合通过网络共享软件上网的用户。
该软件仍旧是我从电脑报2001年合订本的配套光盘中找的,软件的注册码可以到其网站免费获得...
我们还是要先把它装上(某民工:你小子敢再说一句废话试试!^_^)之后我们用FI看一下它有没有加壳,呵呵,BC++编译,没有加壳,爽!运行它,在注册对话框中随便输入点什么,比如说这星期又出了几部新电影,都叫什么名字等等...
好的,我们接下来请TRW2000出场。先胡乱输入两个字符串,比如第一个输入“英雄的导演是?”第二个输入“可能是赵本山”
接下来就按Ctrl+N把TRW2K叫出来,下bpx hmemcpy,之后按F5退出。
接着可以按确定就成了,程序会被TRW2K断掉,我们紧接着输入bc *以及pmodule。
下面可以开始按F12了,一共按8下程序就会报错,我们第二次就按7下然后开始按F10,按70下F10程序就又报错了(呵呵,一定要有耐心哦)。
好的,我把反汇编后的代码给你贴出来:
0167:0041c617 lea edx,[ebp-04] <--7下F12后按一下F10来到这里
0167:0041c61a mov ecx,[0052ae7c]
0167:0041c620 mov eax,[ecx]
0167:0041c622 mov eax,[eax+0318]
0167:0041c628 add eax,byte +2c
0167:0041c62b call 00517740
0167:0041c630 dec dword [ebp-20]
0167:0041c633 lea eax,[ebp-04]
0167:0041c636 mov edx,02
0167:0041c63b call 00517710
0167:0041c640 mov word [ebp-2c],14
0167:0041c646 lea eax,[ebp-08]
0167:0041c649 call 00401d60
0167:0041c64e mov edx,eax
0167:0041c650 inc dword [ebp-20]
0167:0041c653 mov ecx,[ebp-40]
0167:0041c656 mov eax,[ecx+02e0]
0167:0041c65c call 004b9f14
0167:0041c661 lea edx,[ebp-08]
0167:0041c664 mov ecx,[0052ae7c]
0167:0041c66a mov eax,[ecx]
0167:0041c66c mov eax,[eax+0318]
0167:0041c672 add eax,byte +30
0167:0041c675 call 00517740
0167:0041c67a dec dword [ebp-20]
0167:0041c67d lea eax,[ebp-08]
0167:0041c680 mov edx,02
0167:0041c685 call 00517710
0167:0041c68a lea eax,[ebp-10]
0167:0041c68d call 00401d60
0167:0041c692 mov edx,eax
0167:0041c694 inc dword [ebp-20]
0167:0041c697 mov ecx,[ebp-40]
0167:0041c69a mov eax,[ecx+02e0]
0167:0041c6a0 call 004b9f14
0167:0041c6a5 lea edx,[ebp-10]
0167:0041c6a8 push dword [edx]
0167:0041c6aa mov word [ebp-2c],20
0167:0041c6b0 lea eax,[ebp-0c]
0167:0041c6b3 call 00401d60
0167:0041c6b8 mov edx,eax
0167:0041c6ba inc dword [ebp-20]
0167:0041c6bd mov ecx,[ebp-40]
0167:0041c6c0 mov eax,[ecx+02d4]
0167:0041c6c6 call 004b9f14
0167:0041c6cb lea edx,[ebp-0c]
0167:0041c6ce mov edx,[edx]
0167:0041c6d0 mov eax,[0052ae7c]
0167:0041c6d5 mov eax,[eax]
0167:0041c6d7 pop ecx
0167:0041c6d8 call 0040525c
0167:0041c6dd mov [ebp-45],al
0167:0041c6e0 dec dword [ebp-20]
0167:0041c6e3 lea eax,[ebp-10]
0167:0041c6e6 mov edx,02
0167:0041c6eb call 00517710
0167:0041c6f0 dec dword [ebp-20]
0167:0041c6f3 lea eax,[ebp-0c]
0167:0041c6f6 mov edx,02
0167:0041c6fb call 00517710
0167:0041c700 cmp byte [ebp-45],00
0167:0041c704 jz 0041c750 <--按了60多下F10后会在这里发现一个跳转,嘿嘿,就是它了!!!
0167:0041c706 mov ecx,[0052ae7c]
0167:0041c70c mov eax,[ecx]
0167:0041c70e mov eax,[eax+0318]
0167:0041c714 call 00411fd0
0167:0041c719 mov word [ebp-2c],2c
0167:0041c71f mov edx,00521b50
0167:0041c724 lea eax,[ebp-14]
0167:0041c727 call 005175b0
0167:0041c72c inc dword [ebp-20]
0167:0041c72f mov eax,[eax]
0167:0041c731 call 004b41b0
0167:0041c736 dec dword [ebp-20]
0167:0041c739 lea eax,[ebp-14]
0167:0041c73c mov edx,02
0167:0041c741 call 00517710
0167:0041c746 mov eax,[ebp-40]
0167:0041c749 call 004a81d0
0167:0041c74e jmp short 0041c77d
0167:0041c750 mov word [ebp-2c],38
0167:0041c756 mov edx,00521b6b
0167:0041c75b lea eax,[ebp-18]
0167:0041c75e call 005175b0
0167:0041c763 inc dword [ebp-20]
找到了关键跳转之后就别闲着了,呵呵,放雷管吧!(你可以用W32Dasm打开这个文件,然后按Shift+F12,之后输入0041c704,这样就可以在右下角看到相应的偏移地址了)
小技巧:在TRW中,如果你觉的某处可能是关键跳转的话,可以用r fl z这个指令来进行测试,该指令可使以成立的条件取反,比如说本来JZ XXXXXXXX成立,可以跳走了,用r fl z指令后该条指令就不成立了,即就不会跳走了。以上也是,你可以在0041c704处输入r fl z,呵呵,再执行几步看看,是不是成功了?还有就是如果你只是想达到注册软件的目的,且该软件只在注册的时候验证一次的话,用这个方法就可以代替雷管了!
呵呵,最后还是要说一句,爆破只是一些雕虫小技。刚入门时玩几次就够了,切莫就此不前...
后话:你可能慢慢就会发现,有一些软件其实并没有你想象中那么简单,你甚至连找到它的关键跳转都找不到。这很正常,你要做的便是多动手多练习,慢慢你就会明白过来的。我今天之所以给你举这两个例子,就是因为它们两个都比较简单,且能说明重点,给你讲那些比较那个的软件的爆破,反而会让你看的一头雾水
第七章-寻找软件的注册码
我们来寻找软件真正的注册码!
寻找软件的注册码就像你小时玩的躲猫猫一样,简单又有趣,虽然后来你会不这样觉的
好的,我们开始。
我不知道你有没有明白我前面在原理中讲的那些东西,如果没明白,我就再说一遍
软件通过你输入的用户名或者机器码什么的生成一个正确的注册码来与你输入的注册码进行比较,如果两个相同,也就是说你输入的注册码是正确的话,那软件就会完成注册。如果你输入的不正确,嘿嘿,当然就不会注册成功。
好的,现在你已经知道软件会有一个比较两个注册码的过程,这就是关键所在。一般如果你遇到的是那种明码比较的软件,这会是一件非常另人愉快的事情的
软件会先计算出正确的注册码然后再与你输入的那个进行比较,也就是说正确的注册码会被软件自己算出来!嘿嘿,搜身你会吗?虽然法律以及道德不允许我们去搜身,但…
我接着说,虽然现在的软件已经比以前要厉害上许多,但,那种用明码比较的,还是大有人在的。所谓明码比较,就是说软件先算出正确的注册码,然后放到内存或你家的沙发下面,之后再得到你输入的那个注册码,接着就比较了。呵呵,好理解吧,我们只要找到那个比较的地方,看一下软件把注册码放到内存的哪里了,再到相应的内存处瞧一瞧,就万事OK了!
还记的对你说过的那些常见的(也是最菜的)比较吗?我捡其中最简单的一个来给你再解释一下:
mov eax [ ] 这里可以是地址,也可以是其它寄存器 该条指令也可以是mov eax [ ]
mov edx [ ] 同上 通常这两个地址就储存着重要信息 该指令也可以是 pop edx
call 00?????? 关键call
jz(jnz)或 jne(je) 关键跳转
第一条mov eax [ ]指令是将一个内存地址或另外一个寄存器(该寄存器中装的是内存地址)装入eax中。第二条指令与其相同,是将一个内存地址或另外一个寄存器中的内存地址装入edx中。而这两条指令是干什么的呢?嘿嘿嘿嘿…
这两条指令就是用来存放真假两个注册码的地址的,也就是说eax和edx这两个寄存器中此时一个装的是正确的注册码的内存地址,一个是你输入的那个错误的注册码的内存地址。软件在比较注册码前将两个注册码的内存地址分别装入到两个寄存器中,然后就是关键Call出场。在这个关键Call中对注册码进行比较时,软件会从相应的寄存器中取出两个注册码来比较,接着出来就是一个关键跳转,通过上面Call中的比较结果来做相应的跳转…
你应该已经想到什么了吧!没错,我们只要找到软件的关键Call,然后在关键Call处来查看相应的内存地址就可以找到正确的注册码了 而这一切,都可以通过调试器来完成。从某种意义上来说,如果你能自己一个人把你家的微波炉修好,那你就绝对会用调试器 我们在调试器中,只要一步一步执行到关键Call处,然后用d eax和d edx就可以查看两个地址中放的两个注册码,如果你发现其中的一个是你自己刚才输入的,那么另一条就是正确的
而所谓的内存注册机呢?我这里就不再多说了,它的原理就是自动在软件注册的时候中断到相应的地方,并显示相应内存处的值,当然它是需要配置的... 此类软件有CRACKCODE2000和注册机编写器keymake,具体用法你可以参考软件的联机帮助^_^
我们剩下的问题就是如何来找个这关键Call了,基本上来说你就用前边给你讲爆破时的那种方法就可以了,很简单的
但是就像你家后门的玻璃可能永远擦不干净一样,我们家后门的玻璃也从来没擦干净过 导演:NG!重说,就像所有事情都有例外一样,有些软件的关键Call会比较难找一点,但如果你掌握了适当的方法,同样也会很好找的...
我们就来玩玩吧:
首先,我们还来用*P这个软件上上手^_^
它已经是我们的老朋友了,所以就不用再介绍它了吧
好的,我们先装上它(嘿嘿,偶就是喜欢说废话,你打偶偶也要说^_^)接着我们点帮助-注册,输入Name:Suunb[CCG],Code:19870219
然后请出我们的老伙计TRW2000,下bpx hmemcpy 按F5点确定被拦:
KERNEL?HMEMCPY
0147:9e62 push bp
0147:9e63 mov bp,sp
0147:9e65 push ds
0147:9e66 push edi
0147:9e68 push esi
0147:9e6a cld
0147:9e6b mov ecx,[bp+06]
0147:9e6f jcxz 9ee9
...省略N多代码...
输入bc *,删除断点。pmodule ,直接跳到程序领空:
0167:00436d13 mov [ebx+0c],eax
0167:00436d16 mov eax,[ebx]
0167:00436d18 cmp eax,byte +0c
0167:00436d1b jnz 00436d38
0167:00436d1d mov edx,[ebx+08]
0167:00436d20 push edx
0167:00436d21 mov ecx,[ebx+04]
0167:00436d24 mov edx,eax
0167:00436d26 mov eax,esi
0167:00436d28 call 00432b24
...省略N多代码...
按8下F12就会提示出错,我们第二次就按7次 接着我们再来按F10,按16下就会报错,好的,我们再来:这一次我们按F10的时候,就按我前边说过的方法,到与上次按的次数相差五六次的时候就慢下来。好的,我们按十来下的时候就慢下来仔细瞅瞅,呵呵,一下子就看到004f4dec处的那个跳转以及它上面的关键CALL了 我们按F10单步执行到004f4de7处(即关键CALL处)后下指令d edx就可看到真正的注册码,而d eax则可以看到我刚才输入的19870219 代码给你:
0167:004f4dc4 mov eax,[ebp-08] <---7下F12,1下F10就来到这里(此时ebp-08处放的是刚才输入的注册码19870219)
0167:004f4dc7 push eax <---将EAX压栈;
0167:004f4dc8 lea edx,[ebp-10]
0167:004f4dcb mov eax,[ebx+02e0]
0167:004f4dd1 call 00432f24 <---该CALL用来得到用户输入的用户名,其实就是某个API函数,嘿嘿,好奇的话可以追进去看看
0167:004f4dd6 mov edx,[ebp-10] <---将得到的用户名放入EDX;
0167:004f4dd9 lea ecx,[ebp-0c]
0167:004f4ddc mov eax,ebx
0167:004f4dde call 004f4fac <---该CALL用来计算出真正的注册码;
0167:004f4de3 mov edx,[ebp-0c] <---将计算出的真.注册码放入EDX,在下条指令时可用D EDX查看;
0167:004f4de6 pop eax <---先前压入的注册码出栈;
0167:004f4de7 call 0040411c <---该CALL用来比较两个注册码,罪魁祸首啊!;
0167:004f4dec jnz 004f4e64 <---不相等则跳,跳必死,暴破将75改为74或EB,当然90也行;
0167:004f4dee mov dl,01
0167:004f4df0 mov eax,[00452558]
0167:004f4df5 call 00452658
0167:004f4dfa mov [ebp-04],eax
0167:004f4dfd xor eax,eax
0167:004f4dff push ebp
0167:004f4e00 push dword 004f4e5d
0167:004f4e05 push dword [fs:eax]
0167:004f4e08 mov [fs:eax],esp
0167:004f4e0b mov cl,01
0167:004f4e0d mov edx,004f4ea8
0167:004f4e12 mov eax,[ebp-04]
0167:004f4e15 call 0045283c
0167:004f4e1a mov ecx,004f4ecc
0167:004f4e1f mov edx,004f4ef4
0167:004f4e24 mov eax,[ebp-04]
0167:004f4e27 call 00452c80
0167:004f4e2c mov eax,004f4f00
0167:004f4e31 call 00458b8c
0167:004f4e36 mov eax,[0050306c]
0167:004f4e3b mov eax,[eax]
0167:004f4e3d mov edx,004f4f24
0167:004f4e42 call 00432f54
0167:004f4e47 xor eax,eax
0167:004f4e49 pop edx
0167:004f4e4a pop ecx
0167:004f4e4b pop ecx
0167:004f4e4c mov [fs:eax],edx
0167:004f4e4f push dword 004f4e6e
0167:004f4e54 mov eax,[ebp-04]
0167:004f4e57 call 004030c4
0167:004f4e5c ret
0167:004f4e5d jmp 00403824
0167:004f4e62 jmp short 004f4e54
0167:004f4e64 mov eax,004f4f48 <---由上面的0167:004f4dec处跳来,挂!;
0167:004f4e69 call 00458b8c
0167:004f4e6e xor eax,eax
整理:
Name:Suunb[CCG]
Code:SCCG5296
可以真接在TRW2000中下断点bpx 004f4de6,中断后用D EDX来查看真.注册码。
另附:CRACKCODE2000的CRACKCODE.INI
[Options]
CommandLine=*P.exe
Mode=2
First_Break_Address=4f4de7
First_Break_Address_Code=E8
First_Break_Address_Code_Lenth=5
Second_Break_Address=404123
Second_Break_Address_Code_Lenth=2
Save_Code_Address=EDX
呵呵,是不是很简单?我说过了嘛,其实并不难的
我不知道你有没有发现,其实上面的软件的关键CALL还是很好找的,相信你用W32Dasm就中以找出来,那为什么不用呢?对于那些比较简单的软件,何必非请出调试器呢?
给你贴个用W32Dasm找关键CALL的:
【软件名称】e族百变桌面
【软件版本】4.0
【文件大小】1316KB
【适用平台】Win9x/Me/NT/2000
【软件简介】提供25种变换桌面的方式,让你的桌面焕然一新。操作简单,无需费力学习。支持多种Internet流行图片格式。将壁纸文件打包,方便存储、转发。将壁纸包展开,还原图片文件。
嘿嘿,我也懒的去折腾我的小猫了,咱们就还用电脑报2001年合订本配套光盘上的软件吧 (2002年的偶没有买)
首先装上它(嘿嘿,你习惯了?为什么不丢东西了? ^_^)运行一下该软件先,该软件自动生成了相应的机器码,并停留在注册项上,输入注册码19870219,点确定,挂!
用fi检查,该软件为Delphi编译,没加壳。
用W32DASM打开该执行文件,参考-串式参考,在最下边,见到了刚才弹出的"注册码不正确,请联系作者"。
用鼠标双击,发现只有一处调用,在00488E97处,接着在串式参考对话框中在"注册码不正确,请联系作者"处向上找,找到"感谢您支持国产软件,祝您好运"(说的我都不好意思了)
用鼠标双击,仍旧只有一处调用,在00488DF7处:
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00488DCD(U)
|
:00488DD9 8B45FC mov eax, dword ptr [ebp-04]
:00488DDC 8B8020040000 mov eax, dword ptr [eax+00000420]
:00488DE2 35280BB61E xor eax, 1EB60B28
:00488DE7 3B45F8 cmp eax, dword ptr [ebp-08] <---关键比较,? EAX来查看软件正确的注册码;
:00488DEA 0F85A0000000 jne 00488E90 <---关键跳转,不相等就跳,跳必挂!
:00488DF0 6A40 push 00000040
* Possible StringData Ref from Code Obj ->"注册成功"
|
:00488DF2 68D48E4800 push 00488ED4
* Possible StringData Ref from Code Obj ->"感谢您支持国产软件,祝您好运!"
|
:00488DF7 68E08E4800 push 00488EE0 <---双击串式参考便跳到此行,我们向上找第一个跳转处就是关键跳转,关键跳转上面就是关键比较;
:00488DFC 8B45FC mov eax, dword ptr [ebp-04]
:00488DFF E81CD2FBFF call 00446020
:00488E04 50 push eax
...省略代码若干...
向上看,00488DEA处有一跳转,不相等便跳到00488E90处,跳必挂!还记的00488E97处的出错对话框吧! 罪魁祸首啊!
在向上一行,看00488DE7处:cmp eax, dword ptr [ebp-08],此为关键比较。可用? EAX查看软件正确的注册码。
整理:
打开该软件,在注册码处输入19870219,打开TRW2000,下断点bpx 00488DE7,点注册被拦。输入? EAX得到软件正确的注册码。
机器码:533226313
注册码:25061473
用注册机编写器keymake编写该软件的注册机:
点其它-另类注册机(F8),软件名称输入ePaper.exe,注册码选寄存器方式 EAX 十进制。
添加断点,中断地址:00488DE7,中断次数:1,第一字节:3B,指令长度:3。
生成注册机后完工,万事OK!
嘿嘿,现在是不是觉的找软件的注册码越来越像小时候玩的躲猫猫了? 可惜偶小时候没有青梅竹马那种类型的伙伴...
好的,我们这次讲个有点儿名气的软件,WinZIP8.1,这个软件相信大家都用过吧,反正偶是喜欢用RAR,不过也多少用过几天这玩意儿...
如果你没听说过,那看介绍好了
【软件名称】WinZIP
【软件版本】8.1 Beta 2
【文件大小】1757KB
【适用平台】Win9x/Me/NT/2000
【软件简介】一个强大并且易用的压缩实用程序,支持ZIP、CAB、TAR、GZIP、MIME,以及更多格式的压缩文件。其特点是紧密地与Windows资源管理器拖放集成,不用离开资源管理器而进行压缩、解压缩。
不用我说了吧,出处仍旧是电脑报2001年合订本的配套光盘
我之所以先择它,是因为觉得它的关键CALL没有前边那两个那样好找(其实也就那样了^_^)极具代表性,而且通过它可以让你感受一下Ollydbg这个魅力比你家的荼几还大的调试器
这里之所以提到Ollydbg,是觉的它真是一个非常非常棒的调试器...强烈建议你多玩几次...(MP3好听吗? ^_^)
我们来吧,首先当然还是要装上它(左闪术,右闪术),然后用Ollydbg来载入,此时界面会被分成四个部分,左上方是软件反汇编后的代码,右上方是寄存器开会的地方,左下方是内存区,右下方显示的则是堆栈的信息。
我们来下断点,按Alt+F4,之后选USER32,然后再鼠标右键-->搜索-->当前模块中的名称,然后在那一大堆函数中找到GetDlgItemTextA,按F2来下断点,它会提示你错误,并说无法设置中断点,是不是很过瘾?(呜呜呜...大哥,我错了,再也不敢了...)
呵呵,这个我也不知道什么原因,明明是用了这个函数嘛,就是不让断,其实我对Ollydbg也不是太那个(关键是讨厌它的下断方式)看来还是用我们的万能断点吧,输入注册名Suunb[CCG],输入注册码19870219,然后用TRW2000下断bpx hmemcpy,断到之后,pmodule返回领空后一次F12就会出错,看来所有的东东就在这里了...
我们用TRW2000再断一下,返回领空之后记着第一条指令的地址0040bd5f,呜呜呜...上条指令明明是调用GetDlgItemTextA,为什么在Ollydbg中不让下呢?
没关系,我们记下这个地址后仍旧用Ollydbg来加载程序,之后在反汇编窗口中找到0040bd5f处,然后按下F2来下断(会变为红色),下断之后便按F9来运行程序,接着输入注册名Suunb[CCG],注册码19870219后按确定,程序会被Ollydbg给断到:
0040BD5F |. 57 PUSH EDI
0040BD60 |. E8 F34A0500 CALL WINZIP32.00460858
0040BD65 |. 57 PUSH EDI ; /Arg1
0040BD66 |. E8 164B0500 CALL WINZIP32.00460881 ; \WINZIP32.00460881
0040BD6B |. 59 POP ECX
0040BD6C |. BE 1CCA4C00 MOV ESI,WINZIP32.004CCA1C
0040BD71 |. 59 POP ECX
0040BD72 |. 6A 0B PUSH 0B ; /Count = B (11.)
0040BD74 |. 56 PUSH ESI ; |Buffer => WINZIP32.004CCA1C
0040BD75 |. 68 810C0000 PUSH 0C81 ; |ControlID = C81 (3201.)
0040BD7A |. 53 PUSH EBX ; |hWnd
0040BD7B |. FF15 F4C54A00 CALL DWORD PTR DS:[<&USER32.GetDlgItemTe>; \GetDlgItemTextA
0040BD81 |. 56 PUSH ESI
0040BD82 |. E8 D14A0500 CALL WINZIP32.00460858
0040BD87 |. 56 PUSH ESI
0040BD88 |. E8 F44A0500 CALL WINZIP32.00460881
0040BD8D |. 803D F0C94C00 >CMP BYTE PTR DS:[4CC9F0],0
0040BD94 |. 59 POP ECX
0040BD95 |. 59 POP ECX
0040BD96 |. 74 5F JE SHORT WINZIP32.0040BDF7
0040BD98 |. 803D 1CCA4C00 >CMP BYTE PTR DS:[4CCA1C],0
0040BD9F |. 74 56 JE SHORT WINZIP32.0040BDF7
0040BDA1 |. E8 31F9FFFF CALL WINZIP32.0040B6D7 <--关键CALL,等会儿进去玩玩
0040BDA6 |. 84C0 TEST AL,AL <--根据关键CALL中比较的结果来做相应的测试
0040BDA8 |. 74 4D JE SHORT WINZIP32.0040BDF7 <--跳走就没戏!
0040BDAA |. 57 PUSH EDI
0040BDAB |. 68 08DE4B00 PUSH WINZIP32.004BDE08 ; ASCII "Name"
0040BDB0 |. FF35 1CC74A00 PUSH DWORD PTR DS:[4AC71C] ; WINZIP32.004BDDEC
0040BDB6 |. E8 8AFA0400 CALL WINZIP32.0045B845
0040BDBB |. 56 PUSH ESI
0040BDBC |. 68 C8EB4B00 PUSH WINZIP32.004BEBC8 ; ASCII "SN"
0040BDC1 |. FF35 1CC74A00 PUSH DWORD PTR DS:[4AC71C] ; WINZIP32.004BDDEC
0040BDC7 |. E8 79FA0400 CALL WINZIP32.0045B845
0040BDCC |. FF35 18C74A00 PUSH DWORD PTR DS:[4AC718] ; |Arg4 = 004BDDF4 ASCII "winzip32.ini"
0040BDD2 |. 6A 00 PUSH 0 ; |Arg3 = 00000000
0040BDD4 |. 6A 00 PUSH 0 ; |Arg2 = 00000000
0040BDD6 |. 68 14DE4B00 PUSH WINZIP32.004BDE14 ; |Arg1 = 004BDE14 ASCII "rrs"
0040BDDB |. E8 4CFA0400 CALL WINZIP32.0045B82C ; \WINZIP32.0045B82C
0040BDE0 |. A1 A8914C00 MOV EAX,DWORD PTR DS:[4C91A8]
0040BDE5 |. 83C4 28 ADD ESP,28
0040BDE8 |. 85C0 TEST EAX,EAX
0040BDEA |. 74 07 JE SHORT WINZIP32.0040BDF3
0040BDEC |. 50 PUSH EAX ; /hObject => 000013F4 (font)
0040BDED |. FF15 80C04A00 CALL DWORD PTR DS:[<&GDI32.DeleteObject>>; \DeleteObject
0040BDF3 |> 6A 01 PUSH 1
0040BDF5 |. EB 30 JMP SHORT WINZIP32.0040BE27
0040BDF7 |> E8 C3020000 CALL WINZIP32.0040C0BF
0040BDFC |. 68 8E020000 PUSH 28E
0040BE01 |. E8 61470500 CALL WINZIP32.00460567
0040BE06 |. 50 PUSH EAX ; |Arg3
0040BE07 |. 53 PUSH EBX ; |Arg2
0040BE08 |. 6A 3D PUSH 3D ; |Arg1 = 0000003D
0040BE0A |. E8 C8050400 CALL WINZIP32.0044C3D7 ; \WINZIP32.0044C3D7
我们用Ollydbg断到之后,可以像在TRW2000中一样通过F8(这个调试器跟我一样,也不喜欢F4^_^)来单步执行程序,我们按32下F8后程序就会出错,那我们在第二遍载入时按F8按到20多下时就仔细看看有没有可疑的地方,你一眼就可以看到0040BDA1处的这个关键CALL,我们只要追到这里时追进去就有可能看到软件正确的注册码
那还等什么呢?我们就进去吧...
按F7跟进后你会看的眼花眼花缭乱,到处都是PUSH跟POP,到底哪个才是呢?现在知道我为什么让你用Ollydbg了吧(偶起初也是要用TRW2000的,但临时改变主意 ^_^)用Ollydbg的一个最大好处就是可以真接看到寄存器中的值,特别是你通过F8来单步执行的时候,在反汇编代码的下边,会有一个小窗体,在那里可以显示相关指令中所使用的寄存器的值,爽吧!
我们按76下F8之后,在0040B803处就可以第一次看到正确的注册码了,呵呵,我这边儿是71C20EDC,然后你还会再陆续看到几次,爽?
另外我还发现一个有趣的事情,在WinZIP8.1中,一个注册名可以有两个注册码,呵呵,不知道是不是还有为特别用户准备的特别注册码以用来和普通的做区别 当程序通过比较,发现你输入的注册码不正确后竟然会再次算出另一个注册码来再比较一次,嘿嘿,我的第二个注册码是25170288
追入关键CALL里的代码:
0040B6D7 /$ 55 PUSH EBP
0040B6D8 |. 8BEC MOV EBP,ESP
0040B6DA |. 81EC 0C020000 SUB ESP,20C
0040B6E0 |. 8065 FF 00 AND BYTE PTR SS:[EBP-1],0
0040B6E4 |. 803D F0C94C00 >CMP BYTE PTR DS:[4CC9F0],0
0040B6EB |. 53 PUSH EBX
0040B6EC |. 56 PUSH ESI
0040B6ED |. 57 PUSH EDI
0040B6EE |. 0F84 FB000000 JE WINZIP32.0040B7EF
0040B6F4 |. 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18]
0040B6F7 |. 50 PUSH EAX
0040B6F8 |. 68 C0E84B00 PUSH WINZIP32.004BE8C0
0040B6FD |. E8 DE61FFFF CALL WINZIP32.004018E0
0040B702 |. 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18]
0040B705 |. 50 PUSH EAX
0040B706 |. E8 F57C0800 CALL WINZIP32.00493400
0040B70B |. 83C4 0C ADD ESP,0C
0040B70E |. 83F8 14 CMP EAX,14
0040B711 |. 72 11 JB SHORT WINZIP32.0040B724
0040B713 |. BF 20C74A00 MOV EDI,WINZIP32.004AC720 ; ASCII "auth.c"
0040B718 |. 6A 21 PUSH 21
0040B71A |. 57 PUSH EDI
0040B71B |. E8 86F60000 CALL WINZIP32.0041ADA6
0040B720 |. 59 POP ECX
0040B721 |. 59 POP ECX
0040B722 |. EB 05 JMP SHORT WINZIP32.0040B729
0040B724 |> BF 20C74A00 MOV EDI,WINZIP32.004AC720 ; ASCII "auth.c"
0040B729 |> 8D85 F4FDFFFF LEA EAX,DWORD PTR SS:[EBP-20C]
0040B72F |. BB F0C94C00 MOV EBX,WINZIP32.004CC9F0 ; ASCII "Suunb[CCG]"
0040B734 |. 50 PUSH EAX
0040B735 |. 53 PUSH EBX
0040B736 |. E8 50030000 CALL WINZIP32.0040BA8B
0040B73B |. 8D85 F4FDFFFF LEA EAX,DWORD PTR SS:[EBP-20C]
0040B741 |. 50 PUSH EAX
0040B742 |. E8 B97C0800 CALL WINZIP32.00493400
0040B747 |. BE C8000000 MOV ESI,0C8
0040B74C |. 83C4 0C ADD ESP,0C
0040B74F |. 3BC6 CMP EAX,ESI
0040B751 |. 72 0A JB SHORT WINZIP32.0040B75D
0040B753 |. 6A 23 PUSH 23
0040B755 |. 57 PUSH EDI
0040B756 |. E8 4BF60000 CALL WINZIP32.0041ADA6
0040B75B |. 59 POP ECX
0040B75C |. 59 POP ECX
0040B75D |> 8D85 F4FDFFFF LEA EAX,DWORD PTR SS:[EBP-20C]
0040B763 |. 50 PUSH EAX
0040B764 |. 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18]
0040B767 |. 50 PUSH EAX
0040B768 |. E8 03300900 CALL WINZIP32.0049E770
0040B76D |. 59 POP ECX
0040B76E |. 85C0 TEST EAX,EAX
0040B770 |. 59 POP ECX
0040B771 |. 75 04 JNZ SHORT WINZIP32.0040B777
0040B773 |. C645 FF 01 MOV BYTE PTR SS:[EBP-1],1
0040B777 |> 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18]
0040B77A |. 50 PUSH EAX
0040B77B |. 68 D0E84B00 PUSH WINZIP32.004BE8D0
0040B780 |. E8 5B61FFFF CALL WINZIP32.004018E0
0040B785 |. 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18]
0040B788 |. 50 PUSH EAX
0040B789 |. E8 727C0800 CALL WINZIP32.00493400
0040B78E |. 83C4 0C ADD ESP,0C
0040B791 |. 83F8 14 CMP EAX,14
0040B794 |. 72 0A JB SHORT WINZIP32.0040B7A0
0040B796 |. 6A 27 PUSH 27
0040B798 |. 57 PUSH EDI
0040B799 |. E8 08F60000 CALL WINZIP32.0041ADA6
0040B79E |. 59 POP ECX
0040B79F |. 59 POP ECX
0040B7A0 |> 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18]
0040B7A3 |. 50 PUSH EAX
0040B7A4 |. 53 PUSH EBX
0040B7A5 |. E8 C62F0900 CALL WINZIP32.0049E770
0040B7AA |. 59 POP ECX
0040B7AB |. 85C0 TEST EAX,EAX
0040B7AD |. 59 POP ECX
0040B7AE |. 75 0E JNZ SHORT WINZIP32.0040B7BE
0040B7B0 |. FF15 F0C14A00 CALL DWORD PTR DS:[<&KERNEL32.GetTickCou>; [GetTickCount
0040B7B6 |. A8 01 TEST AL,1
0040B7B8 |. 74 04 JE SHORT WINZIP32.0040B7BE
0040B7BA |. C645 FF 01 MOV BYTE PTR SS:[EBP-1],1
0040B7BE |> 6A 14 PUSH 14
0040B7C0 |. 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18]
0040B7C3 |. 6A 00 PUSH 0
0040B7C5 |. 50 PUSH EAX
0040B7C6 |. E8 75820800 CALL WINZIP32.00493A40
0040B7CB |. 56 PUSH ESI
0040B7CC |. 8D85 F4FDFFFF LEA EAX,DWORD PTR SS:[EBP-20C]
0040B7D2 |. 6A 00 PUSH 0
0040B7D4 |. 50 PUSH EAX
0040B7D5 |. E8 66820800 CALL WINZIP32.00493A40
0040B7DA |. 83C4 18 ADD ESP,18
0040B7DD |. 807D FF 00 CMP BYTE PTR SS:[EBP-1],0
0040B7E1 |. 74 13 JE SHORT WINZIP32.0040B7F6
0040B7E3 |. E8 D7080000 CALL WINZIP32.0040C0BF
0040B7E8 |. 8025 EDBF4C00 >AND BYTE PTR DS:[4CBFED],0
0040B7EF |> 32C0 XOR AL,AL
0040B7F1 |. E9 F5000000 JMP WINZIP32.0040B8EB
0040B7F6 |> 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144]
0040B7FC |. 50 PUSH EAX
0040B7FD |. 53 PUSH EBX
0040B7FE |. E8 ED000000 CALL WINZIP32.0040B8F0 <--参与计算软正确的注册码
0040B803 |. 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144] <--在这里第一次发现软件正确的注册码
0040B809 |. 50 PUSH EAX
0040B80A |. E8 F17B0800 CALL WINZIP32.00493400
0040B80F |. BE 2C010000 MOV ESI,12C
0040B814 |. 83C4 0C ADD ESP,0C
0040B817 |. 3BC6 CMP EAX,ESI
0040B819 |. 72 0A JB SHORT WINZIP32.0040B825
0040B81B |. 6A 39 PUSH 39
0040B81D |. 57 PUSH EDI
0040B81E |. E8 83F50000 CALL WINZIP32.0041ADA6
0040B823 |. 59 POP ECX
0040B824 |. 59 POP ECX
0040B825 |> BF 1CCA4C00 MOV EDI,WINZIP32.004CCA1C ; ASCII "19870219" <--将刚才输入的错误的注册码放入EDI
0040B82A |. 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144] <--EAX中装入正确的注册码所在的地址
0040B830 |. 57 PUSH EDI <--用户输入的注册码入栈
0040B831 |. 50 PUSH EAX <--软件计算出的正确的注册码入栈
0040B832 |. E8 392F0900 CALL WINZIP32.0049E770 <--关键CALL,用于比较用户输入的注册码
0040B837 |. F7D8 NEG EAX
0040B839 |. 1AC0 SBB AL,AL
0040B83B |. 59 POP ECX
0040B83C |. FEC0 INC AL
0040B83E |. 59 POP ECX
0040B83F |. A2 EDBF4C00 MOV BYTE PTR DS:[4CBFED],AL
0040B844 |. 0F85 8A000000 JNZ WINZIP32.0040B8D4
0040B84A |. 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144]
0040B850 |. 50 PUSH EAX
0040B851 |. 53 PUSH EBX
0040B852 |. E8 33010000 CALL WINZIP32.0040B98A <--参与计算软件的第二个注册码
0040B857 |. 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144] <--此时软件会再算出另外一个注册码
0040B85D |. 50 PUSH EAX
0040B85E |. E8 9D7B0800 CALL WINZIP32.00493400
0040B863 |. 83C4 0C ADD ESP,0C
0040B866 |. 3BC6 CMP EAX,ESI
0040B868 |. 72 0E JB SHORT WINZIP32.0040B878
0040B86A |. 6A 3E PUSH 3E
0040B86C |. 68 20C74A00 PUSH WINZIP32.004AC720 ; ASCII "auth.c"
0040B871 |. E8 30F50000 CALL WINZIP32.0041ADA6
0040B876 |. 59 POP ECX
0040B877 |. 59 POP ECX
0040B878 |> 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144] <--软件计算出的第二个注册码装入EAX中
0040B87E |. 57 PUSH EDI <--用户输入的注册码入栈
0040B87F |. 50 PUSH EAX <--软件计算出的第二个注册码入栈
0040B880 |. E8 EB2E0900 CALL WINZIP32.0049E770 <--另一个关键CALL,用于比较第二次生成的注册码
0040B885 |. F7D8 NEG EAX
0040B887 |. 1AC0 SBB AL,AL
0040B889 |. 59 POP ECX
0040B88A |. FEC0 INC AL
0040B88C |. 59 POP ECX
0040B88D |. A2 EDBF4C00 MOV BYTE PTR DS:[4CBFED],AL
0040B892 |. 75 40 JNZ SHORT WINZIP32.0040B8D4
0040B894 |. 8D85 C0FEFFFF LEA EAX,DWORD PTR SS:[EBP-140]
0040B89A |. 6A 04 PUSH 4
0040B89C |. 50 PUSH EAX
0040B89D |. 57 PUSH EDI
0040B89E |. E8 DD690900 CALL WINZIP32.004A2280
0040B8A3 |. 83C4 0C ADD ESP,0C
0040B8A6 |. 85C0 TEST EAX,EAX
0040B8A8 |. 75 23 JNZ SHORT WINZIP32.0040B8CD
0040B8AA |. 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144]
0040B8B0 |. 6A 04 PUSH 4
0040B8B2 |. 50 PUSH EAX
0040B8B3 |. 68 20CA4C00 PUSH WINZIP32.004CCA20 ; ASCII "0219"
0040B8B8 |. E8 C3690900 CALL WINZIP32.004A2280
0040B8BD |. 83C4 0C ADD ESP,0C
0040B8C0 |. 85C0 TEST EAX,EAX
0040B8C2 |. 75 09 JNZ SHORT WINZIP32.0040B8CD
0040B8C4 |. C605 EDBF4C00 >MOV BYTE PTR DS:[4CBFED],1
0040B8CB |. EB 07 JMP SHORT WINZIP32.0040B8D4
0040B8CD |> 8025 EDBF4C00 >AND BYTE PTR DS:[4CBFED],0
0040B8D4 |> 56 PUSH ESI
0040B8D5 |. 8D85 BCFEFFFF LEA EAX,DWORD PTR SS:[EBP-144]
0040B8DB |. 6A 00 PUSH 0
0040B8DD |. 50 PUSH EAX
0040B8DE |. E8 5D810800 CALL WINZIP32.00493A40
0040B8E3 |. A0 EDBF4C00 MOV AL,BYTE PTR DS:[4CBFED]
0040B8E8 |. 83C4 0C ADD ESP,0C
0040B8EB |> 5F POP EDI
0040B8EC |. 5E POP ESI
0040B8ED |. 5B POP EBX
0040B8EE |. C9 LEAVE
0040B8EF \. C3 RETN
整理一下:
注册名:Suunb[CCG]
注册码:71C20EDC or 25170288
其实如果你坐在那里肯花上一杯茶的功夫来仔细想一下,就会知道,其实一点儿也不难,只是有一点点麻烦而以
这一章也就到这里吧,我现在巨困无比...
最后说一下的是,现在有仍有N多的软件用的是明码的比较方法,所以,要想找一两个软件练练手还是挺容易的
这一章本来还打算讲一下那些非明码比较的软件的,但忽然发现,如果通过非明码比较的软件能找到注册码的话,那应该也就把它的算法给搞的差不多了,所以,到下一章,分析软件的注册算法时再讲吧...
第八章--注册码是怎样炼成的
你应该明白的是,并不是所有的软件作者都像你想象并希望的那笨 没有人愿意自己的软件被别人在调试器中用一条d指令就能找到正确的注册码...要是那样的话还出来搞什么?
前边儿我们讲的查找软件注册码的方法是有针对性的,必须保证的是该软件使用的是明码比较,这样的话,我们只需找对地方,一个d指令就成了。那既然有明码比较这个词,就不难猜出还有相应的非明码比较...非明码比较也比较容易理解,就是软件比较两个注册码的方法不同而以,并不是计算出正确的注册码后就与用户输入的进行比较,它可能会采用每计算出一位就与注码中的相应位比较一次,一但发现与用户输入的不同,就提示出错等等等等...
遇到这样的软件,我们其实也可以找到其相应的注册码,但有点儿惨,要一位一位的计下来...但是如果人家不给你面子,一但计算出某位不正确就跳走的话,那你怎么办?所以,国民想致富,种树是根本...NG!所以遇到这种软件,我们就只有对其算法进行分析,并做出注册机才是唯一的方法(如果你想写注册机的话)...
你要明白,就算我们能找到那些采用明码比较的软件的注册码,原因也仅仅是因为其采用的是明码比较,所以我们没有什么值的高兴的地方,我们真正要做的,并不是找到一个注册码而以...当然如果你刚入门,那对你的提高还是很有帮助的。我们Crack一个软件的最终目的,是对其进行相应的分析,搞懂它的注册算法并写出注册机,这样才算是成功的Crack了一个软件,成功后的心情是难以表达的!就像你便秘了多天后一下子排了出来一样^_^,呵呵这个比喻虽然粗俗,但是你可以想象一下,对一个软件进行仔细的分析,最后一下把它的算法给搞明白了,那种感觉...我深信不疑的认为有一天你也能体会的到,偶等你
相信你以前看过那些高人大虾的关于软件注册算法分析的文章,同时也相信你有过试图跟踪分析某软件的举动,虽然后来的结果另人不太满意
其实分析一个软件的注册算法,这其中包括了一些技巧性方面的东西以及必要的经验,很难想象一个连调试器的使用都还没掌握的人试图去分析一个软件会是怎样一个场面...嘿嘿,偶是见过的 使用调试器并不难,但那并不意味着你就能去分析一个软件了,见CALL就追这样的举动可不是偶一个人有过的经历,本章我尽量给你说明适当的分析方法。
相信大家都有不在父母陪同下独自使用调试器的能力以及看懂大部分汇编指令的能力了吧,那就够了!我们开始...
正式开始今天的正题,我来举两个例子,相信这两个例子都有足够的表达能力,最起码比我们家楼下那个卖油条的表达能力要强多了...
好的,首先,我们还是请出我们的那位老朋友吧 嘿嘿,在此,偶向*P(中华压缩)v7.0的作者表示我内心最真诚的歉意!相信我用这个老版本的中华压缩不会给您带来经济上的麻烦...
通过前边儿两章的讲解,我们已经把这个软件大体上给搞明白了,并且也追出了其相应的注册码。而我们今天的目的是对其注册算法进行分析,并写出注册机!这个软件的注册算法其实也比较简(并且存在Bug)用它来当例子,很能说明情况...
好的,我们开始,前边儿追注册码的时候我们就已经知道了其用于计算正确注册码的关键CALL的所在位置为004f4dde,我们用TRW2000来对其进行分析!(鉴于目前大部分教程中仍以TRW2000为主,而且这个是大多数菜鸟都会用的调试器,偶就用这个调试器来做具体讲解)
先启动*P,帮助--注册(所以我才说这个软件非常适合写教程用嘛,注册后仍然中以再次注册)输入注册名Suunb[CCG],注册码19870219。之看按Ctrl+N呼出TRW2000,下断点bpx 004f4dde,F5返回。
接着就按确定吧,呵呵,被TRW2000拦到了。通过前边两章的分析,我们以经知道了004f4dde处的这个CALL用于计算正确的注册码,所以我们直接按F8跟进吧!注册码的算法,就包涵在这个CALL中,把它给分析透了,我们也就能弄明白软件的注册码是怎样生成的了。但是要怎么分析呢?这是一个比较严肃的问题,面对那一堆堆的指令,我不知道你是怎么想的,反正我第一次时是觉的找不着北,我怎么哪些重要哪些不重要呢?再说了,里面又包涵了那么多CALL,我还要一个一个地追进去看看?
呵呵,这就是我说的技巧所在了。其实也没什么可怕的,只要你汇编不是问题,就行了。我们首先可以先把这个计算注册码的CALL从头到尾执行一遍,搞明白其中大概的跳转以及其中某些CALL的作用,hehe~~你可以执行过一个CALL后就看一下各个寄存器的变化情况(如果寄存器中的值改变了,颜色就会变)如果某寄存器的值在CALL过之后改变了,我们就可以看一下其包含的值是何类型,如是内存地址就用d指令看一下,如是数值就看一下是不是得到你输入注册名或注册码的位数等等,这样的话就可以淘汰下来一大部分的CALL,因为有许多CALL的作用只是把注册名或注册码装入到内存中的某个地址或者得到注册名(注册码)的位数或注册码某一位的ASCII码,对与这些,我们不必深究。还是推荐你用Ollydbg,执行过一条指令后很多信息都可以看到 好的,我接着说,按F8追入CALL之后先大概走一遍...我给出追入后的反汇编代码,并给出注释,相应的分析看后面...
0167:004f4fac push ebp <--F8跟入后的第一条指令
0167:004f4fad mov ebp,esp
0167:004f4faf push byte +00
0167:004f4fb1 push byte +00
0167:004f4fb3 push byte +00
0167:004f4fb5 push byte +00
0167:004f4fb7 push byte +00
0167:004f4fb9 push byte +00
0167:004f4fbb push byte +00
0167:004f4fbd push ebx
0167:004f4fbe push esi
0167:004f4fbf push edi
0167:004f4fc0 mov [ebp-08],ecx
0167:004f4fc3 mov [ebp-04],edx
0167:004f4fc6 mov eax,[ebp-04]
0167:004f4fc9 call 004041c0
0167:004f4fce xor eax,eax
0167:004f4fd0 push ebp
0167:004f4fd1 push dword 004f5097
0167:004f4fd6 push dword [fs:eax]
0167:004f4fd9 mov [fs:eax],esp
0167:004f4fdc xor esi,esi
0167:004f4fde lea eax,[ebp-0c]
0167:004f4fe1 mov edx,[ebp-04]
0167:004f4fe4 call 00403e24 <--此CALL过后用于得到用户输入的注册名
0167:004f4fe9 mov eax,[ebp-0c] <--将得到的注册名的地址装用eax寄存器
0167:004f4fec call 0040400c <--此CALL用于得到用户输入的注册名的位数,并将其放入eax中
0167:004f4ff1 mov edi,eax <--将注册名的位数装入edi中
0167:004f4ff3 test edi,edi <--对edi进行测试
0167:004f4ff5 jng 004f5051 <--如果edi中的值为0就会跳走
0167:004f4ff7 mov ebx,01 <--ebx置1,用于后面的运算
0167:004f4ffc mov eax,[ebp-0c] <--ebp-0c中装的是注册名的内存地址,此时将其付于eax
0167:004f4fff mov al,[eax+ebx-01] <--eax中此时装的是注册名的内存地址,加上ebx中的值再减去01,用于得到注册码中的相应位的字符,比如说我们第一次执行到这里的时候ebx中装入的是01,再减去01后得到的值其实还是eax本身,这样就能得到注册名中的第一个字符了,而执行到后边再跳回来时ebx会加上1,所以就能得到下一个字符了...
0167:004f5003 call 004f4f60 <--这个CALL很重要,后面会说明我们是怎样知道它很重要的
0167:004f5008 test al,al <--在这里我们会发现一个测试运算,对象是al,而al在前边CALL之前刚装入了注册名中的某一个字符,所以我们可以断定上面的那个CALL会对得到的字符做上一些手脚,待会儿我们再跟入...
0167:004f500a jz 004f5031 <--如果al中装的是0就跳到004f5031处,而al中的值会被004f5003处的那个CALL所改变
0167:004f500c lea eax,[ebp-18]
0167:004f500f mov edx,[ebp-0c] <--ebp-0c中装的是注册名的内存地址,此时装入edx中
0167:004f5012 mov dl,[edx+ebx-01] <--跟前边儿004f4fff处的指令道理相同,得到注册码中的当前参加运算的字符
0167:004f5016 call 00403f34 <--不重要!!
0167:004f501b mov eax,[ebp-18]
0167:004f501e lea edx,[ebp-14]
0167:004f5021 call 004088ac <--不重要!!
0167:004f5026 mov edx,[ebp-14]
0167:004f5029 lea eax,[ebp-10]
0167:004f502c call 00404014 <--该CALL同样比较重要,其作用是这样的,如果当前参加运算的字符在前边004f5003的CALL里进行运算之后符合了要求(符合要求后al会被置非0值)那么在004f500a处的跳转将会失去作用,而执行到这里后该CALL会将当前的这个符合要求的字符保存到00D3B3C4处(内存)!!后边儿会再详细说明
0167:004f5031 cmp ebx,byte +01 <--用此时ebx中装的值减去1
0167:004f5034 jz 004f5040 <--如果为零,也就是说此时计算的是注册名中的第一个字符的话就跳到004f5040处
0167:004f5036 mov eax,[ebp-0c] <--ebp-0c中装的是注册名的内存地址,该指令将注册名的内存地址装入eax中
0167:004f5039 movzx eax,byte [eax+ebx-02] <--用于得到上一个参加运算的字符
0167:004f503e jmp short 004f5046 <--无条件跳转到004f5046处
0167:004f5040 mov eax,[ebp-0c] <--ebp-0c中装的是注册名的内存地址
0167:004f5043 movzx eax,byte [eax] <--得到注册名的第一个字符
0167:004f5046 lea esi,[esi+eax*4+a8] <--!!!这一条指令就是关键所在,后面会说明的!!!此指令先得到本轮参加运算的字符的ASCII码,然后乘以6,之后再加上a8(即十进制数168,呵呵,可以理解)同时再将这个字符计算得到的值与前面已经运算过的字符的值的和相加!
0167:004f504d inc ebx <--ebx加1,用于得到注册码的下一个字符
0167:004f504e dec edi <--edi减1,edi中装的是注册码的位数
0167:004f504f jnz 004f4ffc <--不为零就跳到004f4ffc处开始对下一个字符进行运算...也就是说每计算完一个字符就将edi减去1,直到其为0也就是所有的字符全参加过运算为止。
0167:004f5051 lea edx,[ebp-1c] <--把装注册码后半部分的地址装入edx,传给下面的CALL
0167:004f5054 mov eax,esi <--将前面计算的注册码的后半部分的值装入eax中
0167:004f5056 call 00408c70 <--将前面计算得到的注册码后半部分的值转换为十进制,并装入ebp-1c中
0167:004f505b mov ecx,[ebp-1c] <--epb-1c中装的是注册码的后半部分
0167:004f505e lea eax,[ebp-0c]
0167:004f5061 mov edx,[ebp-10] <--ebp-10中装的是注册码的前半部分
0167:004f5064 call 00404058 <--该CALL用于将前后两部分注册码合并置一起,合并后的注册码会存放置ebp-0c处
0167:004f5069 mov eax,[ebp-08]
0167:004f506c mov edx,[ebp-0c]
0167:004f506f call 00403de0
0167:004f5074 xor eax,eax
0167:004f5076 pop edx
0167:004f5077 pop ecx
0167:004f5078 pop ecx
0167:004f5079 mov [fs:eax],edx
0167:004f507c push dword 004f509e
0167:004f5081 lea eax,[ebp-1c]
0167:004f5084 mov edx,05
0167:004f5089 call 00403db0
0167:004f508e lea eax,[ebp-04]
0167:004f5091 call 00403d8c
呵呵,看了我加了注释后的代码是不是好理解多了?你也许会问,你怎么知道那些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要求的字符)
HOHOHO~~(请模仿周星星式的笑声...)现在我们已经明白了一半了...好的,我们继续...
不管你是从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给搞明白,这样的话,我们才能对注册名中的字符进行筛选...
好的,我们再来:
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码,然后用其减去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);
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;
end;
Result:=Code;
end;
你可以这样来用:
var
S1,S2:String;
begin
S1:=Edit1.text; //Edit1用来输入注册名;
S2:=GetKeyChar(S1); //此时S2中得到的便是注册名中所有符合要求的字符了;
end;
嘿嘿,你现在是不是很想知道都有哪些字符能符合要求?嘿嘿,其实*P自己就可以告诉我们,你只要在输入注册名的时候把所有的可用字符全填上,然后在用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注册一下看看...HOHO~~(请仍旧模仿周星星式的笑声)这个粗心的作者啊,造成这样的原因很简单,软件根本就没有判断注名是否为空并在软件初始化的时候把用于计算注册码后半部分的integer变量付了初始值0,否则的话00D3B3C4处的内存应该为空值..(难不成到时连0都不用输就能注册?)
所以说,Crack并不是一件坏事,像这种情况你完全可以告诉作者的嘛,到时不但交了一个朋友而且说不准还会得到个免费的注册码....(不知道有没有白帽子Cracker?嘿嘿,CCC刚好也可以是注册码的前半部分哦~~)我希望你明白,对于这种注册算法简单且存在Bug的软件(通常也说明其作者还没什么经验 ^_^),我们不应该为能提供它注册机而感到高兴,如果能帮助其作者改善算法或去掉Bug,又何尝不是一件好事呢?毕竟软件上面加的有中华两个字,你忍心???
我不知道上面给你讲的中华压缩注册分析你是否看懂了,我个人认为我讲的还是比较详细的了(几乎每条指令都加了注释且又再三在后面说明)但如果你仍然看不懂的话,请务必相信是本人写的文章不好,不要放弃啊哥们儿~~!
好了,我再来给你举另外一个例子...通过它来给你讲一下另外一种比较常见的注册码计算方法,即将运算的结果与一个表中的字符进行转换,也就是常说的密码表啦^_^
本来是想用网际快车FlashGet的,可是在看雪已经有人贴了最新的1.40版的破文&注册机,正好前些天的时候网友啥也不是在后面跟贴说要帮他看一下语音界面2.0这个软件,down下来后大概看了一下,呵呵,发现这个正是我想要的,注册码计算的过程中采用了密码表并且也不难...hehe~~后来HMILY老哥看到了啥也不是的另一个贴子,也写个注册机和破文,你可以参考一下,嘿嘿,HMILY跟偶是自己人,所以偶不怕他...
我们开始吧...
首先运算一下这个软件,其会自动生成机器码,在我这边儿是xn2urkeUMwpNv5xZ。
这一章,我会用调试器Ollydbg来作讲解,它真的很好用...我们开始吧:
偶不知道你是否喜欢Ollydbg的下断方式,总知偶是不喜欢,从那么多API里面先(字好小),再说了,偶还是喜欢用Hmemcpy来断,除非断不到或在2K/XP下,否则偶才不要去跑API呢,往往要三四次才断到,多累啊 我们还是先请临时演员TRW2000出出一下场吧(把你的MP3先暂停一下 ),下bpx hmemcpy点确定后会被拦,pmodule后返回到0040432f处,我们就从这里开始吧 导演:"Stop!换吴孟达出场" 嘿嘿,继续你的MP3,请同时打开Ollydbg
我们用Ollydbg载入,在反汇编代码处(即左上方那个子窗口中)按Ctrl+G,输入0040432f,回车后便来到了这里。我们大概看看,有经验的话很容易就会看出此地便是了(这次运气比较好,一下就能找到软件计算注册码的地方 ^_^)
好吧,我们按F2在这里下断,接着按F9来运行程序,在注册处输入CHINA Cracking Group Suunb后按确定会被Ollydbg断下来,大概跑跑看看吧 我贴出反汇编代码:
0040432F |. 8D4C24 10 LEA ECX,DWORD PTR SS:[ESP+10]
00404333 |. 6A 05 PUSH 5
00404335 |. 51 PUSH ECX
00404336 |. 68 E8030000 PUSH 3E8
0040433B |. 8BCE MOV ECX,ESI
0040433D |. E8 10050000 CALL
00404342 |. 8BC8 MOV ECX,EAX
00404344 |. E8 7D060000 CALL
00404349 |. 8D5424 18 LEA EDX,DWORD PTR SS:[ESP+18]
0040434D |. 6A 05 PUSH 5
0040434F |. 52 PUSH EDX
00404350 |. 68 E9030000 PUSH 3E9
00404355 |. 8BCE MOV ECX,ESI
00404357 |. E8 F6040000 CALL
0040435C |. 8BC8 MOV ECX,EAX
0040435E |. E8 63060000 CALL
00404363 |. 8D4424 20 LEA EAX,DWORD PTR SS:[ESP+20]
00404367 |. 6A 05 PUSH 5
00404369 |. 50 PUSH EAX
0040436A |. 68 EA030000 PUSH 3EA
0040436F |. 8BCE MOV ECX,ESI
00404371 |. E8 DC040000 CALL
00404376 |. 8BC8 MOV ECX,EAX
00404378 |. E8 49060000 CALL
0040437D |. 8D4C24 28 LEA ECX,DWORD PTR SS:[ESP+28]
00404381 |. 6A 05 PUSH 5
00404383 |. 51 PUSH ECX
00404384 |. 68 EB030000 PUSH 3EB
00404389 |. 8BCE MOV ECX,ESI
0040438B |. E8 C2040000 CALL
00404390 |. 8BC8 MOV ECX,EAX
00404392 |. E8 2F060000 CALL
00404397 |. 8B7C24 68 MOV EDI,DWORD PTR SS:[ESP+68]
0040439B |. 33DB XOR EBX,EBX
0040439D |. 33C9 XOR ECX,ECX
0040439F |. 8D04BF LEA EAX,DWORD PTR DS:[EDI+EDI*4]
004043A2 |. 8D0480 LEA EAX,DWORD PTR DS:[EAX+EAX*4]
004043A5 |. 8D3480 LEA ESI,DWORD PTR DS:[EAX+EAX*4] <--上面的这些你都不需要管,因为它们帮不了我们什么忙
004043A8 |. C1E6 02 SHL ESI,2 <--执行后ESI的值为5DC,及十进制数1500
004043AB |> 0FBE440C 50 /MOVSX EAX,BYTE PTR SS:[ESP+ECX+50] <--ESP+ECX+50就是机器码在内存中的地址(首次执行到这里明ECX为0,得到的就是机器码的第一位,第二次到这里时ECX会加上1,得到的是第二位...)
004043B0 |. 03C6 |ADD EAX,ESI <--与ESI相加,也就是加上1500
004043B2 |. BD 3E000000 |MOV EBP,3E <--EBP置3E,及十进制数62
004043B7 |. 99 |CDQ <--扩展...
004043B8 |. F7FD |IDIV EBP <--EAX中装的机器码的第1 or 2 or 3 or 4位与1500的和与62相除
004043BA |. 0FBE440C 54 |MOVSX EAX,BYTE PTR SS:[ESP+ECX+54] <--ESP+ECX+54得到机器码的第5位(还记的ESP+ECX+50中装的是第一位吗?首次执行到这里明ECX为0,得到的就是机器码的第5位,第二次到这里时ECX会加上1,得到的是第6位...)
004043BF |. 03C6 |ADD EAX,ESI <--同样加上1500
004043C1 |. 8A92 E4704000 |MOV DL,BYTE PTR DS:[EDX+4070E4] <--重要的地方来了!此时EDX中装的是前面的第1 or 2 or 3 or 4位机器码加上1500后再除以62的余数,那4070E4处是什么呢?如果在TRW2000中,我们用d 004070E4就可以看到,在Ollydbg中,我们可以在左下角处按Ctrl+G来输入相应的内存地址,这样的话就可以看到了。我们会发现从4070E4开始,装的是一串字符,依次是0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ。呵呵明白什么意思了吗?4070E4处指的是0,那4070E4加上edx中装的余数后的内存地址中装的便是当前机器码所对应的注册码。(如余数为5,那么从4070E4处的0开始往后数第5个字符就是了)该条指令执行过后就会把相应的注册码装入dl中
004043C7 |. 88540C 30 |MOV BYTE PTR SS:[ESP+ECX+30],DL <--将机器码的第1 or 2 or 3 or 4所对应的注册码装入ESP+ECX+30处
004043CB |. 99 |CDQ <--前边儿得到的第5 or 6 or 7 or 8位机器码扩展
004043CC |. F7FD |IDIV EBP <--同样除以62
004043CE |. 8A82 E4704000 |MOV AL,BYTE PTR DS:[EDX+4070E4] <--与004043C1处作用相同,不用我多说了吧,就是得到当前机器码的对应注册码
004043D4 |. 88440C 38 |MOV BYTE PTR SS:[ESP+ECX+38],AL <--装入ESP+ECX+38处
004043D8 |. 0FBE440C 58 |MOVSX EAX,BYTE PTR SS:[ESP+ECX+58] <--得到机器码的第9 or 10 or 11 or 12位
004043DD |. 03C6 |ADD EAX,ESI <--与1500相加
004043DF |. 99 |CDQ <--扩展....
004043E0 |. F7FD |IDIV EBP <--除以62
004043E2 |. 0FBE440C 5C |MOVSX EAX,BYTE PTR SS:[ESP+ECX+5C] <--得到机器码的第13 or 14 or 15 or 16位
004043E7 |. 03C6 |ADD EAX,ESI <--与1500相加
004043E9 |. 8A92 E4704000 |MOV DL,BYTE PTR DS:[EDX+4070E4] <--前边的第9 or 10 or 11 or 12位所对应的注册码
004043EF |. 88540C 40 |MOV BYTE PTR SS:[ESP+ECX+40],DL <--装入ESP+ECX+40处
004043F3 |. 99 |CDQ <--扩展
004043F4 |. F7FD |IDIV EBP <--除以62
004043F6 |. 41 |INC ECX <--ECX加1
004043F7 |. 83F9 04 |CMP ECX,4 <--看ECX是否为4(前边儿是一次计算四位的嘛,第一次计算第1、5、9、13位,第二次是2、6、10、14...)
004043FA |. 8A82 E4704000 |MOV AL,BYTE PTR DS:[EDX+4070E4] <--得到前边的第13 or 14 or 15 or 16位机器码所对应的注册码
00404400 |. 88440C 47 |MOV BYTE PTR SS:[ESP+ECX+47],AL <--装入ESP+ECX+47处
00404404 |.^7C A5 \JL SHORT LIAOCACH.004043AB <--ECX小于4就从头再来一遍(直到16位机器码都计算完为止)
00404406 |. 8B35 AC524000 MOV ESI,DWORD PTR DS:[<&MSVCRT.atoi>] ; MSVCRT.atoi
0040440C |. 8D4C24 10 LEA ECX,DWORD PTR SS:[ESP+10]
00404410 |. 51 PUSH ECX ; /s
00404411 |. 885C24 38 MOV BYTE PTR SS:[ESP+38],BL ; |
00404415 |. 885C24 40 MOV BYTE PTR SS:[ESP+40],BL ; |
00404419 |. 885C24 48 MOV BYTE PTR SS:[ESP+48],BL ; |
0040441D |. 885C24 50 MOV BYTE PTR SS:[ESP+50],BL ; |
00404421 |. FFD6 CALL ESI ; \atoi
00404423 |. 83C4 04 ADD ESP,4
00404426 |. 83F8 01 CMP EAX,1
00404429 |. 75 3C JNZ SHORT LIAOCACH.00404467
0040442B |. 8D5424 18 LEA EDX,DWORD PTR SS:[ESP+18]
0040442F |. 52 PUSH EDX
00404430 |. FFD6 CALL ESI
00404432 |. 83C4 04 ADD ESP,4
00404435 |. 83F8 01 CMP EAX,1
00404438 |. 75 2D JNZ SHORT LIAOCACH.00404467
0040443A |. 8D4424 20 LEA EAX,DWORD PTR SS:[ESP+20]
0040443E |. 50 PUSH EAX
0040443F |. FFD6 CALL ESI
00404441 |. 83C4 04 ADD ESP,4
00404444 |. 83F8 01 CMP EAX,1
00404447 |. 75 1E JNZ SHORT LIAOCACH.00404467
00404449 |. 8D4C24 28 LEA ECX,DWORD PTR SS:[ESP+28]
0040444D |. 51 PUSH ECX
0040444E |. FFD6 CALL ESI
00404450 |. 83C4 04 ADD ESP,4
00404453 |. 83F8 01 CMP EAX,1
00404456 |. 75 0F JNZ SHORT LIAOCACH.00404467
00404458 |. 5F POP EDI
00404459 |. 5E POP ESI
0040445A |. 5D POP EBP
0040445B |. B8 FEFFFFFF MOV EAX,-2
00404460 |. 5B POP EBX
00404461 |. 83C4 54 ADD ESP,54
00404464 |. C2 0400 RETN 4
00404467 |> 8D7424 30 LEA ESI,DWORD PTR SS:[ESP+30] <--正确的注册码的前4位的地址装入ESI中,嘿嘿,执行到这里时我们就可以看到正确的注册的前4位了,在TRW2000 or SoftICE中可以用d ESP+30来查看而在Ollydbg中什么都不用做就可以在左上方反汇编代码区与左下内存区中间的那个小窗体中看见 在TRW2000中下过D指令后按Alt+上下键翻几下,就可以看到所有的注册码了(它们并没有分太开嘛0063F5E0-0063F5E3是前4位,0063F5E8-0063F5EB是5-8位,0063F5F0-0063F5F3是9-12位,0063F5F8-0063F5FB是最后4位,而你输入的注册码的内存地址:0063F5C0-0063F5C3是前4位,0063F5C8-0063F5CB是5-8位,0063F5D0-0063F5D3是9-12位,0063F5D8-0063F5DB是最后4位,机器码存放的地址是0063F6000-0063F60F)
0040446B |. 8D4424 10 LEA EAX,DWORD PTR SS:[ESP+10] <--你输入的注册码的前4位的地址装入EAX中
0040446F |> 8A10 /MOV DL,BYTE PTR DS:[EAX] <--得到你输入的注册码的第1位或第3位(EAX中的值到了后边儿会加上2,这样再执行到这里时得到的就是第3位了)
00404471 |. 8ACA |MOV CL,DL <--传入cl中
00404473 |. 3A16 |CMP DL,BYTE PTR DS:[ESI] <--与正确的注册码的第1位或第3位比较(ESI中的值会与EAX中的值一起改变)
00404475 |. 75 1C |JNZ SHORT LIAOCACH.00404493 <--不相等就跳走
00404477 |. 3ACB |CMP CL,BL <--CL与BL比较,BL中的值为00000000(也就是空啦),这条指令有什么用呢?其实很简单了,每4位注册码的后面都会再跟一个空值,也就是如你在内存中可以看到1234.那个.就是空值,明白过来了吧,等到前4位都被测试过了,cl中就会装入.也就是00000000,到时就可以在后面跳走了
00404479 |. 74 14 |JE SHORT LIAOCACH.0040448F <--如果CL中的值为零(即4位已经全部比较过了),就跳走
0040447B |. 8A50 01 |MOV DL,BYTE PTR DS:[EAX+1] <--EAX+1后得到的会是你输入的注册码的第2位或者第4位(视EAX的值而定)
0040447E |. 8ACA |MOV CL,DL <--传入CL
00404480 |. 3A56 01 |CMP DL,BYTE PTR DS:[ESI+1] <--与正确的注册码的第2位或第4位比较(ESI的值会与EAX一起改变)
00404483 |. 75 0E |JNZ SHORT LIAOCACH.00404493 <--不相等就跳走
00404485 |. 83C0 02 |ADD EAX,2 <--EAX加上2,这样的话待会儿再跳到0040446F处时,再得到的就是你输入的注册码的第3位了
00404488 |. 83C6 02 |ADD ESI,2 <--同上,ESI加上2后再跳到0040446F处重新再来一遍时就会得到正确的注册码的第3位
0040448B |. 3ACB |CMP CL,BL <--再次比较CL是否为空
0040448D |.^75 E0 \JNZ SHORT LIAOCACH.0040446F <--不为空就再跳到0040446F处,来继续比较前4位中的1、3两位
0040448F |> 33C0 XOR EAX,EAX <--1-4位全部比较完后会跳到这里
00404491 |. EB 05 JMP SHORT LIAOCACH.00404498
00404493 |> 1BC0 SBB EAX,EAX
00404495 |. 83D8 FF SBB EAX,-1
00404498 |> 3BC3 CMP EAX,EBX
0040449A |. 0F85 AB000000 JNZ LIAOCACH.0040454B
004044A0 |. 8D7424 38 LEA ESI,DWORD PTR SS:[ESP+38] <--与上面的大体相同嘛,将正确注册码的5-8位的内存地址装入ESI中
004044A4 |. 8D4424 18 LEA EAX,DWORD PTR SS:[ESP+18] <--你输入的注册码的5-8位的内存地址装入EAX中
004044A8 |> 8A10 /MOV DL,BYTE PTR DS:[EAX] <--得到你输入的注册码的第5 or 7位(道理我相信你一定已经明白了)
004044AA |. 8ACA |MOV CL,DL <--再装入CL中
004044AC |. 3A16 |CMP DL,BYTE PTR DS:[ESI] <--与正确的注册码的第5 or 7位比较
004044AE |. 75 1C |JNZ SHORT LIAOCACH.004044CC <--不相等就跳走
004044B0 |. 3ACB |CMP CL,BL <--与BL中的00000000比较,看5-8位是否已经全部比较完毕
004044B2 |. 74 14 |JE SHORT LIAOCACH.004044C8 <--是的话就跳走
004044B4 |. 8A50 01 |MOV DL,BYTE PTR DS:[EAX+1] <--得到你输入的注册码的第6 or 8位
004044B7 |. 8ACA |MOV CL,DL <--装入CL
004044B9 |. 3A56 01 |CMP DL,BYTE PTR DS:[ESI+1] <--与正确的注册码的第6 or 8位比较
004044BC |. 75 0E |JNZ SHORT LIAOCACH.004044CC <--不正确就跳走
004044BE |. 83C0 02 |ADD EAX,2 <--EAX加2,这样做的目的相信你已经知道了吧
004044C1 |. 83C6 02 |ADD ESI,2 <--ESI也加上2
004044C4 |. 3ACB |CMP CL,BL <--比较CL是否为空
004044C6 |.^75 E0 \JNZ SHORT LIAOCACH.004044A8 <--不是就跳回去再来一遍
004044C8 |> 33C0 XOR EAX,EAX <--5-8位全部比较完后全跳到这里
004044CA |. EB 05 JMP SHORT LIAOCACH.004044D1
004044CC |> 1BC0 SBB EAX,EAX
004044CE |. 83D8 FF SBB EAX,-1
004044D1 |> 3BC3 CMP EAX,EBX
004044D3 |. 75 76 JNZ SHORT LIAOCACH.0040454B
004044D5 |. 8D7424 40 LEA ESI,DWORD PTR SS:[ESP+40] <--将正确的注册码的9-12位的内存地址装入ESI
004044D9 |. 8D4424 20 LEA EAX,DWORD PTR SS:[ESP+20] <--你输入的
004044DD |> 8A10 /MOV DL,BYTE PTR DS:[EAX] <--得到你输入的注册码的第9 or 11位
004044DF |. 8ACA |MOV CL,DL <--装入CL
004044E1 |. 3A16 |CMP DL,BYTE PTR DS:[ESI] <--与正确的注册码的第9 or 11位比较
004044E3 |. 75 1C |JNZ SHORT LIAOCACH.00404501 <--不对便跳走
004044E5 |. 3ACB |CMP CL,BL <--看CL是否为空,即看9-12位是否全部比较完毕
004044E7 |. 74 14 |JE SHORT LIAOCACH.004044FD <--是的话跳走
004044E9 |. 8A50 01 |MOV DL,BYTE PTR DS:[EAX+1] <--得到你输入的注册码的第10 or 12位
004044EC |. 8ACA |MOV CL,DL <--装入CL
004044EE |. 3A56 01 |CMP DL,BYTE PTR DS:[ESI+1] <--与正确的注册码的第10 or 12位比较
004044F1 |. 75 0E |JNZ SHORT LIAOCACH.00404501 <--不相等就跳走
004044F3 |. 83C0 02 |ADD EAX,2 <--EAX加2
004044F6 |. 83C6 02 |ADD ESI,2 <--ESI加2
004044F9 |. 3ACB |CMP CL,BL <--看是否全部比较完毕
004044FB |.^75 E0 \JNZ SHORT LIAOCACH.004044DD <--没有就跳回去再来一遍
004044FD |> 33C0 XOR EAX,EAX <--9-12位全部比较完毕后会跳到这里
004044FF |. EB 05 JMP SHORT LIAOCACH.00404506
00404501 |> 1BC0 SBB EAX,EAX
00404503 |. 83D8 FF SBB EAX,-1
00404506 |> 3BC3 CMP EAX,EBX
00404508 |. 75 41 JNZ SHORT LIAOCACH.0040454B
0040450A |. 8D7424 48 LEA ESI,DWORD PTR SS:[ESP+48] <--嘿嘿,都讲了三遍了,你一定明白了吧,所以我把这最后一段留给你自己来看吧,从这里到00404530处我相信你猜也猜的出来 (众人:鄙视你!)
0040450E |. 8D4424 28 LEA EAX,DWORD PTR SS:[ESP+28]
00404512 |> 8A10 /MOV DL,BYTE PTR DS:[EAX]
00404514 |. 8ACA |MOV CL,DL
00404516 |. 3A16 |CMP DL,BYTE PTR DS:[ESI]
00404518 |. 75 1C |JNZ SHORT LIAOCACH.00404536
0040451A |. 3ACB |CMP CL,BL
0040451C |. 74 14 |JE SHORT LIAOCACH.00404532
0040451E |. 8A50 01 |MOV DL,BYTE PTR DS:[EAX+1]
00404521 |. 8ACA |MOV CL,DL
00404523 |. 3A56 01 |CMP DL,BYTE PTR DS:[ESI+1]
00404526 |. 75 0E |JNZ SHORT LIAOCACH.00404536
00404528 |. 83C0 02 |ADD EAX,2
0040452B |. 83C6 02 |ADD ESI,2
0040452E |. 3ACB |CMP CL,BL
00404530 |.^75 E0 \JNZ SHORT LIAOCACH.00404512
00404532 |> 33C0 XOR EAX,EAX <--全部通过后来到这里
00404534 |. EB 05 JMP SHORT LIAOCACH.0040453B
00404536 |> 1BC0 SBB EAX,EAX
00404538 |. 83D8 FF SBB EAX,-1
0040453B |> 3BC3 CMP EAX,EBX
0040453D |. 75 0C JNZ SHORT LIAOCACH.0040454B
0040453F |. 8BC7 MOV EAX,EDI
00404541 |. 5F POP EDI
00404542 |. 5E POP ESI
00404543 |. 5D POP EBP
00404544 |. 5B POP EBX
00404545 |. 83C4 54 ADD ESP,54
00404548 |. C2 0400 RETN 4
我的注释写的还算清楚吧 ^_^,我再大概给你讲解一下:
软件的注册码是这样计算出来的,机器码中的各个字符的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;