本帖最后由 pk8900 于 2017-12-28 20:34 编辑
【适合破解新手的160个crackme练手】第54个eKH.1.exe,这个比较简单,前两个五星级的可以说难度很大,第52个用了三天多时间,完美反推写出注册机,参考帖子(https://www.52pojie.cn/thread-677861-1-1.html),第53个算法是弄清了,是64位的奇偶较验,用注册机穷举根本不现实,用了一星期的时间,也没有想到简便的算法,等注册机研究成功后再发贴了,今天就分享一下这个Crackme 054的算法及注册机过程。
【crackme简介】
下载地址:http://pan.baidu.com/share/link?shareid=541269&uk=4146939145
Borland Delphi 3.0编写,无壳,是一个用户名+序列号式验证方式,有提示文字。
标注为?,估计难度为1星。
工具:X64dbg,Ldr,注册编写:VS2013
【crackme截图】
【算法部分】
首先用Ldr载入,找到按钮事件地址:方法可以参照下图:
找到事件地址下断点,中断后跟踪,代码如下:
验证注册流程一:检查输入的用户名长度,不为空。
[Asm] 纯文本查看 复制代码 00427B6A | 8B 45 FC | mov eax, dword ptr ss:[ebp-0x4] | [ebp-4]:"pk8900"用户名
00427B6D | E8 3E BC FD FF | call ekh.1.4037B0 | 取长度
00427B72 | 48 | dec eax |
00427B73 | 7C 30 | jl ekh.1.427BA5 | 跳去失败
00427B75 | 8D 55 FC | lea edx, dword ptr ss:[ebp-0x4] |
00427B78 | 8B 83 EC 01 00 00 | mov eax, dword ptr ds:[ebx+0x1EC] |
00427B7E | E8 0D E2 FE FF | call ekh.1.415D90 |
00427B83 | 8B 45 FC | mov eax, dword ptr ss:[ebp-0x4] | [ebp-4]:"12345678"
00427B86 | 50 | push eax |
00427B87 | 8D 55 F8 | lea edx, dword ptr ss:[ebp-0x8] |
00427B8A | 8B 83 DC 01 00 00 | mov eax, dword ptr ds:[ebx+0x1DC] |
00427B90 | E8 FB E1 FE FF | call ekh.1.415D90 |
00427B95 | 8B 45 F8 | mov eax, dword ptr ss:[ebp-0x8] |
00427B98 | 5A | pop edx |
00427B99 | E8 82 FE FF FF | call ekh.1.427A20 | 关键算法
00427B9E | 3D 4E 61 BC 00 | cmp eax, 0xBC614E | 成功比较值:0xBC614E
00427BA3 | 7D 1E | jge ekh.1.427BC3 |
00427BA5 | 6A 00 | push 0x0 |
00427BA7 | 68 08 7C 42 00 | push ekh.1.427C08 | 427C08:"ERROR"
00427BAC | 68 10 7C 42 00 | push ekh.1.427C10 | 427C10:"Wrong Serial Number !"
验证注册流程二:关键算法部分:共两个循环加密部分,先看第一部分:
[Asm] 纯文本查看 复制代码 00427A56 | 8B 45 FC | mov eax, dword ptr ss:[ebp-0x4] | 用户名
00427A59 | E8 52 BD FD FF | call ekh.1.4037B0 | 取长度
00427A5E | 8B F0 | mov esi, eax |
00427A60 | 85 F6 | test esi, esi |
00427A62 | 7E 3C | jle ekh.1.427AA0 |
00427A64 | B8 01 00 00 00 | mov eax, 0x1 |
00427A69 | 8B D0 | mov edx, eax | 循环部分 起点
00427A6B | 8B 4D FC | mov ecx, dword ptr ss:[ebp-0x4] | [ebp-4]:"pk8900"
00427A6E | 0F B6 4C 11 FF | movzx ecx, byte ptr ds:[ecx+edx-0x1] | 取用户名 +循环计数器位字符
00427A73 | 03 D9 | add ebx, ecx | EBX累加
00427A75 | 71 05 | jno ekh.1.427A7C |
00427A77 | E8 B4 AF FD FF | call <ekh.1.sub_402A30> |
00427A7C | C1 E3 08 | shl ebx, 0x8 | 左移 8位
00427A7F | 8B 0D 80 88 42 00 | mov ecx, dword ptr ds:[0x428880] | 加密字符串1 &"LANNYDIBANDINGINANAKEKHYANGNGENTOT"
00427A85 | 0F B6 54 11 FF | movzx edx, byte ptr ds:[ecx+edx-0x1] | 取计算数 位置字符
00427A8A | 0B DA | or ebx, edx | 累加
00427A8C | 85 DB | test ebx, ebx |
00427A8E | 7D 0C | jge ekh.1.427A9C | 如果变负数则 乘 -1,转为正数
00427A90 | 6B D3 FF | imul edx, ebx, 0xFFFFFFFF |
00427A93 | 71 05 | jno ekh.1.427A9A |
00427A95 | E8 96 AF FD FF | call <ekh.1.sub_402A30> |
00427A9A | 8B DA | mov ebx, edx |
00427A9C | 40 | inc eax | 累加计数器
00427A9D | 4E | dec esi |
00427A9E | 75 C9 | jne ekh.1.427A69 | 下一循环
00427AA0 | 81 F3 78 56 34 12 | xor ebx, 0x12345678 | 异或 0x12345678
00427AA6 | 8D 55 F0 | lea edx, dword ptr ss:[ebp-0x10] |
00427AA9 | 8B C3 | mov eax, ebx |
00427AAB | E8 44 E9 FD FF | call <ekh.1.sub_4063F4> |
00427AB0 | 8B 45 F0 | mov eax, dword ptr ss:[ebp-0x10] | [ebp-10]:"1792442052"转十字制字符
这部分代码,取我们输入的用户名长度为循环次数,逐个取用户名字符加入累加值,左移8位,然后逐个取"LANNYDIBANDINGINANAKEKHYANGNGENTOT"字符累加,如果是负数则乘-1转为正数,循环完成后得到的值,与0x12345678异或,得到一个32位的数值。然后进行一下加密循环:
[Asm] 纯文本查看 复制代码 00427AAB | E8 44 E9 FD FF | call <ekh.1.sub_4063F4> |
00427AB0 | 8B 45 F0 | mov eax, dword ptr ss:[ebp-0x10] | [ebp-10]:"1792442052"转十字制字符
00427AB3 | E8 F8 BC FD FF | call ekh.1.4037B0 |
00427AB8 | 8B F0 | mov esi, eax | 得到长度,进行循环
00427ABA | 85 F6 | test esi, esi |
00427ABC | 7E 38 | jle ekh.1.427AF6 |
00427ABE | 8B C3 | mov eax, ebx |
00427AC0 | B9 0A 00 00 00 | mov ecx, 0xA | A:'\n'
00427AC5 | 99 | cdq |
00427AC6 | F7 F9 | idiv ecx | 除0xA(10)除余数
00427AC8 | 62 15 3C 7B 42 00 | bound edx, qword ptr ds:[0x427B3C] |
00427ACE | 8A 92 84 88 42 00 | mov dl, byte ptr ds:[edx+0x428884] | edx+428884:"LANNY5646521"
00427AD4 | 8D 45 F0 | lea eax, dword ptr ss:[ebp-0x10] |
00427AD7 | E8 FC BB FD FF | call <ekh.1.sub_4036D8> | 取字符串:第余数位
00427ADC | 8B 55 F0 | mov edx, dword ptr ss:[ebp-0x10] |
00427ADF | 8D 45 F4 | lea eax, dword ptr ss:[ebp-0xC] |
00427AE2 | E8 D1 BC FD FF | call <ekh.1.sub_4037B8> |
00427AE7 | 8B C3 | mov eax, ebx |
00427AE9 | B9 0A 00 00 00 | mov ecx, 0xA | A:'\n'
00427AEE | 99 | cdq |
00427AEF | F7 F9 | idiv ecx |
00427AF1 | 8B D8 | mov ebx, eax |
00427AF3 | 4E | dec esi |
00427AF4 | 75 C8 | jne ekh.1.427ABE |
00427AF6 | 8B 45 F4 | mov eax, dword ptr ss:[ebp-0xC] | [ebp-C]:"N5LNYYN54A"
这部分是将上个循环得到的数值转为字符,取长度为循环次数,每次除10取余,根据余数取字符串"LANNY5646521"中相应位的字符,组成一个字符串,就是正确的序列号。
算法部分分析完成,根据以上算法编写注册机代码如下:
[C++] 纯文本查看 复制代码 #define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
void main()
{
long key_tmp = 0;
char * key1 = "LANNYDIBANDINGINANAKEKHYANGNGENTOT"; //固定值,用于加密
char * key2 = "LANNY5646521"; //固定值,用于加密
char * yourName;
char * serial=new char[260];
memset(serial, 0, 260);
yourName = new char[260];
memset(yourName, 0, 260);
cout << "输入用户名:";
gets(yourName);
if (strlen(yourName) <= 0)
{
cout << "用户名不能为空" << endl;
return;
}
for (unsigned int x = 0; x < strlen(yourName); x++)
{
key_tmp += yourName[x];
key_tmp = key_tmp << 8;
key_tmp |= key1[x];
if (key_tmp<0)
{
key_tmp *= -1;
}
}
key_tmp ^= 0x12345678;
long tmp1=0;
while (key_tmp)
{
serial [tmp1] = key2[key_tmp % 10];
tmp1++;
key_tmp /= 10;
}
cout << "序列号为:" << serial << endl;
system("pause");
成功测试注册用户名:52pojie.cn 序列号:Y55565NL4A
分析过程有不对或不到位的地方,欢迎大家回贴交流,共同探讨。
|