好友
阅读权限10
听众
最后登录1970-1-1
|
本帖最后由 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:[0x5B7B5C]
004CB6AB . 33C4 xor eax,esp
004CB6AD . 898424 300200>mov dword ptr ss:[esp+0x230],eax
004CB6B4 . 53 push ebx
004CB6B5 . 55 push ebp
004CB6B6 . 8D4424 08 lea eax,dword ptr ss:[esp+0x8]
004CB6BA . 50 push eax
004CB6BB . 8BE9 mov ebp,ecx
004CB6BD . 68 F4010000 push 0x1F4
004CB6C2 . 8D4C24 4C lea ecx,dword ptr ss:[esp+0x4C]
004CB6C6 . 51 push ecx
004CB6C7 . 8D95 C8000000 lea edx,dword ptr ss:[ebp+0xC8]
004CB6CD . 52 push edx
004CB6CE . E8 9DFEFFFF call editplus.004CB570 ; 得到用户名和长度
004CB6D3 . 83C4 10 add esp,0x10
004CB6D6 . 837C24 08 00 cmp dword ptr ss:[esp+0x8],0x0 ; 比较用户名长度
004CB6DB . 8BD8 mov ebx,eax
004CB6DD . 895C24 0C mov dword ptr ss:[esp+0xC],ebx
004CB6E1 . 0F84 20010000 je editplus.004CB807 ; 用户名长度=0就跳
004CB6E7 . 57 push edi
004CB6E8 . 8D4424 0C lea eax,dword ptr ss:[esp+0xC]
004CB6EC . 50 push eax
004CB6ED . 6A 32 push 0x32
004CB6EF . 8D4C24 1C lea ecx,dword ptr ss:[esp+0x1C]
004CB6F3 . 51 push ecx
004CB6F4 . 8D55 74 lea edx,dword ptr ss:[ebp+0x74]
004CB6F7 . 52 push edx
004CB6F8 . E8 73FEFFFF call editplus.004CB570 ; 得到密码和长度
004CB6FD . 8BF8 mov edi,eax
004CB6FF . 8B4424 1C mov eax,dword ptr ss:[esp+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:[esp]
004CB71E . 8BFF mov edi,edi
004CB720 > 66:0FB6043E movzx ax,byte ptr ds:[esi+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:[edx*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:[esi+edi],al ; --------------
004CB75C . 46 inc esi ; i++
004CB75D . 3B7424 10 cmp esi,dword ptr ss:[esp+0x10] ; i<7
004CB761 .^ 7C BD jl Xeditplus.004CB720 ; 上面的循环把密码小写部分变成大写
004CB763 . 8B5C24 14 mov ebx,dword ptr ss:[esp+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:[0x5B7B5C]
004CB078 |. 33C4 xor eax,esp
004CB07A |. 894424 0C mov dword ptr ss:[esp+0xC],eax
004CB07E |. 53 push ebx
004CB07F |. 55 push ebp
004CB080 |. 8B6C24 1C mov ebp,dword ptr ss:[esp+0x1C] ; ebp=用户名
004CB084 |. 56 push esi
004CB085 |. 8B7424 24 mov esi,dword ptr ss:[esp+0x24] ; esi=注册码
004CB089 |. 8BC5 mov eax,ebp ; eax=用户名
004CB08B |. 57 push edi
004CB08C |. 8D50 01 lea edx,dword ptr ds:[eax+0x1]
004CB08F |. 90 nop
004CB090 |> 8A08 /mov cl,byte ptr ds:[eax] ; 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:[eax+0x1]
004CB0A0 |> 8A08 /mov cl,byte ptr ds:[eax]
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:[esp+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:[esi+0x2] ; edx=key[2]
004CB0D0 |. 0FBE4C24 28 movsx ecx,byte ptr ss:[esp+0x28] ; ecx=name2[0]
004CB0D5 |. 8D46 02 lea eax,dword ptr ds:[esi+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:[esp+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:[esi+0x3]
004CB0F8 |. 0FBE4C24 11 movsx ecx,byte ptr ss:[esp+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:[esp+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:[esi] ; ecx=key[0]
004CB123 |. 0FBE5424 28 movsx edx,byte ptr ss:[esp+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:[esi+0x1] ; eax=key[1]
004CB133 |. 0FBE4C24 11 movsx ecx,byte ptr ss:[esp+0x11] ; ecx=name3[1]
004CB138 |. 33D2 xor edx,edx
004CB13A |. 3BC1 cmp eax,ecx
004CB13C |. 8B4C24 1C mov ecx,dword ptr ss:[esp+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:[esp]
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] ; ebx=用户名长度
004CA7D0 > 85CA test edx,ecx
004CA7D2 . 74 08 je Xeditplus.004CA7DC
004CA7D4 . 66:310455 B0D>xor word ptr ds:[edx*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[i1]=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:[esp+0x8] ; ecx=用户名
004CA804 |. 8B4424 0C mov eax,dword ptr ss:[esp+0xC] ; eax=用户长度
004CA808 |. 8D1401 lea edx,dword ptr ds:[ecx+eax] ; edx=用户名地址+4位
004CA80B |. 3BCA cmp ecx,edx
004CA80D |. 73 25 jnb Xeditplus.004CA834
004CA80F |. 8B4424 04 mov eax,dword ptr ss:[esp+0x4] ; eax=arg1=0
004CA813 |. 56 push esi
004CA814 |. 57 push edi
004CA815 |> 0FB639 /movzx edi,byte ptr ds:[ecx] ; 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:[esi*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 04 mov ax,word ptr ss:[esp+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[esi];
eax=unsigned short (eax);
}
return eax;
}
C、总结算法流程
1)004CB0AB call editplus.004CA7A0 ; 运算得到byte[512]大小的因子数据,后面的运算要用到这里面的数据
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:[esi+0x2] ; edx=key[2]
004CB0D0 movsx ecx,byte ptr ss:[esp+0x28] ; ecx=name2[0]
004CB0D5 lea eax,dword ptr ds:[esi+0x2]
004CB0D8 add esp,0x18
004CB0DB | cmp edx,ecx
004CB0DD je Xeditplus.004CB0F4 ; 不跳就说明失败了
//上面的代码很简单,就是说判断输入的注册码的第3位要与第三步算出的内容的第1位要一样。否则验证就失败。
5)
004CB0F4 |> \0FB656 03 movzx edx,byte ptr ds:[esi+0x3]
004CB0F8 |. 0FBE4C24 11 movsx ecx,byte ptr ss:[esp+0x11] ; name2[3]
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:[esi] ; ecx=key[0]
004CB123 |. 0FBE5424 28 movsx edx,byte ptr ss:[esp+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:[esi+0x1] ; eax=key[1]
004CB133 |. 0FBE4C24 11 movsx ecx,byte ptr ss:[esp+0x11] ; ecx=name3[1]
//上面就是说,注册码的第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[i1]=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[esi];
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[i+2];i++)
{
key2=key[i+2];
}
/*char key2[10]="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
|
免费评分
-
查看全部评分
|