【文章标题】: 写个简单的注册机
【文章作者】: spacenumen
【使用工具】: OD
【软件介绍】: Splish
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
写一个简单的注册码练习练习,就用论坛里的软件吧!有问题还可以参考一下!
在拿到这个软件的时候,先双击运行看看软件的运行情况!这个软件有三个输入口,在hard Coded输入“abcdefg”点击check提示错误!
在name上输入“abcdefg”serial上输入“1234567”点击check提示“sorry please try again”。
到这里我们大概知道,软件有两种注册方法,一、用hard Coded。二、用帐号和注册码注册。这个软件没有加壳,我们直接用od载入。
载入了软件后我们先看看能不能用文本参考。
载入后程序停在401000处。
00401000 >/$ 6A 00 push 0 ; /pModule = NULL
00401002 |. E8 83070000 call <jmp.&KERNEL32.GetModuleHandleA> ; \GetModuleHandleA
00401007 |. A3 80344000 mov dword ptr [403480], eax
0040100C |. E8 73070000 call <jmp.&KERNEL32.GetCommandLineA> ; [GetCommandLineA
在这里右键点击,选择查找-》所有参考文本字符串。这时出现了出现了一个新的窗口,在这个窗口中找到提示注册失败的文本字串
“sorry please try again”。双击“sorry please try again”文本字串,跳到004016e9这个地方。
004016E0 |. /EB 13 jmp short 004016F5
004016E2 |> |6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
004016E4 |. |68 0A304000 push 0040300A ; |Title = "Splish, Splash"
004016E9 |. |68 67304000 push 00403067 ; |Text = "Sorry, please try again."
004016EE |. |6A 00 push 0 ; |hOwner = NULL
004016F0 |. |E8 53000000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
004016F5 |> \C9 leave
004016F6 \. C2 0800 retn 8
这段代码是调用MessageBoxA函数创建提示失败的对话框。在附近找找,在004016c8 jnz short 004016E2不相等就跳转提示注册失败
在004016bc处相等就跳转到提示注册成功。看起来这里是注册的关键地方,我们在004016b6的地方下个断点标记一下。我接着往上看看
有没有获取帐号和注册码的地方,果然在004015f1附近两次调用了GetWindowTextA函数,获取输入值,一个是存在00403242的地方,另一
个存在00403236的地址处,这里面肯定是输入的注册码和帐号,我们来验证一下。
在00401614处按F2下断点,按F9运行,软件停止输入框中等待输入。我们在name上输入“abcdefg”,在serial上输入“1234567”单击check
程序停止断点00401614处,这时已获取了输入值,我们在数据内存区找到00403242和00403236地址。
00403230 00 00 00 00 00 00 61 62 63 64 65 66 67 00 00 00 ......abcdefg...
00403240 00 00 31 32 33 34 35 36 37 38 00 00 00 00 00 00 ..12345678......
这时我们看见00403242存的是刚才输入的注册码serial,00403236存的是帐号name。到这里我们找到了帐号和注册码,我在这里到提示失败
的地方的汇编注释先贴出来:
004015E4 /$ 55 push ebp
004015E5 |. 8BEC mov ebp, esp ; 判断注册码的函数
004015E7 |. 6A 20 push 20 ; /Count = 20 (32.)
004015E9 |. 68 42324000 push 00403242 ; |Buffer = Splish.00403242
004015EE |. FF75 0C push dword ptr [ebp+C] ; |hWnd
004015F1 |. E8 34010000 call <jmp.&USER32.GetWindowTextA> ; \GetWindowTextA
004015F6 |. 85C0 test eax, eax
004015F8 |. 0F84 95000000 je 00401693 ; 获得输入name,输入为零时跳走
004015FE |. A3 67344000 mov dword ptr [403467], eax ; 注册码长度lens
00401603 |. 6A 0B push 0B ; /Count = B (11.)
00401605 |. 68 36324000 push 00403236 ; |Buffer = Splish.00403236
0040160A |. FF75 08 push dword ptr [ebp+8] ; |hWnd
0040160D |. E8 18010000 call <jmp.&USER32.GetWindowTextA> ; \GetWindowTextA
00401612 |. 85C0 test eax, eax ; 获得输入serial
00401614 |. 74 68 je short 0040167E ; 输入为零跳走
00401616 |. A3 63344000 mov dword ptr [403463], eax ; len取serial的长度
0040161B |. 33C9 xor ecx, ecx
0040161D |. 33DB xor ebx, ebx ; 变量i
0040161F |. 33D2 xor edx, edx
00401621 |. 8D35 36324000 lea esi, dword ptr [403236] ; 字符串name
00401627 |. 8D3D 58324000 lea edi, dword ptr [403258] ; 缓存hname
0040162D |. B9 0A000000 mov ecx, 0A ; 变量n设10
00401632 |> 0FBE041E /movsx eax, byte ptr [esi+ebx] ; 取name的第一个字符
00401636 |. 99 |cdq ; 扩成八字节,就是用符号为给edx赋值
00401637 |. F7F9 |idiv ecx ; edx:eax除于n就是name的第一个字符除于10
00401639 |. 33D3 |xor edx, ebx ; 余数edx和ebx异或,存入edx
0040163B |. 83C2 02 |add edx, 2 ; edx加2
0040163E |. 80FA 0A |cmp dl, 0A
00401641 |. 7C 03 |jl short 00401646
00401643 |. 80EA 0A |sub dl, 0A
00401646 |> 88141F |mov byte ptr [edi+ebx], dl ; 将name的ebx个字符处理后放到缓存hname的第ebx个字符
00401649 |. 43 |inc ebx
0040164A |. 3B1D 63344000 |cmp ebx, dword ptr [403463] ; ebx和serial的长度len比较
00401650 |.^ 75 E0 \jnz short 00401632
00401652 |. 33C9 xor ecx, ecx
00401654 |. 33DB xor ebx, ebx ; 变量i=0
00401656 |. 33D2 xor edx, edx
00401658 |. 8D35 42324000 lea esi, dword ptr [403242] ; 注册码serial
0040165E |. 8D3D 4D324000 lea edi, dword ptr [40324D] ; 缓存注册码hserial
00401664 |. B9 0A000000 mov ecx, 0A ; 变量n等于10
00401669 |> 0FBE041E /movsx eax, byte ptr [esi+ebx] ; 将注册码serial的第i字符取出
0040166D |. 99 |cdq ; 扩成八位
0040166E |. F7F9 |idiv ecx ; 第一字符除以n,就是除以10
00401670 |. 88141F |mov byte ptr [edi+ebx], dl ; 把余数放到hserial的第i个里
00401673 |. 43 |inc ebx ; i++
00401674 |. 3B1D 67344000 |cmp ebx, dword ptr [403467] ; i和注册码的长度lens比较大小
0040167A |.^ 75 ED \jnz short 00401669
0040167C |. EB 2A jmp short 004016A8 ; 有
0040167E |> 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
00401680 |. 68 0A304000 push 0040300A ; |Title = "Splish, Splash"
00401685 |. 68 A0304000 push 004030A0 ; |Text = "Please enter your name."
0040168A |. 6A 00 push 0 ; |hOwner = NULL
0040168C |. E8 B7000000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
00401691 |. EB 62 jmp short 004016F5
00401693 |> 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
00401695 |. 68 0A304000 push 0040300A ; |Title = "Splish, Splash"
0040169A |. 68 B8304000 push 004030B8 ; |Text = "Please enter your serial number."
0040169F |. 6A 00 push 0 ; |hOwner = NULL
004016A1 |. E8 A2000000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
004016A6 |. EB 4D jmp short 004016F5
004016A8 |> 8D35 4D324000 lea esi, dword ptr [40324D] ; 注册码缓存hserial
004016AE |. 8D3D 58324000 lea edi, dword ptr [403258] ; 帐号缓存hname
004016B4 |. 33DB xor ebx, ebx ; 变量i
004016B6 |> 3B1D 63344000 /cmp ebx, dword ptr [403463] ; i和注册码长度len比较
004016BC |. 74 0F |je short 004016CD ; 相等才可以提示正确
004016BE |. 0FBE041F |movsx eax, byte ptr [edi+ebx] ; hname[i]
004016C2 |. 0FBE0C1E |movsx ecx, byte ptr [esi+ebx] ; hserial[i]
004016C6 |. 3BC1 |cmp eax, ecx ; 如果hname【i】和hserial【i】相等,i++
004016C8 |. 75 18 |jnz short 004016E2 ; 不相等就注册失败
004016CA |. 43 |inc ebx
004016CB |.^ EB E9 \jmp short 004016B6
004016CD |> 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
004016CF |. 68 0A304000 push 0040300A ; |Title = "Splish, Splash"
004016D4 |. 68 42304000 push 00403042 ; |Text = "Good job, now keygen it."
004016D9 |. 6A 00 push 0 ; |hOwner = NULL
004016DB |. E8 68000000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
004016E0 |. EB 13 jmp short 004016F5
004016E2 |> 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
004016E4 |. 68 0A304000 push 0040300A ; |Title = "Splish, Splash"
004016E9 |. 68 67304000 push 00403067 ; |Text = "Sorry, please try again."
004016EE |. 6A 00 push 0 ; |hOwner = NULL
004016F0 |. E8 53000000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
004016F5 |> C9 leave
004016F6 \. C2 0800 retn 8
这个注册机制是f1(name)=f2(serial)的形式,name是帐号,serial是注册码,在获取了输入后就计算hname=f1(name)的值
hname存在00403258。计算代码:
0040161B |. 33C9 xor ecx, ecx
0040161D |. 33DB xor ebx, ebx ; 变量i
0040161F |. 33D2 xor edx, edx
00401621 |. 8D35 36324000 lea esi, dword ptr [403236] ; 字符串name
00401627 |. 8D3D 58324000 lea edi, dword ptr [403258] ; 缓存hname
0040162D |. B9 0A000000 mov ecx, 0A ; 变量n设10
00401632 |> 0FBE041E /movsx eax, byte ptr [esi+ebx] ; 取name的第i个字符
00401636 |. 99 |cdq ; 扩成八字节,就是用符号为给edx赋值
00401637 |. F7F9 |idiv ecx ; edx:eax除于n就是name的第一个字符除于10
00401639 |. 33D3 |xor edx, ebx ; 余数edx和ebx异或,存入edx
0040163B |. 83C2 02 |add edx, 2 ; edx加2
0040163E |. 80FA 0A |cmp dl, 0A
00401641 |. 7C 03 |jl short 00401646
00401643 |. 80EA 0A |sub dl, 0A
00401646 |> 88141F |mov byte ptr [edi+ebx], dl ; 将name的ebx个字符处理后放到缓存hname的第ebx个字符
00401649 |. 43 |inc ebx
0040164A |. 3B1D 63344000 |cmp ebx, dword ptr [403463] ; ebx和serial的长度len比较
00401650 |.^ 75 E0 \jnz short 00401632
这里可以提取出函数f1,将帐号name的每个字符取出,经过除10取余,和字符位数相异或,再加2的计算处理后存到hname中。我们对应的
c语言的实现:
lenn=strlen(name);
for(i=0;i<lenn;i++)
{
tm=name[i];
tm%=10;
tm^=i;
tm+=2;
if(tm>=10)
{
tm-=10;
}
hname[i]=tm;
}
往后就是hserial=f2(serial)值的计算,汇编代码:
00401652 |. 33C9 xor ecx, ecx
00401654 |. 33DB xor ebx, ebx ; 变量i=0
00401656 |. 33D2 xor edx, edx
00401658 |. 8D35 42324000 lea esi, dword ptr [403242] ; 注册码serial
0040165E |. 8D3D 4D324000 lea edi, dword ptr [40324D] ; 缓存注册码hserial
00401664 |. B9 0A000000 mov ecx, 0A ; 变量n等于10
00401669 |> 0FBE041E /movsx eax, byte ptr [esi+ebx] ; 将注册码serial的第i字符取出
0040166D |. 99 |cdq ; 扩成八位
0040166E |. F7F9 |idiv ecx ; 第一字符除以n,就是除以10
00401670 |. 88141F |mov byte ptr [edi+ebx], dl ; 把余数放到hserial的第i个里
00401673 |. 43 |inc ebx ; i++
00401674 |. 3B1D 67344000 |cmp ebx, dword ptr [403467] ; i和注册码的长度lens比较大小
0040167A |.^ 75 ED \jnz short 00401669
可以看出f2的计算比较简单,就是serial的每个字符ascii码除于10的余数存到hserial中。
在计算出hname=f1(name)和hserial=f2(serial)之后就是比较hname和hserial的大小。
汇编代码:
004016A8 |> \8D35 4D324000 lea esi, dword ptr [40324D] ; 注册码缓存hserial
004016AE |. 8D3D 58324000 lea edi, dword ptr [403258] ; 帐号缓存hname
004016B4 |. 33DB xor ebx, ebx ; 变量i
004016B6 |> 3B1D 63344000 /cmp ebx, dword ptr [403463] ; i和注册码长度len比较
004016BC |. 74 0F |je short 004016CD ; 相等才可以提示正确
004016BE |. 0FBE041F |movsx eax, byte ptr [edi+ebx] ; hname[i]
004016C2 |. 0FBE0C1E |movsx ecx, byte ptr [esi+ebx] ; hserial[i]
004016C6 |. 3BC1 |cmp eax, ecx ; 如果hname【i】和hserial【i】相等,i++
004016C8 |. 75 18 |jnz short 004016E2 ; 不相等就注册失败
004016CA |. 43 |inc ebx
004016CB |.^ EB E9 \jmp short 004016B6
只有hname和hserial的所有第i个字符相等才能注册成功,有一个不相等在4016c8处就会跳到注册失败提示上去!
写注册机就是求serial,serial=f2^-1(hserial),hserial=hname=f1(name)。f2是除10的余数,f2的逆f2^-1求出来的值不是唯一的
是加上10的倍数就可以,就是说serial=hserial+10*n,n是0,1,2,3……,hserial是0到九的数,我们要serial的值是字母的ascii
值n取70,serial的值在70到80之间是F~P的ascii值,所以我们的注册机代码:
#include<stdio.h>
#include<string.h>
main()
{
char name[19]="abcdefg";
char serial[19];
char hname[19];
char hserial[19];
int lenn;
int lens;
char ch;
int tm;
int i=0;
printf("输入帐号:");
scanf("%s",name);
printf("\n");
lenn=strlen(name);
for(i=0;i<lenn;i++)
{
tm=name[i];
tm%=10;
tm^=i;
tm+=2;
if(tm>=10)
{
tm-=10;
}
hname[i]=tm;
}
printf("注册码:");
for(i=0;i<lenn;i++)
{
printf("%c",(hname[i]+70));
}
printf("\n");
system("pause");
}
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2009年11月06日 10:56:20
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!