henshan2002 发表于 2013-12-29 16:56

对EditPlus3.6的注册算法分析及注册机写法(C语言)

本帖最后由 henshan2002 于 2013-12-29 16:59 编辑

对EditPlus3.6的注册算法分析及注册机源码【逆向思路】
1、分析程序验证流程,找出关键判断点。
2、分析关键算法。
3、写出注册机。
【第一步:分析验证大概流程】
这个软件的界面非常友好。安装后,会出现对话框 如图1

file:///C:/Users/ljb/AppData/Local/Temp/Wiz/7ce61343-1029-4669-b6cc-f6ecae7a9477_128_files/15097948.png
当输入用户名和一个regcode后,出现一个提示错误信息的对话框 如图2


file:///C:/Users/ljb/AppData/Local/Temp/Wiz/7ce61343-1029-4669-b6cc-f6ecae7a9477_128_files/15380481.png

好吧,我们就在这里下断吧。bp MessageBoxExW 断到了系统里面。接着ALT+F9返回到用户领空来到这里 如图3


file:///C:/Users/ljb/AppData/Local/Temp/Wiz/7ce61343-1029-4669-b6cc-f6ecae7a9477_128_files/15788237.png
这里并不是注册分析的关键点,经过几次CTR+F9返回到这里
004CB77F   .E8 4F0D0400   call editplus.0050C4D3                   ;错误4
观察到这段代码的上面有个字符处理函数,因此我们估计得出,回到了分析代码里面了。为了证明这一点,实际操作过程中还要重新来过,进入到这里观察寄存器和堆栈里的数据

这里具体的过程,我就不给图了。
程序验证时大概流程如下:
头部是
004CB6A0   .81EC 34020000 sub esp,0x234                            ;这里是检测注册码头部
关键算法是这里
004CB769   .E8 02F9FFFF   call editplus.004CB070                   ;关键算法运算
运算的结果由 eax带回,1就是正确,0当然是失败了。如下图:(修改那个jnz为jmp就 不会有错误提示了,但重启软件会再验证,所以修改这里一处还不能爆破成功)如图4


file:///C:/Users/ljb/AppData/Local/Temp/Wiz/7ce61343-1029-4669-b6cc-f6ecae7a9477_128_files/17590501.png



【第二步:算法分析】

​A、初始化代码处的分析



004CB6A0   .81EC 34020000 sub esp,0x234                            ;这里是检测注册码头部
004CB6A6   .A1 5C7B5B00   mov eax,dword ptr ds:
004CB6AB   .33C4          xor eax,esp
004CB6AD   .898424 300200>mov dword ptr ss:+0x230],eax
004CB6B4   .53            push ebx
004CB6B5   .55            push ebp
004CB6B6   .8D4424 08   lea eax,dword ptr ss:+0x8]
004CB6BA   .50            push eax
004CB6BB   .8BE9          mov ebp,ecx
004CB6BD   .68 F4010000   push 0x1F4
004CB6C2   .8D4C24 4C   lea ecx,dword ptr ss:+0x4C]
004CB6C6   .51            push ecx
004CB6C7   .8D95 C8000000 lea edx,dword ptr ss:+0xC8]
004CB6CD   .52            push edx
004CB6CE   .E8 9DFEFFFF   call editplus.004CB570                   ;得到用户名和长度
004CB6D3   .83C4 10       add esp,0x10
004CB6D6   .837C24 08 00cmp dword ptr ss:+0x8],0x0         ;比较用户名长度
004CB6DB   .8BD8          mov ebx,eax
004CB6DD   .895C24 0C   mov dword ptr ss:+0xC],ebx
004CB6E1   .0F84 20010000 je editplus.004CB807                     ;用户名长度=0就跳
004CB6E7   .57            push edi
004CB6E8   .8D4424 0C   lea eax,dword ptr ss:+0xC]
004CB6EC   .50            push eax
004CB6ED   .6A 32         push 0x32
004CB6EF   .8D4C24 1C   lea ecx,dword ptr ss:+0x1C]
004CB6F3   .51            push ecx
004CB6F4   .8D55 74       lea edx,dword ptr ss:+0x74]
004CB6F7   .52            push edx
004CB6F8   .E8 73FEFFFF   call editplus.004CB570                   ;得到密码和长度
004CB6FD   .8BF8          mov edi,eax
004CB6FF   .8B4424 1C   mov eax,dword ptr ss:+0x1C]          ;eax=密码长度
004CB703   .83C4 10       add esp,0x10
004CB706   .85C0          test eax,eax
004CB708   .0F84 F8000000 je editplus.004CB806                     ;密码长度要>0
004CB70E   .56            push esi
004CB70F   .33F6          xor esi,esi                              ;i=0
004CB711   .85C0          test eax,eax
004CB713   .7E 52         jle Xeditplus.004CB767
004CB715   .EB 09         jmp Xeditplus.004CB720
004CB717   .8DA424 000000>lea esp,dword ptr ss:
004CB71E   .8BFF          mov edi,edi
004CB720   >66:0FB6043E   movzx ax,byte ptr ds:+edi]         ;eax=密码
004CB725   .0FB7D8      movzx ebx,ax
004CB728   .B9 00010000   mov ecx,0x100
004CB72D   .66:3BD9       cmp bx,cx
004CB730   .73 0D         jnb Xeditplus.004CB73F                   ;密码大于0x100则跳
004CB732   .0FB6D3      movzx edx,bl                           ;edx=密码
004CB735   .0FB70455 C8CC>movzx eax,word ptr ds:*2+0x5ECCC8]   ;eax=字符阵列[密码*2]
004CB73D   .EB 1A         jmp Xeditplus.004CB759
004CB73F   >0FB7C3      movzx eax,bx
004CB742   .50            push eax                                 ; /StringOrChar
004CB743   .FF15 50E75600 call dword ptr ds:[<&USER32.CharUpperW>] ; \CharUpperW
004CB749   .0FB7C0      movzx eax,ax
004CB74C   .66:85C0       test ax,ax
004CB74F   .75 05         jnz Xeditplus.004CB756
004CB751   .0FB7C3      movzx eax,bx
004CB754   .EB 03         jmp Xeditplus.004CB759
004CB756   >0FB7C0      movzx eax,ax
004CB759   >88043E      mov byte ptr ds:+edi],al             ;--------------
004CB75C   .46            inc esi                                  ;i++
004CB75D   .3B7424 10   cmp esi,dword ptr ss:+0x10]          ;i<7
004CB761   .^ 7C BD         jl Xeditplus.004CB720                  ;上面的循环把密码小写部分变成大写
004CB763   .8B5C24 14   mov ebx,dword ptr ss:+0x14]          ;ebx=用户名


上面的代码比较简单,总结来说,做了三件事:
1、得到输入的用户名和长度;
2、得到输入的注册码和长度;
3、把注册码转成大写;
用c语言实现如下:(注,为了使流程与源码一致,没有做任何优化,可能看起来比较臃肿)

int iGetLeng(char* p)
{//得到长度
    for (int i=0;p;i++);
    return i;
}
void vHitoLow( char* p)
{//小写转大写
    for (int i=0;p;i++)
    {
      p=(p>='a'&&p<='z')?(p-32):p;
    }
}
int main(int argc, char* argv[])
{

    long eax;
    int name_len,key_len;
    char name[20], key[15];
    printf("请输入你的注册名\n");
    scanf("%s",name);
    name_len=iGetLeng(name);
    if (!name_len) return;
   
    printf("请输入你的注册码\n");
    scanf("%s",key);
    key_len=iGetLeng(key);
      if (!key_len) return;
vHitoLow(key);//小写转大写
}




B、核心算法分析
原代码如下
004CB070/$83EC 10       sub esp,0x10                           ;关键运算程序
004CB073|.A1 5C7B5B00   mov eax,dword ptr ds:
004CB078|.33C4          xor eax,esp
004CB07A|.894424 0C   mov dword ptr ss:+0xC],eax
004CB07E|.53            push ebx
004CB07F|.55            push ebp
004CB080|.8B6C24 1C   mov ebp,dword ptr ss:+0x1C]          ;ebp=用户名
004CB084|.56            push esi
004CB085|.8B7424 24   mov esi,dword ptr ss:+0x24]          ;esi=注册码
004CB089|.8BC5          mov eax,ebp                              ;eax=用户名
004CB08B|.57            push edi
004CB08C|.8D50 01       lea edx,dword ptr ds:+0x1]
004CB08F|.90            nop
004CB090|>8A08          /mov cl,byte ptr ds:                ;cl=用户名[0]
004CB092|.40            |inc eax
004CB093|.84C9          |test cl,cl
004CB095|.^ 75 F9         \jnz Xeditplus.004CB090
004CB097|.2BC2          sub eax,edx
004CB099|.8BD8          mov ebx,eax                              ;ebx=用户名长度
004CB09B|.8BC6          mov eax,esi                              ;eax=注册码
004CB09D|.8D50 01       lea edx,dword ptr ds:+0x1]
004CB0A0|>8A08          /mov cl,byte ptr ds:
004CB0A2|.40            |inc eax
004CB0A3|.84C9          |test cl,cl
004CB0A5|.^ 75 F9         \jnz Xeditplus.004CB0A0
004CB0A7|.2BC2          sub eax,edx
004CB0A9|.8BF8          mov edi,eax                              ;edi=注册码长度-------整个上面代码就是得到用户名长度和注册码长度
004CB0AB|.E8 F0F6FFFF   call editplus.004CA7A0                   ;【这里面运算得到byte[512]因子】--重要1
004CB0B0|.53            push ebx
004CB0B1|.55            push ebp
004CB0B2|.6A 00         push 0x0
004CB0B4|.E8 47F7FFFF   call editplus.004CA800                   ;对name进行运算-返回一个eax-重要2
004CB0B9|.0FB7C0      movzx eax,ax
004CB0BC|.50            push eax                                 ;Arg3
004CB0BD|.8D4C24 20   lea ecx,dword ptr ss:+0x20]
004CB0C1|.68 404A5800   push editplus.00584A40                   ;ASCII "%02X"
004CB0C6|.51            push ecx                                 ;Arg1
004CB0C7 >|.E8 54F9FFFF   call editplus.004CAA20                   ;sprintf(ecx,"%02X", eax)
004CB0CC|.0FB656 02   movzx edx,byte ptr ds:+0x2]          ;edx=key[2]
004CB0D0|.0FBE4C24 28   movsx ecx,byte ptr ss:+0x28]         ;ecx=name2[0]
004CB0D5|.8D46 02       lea eax,dword ptr ds:+0x2]
004CB0D8|.83C4 18       add esp,0x18
004CB0DB|.3BD1          cmp edx,ecx
004CB0DD|.74 15         je Xeditplus.004CB0F4                  ;不跳就说明失败了
004CB0DF|>5F            pop edi
004CB0E0|.5E            pop esi
004CB0E1|.5D            pop ebp
004CB0E2|.33C0          xor eax,eax
004CB0E4|.5B            pop ebx
004CB0E5|.8B4C24 0C   mov ecx,dword ptr ss:+0xC]
004CB0E9|.33CC          xor ecx,esp
004CB0EB|.E8 4F460700   call editplus.0053F73F
004CB0F0|.83C4 10       add esp,0x10
004CB0F3|.C3            retn
004CB0F4|>0FB656 03   movzx edx,byte ptr ds:+0x3]
004CB0F8|.0FBE4C24 11   movsx ecx,byte ptr ss:+0x11]         ;name2[3]
004CB0FD|.3BD1          cmp edx,ecx
004CB0FF|.^ 75 DE         jnz Xeditplus.004CB0DF                   ;跳走则失败了
004CB101|.83C7 FE       add edi,-0x2
004CB104|.57            push edi
004CB105|.50            push eax
004CB106|.6A 00         push 0x0
004CB108|.E8 F3F6FFFF   call editplus.004CA800                   ; 对key2进行运算-返回一个eax-重要2
004CB10D|.0FB7D0      movzx edx,ax                           ;eax=2627
004CB110|.52            push edx
004CB111|.8D4424 20   lea eax,dword ptr ss:+0x20]
004CB115|.68 404A5800   push editplus.00584A40                   ;ASCII "%02X"
004CB11A|.50            push eax
004CB11B|.E8 00F9FFFF   call editplus.004CAA20
004CB120|.0FB60E      movzx ecx,byte ptr ds:            ;ecx=key[0]
004CB123|.0FBE5424 28   movsx edx,byte ptr ss:+0x28]         ;edx=name3[0]
004CB128|.83C4 18       add esp,0x18
004CB12B|.3BCA          cmp ecx,edx
004CB12D|.^ 75 B0         jnz Xeditplus.004CB0DF
004CB12F|.0FB646 01   movzx eax,byte ptr ds:+0x1]          ;eax=key[1]
004CB133|.0FBE4C24 11   movsx ecx,byte ptr ss:+0x11]         ;ecx=name3[1]
004CB138|.33D2          xor edx,edx
004CB13A|.3BC1          cmp eax,ecx
004CB13C|.8B4C24 1C   mov ecx,dword ptr ss:+0x1C]
004CB140|.5F            pop edi
004CB141|.0F94C2      sete dl
004CB144|.5E            pop esi
004CB145|.5D            pop ebp
004CB146|.5B            pop ebx
004CB147|.33CC          xor ecx,esp
004CB149|.8BC2          mov eax,edx
004CB14B|.E8 EF450700   call editplus.0053F73F
004CB150|.83C4 10       add esp,0x10
004CB153\.C3            retn



其中
004CB0AB|.E8 F0F6FFFF   call editplus.004CA7A0
内容如下
004CA7A0   $68 00020000   push 0x200                               ;这里也是非常重要的计算
004CA7A5   .6A 00         push 0x0
004CA7A7   .68 B0DC5E00   push editplus.005EDCB0
004CA7AC   .E8 CF570700   call editplus.0053FF80                   ;BYTE dizhi[512]={0};
004CA7B1   .83C4 0C       add esp,0xC
004CA7B4   .33D2          xor edx,edx
004CA7B6   .EB 08         jmp Xeditplus.004CA7C0
004CA7B8   .8DA424 000000>lea esp,dword ptr ss:
004CA7BF   .90            nop
004CA7C0   >B8 C1C00000   mov eax,0xC0C1                           ;eax=c0c1---这是个固定的因子
004CA7C5   .B9 01000000   mov ecx,0x1                              ;ecx=1
004CA7CA   .8D9B 00000000 lea ebx,dword ptr ds:               ;ebx=用户名长度
004CA7D0   >85CA          test edx,ecx
004CA7D2   .74 08         je Xeditplus.004CA7DC
004CA7D4   .66:310455 B0D>xor word ptr ds:*2+0x5EDCB0],ax      ;这是一个16位的数
004CA7DC   >03C0          add eax,eax
004CA7DE   .35 03400000   xor eax,0x4003
004CA7E3   .03C9          add ecx,ecx                              ;ecx=ecx*2
004CA7E5   .81F9 00010000 cmp ecx,0x100
004CA7EB   .0FB7C0      movzx eax,ax
004CA7EE   .^ 7C E0         jl Xeditplus.004CA7D0                  ;ecx<100
004CA7F0   .42            inc edx                                  ;i++;
004CA7F1   .81FA 00010000 cmp edx,0x100                            ;i<100
004CA7F7   .^ 7C C7         jl Xeditplus.004CA7C0
004CA7F9   .C3            retn



上面这段代码产生了一个512byte大小的数据区域,且它的内容与输入的用户名与注册码无关,C语言实现如下
void Call004CA7A0()
{
    long eax,i2;
      for (int i1=0;i1<256;i1++)
      {
            dizhi=0;
      }

   for (int i=0;i<256;i++)
   {         
      eax=0xc0c1;
      i2=0x1;
      while(i2<256)
      {
            if(!((i&i2)==0)) dizhi=dizhi^(unsigned short) eax;
            eax=(eax*2)^0x4003;
            i2=i2+i2;
            eax=unsigned short (eax);
      }
   }
}



另外一个重要的地方是004CB0B4|.E8 47F7FFFF   call editplus.004CA800                  
004CA800/$8B4C24 08   mov ecx,dword ptr ss:+0x8]         ;ecx=用户名
004CA804|.8B4424 0C   mov eax,dword ptr ss:+0xC]         ;eax=用户长度
004CA808|.8D1401      lea edx,dword ptr ds:+eax]         ;edx=用户名地址+4位
004CA80B|.3BCA          cmp ecx,edx
004CA80D|.73 25         jnb Xeditplus.004CA834
004CA80F|.8B4424 04   mov eax,dword ptr ss:+0x4]         ;eax=arg1=0
004CA813|.56            push esi
004CA814|.57            push edi
004CA815|>0FB639      /movzx edi,byte ptr ds:             ;edi=name
004CA818|.0FB6F0      |movzx esi,al                            ;esi=0
004CA81B|.66:C1E8 08    |shr ax,0x8
004CA81F|.33F7          |xor esi,edi
004CA821|.66:330475 B0D>|xor ax,word ptr ds:*2+0x5EDCB0]    //这里就是前面那个因子数据的地方
004CA829|.41            |inc ecx                                 ;i++
004CA82A|.0FB7C0      |movzx eax,ax
004CA82D|.3BCA          |cmp ecx,edx
004CA82F|.^ 72 E4         \jb Xeditplus.004CA815
004CA831|.5F            pop edi
004CA832|.5E            pop esi
004CA833|.C3            retn
004CA834|>66:8B4424 04mov ax,word ptr ss:+0x4]
004CA839\.C3            retn

分析得出,这里把参数1与那个512byte大小的数据块内容进行一系列运算后得到一个16位数返回。C语言实现如下

long Call004CA800(    char *name)
{
    long eax=0;
   long esi;
   for (int i=0;name;i++)
   {
         esi=BYTE (eax);
         eax=eax>>8;
         esi=esi^((BYTE)(name&0xff));
      eax= unsigned short (eax)^ dizhi;
         eax=unsigned short (eax);
   }
return eax;
}


C、总结算法流程
1)004CB0AB   call editplus.004CA7A0   ;运算得到byte大小的因子数据,后面的运算要用到这里面的数据

2)004CB0B4   call editplus.004CA800   ;利用上面那个数据块对用户名进行运算,因为这里push的是用户名

3)004CB0C7 > call editplus.004CAA20   ;sprintf(ecx,"%02X", eax),这里是把上面运算的结果,以字符形式输出到ecx中,
                                          ;比方说,eax=0x123;运算之后,ecx="123";
4)
004CB0CC   movzx edx,byte ptr ds:          ;edx=key
004CB0D0   movsx ecx,byte ptr ss:         ;ecx=name2
004CB0D5   lea eax,dword ptr ds:
004CB0D8   add esp,0x18
004CB0DB| cmp edx,ecx
004CB0DDje Xeditplus.004CB0F4                  ;不跳就说明失败了
//上面的代码很简单,就是说判断输入的注册码的第3位要与第三步算出的内容的第1位要一样。否则验证就失败。
5)
004CB0F4|> \0FB656 03   movzx edx,byte ptr ds:
004CB0F8|.0FBE4C24 11   movsx ecx,byte ptr ss:         ;name2
004CB0FD|.3BD1          cmp edx,ecx
004CB0FF|.^ 75 DE         jnz Xeditplus.004CB0DF                   ;跳走则失败了
//接下来是比较注册码的第4位要与第三步算出的内容的第2位要一样。否则验证就失败。


6)004CB108|.E8 F3F6FFFF   call editplus.004CA800这里再一次进行运算,就是上面第2步那个函数,但这一次push的内容不是用户名了,而是注册码从第3位开始的内容
                                                                                       比如输入的注册码是“52PoJie”那这时push的是"POJIE"注意是大写。
7)004CB11B|.E8 00F9FFFF   call editplus.004CAA20和上面那个一样,就是把0xh的内容,转成字符
8)
004CB120|.0FB60E      movzx ecx,byte ptr ds:            ;ecx=key
004CB123|.0FBE5424 28   movsx edx,byte ptr ss:         ;edx=name3
004CB128|.83C4 18       add esp,0x18
004CB12B|.3BCA          cmp ecx,edx
004CB12D|.^ 75 B0         jnz Xeditplus.004CB0DF
004CB12F|.0FB646 01   movzx eax,byte ptr ds:          ;eax=key
004CB133|.0FBE4C24 11   movsx ecx,byte ptr ss:         ;ecx=name3
//上面就是说,注册码的第1位要与第7步算出来的值的第1位一样,否则,直接失败
//注册码的第2位要与第7步算出来的值的第2位一样,否则,直接失败

仔细看,程序只验证了输入的注册码的前四位。


【第三步:注册机写法】

// crEditPlus.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#define BYTE unsigned char


unsigned short dizhi[256]={0};

void Call004CA7A0()
{
    long eax,i2;
      for (int i1=0;i1<256;i1++)
      {
            dizhi=0;
      }

for (int i=0;i<256;i++)
{         
      eax=0xc0c1;
         i2=0x1;
      
         while(i2<256)
      {
            if(!((i&i2)==0)) dizhi=dizhi^(unsigned short) eax;
            eax=(eax*2)^0x4003;
            i2=i2+i2;
            eax=unsigned short (eax);
      }
}
}

long Call004CA800(    char *name)
{



    long eax=0;
   long esi;
   for (int i=0;name;i++)
   {
         esi=BYTE (eax);
         eax=eax>>8;
         esi=esi^((BYTE)(name&0xff));
      eax= unsigned short (eax)^ dizhi;
         eax=unsigned short (eax);
   }
return eax;
}

void vHitoLow( char* p)
{//小写转大写
    for (int i=0;p;i++)
    {
      p=(p>='a'&&p<='z')?(p-32):p;
    }
}
int iGetLeng(char* p)
{//得到长度
    for (int i=0;p;i++);
    return i;
}
int main(int argc, char* argv[])
{

    long eax;
int name_len,key_len;
    char name[20];
    printf("请输入你的注册名\n");
    scanf("%s",name);
    name_len=iGetLeng(name);
    if (!name_len) return;
   
    char key[15];
    printf("请输入你的注册码\n");
    scanf("%s",key);
    key_len=iGetLeng(key);
      if (!key_len) return;
vHitoLow(key);

Call004CA7A0();   


eax=Call004CA800(name);
eax=unsigned short (eax);
char name2[15];

char key2[15]={0x0};
sprintf(name2,"%02x",eax);
key[2]=name2[0];
key[3]=name2[1];
for (int i=0;key+2];i++)
{
    key2=key+2];
}

/*char key2="99DIY";*/
vHitoLow(key2);
eax=Call004CA800(key2);
char name3[15];
sprintf(name3,"%02x",eax);
key[0]=name3[0];
key[1]=name3[1];


    printf("the key is:%s\n",key);

}



让我们来看看是否正确: 如图5 6




file:///C:/Users/ljb/AppData/Local/Temp/Wiz/1269364.png
file:///C:/Users/ljb/AppData/Local/Temp/Wiz/1338722.png





















henshan2002 发表于 2015-2-10 18:57

_Fezz 发表于 2014-11-26 16:10
前辈还在吗?过了一年,EditPlus也仅仅是从3.60升到了3.70,我看了下3.70的算法,和3.60几乎一模一样,但这 ...

它的重启验证我没有仔细分析。我上面破文确实没有完全破解,这个注册机有大概20%的机率算不出来,因为目前没有再研究,所以不能给你更多的信息了,请你自己去研究吧,我想不难。

_Fezz 发表于 2014-11-26 16:10

前辈还在吗?过了一年,EditPlus也仅仅是从3.60升到了3.70,我看了下3.70的算法,和3.60几乎一模一样,但这样写的注册机算出来的注册码是无法绕过3.70的重启验证的,难道之前3.60能绕过,还是说前辈有什么别的解决方案。

henshan2002 发表于 2013-12-29 17:01

自己先沙发,

asdf654312 发表于 2013-12-29 17:05

看看怎么用

Ascian 发表于 2013-12-29 17:13

c太深奥啦支持一下

ghui 发表于 2013-12-29 18:45

图文并貌好

ssmc 发表于 2013-12-31 01:49

新手表示看不懂。但是很厉害的样子!

2314902431 发表于 2013-12-31 15:25

膜拜一下~

zzzzyy 发表于 2014-1-1 13:10

写的很仔细,感激不尽

boyljx 发表于 2014-1-10 23:23

看起来很厉害的样子!

ISBN 发表于 2014-1-11 11:25

好厉害的样子,太长了
页: [1] 2 3 4 5 6 7 8
查看完整版本: 对EditPlus3.6的注册算法分析及注册机写法(C语言)