菜鸟 学注册机编写之 “查表”
测试环境
系统: 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.00415CDE
4.分析关键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.00415852
6.分析总结
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)
当把学习当成一种习惯!