下载地址:http://www.crackmes.de/users/starzboy/keygen_me_2/
使用工具:OllyDbg
前言:要考试了,没时间弄复杂的程序,看见这个比较“浅显易懂”,拿来小搞一把,
大概花不了半天时间吧。 解密:
这是一个基于控制台的程序,注册信息包含三方面内容:User_Name、User_ID以及
password。用OllyDbg载入,查找相关的字串,很快定位到如下代码处: //////////////// 以下是代码 ////////////////
004014D3 |. C74424 04 CF0>mov dword ptr [esp+4], 004400CF ; ASCII "Enter User-Name: "
004014DB |. C70424 C03344>mov dword ptr [esp], 004433C0
004014E2 |. E8 71AE0300 call 0043C358 ; printf
004014E7 |. 8D85 F8FEFFFF lea eax, [ebp-108] ; |
004014ED |. 890424 mov [esp], eax ; |
004014F0 |. E8 8BF40000 call <jmp.&msvcrt.gets> ; \gets
004014F5 |. 80BD F8FEFFFF>cmp byte ptr [ebp-108], 0
004014FC |. 0F84 A8010000 je 004016AA
00401502 |. C74424 04 E10>mov dword ptr [esp+4], 004400E1 ; ASCII "Enter User-ID: "
0040150A |. C70424 C03344>mov dword ptr [esp], 004433C0
00401511 |. E8 42AE0300 call 0043C358 ; printf
00401516 |. 8D85 DCFEFFFF lea eax, [ebp-124]
0040151C |. 894424 04 mov [esp+4], eax
00401520 |. C70424 603444>mov dword ptr [esp], 00443460
00401527 |. E8 C4720200 call 004287F0 ; scanf("%ld",...)
0040152C |. C785 D8FEFFFF>mov dword ptr [ebp-128], 0
00401536 |> 8D85 F8FEFFFF /lea eax, [ebp-108] ; |
0040153C |. 890424 |mov [esp], eax ; |
0040153F |. E8 2CF40000 |call <jmp.&msvcrt.strlen> ; \strlen
00401544 |. 3B85 D8FEFFFF |cmp eax, [ebp-128] ; 计数器
0040154A |. 76 5B |jbe short 004015A7
0040154C |. 8D45 F8 |lea eax, [ebp-8]
0040154F |. 0385 D8FEFFFF |add eax, [ebp-128]
00401555 |. 2D 00010000 |sub eax, 100
0040155A |. 0FBE00 |movsx eax, byte ptr [eax] ; 依次取User_Name各字符
0040155D |. 8985 F4FEFFFF |mov [ebp-10C], eax
00401563 |. 8D85 F4FEFFFF |lea eax, [ebp-10C]
00401569 |. 8100 656C2A03 |add dword ptr [eax], 32A6C65
0040156F |. 8B85 DCFEFFFF |mov eax, [ebp-124] ; UserID
00401575 |. 0FAF85 DCFEFF>|imul eax, [ebp-124]
0040157C |. 01C0 |add eax, eax
0040157E |. 0385 F4FEFFFF |add eax, [ebp-10C]
00401584 |. 05 237AD602 |add eax, 2D67A23
00401589 |. 8985 F4FEFFFF |mov [ebp-10C], eax
0040158F |. 8B95 F4FEFFFF |mov edx, [ebp-10C]
00401595 |. 8D85 F0FEFFFF |lea eax, [ebp-110]
0040159B |. 0110 |add [eax], edx ; 计算双字累加和
0040159D |. 8D85 D8FEFFFF |lea eax, [ebp-128]
004015A3 |. FF00 |inc dword ptr [eax]
004015A5 |.^ EB 8F \jmp short 00401536
004015A7 |> C74424 04 F10>mov dword ptr [esp+4], 004400F1 ; ASCII "Enter Password: "
004015AF |. C70424 C03344>mov dword ptr [esp], 004433C0
004015B6 |. E8 9DAD0300 call 0043C358 ; printf
004015BB |. 8D85 E8FEFFFF lea eax, [ebp-118]
004015C1 |. 894424 04 mov [esp+4], eax
004015C5 |. C70424 603444>mov dword ptr [esp], 00443460
004015CC |. E8 1F720200 call 004287F0 ; scanf("%ld",...)
004015D1 |. C74424 04 CE0>mov dword ptr [esp+4], 004400CE
004015D9 |. C70424 C03344>mov dword ptr [esp], 004433C0
004015E0 |. E8 73AD0300 call 0043C358
004015E5 |. C74424 04 28B>mov dword ptr [esp+4], 0043B128
004015ED |. 890424 mov [esp], eax
004015F0 |. E8 BB8C0200 call 0042A2B0
004015F5 |. 8B85 F0FEFFFF mov eax, [ebp-110]
004015FB |. 3B85 E8FEFFFF cmp eax, [ebp-118] ; 相等则注册成功
00401601 |. 75 3A jnz short 0040163D
00401603 |. C74424 04 020>mov dword ptr [esp+4], 00440102 ; ASCII "Access Granted !"
0040160B |. C70424 C03344>mov dword ptr [esp], 004433C0
00401612 |. E8 41AD0300 call 0043C358
00401617 |. C74424 04 28B>mov dword ptr [esp+4], 0043B128
0040161F |. 890424 mov [esp], eax
00401622 |. E8 898C0200 call 0042A2B0
00401627 |. C74424 04 140>mov dword ptr [esp+4], 00440114 ; ASCII "You did it Cracker...now make a keygen !"
0040162F |. C70424 C03344>mov dword ptr [esp], 004433C0
00401636 |. E8 1DAD0300 call 0043C358
//////////////// 以上是代码 //////////////// 不知道为什么,OllyDbg在加载这种控制台程序时似乎无法保留上次的断点。如果对C
语言的几个标准库函数有些印象的话,应该不难看出过程43C358相当于printf,而过
程4287F0则相当于scanf。生成注册码部分的算法完全包含在从401536到4015A5的循
环中,在4015FB处只是将输入的Password同程序中算出来的进行比较。唯一要注意的
是User_ID和Password都只能输入十进制数值。关于这一点,我曾经尝试过在User_ID
中输入字符串,结果在40156F处的[ebp-124]中出现的内容就是一个奇怪的堆栈地址,
当时就纳闷了:程序在不同的环境下分配到的堆栈段地址也可能不一样,如果以这个
作为注册算法的一个运算数,就意味着注册算法在每次运行时都不一样,哪里还有通
用的注册机可言?后来无意中输入了一个比较小的纯数值,发现上述[ebp-124]中就
是这个数值,于是明白这个地方原本是只能输数值的。再测试一下可知:象
2147483647以及-1这样的数值都可以输,但2147483648却不能输,否则又变成22xxxx
之类的堆栈地址了,相当于scanf带"%ld"格式符的情况。实际上,注意到取用户名的
时候用的函数是gets,而User_ID和password都是用scanf,大致也能明白一些了吧。
将401536到4015A5这段代码翻译成伪代码形式就是:
N = strlen(User_Name)
c = 0
i = 1
循环:当i <= N
a = User_Name第i个字符的ASCII码
a = a + 32A6C65h
b = User_ID
b = b * b
b = b + b
b = b + a
b = b + 2D67A23h
c = c + b
i = i + 1
循环尾
return c――password
将此算法再优化一番,把与循环无关的运算都提出来。其中可以手算:
32A6C65h + 2D67A23h = 600E688h
则可以得到:
#include <stdio.h>
#include <string.h>
long int liPasswordValue(char *szUserName, long int liUserIDValue)
{
long int loc_liMagicAdditive = (liUserIDValue * liUserIDValue << 1) + 0x600E688;
long int loc_liDwordCheckSum = 0;
int loc_N = strlen(szUserName);
for(int i = 0; i < loc_N; i++)
{
loc_liDwordCheckSum = loc_liDwordCheckSum + (long int)szUserName[i] + loc_liMagicAdditive;
}
return loc_liDwordCheckSum;
}
/* 主函数中可如下调用 */
int main(int argc, char *argv[])
{
char szUserName[80];
long int liUserIDValue;
printf("请输入用户名:");
gets(szUserName);
printf("\n请输入用户ID:");
scanf("%ld", &liUserIDValue);
printf("\n你的密码已生成:%ld\n", liPasswordValue(szUserName, liUserIDValue));
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)