测试环境
系统: xp sp3
调试器 :od 1.10
高手不要见笑,仅供小菜玩乐,有不对或不足的地方还请多多指教,不胜感激!
1.首先运行程序随便输入用户与注册码如下图所示:
2.载入OD通过下MessageBoxA函数, F9运行程序, 随便输入用户名与注册码, 点ok后断下,如下图所示:
3.然后堆栈回溯,来到如下关键地方
00415BB0 6A FF push -0x1
00415BB2 68 58514200 push ahead_dv.00425158
00415BB7 64:A1 00000000 mov eax,dword ptr fs:[0]
00415BBD 50 push eax
00415BBE 64:8925 0000000>mov dword ptr fs:[0],esp
00415BC5 83EC 08 sub esp,0x8
00415BC8 56 push esi
00415BC9 8BF1 mov esi,ecx
00415BCB 8D4C24 04 lea ecx,dword ptr ss:[esp+0x4]
00415BCF E8 74C90000 call <jmp.&MFC42.#??0CString@@QAE@XZ_540>
00415BD4 6A 01 push 0x1
00415BD6 8BCE mov ecx,esi
00415BD8 C74424 18 00000>mov dword ptr ss:[esp+0x18],0x0
00415BE0 E8 7BC90000 call <jmp.&MFC42.#?UpdateData@CWnd@@QAEHH@Z_6334> ; ----获得注册码
00415BE5 E8 8EC90000 call <jmp.&MFC42.#?AfxGetModuleState@@YGPAVAFX_MODULE_ST>
00415BEA 8B48 04 mov ecx,dword ptr ds:[eax+0x4]
00415BED E8 44CF0000 call <jmp.&MFC42.#?BeginWaitCursor@CCmdTarget@@QAEXXZ_16>
00415BF2 8B46 64 mov eax,dword ptr ds:[esi+0x64]
00415BF5 8B4E 60 mov ecx,dword ptr ds:[esi+0x60]
00415BF8 50 push eax ; ----注册码
00415BF9 51 push ecx ; ----用户名
00415BFA C64424 1C 01 mov byte ptr ss:[esp+0x1C],0x1
00415BFF E8 CCFCFFFF call ahead_dv.004158D0 ; 关键CALL 注册码相同则返回1
00415C04 83C4 08 add esp,0x8
00415C07 85C0 test eax,eax
00415C09 75 18 jnz short ahead_dv.00415C23 ; 关键跳
00415C0B 6A 40 push 0x40
00415C0D 68 6C294300 push ahead_dv.0043296C ; ASCII "Sorry"
00415C12 68 40294300 push ahead_dv.00432940 ; ASCII "Invalid username or
registration code "
00415C17 8BCE mov ecx,esi
00415C19 E8 82CB0000 call <jmp.&MFC42.#?MessageBoxA@CWnd@@QAEHPBD0I@Z_4224> ; 提示无效注册码
00415C1E E9 BB000000 jmp ahead_dv.00415CDE4.分析关键CALL 004158D0 ; 关键CALL 注册码相同则返回1
004158D0 53 push ebx
004158D1 55 push ebp
004158D2 8B6C24 0C mov ebp,dword ptr ss:[esp+0xC]
004158D6 56 push esi
004158D7 57 push edi
004158D8 BE 60354300 mov esi,ahead_dv.00433560
004158DD 8BC5 mov eax,ebp
004158DF 8A10 mov dl,byte ptr ds:[eax] ; ---取用户名1字节
004158E1 8A1E mov bl,byte ptr ds:[esi]
004158E3 8ACA mov cl,dl ; ---用户名第1字节
004158E5 3AD3 cmp dl,bl ; ---判断是否为0
004158E7 75 1E jnz short ahead_dv.00415907
004158E9 84C9 test cl,cl
004158EB 74 16 je short ahead_dv.00415903
004158ED 8A50 01 mov dl,byte ptr ds:[eax+0x1]
004158F0 8A5E 01 mov bl,byte ptr ds:[esi+0x1]
004158F3 8ACA mov cl,dl
004158F5 3AD3 cmp dl,bl
004158F7 75 0E jnz short ahead_dv.00415907
004158F9 83C0 02 add eax,0x2
004158FC 83C6 02 add esi,0x2
004158FF 84C9 test cl,cl
00415901 ^ 75 DC jnz short ahead_dv.004158DF
00415903 33C0 xor eax,eax
00415905 EB 05 jmp short ahead_dv.0041590C
00415907 1BC0 sbb eax,eax
00415909 83D8 FF sbb eax,-0x1
0041590C 85C0 test eax,eax
0041590E 74 51 je short ahead_dv.00415961
00415910 8B7C24 18 mov edi,dword ptr ss:[esp+0x18] ; --注册码
00415914 BE 60354300 mov esi,ahead_dv.00433560
00415919 8BC7 mov eax,edi
0041591B 8A10 mov dl,byte ptr ds:[eax] ; ---取注册码1字节
0041591D 8A1E mov bl,byte ptr ds:[esi]
0041591F 8ACA mov cl,dl ; 注册码第1字节
00415921 3AD3 cmp dl,bl ; ---判断是否为0
00415923 75 1E jnz short ahead_dv.00415943
00415925 84C9 test cl,cl
00415927 74 16 je short ahead_dv.0041593F
00415929 8A50 01 mov dl,byte ptr ds:[eax+0x1]
0041592C 8A5E 01 mov bl,byte ptr ds:[esi+0x1]
0041592F 8ACA mov cl,dl
00415931 3AD3 cmp dl,bl
00415933 75 0E jnz short ahead_dv.00415943
00415935 83C0 02 add eax,0x2
00415938 83C6 02 add esi,0x2
0041593B 84C9 test cl,cl
0041593D ^ 75 DC jnz short ahead_dv.0041591B
0041593F 33C0 xor eax,eax
00415941 EB 05 jmp short ahead_dv.00415948
00415943 1BC0 sbb eax,eax
00415945 83D8 FF sbb eax,-0x1
00415948 85C0 test eax,eax
0041594A 74 15 je short ahead_dv.00415961
0041594C 57 push edi ; 注册码
0041594D 55 push ebp ; 用户名
0041594E E8 3DFDFFFF call ahead_dv.00415690 ; 算法
5.算法分析
00415690 6A FF push -0x1
00415692 68 C0504200 push ahead_dv.004250C0
00415697 64:A1 00000000 mov eax,dword ptr fs:[0]
0041569D 50 push eax
0041569E 64:8925 0000000>mov dword ptr fs:[0],esp
004156A5 83EC 14 sub esp,0x14
004156A8 8B4424 24 mov eax,dword ptr ss:[esp+0x24] ; 用户名
004156AC 53 push ebx
004156AD 55 push ebp
004156AE 56 push esi
004156AF 57 push edi
004156B0 50 push eax
004156B1 8D4C24 18 lea ecx,dword ptr ss:[esp+0x18] ; 用户名
004156B5 E8 50D00000 call <jmp.&MFC42.#??0CString@@QAE@PBD@Z_537>
004156BA 33F6 xor esi,esi
004156BC 8D4C24 14 lea ecx,dword ptr ss:[esp+0x14]
004156C0 897424 2C mov dword ptr ss:[esp+0x2C],esi
004156C4 E8 67D40000 call <jmp.&MFC42.#?TrimLeft@CString@@QAEXXZ_6282> ; CString::TrimLeft 删除用户名的空格、换行符等
004156C9 8D4C24 14 lea ecx,dword ptr ss:[esp+0x14]
004156CD E8 58D40000 call <jmp.&MFC42.#?TrimRight@CString@@QAEXXZ_6283>
004156D2 6A 20 push 0x20
004156D4 8D4C24 18 lea ecx,dword ptr ss:[esp+0x18]
004156D8 E8 93D00000 call <jmp.&MFC42.#?GetBuffer@CString@@QAEPADH@Z_2915> ; CString::GetBuffer 用户名
004156DD 8B4C24 38 mov ecx,dword ptr ss:[esp+0x38]
004156E1 8BD8 mov ebx,eax
004156E3 51 push ecx
004156E4 8D4C24 14 lea ecx,dword ptr ss:[esp+0x14]
004156E8 E8 1DD00000 call <jmp.&MFC42.#??0CString@@QAE@PBD@Z_537>
004156ED 8D4C24 10 lea ecx,dword ptr ss:[esp+0x10]
004156F1 C64424 2C 01 mov byte ptr ss:[esp+0x2C],0x1
004156F6 E8 35D40000 call <jmp.&MFC42.#?TrimLeft@CString@@QAEXXZ_6282> ; CString::TrimLeft 删除注册码的空格、换行符等
004156FB 8D4C24 10 lea ecx,dword ptr ss:[esp+0x10]
004156FF E8 26D40000 call <jmp.&MFC42.#?TrimRight@CString@@QAEXXZ_6283>
00415704 6A 20 push 0x20
00415706 8D4C24 14 lea ecx,dword ptr ss:[esp+0x14]
0041570A E8 61D00000 call <jmp.&MFC42.#?GetBuffer@CString@@QAEPADH@Z_2915> ; CString::GetBuffer 注册码
0041570F 8BD0 mov edx,eax
00415711 83C9 FF or ecx,0xFFFFFFFF
00415714 8BFA mov edi,edx
00415716 33C0 xor eax,eax
00415718 F2:AE repne scas byte ptr es:[edi]
0041571A F7D1 not ecx
0041571C 49 dec ecx ; ---注册码长度
0041571D 8BFB mov edi,ebx
0041571F 8BE9 mov ebp,ecx
00415721 83C9 FF or ecx,0xFFFFFFFF
00415724 F2:AE repne scas byte ptr es:[edi]
00415726 F7D1 not ecx
00415728 49 dec ecx ; 用户名长度
00415729 895424 20 mov dword ptr ss:[esp+0x20],edx ; ---注册码地址
0041572D 3BCD cmp ecx,ebp ; --用户名长度与注册码长度比较
0041572F 0F87 64010000 ja ahead_dv.00415899
00415735 8BFB mov edi,ebx
00415737 83C9 FF or ecx,0xFFFFFFFF
0041573A F2:AE repne scas byte ptr es:[edi]
0041573C F7D1 not ecx
0041573E 49 dec ecx ; 用户名长度
0041573F 0F84 54010000 je ahead_dv.00415899
00415745 8BFA mov edi,edx
00415747 83C9 FF or ecx,0xFFFFFFFF
0041574A F2:AE repne scas byte ptr es:[edi]
0041574C F7D1 not ecx
0041574E 49 dec ecx ; 注册码长度
0041574F 0F84 44010000 je ahead_dv.00415899
00415755 897424 38 mov dword ptr ss:[esp+0x38],esi
00415759 8B5424 38 mov edx,dword ptr ss:[esp+0x38]
0041575D 8D4C24 34 lea ecx,dword ptr ss:[esp+0x34] ; 用户名地址
00415761 8A82 C0284300 mov al,byte ptr ds:[edx+0x4328C0] ; 字符sdR
00415767 884424 18 mov byte ptr ss:[esp+0x18],al
0041576B E8 D8CD0000 call <jmp.&MFC42.#??0CString@@QAE@XZ_540>
00415770 8BFB mov edi,ebx
00415772 83C9 FF or ecx,0xFFFFFFFF
00415775 33C0 xor eax,eax
00415777 33ED xor ebp,ebp
00415779 F2:AE repne scas byte ptr es:[edi]
0041577B F7D1 not ecx
0041577D 49 dec ecx ; 用户名长度
0041577E C64424 2C 02 mov byte ptr ss:[esp+0x2C],0x2
00415783 74 50 je short ahead_dv.004157D5 ; --查表开始
00415785 8A0C2B mov cl,byte ptr ds:[ebx+ebp] ; ---取用户名1字节
00415788 33F6 xor esi,esi
0041578A B8 58284300 mov eax,ahead_dv.00432858 ; ASCII "aGbEcVdmelfSgmhkiEjckxlsmtnYobpkqDrtsatfuwvlwjxDyIzPAZBXCPDoEKFgGyHmItJaKrLqMNNQOUPuQGRJSLTnUbVCWFXH"
0041578F 3A08 cmp cl,byte ptr ds:[eax] ; --用户名1字节与表1字节相比较
00415791 74 0D je short ahead_dv.004157A0 ; --是否相等
00415793 83C0 02 add eax,0x2 ; ---表往后移2个元素
00415796 46 inc esi ; 计数加1
00415797 3D C0284300 cmp eax,ahead_dv.004328C0 ; 判断表是否到了结尾 ("sdR")
0041579C ^ 7C F1 jl short ahead_dv.0041578F
0041579E EB 11 jmp short ahead_dv.004157B1
004157A0 8A0C75 59284300 mov cl,byte ptr ds:[esi*2+0x432859] ; ----取表中与用户名1字节相等的后一个元素
004157A7 51 push ecx
004157A8 8D4C24 38 lea ecx,dword ptr ss:[esp+0x38]
004157AC E8 93D10000 call <jmp.&MFC42.#??YCString@@QAEABV0@D@Z_940> ; ---存放查表后的结果
004157B1 83FE 34 cmp esi,0x34 ; ---判断计数是否为0x34
004157B4 75 0E jnz short ahead_dv.004157C4
004157B6 8B5424 18 mov edx,dword ptr ss:[esp+0x18] ; ---在表中没查到情况
004157BA 8D4C24 34 lea ecx,dword ptr ss:[esp+0x34]
004157BE 52 push edx
004157BF E8 80D10000 call <jmp.&MFC42.#??YCString@@QAEABV0@D@Z_940> ; 存放数字
004157C4 8BFB mov edi,ebx
004157C6 83C9 FF or ecx,0xFFFFFFFF
004157C9 33C0 xor eax,eax
004157CB 45 inc ebp
004157CC F2:AE repne scas byte ptr es:[edi]
004157CE F7D1 not ecx
004157D0 49 dec ecx ; 用户名长度
004157D1 3BE9 cmp ebp,ecx ; ---判断是否查表完成
004157D3 ^ 72 B0 jb short ahead_dv.00415785
004157D5 8B4424 34 mov eax,dword ptr ss:[esp+0x34] ; ----根据用户名从表中查出的字符
004157D9 8B48 F8 mov ecx,dword ptr ds:[eax-0x8] ; ---长度
004157DC 83F9 10 cmp ecx,0x10 ; 判断是否大于等于0x10
004157DF 7D 3A jge short ahead_dv.0041581B ; 当用户名查表完后注册码长度不大于等于0x10,就自动填充
004157E1 8BC1 mov eax,ecx
004157E3 B9 10000000 mov ecx,0x10
004157E8 2BC8 sub ecx,eax
004157EA 8D5424 1C lea edx,dword ptr ss:[esp+0x1C]
004157EE 51 push ecx
004157EF 52 push edx
004157F0 B9 04374300 mov ecx,ahead_dv.00433704
004157F5 E8 88CF0000 call <jmp.&MFC42.#?Left@CString@@QBE?AV1@H@Z_4129>
004157FA 50 push eax ; 自动填充的字符
004157FB 8D4C24 38 lea ecx,dword ptr ss:[esp+0x38]
004157FF C64424 30 03 mov byte ptr ss:[esp+0x30],0x3
00415804 E8 69CD0000 call <jmp.&MFC42.#??YCString@@QAEABV0@ABV0@@Z_939> ; 与查表后的字符连接
00415809 8D4C24 1C lea ecx,dword ptr ss:[esp+0x1C]
0041580D C64424 2C 02 mov byte ptr ss:[esp+0x2C],0x2
00415812 E8 1FCD0000 call <jmp.&MFC42.#??1CString@@QAE@XZ_800>
00415817 8B4424 34 mov eax,dword ptr ss:[esp+0x34] ; 连接后的注册码
0041581B 8B4C24 20 mov ecx,dword ptr ss:[esp+0x20] ; 输入的注册码
0041581F 51 push ecx
00415820 50 push eax
00415821 FF15 94784200 call dword ptr ds:[<&MSVCRT._mbscmp>] ; 比较注册码是否相同
00415827 83C4 08 add esp,0x8
0041582A 85C0 test eax,eax
0041582C 74 24 je short ahead_dv.004158526.分析总结
a) 判断输入的用户名与注册码第1字节是否为空
b) 取输入的用户名循环在表中查找是否有该元素,如果有,则取表中该元素后一个元素做为注册码。
c) 如果在表中没有查找到元素,则注册码给值为字符’s’,这里会出现万能注册码,只要输入的用户名为0-9的数字,注册码都为 “ssssssssssssssss”。
d) 如果根用户名查找获得的注册码长度小于0x10则用固定字符填充。
7. 算法分析明白,就开始写注册机
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
//-密码表
char *Table = "aGbEcVdmelfSgmhkiEjckxlsmtnYobpkqDrtsatfuwvlwjxDyIzPAZBXCPDoEKFgGyHmItJaKrLqMNNQOUPuQGRJSLTnUbVCWFXHYoZwsdR";
//表结束标志
char tag[4] = "sdR";
//当查表后注册码不足16位长度时会用下面数据填充
char *AtuoCode = "OgEScVdhYoalmbwp";
int main(int argc, char* argv[])
{
char UserName[256] = {0};
char License[256] = {0};
printf("--------------Ahead DVD Copy 注册机------------------------\n\n");
printf("请输入用户名: ");
scanf("%s",UserName);
if (0x10 < strlen(UserName)-1)
{
printf("用户名不能超过16位,请重新输入!\n");
memset(UserName, 0, strlen(UserName));
scanf("%s",UserName);
}
//-判断用户名是否为空
if (0 == UserName[0])
{
printf("请输入用户名\n");
return -1;
}
for (int i=0; i<strlen(UserName); i++)
{
int index = 0;
//--开始找表
for (int n=0; n<= strlen(Table); n +=2)
{
//--如果在表中查到用户名数字
if (UserName[i] == Table[n])
{
////-------取表下一位数字做注册码
License[i] = Table[index*2+1];
break;
}
//--判断表是否结束
if (!strncmp(Table+n, tag, 3))
{
break;
}
index++;
}
//---判断表是否查找完
if (0x34 == index)
{
License[i] = 's';
}
}
//--------当用户名查表完后注册码长度不等于0x10,就用固定字符填充
if (0x10 != strlen(License)-1)
{
//--填充注册码
strncpy(License+i, AtuoCode, strlen(AtuoCode)-strlen(License));
}
printf("\n");
printf("注册码:%s\n",License);
system("pause");
return 0;
}8. 测试注册机
输入用户名test
如下图:
9.运行软件输入注册码测试,如下图
10.点确定后注册成功,如下图
完成。
样本及注册机源码下载
http://yunpan.cn/cAFXm3tRsHJRU (提取码:2cac)