【文章标题】: happytown'crackme算法分析
【文章作者】: netwind
【软件名称】: crackme
【下载地址】: http://bbs.pediy.com/attachment.php?s=&attachmentid=4026
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
右键,查找 所有参考文本串,找到good boy,双击来到如下位置:
004012BE . 6A 40 push 40 ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
004012C0 . 68 DB624000 push 004062DB ; |Title = "Good boy..."
004012C5 . 68 AC624000 push 004062AC ; |Text = "Yep, thats the right code!",LF,CR,"Go write a keygen!"
004012CA . FF75 08 push dword ptr [ebp+8] ; |hOwner
004012CD . E8 CA000000 call <jmp.&user32.MessageBoxA> ; \MessageBoxA
此处就是正确提示的地方
向上观察有如下代码:
00401141 . E8 4A020000 call <jmp.&user32.GetDlgItemTextA> ; \GetDlgItemTextA
00401146 . 83F8 03 cmp eax, 3 ;判断用户名长度是否小于等于3
00401149 . 77 18 ja short 00401163 ;是则出错。
0040114B . 6A 10 push 10 ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
0040114D . 68 06634000 push 00406306 ; |Title = "Bad boy..."
00401152 . 68 0A624000 push 0040620A ; |Text = "Username must have at least 4 chars..."
00401157 . FF75 08 push dword ptr [ebp+8] ; |hOwner
0040115A . E8 3D020000 call <jmp.&user32.MessageBoxA> ; \MessageBoxA
0040115F . C9 leave
00401160 . C2 1000 retn 10
00401163 > 8D15 49634000 lea edx, [406349]
00401169 . 52 push edx ; /String => ""
0040116A . E8 8D020000 call <jmp.&kernel32.lstrlenA> ; \lstrlenA
0040116F . 8BE8 mov ebp, eax
00401171 . B9 05000000 mov ecx, 5
00401176 . 33F6 xor esi, esi
00401178 . 33C0 xor eax, eax
0040117A > /8A0C16 mov cl, [esi+edx] ; 第一处算法开始,从注册名第二个字符开始取字符
0040117D . |8AD9 mov bl, cl
0040117F . |3298 28634000 xor bl, [eax+406328] ; 将取得字符与内存eax+406328处值xor运算
00401185 . |40 inc eax
00401186 . |83F8 05 cmp eax, 5
00401189 . |881C32 mov [edx+esi], bl ; 将xor后的值存放
0040118C . |8888 27634000 mov [eax+406327], cl ; 将取得的用户名字符的值存放
00401192 . |75 02 jnz short 00401196 ; eax 大于5则取 eax为0
00401194 . |33C0 xor eax, eax
00401196 > |46 inc esi
00401197 . |3BF5 cmp esi, ebp ; 判断运算次数是否大于用户名长度
00401199 .^\72 DF jb short 0040117A ;循环运算
0040119B . 33FF xor edi, edi
0040119D . 33C9 xor ecx, ecx
0040119F . 85ED test ebp, ebp
004011A1 . 76 26 jbe short 004011C9
004011A3 > 8A9F 2D634000 mov bl, [edi+40632D] ; 第二步算法开始,接着从内存取值
004011A9 . 8BF5 mov esi, ebp
004011AB . 2BF1 sub esi, ecx
004011AD . 4E dec esi
004011AE . 8A0432 mov al, [edx+esi] ; 取上面xor运算后存放的值
004011B1 . 32D8 xor bl, al ; 将取出的值同内存存放的值xor运算
004011B3 . 47 inc edi
004011B4 . 881C32 mov [edx+esi], bl
004011B7 . 8887 2C634000 mov [edi+40632C], al
004011BD . 83FF 05 cmp edi, 5 ; 如果大于5 ,再从40632d开始取
004011C0 . 75 02 jnz short 004011C4
004011C2 . 33FF xor edi, edi
004011C4 > 41 inc ecx
004011C5 . 3BCD cmp ecx, ebp ; 运算次数为用户名长度
004011C7 .^ 72 DA jb short 004011A3
004011C9 > 33F6 xor esi, esi
004011CB . 33FF xor edi, edi
004011CD . 85ED test ebp, ebp
004011CF . 76 21 jbe short 004011F2
004011D1 > 8A043A mov al, [edx+edi] ; 第三处算法开始,取上一步xor后的值
004011D4 . 8A8E 32634000 mov cl, [esi+406332] ; 继续从内存取值
004011DA . 32C8 xor cl, al
004011DC . 46 inc esi
004011DD . 880C3A mov [edx+edi], cl ; 存放xor 运算后的值
004011E0 . 8886 31634000 mov [esi+406331], al
004011E6 . 83FE 05 cmp esi, 5 ; 如果大于5 ,再从406332开始取
004011E9 . 75 02 jnz short 004011ED
004011EB . 33F6 xor esi, esi
004011ED > 47 inc edi
004011EE . 3BFD cmp edi, ebp ; 运算次数为用户名长度
004011F0 .^ 72 DF jb short 004011D1
004011F2 > 33FF xor edi, edi
004011F4 . 33C9 xor ecx, ecx
004011F6 . 85ED test ebp, ebp
004011F8 . 76 26 jbe short 00401220
004011FA > 8A9F 37634000 mov bl, [edi+406337] ; 第三处算法开始,接着从内存取值
00401200 . 8BF5 mov esi, ebp
00401202 . 2BF1 sub esi, ecx
00401204 . 4E dec esi
00401205 . 8A0432 mov al, [edx+esi] ; 取上一步xor运算后的值
00401208 . 32D8 xor bl, al ; 将两次取值xor 运算
0040120A . 47 inc edi
0040120B . 881C32 mov [edx+esi], bl
0040120E . 8887 36634000 mov [edi+406336], al
00401214 . 83FF 05 cmp edi, 5 ; 大于5再从本次取值开始取
00401217 . 75 02 jnz short 0040121B
00401219 . 33FF xor edi, edi
0040121B > 41 inc ecx
0040121C . 3BCD cmp ecx, ebp ; 运算次数为用户名长度
0040121E .^ 72 DA jb short 004011FA
00401220 > 8D3D 45634000 lea edi, [406345]
00401226 . 33C0 xor eax, eax
00401228 . 85ED test ebp, ebp
0040122A . C705 45634000>mov dword ptr [406345], 0
00401234 . 76 17 jbe short 0040124D
00401236 > 8BC8 mov ecx, eax ; 第四处运算开始,
00401238 . 83E1 03 and ecx, 3 ; 以ecx计数,四次运算后从头开始从内存取值运算
0040123B . 8A1C0F mov bl, [edi+ecx] ; 从内存取值,此处初始为0
0040123E . 8D340F lea esi, [edi+ecx]
00401241 . 8A0C02 mov cl, [edx+eax] ; 取上一步xor后的值
00401244 . 02D9 add bl, cl ; 将两次取值相加
00401246 . 40 inc eax
00401247 . 3BC5 cmp eax, ebp ; 以用户名长度为运算次数
00401249 . 881E mov [esi], bl ; 将相加后低八位保存
0040124B .^ 72 E9 jb short 00401236
0040124D > 5D pop ebp
0040124E . B9 0A000000 mov ecx, 0A
00401253 . A1 45634000 mov eax, [406345]
00401258 . 33DB xor ebx, ebx
0040125A > 33D2 xor edx, edx ; 第五出运算开始
0040125C . F7F1 div ecx ; 将上面加运算得到的值eax除以0xa取余数,eax取除后的整数
0040125E . 80C2 30 add dl, 30 ; 将余数转为对应的字符
00401261 . 8893 49654000 mov [ebx+406549], dl ; 保存字符
00401267 . 43 inc ebx
00401268 . 85C0 test eax, eax ; 若eax不为0继续运算
0040126A .^ 75 EE jnz short 0040125A
0040126C . 68 49654000 push 00406549 ; /得到上面运算后的字符串
00401271 . E8 86010000 call <jmp.&kernel32.lstrlen>; \lstrlenA
00401276 . 33DB xor ebx, ebx
00401278 > 8A88 48654000 mov cl, [eax+406548]
0040127E . 888B 49674000 mov [ebx+406749], cl
00401284 . 43 inc ebx
00401285 . 48 dec eax
00401286 .^ 75 F0 jnz short 00401278
00401288 . 68 49674000 push 00406749 ; /这里得到的是将上面字符串取反得到的串,就是注册码
0040128D . 68 49654000 push 00406549 ; |String1 = crackme.00406549
00401292 . E8 5F010000 call <jmp.&kernel32.lstrcpy>; \lstrcpyA
00401297 . 68 00020000 push 200 ; /Count = 200 (512.)
0040129C . 68 49694000 push 00406949 ; |Buffer = crackme.00406949
004012A1 . 6A 64 push 64 ; |ControlID = 64 (100.)
004012A3 . FF75 08 push dword ptr [ebp+8] ; |hWnd
004012A6 . E8 E5000000 call <jmp.&user32.GetDlgIte>; \GetDlgItemTextA
004012AB . 68 49654000 push 00406549 ; /String2 = ""
004012B0 . 68 49694000 push 00406949 ; |String1 = ""
004012B5 . E8 36010000 call <jmp.&kernel32.lstrcmp>; \lstrcmpA
004012BA . 0BC0 or eax, eax
004012BC . 75 16 jnz short 004012D4
004012BE . 6A 40 push 40 ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
004012C0 . 68 DB624000 push 004062DB ; |Title = "Good boy..."
004012C5 . 68 AC624000 push 004062AC ; |Text = "Yep, thats the right code!",LF,CR,"Go write a keygen!"
004012CA . FF75 08 push dword ptr [ebp+8] ; |hOwner
004012CD . E8 CA000000 call <jmp.&user32.MessageBo>; \MessageBoxA
该crackme完全是对用户名字符串进行一系列xor 运算,把部分结果除以0xa取余数
得到一字符串,再取反得到注册码。
以下为注册机代码:
#include <stdio.h>
#include <string.h>
void main()
{
char name[]="hello",result[20];
int s[]={0xaa,0x89,0xc4,0xfe,0x46,
0x78,0xf0,0xd0,0x03,0xe7,
0xf7,0xfd,0xf4,0xe7,0xb9,
0xb5,0x1b,0xc9,0x50,0x73,
0x00,0x00,0x00,0x00,0x00,
};
int r[20];
int i=0,j=0,tmp=0;
unsigned long rr;
printf("name is: %s\n",name);
for(i=0;j<strlen(name);i++)
{
r[j]=name[j+1]^s[i];
s[i]=name[j+1];
if(i==4)
i=-1;
j++;
}
j=strlen(name)-1;
for(i=5;j>-1;i++)
{
tmp=r[j];
r[j]^=s[i];
s[i]=tmp;
if(i==9)
i=4;
j--;
}
j=0;
for(i=10;j<strlen(name);i++)
{
tmp=r[j];
r[j]^=s[i];
s[i]=tmp;
if(i==14)
i=9;
j++;
}
j=strlen(name)-1;
for(i=15;j>-1;i++)
{
tmp=r[j];
r[j]^=s[i];
s[i]=tmp;
if(i==19)
i=14;
j--;
}
j=4;
for(i=0;j<strlen(name);i++,j++)
{
r[i]+=r[j];
if(r[i]>=0x100)r[i]=r[i]%0x100;
if(i&&(i%3==0))
i=-1;
}
rr=r[0]+r[1]*0x100+r[2]*0x10000+r[3]*0x1000000;
i=0;
printf("key is:");
while(rr)
{
tmp=rr%0xa;
result[i++]=tmp+'0';
rr/=0xa;
}
result[i]='\0';
for(i=strlen(result)-1;i>=0;i--)
printf("%c",result[i]);
printf("\n");
}
完全照汇编代码顺序写下来的,看代码可以帮助理解crackme运算过程。
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2007年01月13日 23:11:51
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2022-7-7 10:14
被netwind编辑
,原因: