【文章标题】: 【原创】Happytown的第35个crackme分析+注册机
【文章作者】: ylp1332
【作者邮箱】: [email]ylp1332@yahoo.com.cn[/email]
【作者主页】: n/a
【作者QQ号】: n/a
【软件名称】: CrackMe_0035.exe
【软件大小】: 220k
【下载地址】: 自己搜索下载
【加壳方式】: none
【保护方式】: md5,大数运算
【编写语言】: visual c++ 6
【使用工具】: peid,ida,ollydbg,vc6
【操作平台】: win32
【软件介绍】: 非商业软件
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
『分析过程』
1、首先用PEiD 0.94检查,用visual c++ 6编译,没有加壳。
2、用PEiD的插件Krypto ANAlyzer检查,发现有MD5算法和大数运算。
3、用IDA载入,进行初步的静态分析。
分析结论:
使用了Miracl大数运算库;
使用了标准的MD5算法;
使用Windows API GetDlgItemTextA从控件中取得用户名、组织和注册码,
用户名和组织名至少要2个字符长,注册码必须都是16进制字符,且不能为空。
验证过程的反汇编代码如下:
.text:00401110
.text:00401110 ; =============== S U B R O U T I N E =======================================
.text:00401110
.text:00401110
.text:00401110 ; int __cdecl OnCheck(HWND hDlg)
.text:00401110 OnCheck proc near ; CODE XREF: DialogFunc+55p
.text:00401110
.text:00401110 var_2C4 = dword ptr -2C4h
.text:00401110 md5_str_out = dword ptr -2C0h
.text:00401110 var_2BB = dword ptr -2BBh
.text:00401110 var_2B7 = dword ptr -2B7h
.text:00401110 var_2B3 = word ptr -2B3h
.text:00401110 var_2B1 = byte ptr -2B1h
.text:00401110 md5_ctx = dword ptr -2B0h
.text:00401110 Serial = dword ptr -258h
.text:00401110 UserName = byte ptr -190h
.text:00401110 Group = byte ptr -0C8h
.text:00401110 hDlg = dword ptr 4
.text:00401110
.text:00401110 sub esp, 2C4h
.text:00401116 push ebx
.text:00401117 push esi
.text:00401118 push edi
.text:00401119 mov ecx, 49
.text:0040111E xor eax, eax
.text:00401120 lea edi, [esp+141h]
.text:00401127 mov [esp+2D0h+UserName], 0
.text:0040112F mov [esp+2D0h+Group], 0
.text:00401137 rep stosd
.text:00401139 stosw
.text:0040113B stosb
.text:0040113C mov ecx, 49
.text:00401141 xor eax, eax
.text:00401143 lea edi, [esp+209h]
.text:0040114A mov byte ptr [esp+2D0h+Serial], 0
.text:0040114F rep stosd
.text:00401151 stosw
.text:00401153 stosb
.text:00401154 mov ecx, 49
.text:00401159 xor eax, eax
.text:0040115B lea edi, [esp+2D0h+Serial+1]
.text:0040115F mov byte ptr [esp+2D0h+md5_str_out], 0
.text:00401164 rep stosd
.text:00401166 stosw
.text:00401168 stosb
.text:00401169 xor eax, eax ; 使 eax = 0
.text:0040116B mov [esp+2D0h+md5_str_out+1], eax
.text:0040116F mov [esp+2D0h+var_2BB], eax
.text:00401173 mov [esp+2D0h+var_2B7], eax
.text:00401177 mov [esp+2D0h+var_2B3], ax
.text:0040117C push eax
.text:0040117D push 300h
.text:00401182 mov [esp+2D8h+var_2B1], al
.text:00401186 call _mirsys ; mirsys(300h,0h)
.text:0040118B mov esi, [esp+2D8h+hDlg]
.text:00401192 mov edi, ds:GetDlgItemTextA
.text:00401198 add esp, 8
.text:0040119B lea ecx, [esp+2D0h+UserName]
.text:004011A2 mov ebx, eax
.text:004011A4 push 0C9h ; nMaxCount
.text:004011A9 push ecx ; lpString
.text:004011AA push 3EDh ; nIDDlgItem
.text:004011AF push esi ; hDlg
.text:004011B0 call edi ; GetDlgItemTextA
.text:004011B2 cmp eax, 2 ; 用户名不少于2个字符
.text:004011B5 jnb short loc_4011C3
.text:004011B7
.text:004011B7 loc_4011B7: ; CODE XREF: OnCheck+135j
.text:004011B7 pop edi
.text:004011B8 pop esi
.text:004011B9 xor eax, eax
.text:004011BB pop ebx
.text:004011BC add esp, 2C4h
.text:004011C2 retn
.text:004011C3 ; ---------------------------------------------------------------------------
.text:004011C3
.text:004011C3 loc_4011C3: ; CODE XREF: OnCheck+A5j
.text:004011C3 lea edx, [esp+2D0h+Group]
.text:004011CA push 0C9h ; nMaxCount
.text:004011CF push edx ; lpString
.text:004011D0 push 3EEh ; nIDDlgItem
.text:004011D5 push esi ; hDlg
.text:004011D6 call edi ; GetDlgItemTextA
.text:004011D8 cmp eax, 2 ; 组织名不少于2个字符
.text:004011DB jnb short loc_4011E9
.text:004011DD pop edi
.text:004011DE pop esi
.text:004011DF xor eax, eax
.text:004011E1 pop ebx
.text:004011E2 add esp, 2C4h
.text:004011E8 retn
.text:004011E9 ; ---------------------------------------------------------------------------
.text:004011E9
.text:004011E9 loc_4011E9: ; CODE XREF: OnCheck+CBj
.text:004011E9 lea eax, [esp+2D0h+Serial]
.text:004011ED push 0C9h ; nMaxCount
.text:004011F2 push eax ; lpString
.text:004011F3 push 3EFh ; nIDDlgItem
.text:004011F8 push esi ; hDlg
.text:004011F9 call edi ; GetDlgItemTextA
.text:004011FB test eax, eax ; 注册码不能为空
.text:004011FD jnz short loc_401209
.text:004011FF pop edi
.text:00401200 pop esi
.text:00401201 pop ebx
.text:00401202 add esp, 2C4h
.text:00401208 retn
.text:00401209 ; ---------------------------------------------------------------------------
.text:00401209
.text:00401209 loc_401209: ; CODE XREF: OnCheck+EDj
.text:00401209 mov al, byte ptr [esp+2D0h+Serial] ; 取注册码的第一个字符
.text:0040120D test al, al
.text:0040120F jz short loc_401253
.text:00401211 lea esi, [esp+2D0h+Serial] ; 取注册码
.text:00401215
.text:00401215 loc_401215: ; CODE XREF: OnCheck+141j
.text:00401215 cmp dword_414FDC, 1 ; 循环检查注册码的每一个字符看是否是16进制字符,否则退出
.text:0040121C jle short loc_401232
.text:0040121E xor ecx, ecx
.text:00401220 push 80h ; int
.text:00401225 mov cl, [esi]
.text:00401227 push ecx ; int
.text:00401228 call __isctype
.text:0040122D add esp, 8
.text:00401230 jmp short loc_401243
.text:00401232 ; ---------------------------------------------------------------------------
.text:00401232
.text:00401232 loc_401232: ; CODE XREF: OnCheck+10Cj
.text:00401232 mov eax, off_414DD0
.text:00401237 xor edx, edx
.text:00401239 mov dl, [esi]
.text:0040123B mov al, [eax+edx*2]
.text:0040123E and eax, 80h
.text:00401243
.text:00401243 loc_401243: ; CODE XREF: OnCheck+120j
.text:00401243 test eax, eax
.text:00401245 jz loc_4011B7
.text:0040124B mov al, [esi+1]
.text:0040124E inc esi
.text:0040124F test al, al
.text:00401251 jnz short loc_401215
.text:00401253
.text:00401253 loc_401253: ; CODE XREF: OnCheck+FFj
.text:00401253 lea ecx, [esp+2D0h+Group] ; 取组织名
.text:0040125A push ebp
.text:0040125B push ecx ; char *
.text:0040125C call __strrev ; 将组织名字符串逆转
.text:00401261 mov edi, eax
.text:00401263 or ecx, 0FFFFFFFFh
.text:00401266 xor eax, eax
.text:00401268 lea edx, [esp+2D8h+UserName] ; 取用户名
.text:0040126F repne scasb
.text:00401271 not ecx
.text:00401273 sub edi, ecx
.text:00401275 mov esi, edi
.text:00401277 mov ebp, ecx
.text:00401279 mov edi, edx
.text:0040127B or ecx, 0FFFFFFFFh
.text:0040127E repne scasb
.text:00401280 mov ecx, ebp
.text:00401282 dec edi
.text:00401283 shr ecx, 2
.text:00401286 rep movsd
.text:00401288 mov ecx, ebp
.text:0040128A lea eax, [esp+2D8h+md5_ctx]
.text:0040128E and ecx, 3
.text:00401291 push eax
.text:00401292 rep movsb ; 用户名和组织名的逆连接起来
.text:00401294 call _MD5_Init
.text:00401299 lea edi, [esp+2DCh+UserName]
.text:004012A0 or ecx, 0FFFFFFFFh
.text:004012A3 xor eax, eax
.text:004012A5 lea edx, [esp+2DCh+md5_ctx]
.text:004012A9 repne scasb
.text:004012AB not ecx
.text:004012AD dec ecx
.text:004012AE push ecx
.text:004012AF lea ecx, [esp+2E0h+UserName]
.text:004012B6 push ecx
.text:004012B7 push edx
.text:004012B8 call _MD5_Update
.text:004012BD lea eax, [esp+2E8h+md5_ctx]
.text:004012C1 lea ecx, [esp+2E8h+md5_str_out]
.text:004012C5 push eax
.text:004012C6 push ecx
.text:004012C7 call _MD5_Final
.text:004012CC push 0
.text:004012CE mov dword ptr [ebx+234h], 10h ; mip->IOBASE = 16;
.text:004012D8 call _mirvar
.text:004012DD push 0
.text:004012DF mov esi, eax
.text:004012E1 call _mirvar
.text:004012E6 push 0
.text:004012E8 mov edi, eax
.text:004012EA call _mirvar
.text:004012EF push 0
.text:004012F1 mov ebp, eax
.text:004012F3 call _mirvar
.text:004012F8 push offset a97944b587e4991 ; "97944B587E49910C2DFDD84BA062BC8917B3085"...
.text:004012FD push ebp
.text:004012FE mov ebx, eax
.text:00401300 call _cinstr
.text:00401305 push offset a10001 ; "10001"
.text:0040130A push edi
.text:0040130B call _cinstr
.text:00401310 lea edx, [esp+310h+md5_str_out]
.text:00401314 push ebx
.text:00401315 push edx
.text:00401316 push 16
.text:00401318 call _bytes_to_big
.text:0040131D add esp, 48h
.text:00401320 lea eax, [esp+2D4h+Serial] ; 取用户输入的注册码,然后转化为大数,与计算出来的注册码比较
.text:00401324 push eax
.text:00401325 push esi
.text:00401326 call _cinstr
.text:0040132B push esi
.text:0040132C push ebp
.text:0040132D push edi
.text:0040132E push esi
.text:0040132F call _powmod
.text:00401334 push ebx
.text:00401335 push esi
.text:00401336 call _compare ; 大数比较。相等则注册成功,否则失败
.text:0040133B mov [esp+2F4h+var_2C4], eax
.text:0040133F push esi
.text:00401340 call _mirkill
.text:00401345 push edi
.text:00401346 call _mirkill
.text:0040134B push ebp
.text:0040134C call _mirkill
.text:00401351 push ebx
.text:00401352 call _mirkill
.text:00401357 add esp, 30h
.text:0040135A call _mirexit
.text:0040135F mov ecx, [esp+2D4h+var_2C4]
.text:00401363 xor eax, eax
.text:00401365 pop ebp
.text:00401366 pop edi
.text:00401367 test ecx, ecx
.text:00401369 pop esi
.text:0040136A pop ebx
.text:0040136B setz al
.text:0040136E add esp, 2C4h
.text:00401374 retn
.text:00401374 OnCheck endp
.text:00401374
注册码验证过程:
1、将用户名字符串和组织名字符串的逆串连接起来,用标准MD5算法计算该字符串hash值,将该hash串转化为大数H。
2、将注册码转化为大数S,进行模幂运算S^e mod p = C
3、如果H = C,则注册成功。
其中 e=10001h,p=97944B587E49910C2DFDD84BA062BC8917B3085FFAB61ABF930A8396CDE8B9E3h
可以看出整个验证过程很简单。
生成注册码的过程:
1、将用户名字符串和组织名字符串的逆串连接起来,用标准MD5算法计算该字符串hash值,将该hash串转化为大数H。
2、令 e*d mod p-1 = 1,则 d = e^-1 mod p-1。用扩展Euclid算法求得e关于模p-1的乘法逆元d,则有
S = C^d mod p = H^d mod p
3、将大数S转化为字符串,即为所求的注册码。
以下是一组正确的注册码:
name: ylp1332
group: hahik
serial: 52575D6C6D8A32B11FF6F8215B93DBC9AE89ACCFFECFCF89B6D442A484F54278
以下是keygen的主要代码,不含md5算法实现。
在visual c++ 6 with sp6 下编译通过。
#include <stdio.h>
#include <string.h>
#include "miracl.h"
#include "md5.h"
#pragma comment ( lib, "miracl.lib" )
#define MAXLEN 50
int main(int argc, char **argv)
{
char usr_name[ MAXLEN ] = {0};
char grp_name[ MAXLEN ] = {0};
char ser_code[ MAXLEN ] = {0};
char hash_out[ MAXLEN ] = {0};
struct MD5Context md5_ctx;
miracl *mip;
big x,y,z,w;
char buf[ MAXLEN ];
// Your Name here, at least 2 chars !
strcpy( usr_name, "ylp1332" );
// Your Group here, at least 2 chars !
strcpy( grp_name, "hahik" );
printf(" name: %s\n", usr_name );
printf(" group: %s\n", grp_name );
memset( buf, 0, MAXLEN );
strcpy( buf, usr_name );
strcat( buf, _strrev(grp_name) );
MD5Init( &md5_ctx );
MD5Update( &md5_ctx, buf, strlen(buf) );
MD5Final( hash_out, &md5_ctx );
mip = mirsys( 0x300, 0 );
mip->IOBASE = 0x10;
x = mirvar( 0 );
y = mirvar( 0 );
z = mirvar( 0 );
w = mirvar( 0 );
bytes_to_big( 16, hash_out, x );
cinstr( y, "648A7A87920C9DE7244271CB87F0B5F980FCC19B58229E0F602AE3298E1EC5DD" );
cinstr( z, "97944B587E49910C2DFDD84BA062BC8917B3085FFAB61ABF930A8396CDE8B9E3" );
powmod( x, y, z, w );
cotstr( w, ser_code );
printf(" serial: %s\n", ser_code );
mirkill( x );
mirkill( y );
mirkill( z );
mirkill( w );
mirexit();
return 0;
}
--------------------------------------------------------------------------------
【经验总结】
静态分析和动态调试相结合。
大数运算要熟悉正逆向推导过程。
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2006年12月15日 5:33:12
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!