• 标 题:GIF Movie Gear v3.0.2 破解小谈(附注册机源码)  
  • 作 者:商朝子
  • 时 间:2003/04/13 04:34pm 
  • 链 接:http://bbs.pediy.com

GIF Movie Gear v3.0.2 破解小谈(附注册机源码)
最近在老罗的坛子里好多人的签名都换成了PL的图片。嘿嘿,偶自然也不愿落后,so便想自己DIY一个英俊潇洒玉树临风貌比潘安才比伯虎的挂上去,可是以前买的Flash MX D版盘怎么也找不到了,于是便到天空那边儿去找做GIF动画的软件,再于是便找到了这个软件,据说是使用简单,方便快捷…
结果,呵呵(冷汗…搞了一个上午也没搞出张像样的图片来)。偷鸡不成,怎么也要抓一把米回来吧,反正今天除了没完成的事儿其它的事儿都做完了… ;)
首先当然是先要看一下人家有没有加壳,结果是另人满意的,过程是另人愉快的,答案是VC6.0的。^_^具然没加壳…
那还等什么呢?点Help菜单中的Register Now…。接着就呼出你埋伏已久的调试器吧,我用的是TRW2000。(随便问一句哥们儿,TRW2000呼出是按Ctrl+N这事儿你知道吧,知道啊。那上海2010年办世博会,这事儿你也知道吧,哦,都知道了,那甲A前天晚上…当!…好好好,我继续就是了)呼出TRW2000后下断点bpx hmemcpy(我用的是98嘛),然后按F5返回。接着在Name处偶输入Suunb[CCG],在Code处输入20030413(呵呵,今天的日期 ^_^) 好的,接下来就按OK吧。
结果是显而易见的,成功被我军爱国者导弹给拦截掉了。之后就一个pmodule导弹过去跳出程序的领空,之后在按F12的时候会发现只按1次就提示出错了。
好的,知道这些后,就再来一遍吧。这次在pmodule返回后就按F10来单步执行,代码如下:
0167:00431972  lea      edx,[esp+c4]             <--会来到这里!
0167:00431979  push     byte +64
0167:0043197b  push     edx
0167:0043197c  push     dword 0450
0167:00431981  push     esi
0167:00431982  call     edi                      <--看上边儿压进去的参数,这个Call应该是调用API函数GetDlgItem来得到注册码输入框的句柄。
0167:00431984  push     eax
0167:00431985  call     ebx                      <--用先前得到的注册码输入框的句柄通过调用GetWindowTextA来得到输入的注册码
0167:00431987  lea      eax,[esp+c4]             <--用d指令可以知道esp+c4处装的是用户输入的注册码,将这个地址装入eax中
0167:0043198e  lea      ecx,[esp+60]             <--同样用d指令可以知道esp+60处装的是用户输入的注册名,装其地址装入ecx中
0167:00431992  push     eax                      <--注册码地址压栈
0167:00431993  push     ecx                      <--注册名地址压栈
0167:00431994  call     00431590                 <--嘿嘿,整个过程就由这个CALL一手操控了
0167:00431999  add      esp,byte +08             <--栈顶还原
0167:0043199c  test     eax,eax                  <--测试eax中装入的值
0167:0043199e  jz       near 00431a51            <--为0就跳到00431a51处,跳过去就Over了
0167:004319a4  lea      edx,[esp+10]             <--从这里开始程序通过调用RegCreateKeyExA、RegSetvalueExA等其它一些注册表相关API来向注册表中写入用户的注册信息。也就是HKEY_CURRENT_USER\Software\gamani\GIFMovieGear\2.0主键下面的RegName3与RegCode3子键。当然这是在你输入的注册码正确的前提下,RegName3中装的是你的注册名,RegCode3中自然就是你输入的册码了…
0167:004319a8  lea      eax,[esp+0c]
0167:004319ac  push     edx
0167:004319ad  push     eax
0167:004319ae  push     byte +00
0167:004319b0  push     dword 000f003f
0167:004319b5  push     byte +00
0167:004319b7  push     dword 0044ed14
0167:004319bc  push     byte +00
0167:004319be  push     dword 0044b3f8           <--此处可以看一下0044b3f8,得到Software\gamani\GIFMovieGear\2.0
0167:004319c3  push     dword 80000001           <--这个值就是HKEY_CURRENT_USER
0167:004319c8  call     `ADVAPI32!RegCreateKeyExA`   <--API函数RegCreateKeyExA    
0167:004319ce  lea      edi,[esp+60]
0167:004319d2  or       ecx,byte -01
0167:004319d5  xor      eax,eax
0167:004319d7  mov      edx,[esp+0c]
0167:004319db  repne scasb
0167:004319dd  not      ecx
0167:004319df  mov      ebx,[00448018]
0167:004319e5  push     ecx
0167:004319e6  lea      ecx,[esp+64]
0167:004319ea  push     ecx
0167:004319eb  push     byte +01
0167:004319ed  push     eax
0167:004319ee  push     dword 0044d498           <--0044d498处就是字符串RegName3
0167:004319f3  push     edx
0167:004319f4  call     ebx                      <--RegSetvalueExA
0167:004319f6  lea      edi,[esp+c4]
0167:004319fd  or       ecx,byte -01
0167:00431a00  xor      eax,eax
0167:00431a02  repne scasb
0167:00431a04  not      ecx
0167:00431a06  lea      eax,[esp+c4]
0167:00431a0d  push     ecx
0167:00431a0e  mov      ecx,[esp+10]
0167:00431a12  push     eax
0167:00431a13  push     byte +01
0167:00431a15  push     byte +00
0167:00431a17  push     dword 0044d4a4           <--0044d4a4嘛,就是字符串RegCode3
0167:00431a1c  push     ecx
0167:00431a1d  call     ebx                      <--RegSetvalueExA
0167:00431a1f  mov      edx,[esp+0c]
0167:00431a23  push     edx
0167:00431a24  call     `ADVAPI32!RegCloseKey`
0167:00431a2a  push     dword 0044d4b0
0167:00431a2f  push     dword 80000002
0167:00431a34  call     `ADVAPI32!RegDeleteKeyA`
0167:00431a3a  push     byte +01
0167:00431a3c  push     esi
0167:00431a3d  call     `USER32!EndDialog`       <--用EndDialog来关掉输入注册码的这个对话框
0167:00431a43  pop      edi
0167:00431a44  pop      esi
0167:00431a45  xor      eax,eax
0167:00431a47  pop      ebx
0167:00431a48  add      esp,011c
0167:00431a4e  ret      10                       <--返回
0167:00431a51  push     byte +30                 <--在前边儿0043199c处如果没通过就会跳到这里,到了这里就一切都玩儿完了 ;)
0167:00431a53  push     dword 9d15
0167:00431a58  push     dword 9d14
0167:00431a5d  push     esi
0167:00431a5e  call     0040ee90
0167:00431a63  add      esp,byte +10
0167:00431a66  push     dword 044f
0167:00431a6b  push     esi
0167:00431a6c  call     edi
0167:00431a6e  push     eax
0167:00431a6f  call     `USER32!SetFocus`
0167:00431a75  pop      edi
0167:00431a76  pop      esi
0167:00431a77  xor      eax,eax
0167:00431a79  pop      ebx
0167:00431a7a  add      esp,011c
0167:00431a80  ret      10

HeHe~~很明显吧,00431994处的那个CALL就是关键的所在嘛。
也就是说如果在00431994处的那个CALL中程序确定注册码是正确的话就会将其与用户名一同输入进注册表,以后每次程序启动的时候都会进行比较。But你输入的是错误的话嘛,程序就懒得去写进注册表了。所以我们尽管可以在0043199c处的时候用r fl z指令来欺骗程序,但一点儿用都没有 (众人:那你说它干嘛)
那接着就再来一遍,这次就可以直接用断点bpx 00431994了。断到后就按F8跟进去吧:
0167:00431590  push     ebx
0167:00431591  push     ebp
0167:00431592  mov      ebp,[esp+10]             <--将注册码的地址装入ebp中
0167:00431596  push     esi
0167:00431597  push     edi
0167:00431598  cmp      byte [ebp+00],6d         <--ebp+00也就是注册码的第一位,用注册码的第一位与6d相比(6d即m的ASCII码)
0167:0043159c  jnz      near 00431642            <--第一位不是m就跳到00431642处,跳过去就玩儿完了。呵呵,我输入的是20030413,在这里就挂掉了,再来一遍,这次把前4位改成mg37,即mg3720030413
0167:004315a2  cmp      byte [ebp+01],67         <--判断注册码的第2位是否为g
0167:004315a6  jnz      near 00431642
0167:004315ac  cmp      byte [ebp+02],33         <--同理,判断第3位是否为3
0167:004315b0  jnz      near 00431642
0167:004315b6  cmp      byte [ebp+03],37         <--第4位是否为4
0167:004315ba  jnz      near 00431642
0167:004315c0  mov      ebx,0044d4c4        
0167:004315c5  mov      edx,[ebx]          
0167:004315c7  or       ecx,byte -01          
0167:004315ca  mov      edi,edx            
0167:004315cc  xor      eax,eax              
0167:004315ce  repne scasb
0167:004315d0  not      ecx
0167:004315d2  dec      ecx
0167:004315d3  mov      edi,edx
0167:004315d5  mov      esi,ebp
0167:004315d7  xor      eax,eax              
0167:004315d9  repe cmpsb                  
0167:004315db  jz       00431642            
0167:004315dd  add      ebx,byte +04
0167:004315e0  cmp      ebx,0044d4c8
0167:004315e6  jl       004315c5
0167:004315e8  cmp      byte [ebp+04],73         <--比较看注册码的第5位是否为s,ebp中装的其实是注册码的地址,这点很重要,后边儿会有说明
0167:004315ec  jnz      004315ef                 <--不是就跳到004315ef处
0167:004315ee  inc      ebp                      <--是的话ebp+1
0167:004315ef  add      ebp,byte +07             <--ebp加上7
0167:004315f2  push     ebp                      <--ebp入栈
0167:004315f3  call     0043f3c8                 <--!!
0167:004315f8  mov      edx,[esp+18]
0167:004315fc  add      esp,byte +04
0167:004315ff  mov      edi,edx
0167:00431601  xor      ecx,ecx                  <--ecx清0
0167:00431603  mov      dl,[edx]                 <--装入注册名中的第一个字符
0167:00431605  mov      esi,0bdf                 <--esi中装入0bdf,即十进制数3039
0167:0043160a  test     dl,dl                    <--看dl中是否为0
0167:0043160c  jz       00431634
0167:0043160e  movsx    edx,dl                   <--霸占整个edx寄存器 ;)
0167:00431611  inc      ecx                      <--ecx加1
0167:00431612  imul     edx,ecx                  <--用edx中此时装入的注册名中的某一位字符乘的ASCII码乘以ecx中的值
0167:00431615  add      esi,edx                  <--用esi中的值加上edx中的值
0167:00431617  cmp      esi,17be                 <--用esi中的值与17be,即十进制数6078比较
0167:0043161d  jng      00431625                 <---不大于不跳到00431625处
0167:0043161f  sub      esi,17be                 <--大于的话就减去它
0167:00431625  cmp      ecx,byte +0a             <--用A(也就是10)与ecx中的值比较
0167:00431628  jng      0043162c                 <--不大于就跳到0043162c处
0167:0043162a  xor      ecx,ecx                  <--ecx清0
0167:0043162c  mov      dl,[edi+01]              <--dl中装入注册名中的下一个字符
0167:0043162f  inc      edi                      <--edi加上1
0167:00431630  test     dl,dl                    <--测试dl
0167:00431632  jnz      0043160e                 <--不为0就跳加0043160e处继续用下一位来进行计算
0167:00431634  cmp      esi,eax                  <--比较esi与eax中的值,esi中装入的就是前边儿0043160e-00431632处将注册名中各位字符进行计算后的值,而eax中的值是通过004315f3处的这个CALL装进来的,相对于我输入的这个注册名及注册码,它的值是76CD,也就是十进制数30413。
0167:00431636  jnz      00431642                 <--不相等就跳到00431642处
0167:00431638  pop      edi
0167:00431639  pop      esi
0167:0043163a  pop      ebp
0167:0043163b  mov      eax,01                   <--相等的话就继续执行到这里,并将eax置1,我们已经知道出来后会比较看eax是否为0,是的话就Game Over
0167:00431640  pop      ebx
0167:00431641  ret    
0167:00431642  pop      edi                      <--从前面跳过来的话就…
0167:00431643  pop      esi
0167:00431644  pop      ebp
0167:00431645  xor      eax,eax                  <--可恶吧,会将eax置0
0167:00431647  pop      ebx
0167:00431648  ret    

我认为我有必要讲一下,其实这并不难理解。在004315e8处的时候我们可以通过d指令知道ebp中装的其实就是注册码的地址,在004315e8处的时候会比较注册码的第5位是否为s,是的话就将ebp加上1。而到了004315ef处的时候会将ebp加上7,之后就将这个地址压栈了。过了004315f3处的那个CALL后EAX中的值就会改变了,完全不必要追进去,猜也能猜出来了。呵呵,这个CALL的作用就是将之前004315f2处压入的ebp中的地址处开始的值装入eax中。而ebp中装的就是注册码的第7位的地址,明白过来了吗?注册码从第7位开始的值就是程序真正要用的 ;)可问题是第7位以后还有几位呢?嘿嘿,这个很容易就能发现,在00431617的时候程序不是会判断esi中的值是否大于6078吗?而6078,也就是6078只有4位,所以嘛,注册码的位数就是7+4即11位!不过在004315e8和我们会发现程序会判断注册码的第5位是否为s,是的话就接着将ebp加上1,所以问题就解决了。正确的注册码位数只有两种情况:一种是11位,另一种是12位,这个12位的第5位是一个固定不变的,即s。
呵呵,我们来看一下正确的注册码的格式吧,第一种11位的是mg37XXXXXXX,另一种12位的是mg37sXXXXXXX。
现在我们知道的已经够多了,那就写注册机吧 ;)
这之前再大概说一遍程序注册码的计算过过程:
首先固定不变的是程序注册码的前4位,永远是mg37,然后有一个可有可无的位,即第5位,要这一位的话,就让它为s (注意如果注册码的第5位是s,那就应该在其后再补上3位),否则就不要它而直接在mg37后边儿随便补充3位,就比如说CCG ^_^。而接下来就是最后4位了,这个也是重要的地方所在了 ;) 后4位的计算过程其实也不难,就是用你输入的注册名的首位字符的ASCII码乘以X(X每计算一位字符就加上1,如果值后来一直累加到11,就会还原为0)。再用这个乘积加上3039。并将其保存起来,这里称它为Y,之后比较Y是否大于6078,如果大于就用Y减去6078。接下来第二位字符参加运算,同样用其值乘以X,然后加上Y,再比较Y是否大于6078,再接下来第三位字符参加运算,用其乘以X,然后加上Y…直到所有字符均参加完运算…
BTW:如果字符参加完运算后得到的是一个3位有效值,比如123,就在1的前边儿补上一个0,如0123。
贴的这个是注册机源码是Delphi的,直接声明这个函数就能用了:
function KeyGen(Name: String): String;
var
 i,Cnt,vai,ASC:integer;
 Serial:String;
begin
 Cnt:=3039;
 vai:=0;
 for i:=1 to Length(Name) do
   begin
     inc(vai);
     ASC:=Ord(Name[i]);
     ASC:=ASC*vai;
     Cnt:=Cnt+ASC;
     if Cnt>6078 then Cnt:=Cnt-6078;
     if vai>10 then vai:=0;
   end;
 Serial:=inttostr(Cnt);
 Case Length(Serial) of
  3:Serial:='0'+Serial;
  2:Serial:='00'+Serial;
  1:Serial:='000'+Serial;
 end;
 Serial:='mg37CCG'+Serial;
 Result:=Serial;
end;

OK,就这些。