关于注册机,我看到风飘在《手册》一月刊上有一篇文章关于写注册机,我学习crack的时间只有他的零头,但是那篇文章似乎夸大了写注册机的简单性,或许对那么厉害的风飘来讲写注册机太简单了。但我想要说的是,可以像那篇文章中的说的可以利用程序本身的源码的只是一类软件,而且现在数量正越来越少。改变多少要有一些。有些我们不可能直接利用原来的代码,否则不是所有的程序都可以轻而易举的破解了吗?那么还要DSA,ECC等算法干吗?
还有如果你一定要用高级语言来写汇编,那么你最好学Delphi或者c这种可以使用内联汇编的语言,但是好像在Delphi加入了.net计划之后就不支持内联汇编,我想vc.net也一样。虽然说有时用VB也可以写注册机,但是有些时候实现起来很麻烦。
举个例子来说吧!让我们看看另一类不可以直接利用源码的软件,应该怎么破解。《照片桌面》我曾在06年的11月刊上发表了对它的爆破,但是要找出注册码,这个还需要一点编程知识,和一点汇编基础。
我们开始,OD载入。关键的地方很容易找到如下:

/*403163*/  MOVZX EDX,BYTE PTR DS:[EAX];EAX是伪码的地址
/*403166*/  MOV ECX,ESI;ESI的址为FFFFFFFF
/*403168*/  AND ECX,0FF
/*40316E*/  XOR ECX,EDX
/*403170*/  MOV EBP,DWORD PTR DS:[EDI+ECX*4];看到这里我们就知道EDI寄存器的址对这个软件的注册有很重要的作用。
/*403173*/  SHR ESI,8
/*403176*/  XOR ESI,EBP
/*403178*/  INC EAX;指针指向下一个
/*403179*/  DEC EBX;EBX是注册码长度
/*40317A*/  JNZ SHORT 照片桌面.00403163
……没用的代码省略了
/*40317D*/  MOV EAX,ESI
……同上一样
/*403181*/  NOT EAX;根据下面一句,也就是说明这里的EAX需要=15A7393A
/*403DBF*/  CMP EAX,EA58C6C5;比较后要true

上面是一个循环,就是代码最关键的地方。所以我们此刻需要一件武器帮助我们,就是OD插件:内存数据格式转换或者data ripper也可以。
看着上面的东西我们不禁感觉到这个东西有点hash的色彩。我们唯一写注册机的方法就是穷举,另外至少我是没有办法了,如果你有幸是一个密码学高手或许可以找出这种算法的漏洞。EDI的值是不定的,但是edi和edi+ecx*4指向的数据块是确定的。所以我们要找出这个表的范围,ECX*4 ,AND ECX,0FF从这句话我们可以知道ECX的值最大不过FF。用工具计算一下ECX*4 =3FC,我们把目光盯住数据窗口。在命令框中输入d edi。我们先选择0012EFB8到12F3B4的区域(这只是我这边),然后用“内存数据格式转换”插件把这些数据提取出来,因为它本身取值就是DWORD,所以我们也要选择以DWORD的形式来复制。如图1

http://bbs.pediy.com/upload/2006/20/image/image001.png_475.png 

图1
然后保存。接下来我们就要实行穷举了。
下面是c++的实现方法。我对c++尤其是vc++不怎么精通,所以我写得差那么就很不好意思了。
其实这么一个东西写注册机要小鸟来写真的难了一点。我一共花了一天写了上文《内联汇编》,又用了一天写了这个东西的注册机。使用过c语言的人都知道它本身就具有位运算,所以假如你够本事的话,基本上可以直接使用位运算和指针达到内联汇编的效果。下面给出我在vc++6.0中调试通过的c++源代码。大家注意我的c++代码和软件本身的汇编代码的区别。

#include <iostream.h>
const int sj[]={我们从数据窗口中拷贝来的数据};
void main()
{
  int n;
  int b;
 for (n=0x 01000000;n<=0xFFFFFFFF;n++)
  {
    __asm
  {
    mov ebx,4
    lea EAX,n
        MOV ESI,0xFFFFFFFF
line1: 
    MOVZX EDX,byte ptr[EAX+ebx-1]
        MOV ECX,ESI
    and ECX,0xFF
    XOR ECX,EDX
    shl ecx,2;相当于ecx*4
    MOV edi,sj[ECX]
    SHR ESI,8
    XOR ESI,edi
    dec EBX;EBX是注册码长度
    JNZ line1
    CMP ESI,0x15A7393A
    JZ line2
    push 0
    pop b
    jmp line3
line2:
    push 1
    pop b
line3:
    }
     if (b==1) 
     {
       zh=n;
       for (n1=0;zh>0;n1++)
      {
        sz[n1]=zh % 256;
        zh=zh / 256;
      };
        n1--;
    for (;n1>=0;n1--)
      {
        cout<<char(sz[n1]);
      };
     }
  }
 
 }
需要解释的是这句:for (n=0x10000000;n<=0xFFFFFFFF;n++),大家肯定感到很奇怪。我这是为了方便处理数据,因为无论是字符串或者数字,在内存中都是用数字来表示的。所以这句话我是模拟了在从4字节即00 00 00 01——FF FF FF FF下所有在内存中出现的情况,因为假如数据再大一点,我们穷举的时间会很大。我也就不能投机使用这种外循环采用for语句。稍高一点的鸟知道,从有符号的角度来讲n的值从0x01000000一直到最大,最后到-1。虽然这有点类似整数溢出,但是这个溢出效果就是我要的。
还有这句:MOVZX EDX,byte ptr[EAX+ebx-1];EAX是伪码的地址,因为软件处理我们的注册码的时候就是从我们输入的第一位开始的,而我们模拟的值在内存中储存着的是反向的。打个比方说:我输入的伪码是1234,那么软件经过处理之后,把它们安排在内存区域里的格式是31 32 33 34,而我们在模拟的n=0x31 32 33 34在内存中是这样存储着的34 33 32 31。所以我们要从最后一位开始。这里大家最好分别用OD调试软件和用vc本身调试这段源代码,就可以明白了。
其它的如mov ebx,4就不难理解了,我这是手动设置byte数。运气很好的是在上述范围内,我追到一组注册码+*05。我想假如把数据扩大到0x0100000000——0x9999999999这样的话肯定可以搜索到更多的注册码,但是在设置4字节下我的赛扬2.66GHZ就跑了好几分钟,所以在5byte数据下要完全搜过的话,不但外围的循环不能投机取巧这样模拟,要改写,而且内部要多不少的指针操作,所以我懒惰一下,不写了。反正我已经找到了一组。有兴趣且够实力的读者不妨自己试试,但千万不要和自己过不去,因为有时候注册机真的很难写。
再说一点废话,上面的循环有一半是做无用功,因为ASCII码值估计它只用了基本的0-128。我把扩展ASCII码表也模拟进去了。