OK,找到了爆破点,就来算法分析和注册机的编写吧
重启程序,输入序列号和用户名在第一个爆破点前的 校验函数下断 ,F7进去
00495440 /$ 81EC 34020000 sub esp, 234 ; 校验注册码和用户函数
00495446 |. 53 push ebx
00495447 |. 8B9C24 3C0200>mov ebx, dword ptr ss:[esp+23C]
0049544E |. 56 push esi
0049544F |. 57 push edi
00495450 |. 53 push ebx
00495451 |. E8 DB3D0300 call EditPlus.004C9231 ; 取用户名长度
00495456 |. 8BF0 mov esi, eax
00495458 |. 83C4 04 add esp, 4
0049545B |. 85F6 test esi, esi ; 用户名是否为空
0049545D |. 75 0A jnz short EditPlus.00495469
0049545F |. 5F pop edi
00495460 |. 5E pop esi
00495461 |. 5B pop ebx
00495462 |. 81C4 34020000 add esp, 234
00495468 |. C3 retn
00495469 |> 8BBC24 480200>mov edi, dword ptr ss:[esp+248]
00495470 |. 55 push ebp
00495471 |. 57 push edi
00495472 |. E8 BA3D0300 call EditPlus.004C9231 ; 去注册码长度
00495477 |. 83C4 04 add esp, 4
0049547A |. 8BE8 mov ebp, eax
0049547C |. 8D4424 50 lea eax, dword ptr ss:[esp+50]
00495480 |. 6A 00 push 0 ; /pDefaultCharUsed = NULL
00495482 |. 6A 00 push 0 ; |pDefaultChar = NULL
00495484 |. 68 F3010000 push 1F3 ; |MultiByteCount = 1F3 (499.)
00495489 |. 50 push eax ; |MultiByteStr
0049548A |. 56 push esi ; |WideCharCount
0049548B |. 53 push ebx ; |WideCharStr
0049548C |. 8B1D 78945000 mov ebx, dword ptr ds:[<&KERNEL32.Wi>; |kernel32.WideCharToMultiByte
00495492 |. 6A 00 push 0 ; |Options = 0
00495494 |. 6A 00 push 0 ; |CodePage = CP_ACP
00495496 |. FFD3 call ebx ; \WideCharToMultiByte
00495498 |. 6A 00 push 0 ; /pDefaultCharUsed = NULL
0049549A |. 6A 00 push 0 ; |pDefaultChar = NULL
0049549C |. 8D4C24 24 lea ecx, dword ptr ss:[esp+24] ; |
004954A0 |. 6A 31 push 31 ; |MultiByteCount = 31 (49.)
004954A2 |. 51 push ecx ; |MultiByteStr
004954A3 |. 55 push ebp ; |WideCharCount
004954A4 |. 8BF0 mov esi, eax ; |
004954A6 |. 57 push edi ; |WideCharStr
004954A7 |. 6A 00 push 0 ; |Options = 0
004954A9 |. 6A 00 push 0 ; |CodePage = CP_ACP
004954AB |. C64434 70 00 mov byte ptr ss:[esp+esi+70], 0 ; |
004954B0 |. FFD3 call ebx ; \WideCharToMultiByte
004954B2 |. 8BF8 mov edi, eax
004954B4 |. C6443C 1C 00 mov byte ptr ss:[esp+edi+1C], 0
004954B9 |. E8 A2F6FFFF call EditPlus.00494B60
004954BE |. 8D5424 50 lea edx, dword ptr ss:[esp+50]
004954C2 |. 56 push esi
004954C3 |. 52 push edx
004954C4 |. 6A 00 push 0
004954C6 |. E8 E5F6FFFF call EditPlus.00494BB0 ; 计算用户名 F7进去
004954CB |. 83C4 0C add esp, 0C ; 计算结果通过eax返回
004954CE |. 25 FFFF0000 and eax, 0FFFF ; 取返回eax的低字
004954D3 |. 50 push eax ; 计算结果入栈
004954D4 |. 8D4424 14 lea eax, dword ptr ss:[esp+14] ; 用于存放格式化后的字符串
004954D8 |. 68 A4B25300 push EditPlus.0053B2A4 ; %02x
004954DD |. 50 push eax
004954DE |. E8 014F0300 call EditPlus.004CA3E4 ; 将计算结果转换成ASCII码
004954E3 |. 8A4C24 2A mov cl, byte ptr ss:[esp+2A]
004954E7 |. 8A4424 1C mov al, byte ptr ss:[esp+1C]
004954EB |. 83C4 0C add esp, 0C
004954EE |. 3AC8 cmp cl, al ; 注册码第3个字符和算出来的第一个字符比较
004954F0 |. 5D pop ebp
004954F1 74 0C je short EditPlus.004954FF ; 必须跳
004954F3 |. 5F pop edi
004954F4 |. 5E pop esi
004954F5 |. 33C0 xor eax, eax
004954F7 |. 5B pop ebx
004954F8 |. 81C4 34020000 add esp, 234
004954FE |. C3 retn
004954FF |> 8A5424 1B mov dl, byte ptr ss:[esp+1B]
00495503 |. 8A4424 0D mov al, byte ptr ss:[esp+D]
00495507 |. 3AD0 cmp dl, al ; 注册码第4个字节和计算出来的第二个字节比较
00495509 74 0C je short EditPlus.00495517 ; 必须跳
0049550B |. 5F pop edi
0049550C |. 5E pop esi
0049550D |. 33C0 xor eax, eax
0049550F |. 5B pop ebx
00495510 |. 81C4 34020000 add esp, 234
00495516 |. C3 retn
00495517 |> 83C7 FE add edi, -2 ; 去掉注册码前2位,计算注册码
0049551A |. 8D4424 1A lea eax, dword ptr ss:[esp+1A]
0049551E |. 57 push edi
0049551F |. 50 push eax
00495520 |. 6A 00 push 0
00495522 |. E8 89F6FFFF call EditPlus.00494BB0 ; 计算注册码,F7进去
00495527 |. 83C4 0C add esp, 0C ; 计算结果通过eax返回
0049552A |. 25 FFFF0000 and eax, 0FFFF
0049552F |. 8D4C24 0C lea ecx, dword ptr ss:[esp+C]
00495533 |. 50 push eax ; 计算结果入栈
00495534 |. 68 A4B25300 push EditPlus.0053B2A4 ; %02x
00495539 |. 51 push ecx ; 用于存放的缓冲区
0049553A |. E8 A54E0300 call EditPlus.004CA3E4 ; 计算结果转换为ASCII码
0049553F |. 8A5424 24 mov dl, byte ptr ss:[esp+24]
00495543 |. 8A4424 18 mov al, byte ptr ss:[esp+18]
00495547 |. 83C4 0C add esp, 0C
0049554A |. 3AD0 cmp dl, al ; 注册码第1位和算出来的第1位比较
0049554C 74 0C je short EditPlus.0049555A ; 必须跳
0049554E |. 5F pop edi
0049554F |. 5E pop esi
00495550 |. 33C0 xor eax, eax
00495552 |. 5B pop ebx
00495553 |. 81C4 34020000 add esp, 234
00495559 |. C3 retn
0049555A |> 8A4C24 19 mov cl, byte ptr ss:[esp+19]
0049555E |. 8A5424 0D mov dl, byte ptr ss:[esp+D]
00495562 |. 33C0 xor eax, eax
00495564 |. 3ACA cmp cl, dl ; 注册码第2位和算出来的第2位比较
00495566 |. 5F pop edi
00495567 |. 5E pop esi
00495568 0F94C0 sete al ; 条件必须为真
0049556B 5B pop ebx
0049556C 81C4 34020000 add esp, 234
00495572 \. C3 retn
Call 00494BB0 F7进去后
00494BB0 /$ 8B4C24 08 mov ecx, dword ptr ss:[esp+8] ; 传入的字符串入ecx
00494BB4 |. 8B4424 0C mov eax, dword ptr ss:[esp+C] ; 长度入eax
00494BB8 |. 56 push esi
00494BB9 |. 8D3401 lea esi, dword ptr ds:[ecx+eax] ; esi指向字符串最后一个字符
00494BBC |. 3BCE cmp ecx, esi ; 字符串是否为空
00494BBE |. 73 2A jnb short EditPlus.00494BEA
00494BC0 |. 8B4424 08 mov eax, dword ptr ss:[esp+8] ; eax = 0
00494BC4 |. 53 push ebx ; eax用于存放结果
00494BC5 |> 8BD0 /mov edx, eax ; 取结果到edx
00494BC7 |. 33DB |xor ebx, ebx
00494BC9 |. 8A19 |mov bl, byte ptr ds:[ecx] ; 取字符串一个字符
00494BCB |. 81E2 FF000000 |and edx, 0FF ; 取上次结果的低字节
00494BD1 |. 33D3 |xor edx, ebx ; 和取的字符字符异或
00494BD3 |. 33DB |xor ebx, ebx ; ebx清零
00494BD5 |. 8ADC |mov bl, ah ; 取上次结果高位
00494BD7 |. 66:8B0455 A80>|mov ax, word ptr ds:[edx*2+570DA8] ; 查表取值
00494BDF |. 66:33C3 |xor ax, bx ; 查的的值和上次结果的高位异或
00494BE2 |. 41 |inc ecx ; 指向下一个字符
00494BE3 |. 3BCE |cmp ecx, esi
00494BE5 |.^ 72 DE \jb short EditPlus.00494BC5
00494BE7 |. 5B pop ebx
00494BE8 |. 5E pop esi
00494BE9 |. C3 retn
OK,根据算法,写出我们的注册机
#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <time.h>
#include <math.h>
using namespace std;
WORD Fun(char String[]); //计算中间结果
bool Check(char Name[],char Sn[]); //校验用户名和注册码
bool ReSetCheck(char Name[],char Sn[]); //重启校验
WORD Table[] = { //表
0x0000 ,0xC1C0 ,0x81C1 ,0x4001 ,0x01C3 ,0xC003 ,0x8002 ,0x41C2,
0x01C6 ,0xC006 ,0x8007 ,0x41C7 ,0x0005 ,0xC1C5 ,0x81C4 ,0x4004 ,
0x01CC ,0xC00C ,0x800D ,0x41CD ,0x000F ,0xC1CF ,0x81CE ,0x400E ,
0x000A ,0xC1CA ,0x81CB ,0x400B ,0x01C9 ,0xC009 ,0x8008 ,0x41C8 ,
0x01D8 ,0xC018 ,0x8019 ,0x41D9 ,0x001B ,0xC1DB ,0x81DA ,0x401A ,
0x001E ,0xC1DE ,0x81DF ,0x401F ,0x01DD ,0xC01D ,0x801C ,0x41DC ,
0x0014 ,0xC1D4 ,0x81D5 ,0x4015 ,0x01D7 ,0xC017 ,0x8016 ,0x41D6 ,
0x01D2 ,0xC012 ,0x8013 ,0x41D3 ,0x0011 ,0xC1D1 ,0x81D0 ,0x4010 ,
0x01F0 ,0xC030 ,0x8031 ,0x41F1 ,0x0033 ,0xC1F3 ,0x81F2 ,0x4032 ,
0x0036 ,0xC1F6 ,0x81F7 ,0x4037 ,0x01F5 ,0xC035 ,0x8034 ,0x41F4 ,
0x003C ,0xC1FC ,0x81FD ,0x403D ,0x01FF ,0xC03F ,0x803E ,0x41FE ,
0x01FA ,0xC03A ,0x803B ,0x41FB ,0x0039 ,0xC1F9 ,0x81F8 ,0x4038 ,
0x0028 ,0xC1E8 ,0x81E9 ,0x4029 ,0x01EB ,0xC02B ,0x802A ,0x41EA ,
0x01EE ,0xC02E ,0x802F ,0x41EF ,0x002D ,0xC1ED ,0x81EC ,0x402C ,
0x01E4 ,0xC024 ,0x8025 ,0x41E5 ,0x0027 ,0xC1E7 ,0x81E6 ,0x4026 ,
0x0022 ,0xC1E2 ,0x81E3 ,0x4023 ,0x01E1 ,0xC021 ,0x8020 ,0x41E0 ,
0x01A0 ,0xC060 ,0x8061 ,0x41A1 ,0x0063 ,0xC1A3 ,0x81A2 ,0x4062 ,
0x0066 ,0xC1A6 ,0x81A7 ,0x4067 ,0x01A5 ,0xC065 ,0x8064 ,0x41A4 ,
0x006C ,0xC1AC ,0x81AD ,0x406D ,0x01AF ,0xC06F ,0x806E ,0x41AE ,
0x01AA ,0xC06A ,0x806B ,0x41AB ,0x0069 ,0xC1A9 ,0x81A8 ,0x4068 ,
0x0078 ,0xC1B8 ,0x81B9 ,0x4079 ,0x01BB ,0xC07B ,0x807A ,0x41BA ,
0x01BE ,0xC07E ,0x807F ,0x41BF ,0x007D ,0xC1BD ,0x81BC ,0x407C ,
0x01B4 ,0xC074 ,0x8075 ,0x41B5 ,0x0077 ,0xC1B7 ,0x81B6 ,0x4076 ,
0x0072 ,0xC1B2 ,0x81B3 ,0x4073 ,0x01B1 ,0xC071 ,0x8070 ,0x41B0 ,
0x0050 ,0xC190 ,0x8191 ,0x4051 ,0x0193 ,0xC053 ,0x8052 ,0x4192 ,
0x0196 ,0xC056 ,0x8057 ,0x4197 ,0x0055 ,0xC195 ,0x8194 ,0x4054 ,
0x019C ,0xC05C ,0x805D ,0x419D ,0x005F ,0xC19F ,0x819E ,0x405E ,
0x005A ,0xC19A ,0x819B ,0x405B ,0x0199 ,0xC059 ,0x8058 ,0x4198 ,
0x0188 ,0xC048 ,0x8049 ,0x4189 ,0x004B ,0xC18B ,0x818A ,0x404A ,
0x004E ,0xC18E ,0x818F ,0x404F ,0x018D ,0xC04D ,0x804C ,0x418C ,
0x0044 ,0xC184 ,0x8185 ,0x4045 ,0x0187 ,0xC047 ,0x8046 ,0x4186 ,
0x0182 ,0xC042 ,0x8043 ,0x4183 ,0x0041 ,0xC181 ,0x8180 ,0x4040 ,
0xFAC1 ,0x0000
};
void main()
{
char Name[20];
char Sn[30];
cout<<"输入用户名"<<endl;
cin>>Name;
srand(time(NULL)); //随即种子初始化
while (1)
{
for (int kk = 0;kk != 29;kk++)
while (1)
{
Sn[kk] = 48 + rand()%33; //随即枚举字符
if (!(Sn[kk]>=58 && Sn[kk]<=64))
break;
}
Sn[5]=Sn[11]=Sn[17]=Sn[23]='-';//为了使序列号好看,固定了这几位,其实不需要
Sn[29] = '\0'; //字符串结尾符
cout<<Sn<<endl; //显示当前枚举的序列号
Check(Name,Sn); //校验当前的序列号
}
}
WORD Fun(char String[]) //F7进去的那个计算函数
{
WORD result = 0;
WORD temp = 0;
WORD temp2 = 0;
WORD temp3 = 0;
WORD ch;
for (int i = 0;i != lstrlen(String);i++)
{
ch = String[i];
temp = result&0xFF;
temp = ch^temp;
temp2 = result >> 8;
result = Table[(int)temp];
temp3 = Table[(int)temp];
temp3 = temp3 << 8;
result = result >> 8;
result = result | temp3;
result = result^temp2;
}
return result;
}
bool Check(char Name[],char Sn[]) //校验用户名和序列号函数
{
char MidRes[10];
for (int i = 0;i != sizeof(Sn)/sizeof(char);i++) //序列号小写转大写
Sn[i]>=97 && Sn[i]<=122 ? Sn[i] = Sn[i] - 32 : Sn[i];
WORD res = Fun(Name);
wsprintf(MidRes,"%x",res);
for (i = 0;i != sizeof(MidRes)/sizeof(char);i++) //中间结果小写转大写
MidRes[i]>=97 && MidRes[i]<=122 ? MidRes[i] -= 32 : MidRes[i];
// printf("%04x",res);
if (MidRes[0] != Sn[2])
{
// cout<<"错误的序列号"<<endl;
return false;
}
if (MidRes[1] != Sn[3])
{
// cout<<"错误的序列号"<<endl;
return false;
}
char *p = Sn;
res = Fun(p+2);
wsprintf(MidRes,"%x",res);
for (i = 0;i != sizeof(MidRes)/sizeof(char);i++) //中间结果小写转大写
MidRes[i]>=97 && MidRes[i]<=122 ? MidRes[i] -= 32 : MidRes[i];
// printf("%04x",res);
if (MidRes[0] != Sn[0])
{
// cout<<"错误的序列号"<<endl;
return false;
}
if (MidRes[1] != Sn[1])
{
// cout<<"错误的序列号"<<endl;
return false;
}
/* if (!ReSetCheck(Name,Sn)) //先不用看这个重启校验的函数
{
cout<<"重启校验失败"<<endl;
return false;
} */
cout<<"正确的序列号"<<endl;
getchar();
getchar();
return true;
}
bool ReSetCheck(char Name[],char Sn[]) //重启校验的函数
{
DWORD Sum = 0x1;
for (int i = 0;i != lstrlen(Name);i++)
Sum = Sum + (BYTE)Name[i];
Sum = Sum + Sum*8 +0xA;
__asm
{
pushad;
mov ecx,Sum; //累加结果放ecx
mov eax,55555556h;
imul ecx; //结果和55555556相乘
mov eax,edx; //高位放eax
shr eax,1Fh;
xor ecx,ecx;
add ecx,edx; //ecx = eax + edx + 24
add ecx,eax;
add ecx,24h;
and ecx,8000000Fh;
jns over;
dec ecx;
or ecx,4294967280d; //or ecx,FFFFFFF0h,这样写居然不行?
inc ecx;
over: mov Sum,ecx;
popad;
}
char Res[10];
wsprintf(Res,"%x",Sum);
for (i = 0;i != sizeof(Res)/sizeof(char);i++) //结果小写转大写
Res[i]>=97 && Res[i]<=122 ? Res[i] -= 32 : Res[i];
if (Res[0] == Sn[4])
return true;
return false;
}
程序通过枚举序列号来和指定的用户名进行 校验检测,如果注册码符合了条件则停止枚举
先不要看那个重启校验的函数,根据这个注册机,输入用户名XIAOBAOZI (大写的),跑了半个小时,出来一个注册码
3A10H-66D5B-EJC72-JML1I-ME9D5
使用这个注册码,弹出让我们重启程序使注册码生效的消息框,说明这个注册码满足了校验函数的要求。重启程序,结果却还是弹出无效的注册码的消息框。。。
为什么呢?很奇怪,既然他重启校验也是使用的那个校验函数,为什么会不对呢?