【文章标题】: 蛇年捏软柿子之 ASA Keygenme#3
【文章作者】: 返璞归真
【软件名称】: ASA Keygenme#3
【软件大小】: 6.50kb
【下载地址】: KeygenMe#3+注册机.rar
【加壳方式】: none
【编写语言】: VC
【操作平台】: WinAll
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
继续捏软柿子,我等菜鸟学习的典型例子。
这个#3版本和之前的类似,程序框架分析见#2的文章。
下面直接开始主要部分。
004010D1 /$ 55 push ebp
004010D2 |. 8BEC mov ebp, esp
004010D4 |. 83C4 F8 add esp, -0x8
004010D7 |. 68 00020000 push 0x200 ;
004010DC |. 68 3C334000 push 0040333C ;
004010E1 |. E8 22040000 call <jmp.&kernel32.RtlZeroMemory> ; \RtlZeroMemory
004010E6 |. 6A 20 push 0x20 ;
004010E8 |. 68 1C334000 push 0040331C ;
004010ED |. E8 16040000 call <jmp.&kernel32.RtlZeroMemory> ; \RtlZeroMemory
004010F2 |. C705 00304000>mov dword ptr [0x403000], 0x200
004010FC |. 68 1C334000 push 0040331C ;
00401101 |. E8 FC030000 call <jmp.&kernel32.GlobalMemoryStatu>; \GlobalMemoryStatus
00401106 |. A1 24334000 mov eax, dword ptr [0x403324]
0040110B |. 8945 F8 mov dword ptr [ebp-0x8], eax
0040110E |. 68 F8324000 push 004032F8 ;
00401113 |. E8 E4030000 call <jmp.&kernel32.GetSystemInfo> ; \GetSystemInfo
00401118 |. 8B3D 14334000 mov edi, dword ptr [0x403314]
0040111E |. A1 10334000 mov eax, dword ptr [0x403310]
00401123 |. 99 cdq
00401124 |. F7EF imul edi
00401126 |. 8B7D F8 mov edi, dword ptr [ebp-0x8]
00401129 |. 33C7 xor eax, edi
0040112B |. 57 push edi
0040112C |. 50 push eax
0040112D |. 68 8C304000 push 0040308C ; |Format = "%X%X"
00401132 |. 68 3C334000 push 0040333C ;
00401137 |. E8 72030000 call <jmp.&user32.wsprintfA> ; \wsprintfA
0040113C |. 83C4 10 add esp, 0x10
---------------------------------以上部分和之前Keygenme#2中的获取方式一样----------------------------
下面是对现在的机器码进行一次MD5加密,发现作者可能写代码的时候有一个BUG,没有保存机器码的长度
导致调用MD5时候传入源串的长度始终为0,从而使计算出来的MD5值总为D41D8CD98F00B204E9800998ECF8427E,也就是空串的MD5值
我已经将代码进行了简单的PATCH,为了不跳转到其余代码执行,所以偷懒了。。。大家一看就知道。。
-----------------------------------------原先的代码------------------------------------------------
0040113F 68 00020000 push 0x200
00401144 68 3C354000 push 0040353C
00401149 E8 BA030000 call <jmp.&kernel32.RtlZeroMemory>
0040114E 68 00020000 push 0x200
00401153 68 3C374000 push 0040373C
00401158 E8 AB030000 call <jmp.&kernel32.RtlZeroMemory>
0040115D |. 50 push eax
---------------------------------------原先的代码结束----------------------------------------------
-----------------------------------------Patch过的代码--------------------------------------------
0040113F |. 50 push eax ;这里为什么要push eax,大家知道 wsprintf返回值是int型代表真正printf的长度~
00401140 |. 68 00040000 push 0x400
00401145 |. 68 3C354000 push 0040353C
0040114A |. E8 B9030000 call <jmp.&kernel32.RtlZeroMemory> ; \RtlZeroMemory
0040114F |. 58 pop eax
00401150 |. 90 nop
00401151 |. 90 nop
00401152 |. 90 nop
00401153 |. 90 nop
00401154 |. 90 nop
00401155 |. 90 nop
00401156 |. 90 nop
00401157 |. 90 nop
00401158 |. 90 nop
00401159 |. 90 nop
0040115A |. 90 nop
0040115B |. 90 nop
0040115C |. 90 nop
0040115D |. 50 push eax ;把长度存下来
---------------------------------------Patch过的代码结束----------------------------------------------
经过上面修改,运行下程序可以发现,不同机器就显示不同的机器码了。
0040115E |. 68 F8304000 push 004030F8 ;MD5_CTX结构,结构如下
00401163 |. E8 C4030000 call <jmp.&cryptdll.MD5Init>
00401168 |. 58 pop eax ;取出之前存的长度
00401169 |. 50 push eax ;长度
0040116A |. 68 3C334000 push 0040333C ;待加密的源串
0040116F |. 68 F8304000 push 004030F8 ;MD5_CTX结构
MD5CTX结构如下
typedef struct {
ULONG i[2];
ULONG buf[4];
unsigned char in[64];
unsigned char digest[16];
} MD5_CTX;
00401174 |. E8 B9030000 call <jmp.&cryptdll.MD5Update>
00401179 |. 68 F8304000 push 004030F8
0040117E |. E8 A3030000 call <jmp.&cryptdll.MD5Final> ;最后调用MD5Final一次MD5HASH完成。
00401183 |. BF F8304000 mov edi, 004030F8 ;指向MD5_CTX
00401188 |. 83C7 08 add edi, 0x8 ;指向MD5_CTX中的
0040118B |. 68 00020000 push 0x200 ; /Length = 200 (512.)
00401190 |. 68 3C334000 push 0040333C ; |Destination = KeygenMe.0040333C
00401195 |. E8 6E030000 call <jmp.&kernel32.RtlZeroMemory> ; \RtlZeroMemory
0040119A |. 68 00020000 push 0x200 ; /Length = 200 (512.)
0040119F |. 68 3C354000 push 0040353C ; |Destination = KeygenMe.0040353C
004011A4 |. E8 5F030000 call <jmp.&kernel32.RtlZeroMemory> ; \RtlZeroMemory
004011A9 |. 8B07 mov eax, dword ptr [edi] ;获取MD5_CTX中的密文字段中的前4个字节
004011AB |. 0FC8 bswap eax ;由于高位先低位后,所以要翻转下顺序,调整为正确的是顺序
004011AD |. 8B5F 04 mov ebx, dword ptr [edi+0x4] ;获取密文中的第5~8个字节
004011B0 |. 0FCB bswap ebx
004011B2 |. 53 push ebx ;
004011B3 |. 50 push eax ;
004011B4 |. 68 91304000 push 00403091 ; |Format = "%.8X-%.8X-"
004011B9 |. 68 3C374000 push 0040373C ;格式化下机器码前半部分是8位一组分2组
004011BE |. E8 EB020000 call <jmp.&user32.wsprintfA> ;
004011C3 |. 83C4 10 add esp, 0x10
004011C6 |. 8B07 mov eax, dword ptr [edi] ;同上
004011C8 |. 8B4F 08 mov ecx, dword ptr [edi+0x8]
004011CB |. 0FC9 bswap ecx
004011CD |. 8B57 0C mov edx, dword ptr [edi+0xC] ;同上
004011D0 |. 0FCA bswap edx
004011D2 |. 52 push edx ;
004011D3 |. 51 push ecx ;
004011D4 |. 68 9C304000 push 0040309C ; |Format = "%.8X-%.8X"
004011D9 |. 68 3C354000 push 0040353C ;
004011DE |. E8 CB020000 call <jmp.&user32.wsprintfA> ; 格式化下机器码后半部分是8位一组分2组
004011E3 |. 83C4 10 add esp, 0x10
004011E6 |. 68 3C354000 push 0040353C ;
004011EB |. 68 3C374000 push 0040373C ;
004011F0 |. E8 19030000 call <jmp.&kernel32.lstrcatA> ; 连接机器码
004011F5 |. 68 3C374000 push 0040373C ;
004011FA |. 68 EA030000 push 0x3EA ;
004011FF |. FF75 08 push dword ptr [ebp+0x8] ; 刷新到控件上
00401202 |. E8 DD020000 call <jmp.&user32.SetDlgItemTextA> ; \SetDlgItemTextA
00401207 |. C9 leave
00401208 \. C2 0400 retn 0x4
---------------------------------------机器码获取部分除了一个BUG之外,别的都比较简单-------------------------------
简单的来说就是 MD5(KeygenMe#2中的机器码) 然后8位一组中间用-分割
下面是核心注册CALL中的验证,同样是软柿子,大家一起捏。。
call 004013C8中是验证用户名合法性,和#2中一样不再多说。
call 0040120B中是计算正确码的部分
0040120B /$ 55 push ebp
0040120C |. 8BEC mov ebp, esp
0040120E |. 83C4 F4 add esp, -0xC
00401211 |. 68 00020000 push 0x200
00401216 |. 68 3C354000 push 0040353C
0040121B |. E8 E8020000 call <jmp.&kernel32.RtlZeroMemory>
00401220 |. 68 00020000 push 0x200
00401225 |. 68 3C334000 push 0040333C
0040122A |. E8 D9020000 call <jmp.&kernel32.RtlZeroMemory>
0040122F |. 68 00020000 push 0x200
00401234 |. 68 3C334000 push 0040333C
00401239 |. 68 EA030000 push 0x3EA
0040123E |. FF75 08 push dword ptr [ebp+0x8]
00401241 |. E8 86020000 call <jmp.&user32.GetDlgItemTextA> ; \GetDlgItemTextA
00401246 |. 68 3C334000 push 0040333C ;
0040124B |. E8 CA020000 call <jmp.&kernel32.lstrlenA> ; \lstrlenA
00401250 |. 8945 FC mov dword ptr [ebp-0x4], eax
;还是和#2中一样获取机器码
00401253 |. 33C9 xor ecx, ecx
00401255 |. 33D2 xor edx, edx
00401257 |. BE 3C334000 mov esi, 0040333C
0040125C |. EB 04 jmp short 00401262
0040125E |> 031431 /add edx, dword ptr [ecx+esi]
00401261 |. 41 |inc ecx
00401262 |> 3B4D FC cmp ecx, dword ptr [ebp-0x4]
00401265 |.^ 75 F7 \jnz short 0040125E
;对机器码进行累加后面要用到
00401267 |. 8955 F8 mov dword ptr [ebp-0x8], edx
0040126A |. 68 00020000 push 0x200
0040126F |. 68 3C334000 push 0040333C
00401274 |. E8 8F020000 call <jmp.&kernel32.RtlZeroMemory> ; \RtlZeroMemory
00401279 |. 68 00020000 push 0x200
0040127E |. 68 3C374000 push 0040373C
00401283 |. E8 80020000 call <jmp.&kernel32.RtlZeroMemory> ; \RtlZeroMemory
00401288 |. 68 00020000 push 0x200
0040128D |. 68 3C334000 push 0040333C
00401292 |. 68 E9030000 push 0x3E9
00401297 |. FF75 08 push dword ptr [ebp+0x8]
0040129A |. E8 2D020000 call <jmp.&user32.GetDlgItemTextA> ; \GetDlgItemTextA
;获取用户名,这次作者没有范之前机器码的错误
0040129F |. 50 push eax
004012A0 |. 68 F8304000 push 004030F8
004012A5 |. E8 82020000 call <jmp.&cryptdll.MD5Init>
004012AA |. 58 pop eax
004012AB |. 50 push eax
004012AC |. 68 3C334000 push 0040333C
004012B1 |. 68 F8304000 push 004030F8
004012B6 |. E8 77020000 call <jmp.&cryptdll.MD5Update>
004012BB |. 68 F8304000 push 004030F8
004012C0 |. E8 61020000 call <jmp.&cryptdll.MD5Final>
004012C5 |. BF F8304000 mov edi, 004030F8
;对用户名进行了一次MD5加密!
004012CA |. 83C7 08 add edi, 0x8
004012CD |. 68 00020000 push 0x200
004012D2 |. 68 3C334000 push 0040333C
004012D7 |. E8 2C020000 call <jmp.&kernel32.RtlZeroMemory> ; \RtlZeroMemory
004012DC |. 68 00020000 push 0x200
004012E1 |. 68 3C374000 push 0040373C
004012E6 |. E8 1D020000 call <jmp.&kernel32.RtlZeroMemory> ; \RtlZeroMemory
004012EB |. 8B07 mov eax, dword ptr [edi]
004012ED |. 0FC8 bswap eax
004012EF |. 8B5F 04 mov ebx, dword ptr [edi+0x4]
004012F2 |. 0FCB bswap ebx
004012F4 |. 53 push ebx ;
004012F5 |. 50 push eax ;
004012F6 |. 68 A6304000 push 004030A6 ; |Format = "%.8X%.8X"
004012FB |. 68 3C334000 push 0040333C ;
00401300 |. E8 A9010000 call <jmp.&user32.wsprintfA> ; \wsprintfA
00401305 |. 83C4 10 add esp, 0x10
;进行格式化了
00401308 |. 8B07 mov eax, dword ptr [edi]
0040130A |. 8B4F 08 mov ecx, dword ptr [edi+0x8]
0040130D |. 0FC9 bswap ecx
0040130F |. 8B57 0C mov edx, dword ptr [edi+0xC]
00401312 |. 0FCA bswap edx
00401314 |. 52 push edx
00401315 |. 51 push ecx
00401316 |. 68 AF304000 push 004030AF ; |Format = "%.8X%.8X"
0040131B |. 68 3C374000 push 0040373C
00401320 |. E8 89010000 call <jmp.&user32.wsprintfA> ; \wsprintfA
00401325 |. 83C4 10 add esp, 0x10
00401328 |. 68 3C374000 push 0040373C
0040132D |. 68 3C334000 push 0040333C
00401332 |. E8 D7010000 call <jmp.&kernel32.lstrcatA>
00401337 |. 68 3C334000 push 0040333C
0040133C |. E8 D9010000 call <jmp.&kernel32.lstrlenA> ; \lstrlenA
00401341 |. 8945 FC mov dword ptr [ebp-0x4], eax
;用户名转换成 Upper(MD5(USERNAME))
00401344 |. 33C9 xor ecx, ecx
00401346 |. 33D2 xor edx, edx
00401348 |. BE 3C334000 mov esi, 0040333C
0040134D |. EB 2A jmp short 00401379
0040134F |> 031431 /add edx, dword ptr [ecx+esi]
00401352 |. 60 |pushad
00401353 |. 51 |push ecx
00401354 |. 81E2 FF0F0000 |and edx, 0xFFF
0040135A |. 8BC2 |mov eax, edx
0040135C |. B9 28000000 |mov ecx, 0x28
00401361 |. 99 |cdq
00401362 |. F7F9 |idiv ecx
00401364 |. 59 |pop ecx
00401365 |. 33C0 |xor eax, eax
00401367 |. BE 04304000 |mov esi, 00403004 ; ASCII "rwuyticgjxcmncvbmvbergjsghkfhjdsb457dg45"
0040136C |. 8A0432 |mov al, byte ptr [edx+esi]
0040136F |. BE 3C354000 |mov esi, 0040353C
00401374 |. 880431 |mov byte ptr [ecx+esi], al
00401377 |. 61 |popad
00401378 |. 41 |inc ecx
00401379 |> 3B4D FC cmp ecx, dword ptr [ebp-0x4]
0040137C |.^ 75 D1 \jnz short 0040134F
;还是和#2中一样对转换后的用户名进行查表变换,顺便对转换后的用户名值进行累加
0040137E |. 8955 F4 mov dword ptr [ebp-0xC], edx
00401381 |. 68 00020000 push 0x200
00401386 |. 68 3C334000 push 0040333C
0040138B |. E8 78010000 call <jmp.&kernel32.RtlZeroMemory> ; \RtlZeroMemory
00401390 |. 8B45 F8 mov eax, dword ptr [ebp-0x8]
00401393 |. 8B55 F4 mov edx, dword ptr [ebp-0xC]
00401396 |. 33D0 xor edx, eax
;和#2中一样处理,
00401398 |. 52 push edx
00401399 |. 68 B8304000 push 004030B8 ; |Format = "%X"
0040139E |. 68 3C334000 push 0040333C
004013A3 |. E8 06010000 call <jmp.&user32.wsprintfA> ; \wsprintfA
004013A8 |. 83C4 0C add esp, 0xC
004013AB |. 68 3C354000 push 0040353C
004013B0 |. 68 3C334000 push 0040333C
004013B5 |. E8 54010000 call <jmp.&kernel32.lstrcatA>
004013BA |. 68 3C334000 push 0040333C
004013BF |. E8 F0000000 call <jmp.&user32.CharUpperA> ; \CharUpperA
;还是和#2中一样处理,这样正确的注册码就出来了。这部分分析可以见我的#2分析
004013C4 |. C9 leave
004013C5 \. C2 0400 retn 0x4
-------------------------------------------------------------------------------
这样整个注册流程就清晰了。
附上很搓的注册机代码
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <string.h>
char target[]="rwuyticgjxcmncvbmvbergjsghkfhjdsb457dg45";
typedef struct {
ULONG i[2];
ULONG buf[4];
unsigned char in[64];
unsigned char digest[16];
} MD5_CTX;
#define PROTO_LIST(list) list
typedef void (WINAPI* PMD5Init) PROTO_LIST ((MD5_CTX *));
typedef void (WINAPI* PMD5Update) PROTO_LIST ((MD5_CTX *, const unsigned char *, unsigned int));
typedef void (WINAPI* PMD5Final )PROTO_LIST ((MD5_CTX *));
void GetMD5String1( char* pSz, char* pSource, int len )
{
MD5_CTX ctx;
PMD5Init MD5Init;
PMD5Update MD5Update;
PMD5Final MD5Final;
HINSTANCE hDLL;
if ( (hDLL = LoadLibrary("cryptdll.dll")) > 0 )
{
MD5Init = (PMD5Init)GetProcAddress(hDLL,"MD5Init");
MD5Update = (PMD5Update)GetProcAddress(hDLL,"MD5Update");
MD5Final = (PMD5Final)GetProcAddress(hDLL,"MD5Final");
MD5Init(&ctx);
MD5Update(&ctx, (const unsigned char*)pSource, len );
MD5Final(&ctx);
for (int i = 0 ; i < 16 ; ++i )
{
sprintf( pSz, "%.2x", ctx.digest[ i ] );
pSz+=2;
}
}
}
void GetMD5String2( char* pSz, char* pSource, int len )
{
MD5_CTX ctx;
PMD5Init MD5Init;
PMD5Update MD5Update;
PMD5Final MD5Final;
HINSTANCE hDLL;
if ( (hDLL = LoadLibrary("cryptdll.dll")) > 0 )
{
MD5Init = (PMD5Init)GetProcAddress(hDLL,"MD5Init");
MD5Update = (PMD5Update)GetProcAddress(hDLL,"MD5Update");
MD5Final = (PMD5Final)GetProcAddress(hDLL,"MD5Final");
MD5Init(&ctx);
MD5Update(&ctx, (const unsigned char*)pSource, len );
MD5Final(&ctx);
for (int i = 0 ; i < 16 ; ++i )
{
sprintf( pSz, "%.2x", ctx.digest[ i ] );
pSz+=2;
if ( (i + 1)%4 ==0 && i <15 )
{
*pSz='-';
pSz++;
}
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
char szUser[ 200 ];
char szTemp[ 200 ];
char szHardWare[ 200 ];
char szKey[ 200 ];
while( true )
{
puts("输入1根据本机机器码计算\n输入2根据输入的机器码计算\n");
gets(szTemp);
bool docal = false;
if ( szTemp[ 0 ] == '1' )
{
MEMORYSTATUS tmpMemSt;
GlobalMemoryStatus( &tmpMemSt );
SYSTEM_INFO tmpSysinfo;
GetSystemInfo( &tmpSysinfo );
sprintf( szHardWare,"%x%x",(tmpSysinfo.dwAllocationGranularity * tmpSysinfo.dwProcessorType) ^ tmpMemSt.dwTotalPhys, tmpMemSt.dwTotalPhys );
CharUpper(szHardWare);
memset( szTemp, 0, sizeof( szTemp ) );
GetMD5String2( szTemp, szHardWare, strlen( szHardWare ) );
CharUpper( szTemp );
memcpy( szHardWare, szTemp, sizeof(szTemp) );
puts("本机机器码为:");
puts( szHardWare );
puts("请输入用户名:");
gets( szUser );
docal = true;
}
else if ( szTemp[ 0 ] == '2' )
{
puts("请输入机器码:");
gets(szHardWare);
puts("请输入用户名:");
gets( szUser );
docal = true;
}
if ( docal )
{
DWORD dwHard = 0;
DWORD* pDS = (DWORD*)szHardWare;
for (int i = 0 ; i < strlen( szHardWare ); ++i )
{
pDS = (DWORD*)&szHardWare[ i ];
dwHard += *pDS;
}
memset( szTemp, 0, sizeof( szTemp ) );
GetMD5String1( szTemp, szUser, strlen( szUser ) );
CharUpper( szTemp );
DWORD dwUser = 0;
for (int i = 0 ; i < strlen( szTemp ) ; ++i )
{
dwUser += *(DWORD*)&szTemp[ i ];
int t = dwUser & 0xFFF;
szUser[ i ] = target[ (t% 0x28) ];
}
szUser[ strlen( szTemp ) ] = 0;
sprintf(szKey,"%x%s", dwUser ^ dwHard, szUser );
CharUpper( szKey );
puts("注册码为:");
puts(szKey);
}
}
return 0;
}
--------------------------------------------------------------------------------
【经验总结】
和#2几乎一样,继续捏软柿子。
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2013年02月17日 22:41:43
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课