本帖最后由 pk8900 于 2017-12-17 23:22 编辑
今晚,继续研究 了【适合破解新手的160个crackme练手】,本文内容为第49个CrackMe: DueList.4.exe,虽然很简单,但论坛没有关于这个CrackMe的帖子,于是写了这篇帖子,分享一下我的分析过程及注册机编写方法。
【crackme简介】
下载地址:http://pan.baidu.com/share/link?shareid=541269&uk=4146939145
MASM32 / TASM32编写,无壳,是一个用户名序列号式验证方式,输入用户名,序列号点击CHECK按钮,有提示英文:Your registration info is invalid... Note that most of the special chars may raise registration problems!。
分析工具:X64dbg
【crackme截图】
【算法分析过程】
这个难度不大,按星级估计算是1星的吧,因为有提示字符,可以直接从字符串入手,找到关键代码位置如下:
[Asm] 纯文本查看 复制代码 00401127 | 6A 00 | push 0x0 | LONG_PTR lParam = 0x0
00401129 | 6A 00 | push 0x0 | UINT_PTR wParam = 0x0
0040112B | 6A 0E | push 0xE | UINT Msg = WM_GETTEXTLENGTH
0040112D | 6A 03 | push 0x3 | int nIDDlgItem = 0x3
0040112F | FF 75 08 | push dword ptr ss:[ebp+0x8] | HANDLE hDlg
00401132 | E8 41 02 00 00 | call <duelis.SendDlgItemMessageA> | SendDlgItemMessageA
00401137 | A3 AF 21 40 00 | mov dword ptr ds:[0x4021AF], eax |
0040113C | 83 F8 00 | cmp eax, 0x0 |
0040113F | 0F 84 D5 00 00 00 | je duelis.40121A |
00401145 | 83 F8 08 | cmp eax, 0x8 |
00401148 | 0F 8F CC 00 00 00 | jg duelis.40121A | 用户名<=8位
0040114E | 8B F0 | mov esi, eax |
00401150 | 6A 00 | push 0x0 | LONG_PTR lParam = 0x0
00401152 | 6A 00 | push 0x0 | UINT_PTR wParam = 0x0
00401154 | 6A 0E | push 0xE | UINT Msg = WM_GETTEXTLENGTH
00401156 | 6A 04 | push 0x4 | int nIDDlgItem = 0x4
00401158 | FF 75 08 | push dword ptr ss:[ebp+0x8] | HANDLE hDlg
0040115B | E8 18 02 00 00 | call <duelis.SendDlgItemMessageA> | SendDlgItemMessageA
00401160 | 83 F8 00 | cmp eax, 0x0 |
00401163 | 0F 84 B1 00 00 00 | je duelis.40121A |
00401169 | 3B F0 | cmp esi, eax | 序列号和用户名位数必须相同
0040116B | 0F 85 A9 00 00 00 | jne duelis.40121A |
00401171 | 68 60 21 40 00 | push duelis.402160 | LONG_PTR lParam = 0x402160 用户名变量
00401176 | 6A 08 | push 0x8 | UINT_PTR wParam = 0x8
00401178 | 6A 0D | push 0xD | UINT Msg = WM_GETTEXT
0040117A | 6A 03 | push 0x3 | int nIDDlgItem = 0x3
0040117C | FF 75 08 | push dword ptr ss:[ebp+0x8] | HANDLE hDlg
0040117F | E8 F4 01 00 00 | call <duelis.SendDlgItemMessageA> | SendDlgItemMessageA
00401184 | 68 79 21 40 00 | push duelis.402179 | LONG_PTR lParam = 0x402179 序列号变量
00401189 | 6A 10 | push 0x10 | UINT_PTR wParam = 0x10
0040118B | 6A 0D | push 0xD | UINT Msg = WM_GETTEXT
0040118D | 6A 04 | push 0x4 | int nIDDlgItem = 0x4
0040118F | FF 75 08 | push dword ptr ss:[ebp+0x8] | HANDLE hDlg
00401192 | E8 E1 01 00 00 | call <duelis.SendDlgItemMessageA> | SendDlgItemMessageA
00401197 | B9 FF FF FF FF | mov ecx, 0xFFFFFFFF |
0040119C | 41 | inc ecx |
0040119D | 0F BE 81 60 21 40 00 | movsx eax, byte ptr ds:[ecx+0x402160] | ecx+402160:"pkabcd"
004011A4 | 83 F8 00 | cmp eax, 0x0 | 用户名字符不能为空
004011A7 | 74 32 | je duelis.4011DB |
004011A9 | BE FF FF FF FF | mov esi, 0xFFFFFFFF |
004011AE | 83 F8 41 | cmp eax, 0x41 | 41:'A'
004011B1 | 7C 67 | jl duelis.40121A |
004011B3 | 83 F8 7A | cmp eax, 0x7A | 7A:'z'
004011B6 | 77 62 | ja duelis.40121A | A到Z之间的字符
004011B8 | 83 F8 5A | cmp eax, 0x5A | 5A:'Z'
004011BB | 7C 03 | jl duelis.4011C0 |
004011BD | 83 E8 20 | sub eax, 0x20 | 小写转为大写
004011C0 | 46 | inc esi | 增加计数器,0开始
004011C1 | 0F BE 96 17 20 40 00 | movsx edx, byte ptr ds:[esi+0x402017] | esi+402017:"A1LSK2DJF4HGP3QWO5EIR6UTYZ8MXN7CBV9"
004011C8 | 3B C2 | cmp eax, edx |
004011CA | 75 F4 | jne duelis.4011C0 | 循环查找用户名字符在字符串中对应的位置
004011CC | 0F BE 86 3C 20 40 00 | movsx eax, byte ptr ds:[esi+0x40203C] | esi+40203C:"SU7CSJKF09NCSDO9SDF09SDRLVK7809S4NF"
004011D3 | 89 81 94 21 40 00 | mov dword ptr ds:[ecx+0x402194], eax | 用上一循环查找的位置取字符串2中的字符,并保存
004011D9 | EB C1 | jmp duelis.40119C |
004011DB | FF 35 AF 21 40 00 | push dword ptr ds:[0x4021AF] |
004011E1 | 68 94 21 40 00 | push duelis.402194 | 402194:加密处理得到的注册码
004011E6 | 68 79 21 40 00 | push duelis.402179 | 402179:我们输入的注册码"123456"
004011EB | E8 54 00 00 00 | call <duelis.sub_401244> | 比较
004011F0 | 83 F8 01 | cmp eax, 0x1 |
004011F3 | 0F 84 DE FE FF FF | je duelis.4010D7 | 跳去正确的提示信息。
004011F9 | EB 1F | jmp duelis.40121A |
004011FB | 83 7D 10 01 | cmp dword ptr ss:[ebp+0x10], 0x1 |
004011FF | 0F 84 22 FF FF FF | je duelis.401127 |
经过对上段代码下断分析,程序的注册算法为:SendDlgItemMessageA 函数参数UINT Msg = WM_GETTEXTLENGTH,取用户名长度,不能超过8位,注册码长度必须和用户名长度相同。
然后调用SendDlgItemMessageA函数,参数UINT Msg = WM_GETTEXT,来获输入的用户名和注册码字符串,对输入的用户名逐个字符进行处理和判断,字符ASC值不能小于‘A’,不能大于‘z’,大于'Z'的话减去0x20,相当于小写转大写,当然ASCII表中Z后的6个字符是个例外,然后在字符串1("A1LSK2DJF4HGP3QWO5EIR6UTYZ8MXN7CBV9")中查找用户名字符的位置,跟据这个位置取第二个字符串("SU7CSJKF09NCSDO9SDF09SDRLVK7809S4NF")中相应位置的字符,并组成最终的正确注册码,接下来就是比较注册码,并跳转到相应的提示信息。
【注册机编写】
收集了以上信息后,就可以着手编写注册机了。
C++代码如下:
[C++] 纯文本查看 复制代码 #define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include <string>
using namespace std;
void main()
{
char * yourName;
char * serial;
string key1 = "A1LSK2DJF4HGP3QWO5EIR6UTYZ8MXN7CBV9";
char * key2 = "SU7CSJKF09NCSDO9SDF09SDRLVK7809S4NF";
yourName = new char[260];
memset(yourName, 0, 260);
serial = new char[260];
memset(serial, 0, 260);
cout << "Enter your name:";
gets(yourName);
if (strlen(yourName) == 0 || strlen(yourName)>8)
{
cout << "用户名必须小于等于8位!" << endl;
system("pause");
return;
}
for (unsigned int x = 0; x < strlen(yourName); x++)
{
if (yourName[x]<'A' || yourName[x]>'z')
{
cout << "用户名必须为A-z之间的字符!" << endl;
system("pause");
return;
}
if (yourName[x] > 'Z')
yourName[x] -= 0x20;
serial[x] = key2[key1.find_first_of(yourName[x])];
}
cout<<"your Code is:"<<serial<<endl;
system("pause");
附注册成功截图:
|