【文章标题】: 一个适合初学者的crackme算法分析
【文章作者】: 红尘岁月
【作者邮箱】: butter9999@21cn.com
【软件名称】: lafarge-crackme2
【软件大小】: 60.0 KB
【下载地址】: http://bbs.pediy.com/showthread.php?s=&threadid=37891
【加壳方式】: 无
【保护方式】: 无
【使用工具】: OD
【操作平台】: Win9x/NT/2000/XP
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
0040112F PUSH 200 ; /Count = 200 (512.)
00401134 PUSH lafarge2.00406349 ; |Buffer = lafarge2.00406349
00401139 PUSH 3EA ; |ControlID = 3EA (1002.)
0040113E PUSH DWORD PTR SS:[EBP+8] ; |hWnd
00401141 CALL <JMP.&user32.GetDlgItemTextA> ; \GetDlgItemTextA
00401146 CMP EAX,3
00401149 JA SHORT lafarge2.00401163 ; 用户名长度大于3
0040114B PUSH 10 ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
0040114D PUSH lafarge2.00406306 ; |Title = "Bad boy..."
00401152 PUSH lafarge2.0040620A ; |Text = "Username must have at least 4 chars..."
00401157 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
0040115A CALL <JMP.&user32.MessageBoxA> ; \MessageBoxA
0040115F LEAVE
00401160 RETN 10
00401163 LEA EDX,DWORD PTR DS:[406349] ; 用户名地址
00401169 PUSH EDX ; /String => ""
0040116A CALL <JMP.&kernel32.lstrlenA> ; \lstrlenA
0040116F MOV EBP,EAX ; 用户长度nlen
00401171 MOV ECX,5
00401176 XOR ESI,ESI ; i=0
00401178 XOR EAX,EAX ; m=0,mone=0
0040117A MOV CL,BYTE PTR DS:[ESI+EDX] ; 第一个循环计算,用户名第二位开始与内存406328处前5个的数据temp做xor运算
0040117D MOV BL,CL ; 四次循环以及后面的处理都只从第二位开始
0040117F XOR BL,BYTE PTR DS:[EAX+406328] ; mone=name[i+1]^temp[m]
00401185 INC EAX ; m++
00401186 CMP EAX,5
00401189 MOV BYTE PTR DS:[EDX+ESI],BL ; longtemp[i]=mone
0040118C MOV BYTE PTR DS:[EAX+406327],CL ; temp[m]=nametemp[i+1]
00401192 JNZ SHORT lafarge2.00401196
00401194 XOR EAX,EAX ; if(m==5)m=0
00401196 INC ESI
00401197 CMP ESI,EBP
00401199 JB SHORT lafarge2.0040117A ; i<nlen,循环次数为nlen次,以下四个循环次数相同
0040119B XOR EDI,EDI ; 第一个循环结束后,用户名地址处得到新的数据longtemp
0040119D XOR ECX,ECX
0040119F TEST EBP,EBP
004011A1 JBE SHORT lafarge2.004011C9
004011A3 MOV BL,BYTE PTR DS:[EDI+40632D] ; 第二个循环计算,longtemp最高位开始与内存40632d处前5个的数据temp2做xor运算
004011A9 MOV ESI,EBP
004011AB SUB ESI,ECX
004011AD DEC ESI
004011AE MOV AL,BYTE PTR DS:[EDX+ESI]
004011B1 XOR BL,AL
004011B3 INC EDI
004011B4 MOV BYTE PTR DS:[EDX+ESI],BL
004011B7 MOV BYTE PTR DS:[EDI+40632C],AL
004011BD CMP EDI,5
004011C0 JNZ SHORT lafarge2.004011C4
004011C2 XOR EDI,EDI
004011C4 INC ECX
004011C5 CMP ECX,EBP
004011C7 JB SHORT lafarge2.004011A3 ; 第二个循环与第一个相似,只是从高位开始,改变longtemp中的数据
004011C9 XOR ESI,ESI
004011CB XOR EDI,EDI
004011CD TEST EBP,EBP
004011CF JBE SHORT lafarge2.004011F2
004011D1 MOV AL,BYTE PTR DS:[EDX+EDI] ; 第三个循环与第一个完全相似,只是longtemp xor temp3
004011D4 MOV CL,BYTE PTR DS:[ESI+406332]
004011DA XOR CL,AL
004011DC INC ESI
004011DD MOV BYTE PTR DS:[EDX+EDI],CL
004011E0 MOV BYTE PTR DS:[ESI+406331],AL
004011E6 CMP ESI,5
004011E9 JNZ SHORT lafarge2.004011ED
004011EB XOR ESI,ESI
004011ED INC EDI
004011EE CMP EDI,EBP
004011F0 JB SHORT lafarge2.004011D1 ; 第三个循环
004011F2 XOR EDI,EDI
004011F4 XOR ECX,ECX
004011F6 TEST EBP,EBP
004011F8 JBE SHORT lafarge2.00401220
004011FA MOV BL,BYTE PTR DS:[EDI+406337] ; 第四个循环与第二个完全相似,longtemp xor temp4
00401200 MOV ESI,EBP
00401202 SUB ESI,ECX
00401204 DEC ESI
00401205 MOV AL,BYTE PTR DS:[EDX+ESI]
00401208 XOR BL,AL
0040120A INC EDI
0040120B MOV BYTE PTR DS:[EDX+ESI],BL
0040120E MOV BYTE PTR DS:[EDI+406336],AL
00401214 CMP EDI,5
00401217 JNZ SHORT lafarge2.0040121B
00401219 XOR EDI,EDI
0040121B INC ECX
0040121C CMP ECX,EBP
0040121E JB SHORT lafarge2.004011FA ; 第四个循环
00401220 LEA EDI,DWORD PTR DS:[406345] ; 下面4行语句,清空406345处4字节32位的内存空间
00401226 XOR EAX,EAX ; i=0
00401228 TEST EBP,EBP
0040122A MOV DWORD PTR DS:[406345],0 ; unsigned char longtemp2[4]={0}
00401234 JBE SHORT lafarge2.0040124D ; 第五个循环,把templong中数据相加产生一个新的数据longtemp2
00401236 MOV ECX,EAX ; m=i
00401238 AND ECX,3 ; if(m==4)m=0
0040123B MOV BL,BYTE PTR DS:[EDI+ECX] ; longtemp2[m]
0040123E LEA ESI,DWORD PTR DS:[EDI+ECX]
00401241 MOV CL,BYTE PTR DS:[EDX+EAX] ; longtemp[i+1]
00401244 ADD BL,CL
00401246 INC EAX
00401247 CMP EAX,EBP
00401249 MOV BYTE PTR DS:[ESI],BL ; longtemp2[m]=longtemp2[m]+longtemp[i+1]
0040124B JB SHORT lafarge2.00401236 ; i<nlen
0040124D POP EBP ; 第五次循环结束
0040124E MOV ECX,0A ; 下面的语句把longtemp2(低低高高)从十六进制变成十进制数字
00401253 MOV EAX,DWORD PTR DS:[406345]
00401258 XOR EBX,EBX
0040125A XOR EDX,EDX
0040125C DIV ECX
0040125E ADD DL,30 ; 把求模结果ascii码+0x30变成数字
00401261 MOV BYTE PTR DS:[EBX+406549],DL
00401267 INC EBX
00401268 TEST EAX,EAX
0040126A JNZ SHORT lafarge2.0040125A
0040126C PUSH lafarge2.00406549 ; /String = ""
00401271 CALL <JMP.&kernel32.lstrlenA> ; \lstrlenA
00401276 XOR EBX,EBX
00401278 MOV CL,BYTE PTR DS:[EAX+406548] ; 字符串倒换strrev(字符串)得到注册码
0040127E MOV BYTE PTR DS:[EBX+406749],CL
00401284 INC EBX
00401285 DEC EAX
00401286 JNZ SHORT lafarge2.00401278
00401288 PUSH lafarge2.00406749 ; /String2 = ""
0040128D PUSH lafarge2.00406549 ; |String1 = lafarge2.00406549
00401292 CALL <JMP.&kernel32.lstrcpyA> ; \lstrcpyA
00401297 PUSH 200 ; /Count = 200 (512.)
0040129C PUSH lafarge2.00406949 ; |Buffer = lafarge2.00406949
004012A1 PUSH 64 ; |ControlID = 64 (100.)
004012A3 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
004012A6 CALL <JMP.&user32.GetDlgItemTextA> ; \GetDlgItemTextA
004012AB PUSH lafarge2.00406549 ; /String2 = ""
004012B0 PUSH lafarge2.00406949 ; |String1 = ""
004012B5 CALL <JMP.&kernel32.lstrcmpA> ; \lstrcmpA
004012BA OR EAX,EAX
004012BC JNZ SHORT lafarge2.004012D4
004012BE PUSH 40 ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
004012C0 PUSH lafarge2.004062DB ; |Title = "Good boy..."
004012C5 PUSH lafarge2.004062AC ; |Text = "Yep, thats the right code!
Go write a keygen!"
004012CA PUSH DWORD PTR SS:[EBP+8] ; |hOwner
004012CD CALL <JMP.&user32.MessageBoxA> ; \MessageBoxA
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
算法总结:
1) 用户名必须大于3 (为了方便在注册码中我假设用户名小于20位)
2) 用户名第二位开始,与内存中的数据做xor运算,共4次,每次循环次数为nlen次
3) 把生成的数据用加位计算成一个32位的数据
4) 把生成的数据先变成十进制,再换位
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
代码如下:
void CMyDlg::OnOK()
{
// TODO: Add extra validation here
//得到用户名
char name[20]; //假设用户名不超过20位,实际上这个注册机假设用户名要小于20位
int len;
len=GetDlgItemText(IDC_EDIT1,name,21); //假设用户名不超过20位,所以只取前20位字符
if(len<4||len>19) //此注册机假设用户名小于20位,一般就受上行GetDlgItemText中21的限制取前20位
{
MessageBox("用户名大于3个字符,小于20位!");
return;
}
char key[20]=""; //假设注册码不超过20位
char nametemp[21]="";
unsigned char longtemp[21];
unsigned char longtemp2[4]={0};
//四个内存区间数据
unsigned char temp[5]={0xAA,0x89,0xC4,0xFE,0x46};
unsigned char temp2[5]={0x78,0xF0,0xD0,0x03,0xE7};
unsigned char temp3[5]={0xF7,0xFD,0xF4,0xE7,0xB9};
unsigned char temp4[5]={0xB5,0x1B,0xC9,0x50,0x73};
int i,m,nlen;
unsigned char mone=0;
nlen=strlen(name);
m=0;
strcpy(nametemp,name);
nametemp[nlen]='\0';
longtemp[0]=name[0];
//第一次循环
for(i=1;i<nlen+1;i++)
{
mone=nametemp[i]^temp[m];
temp[m]=nametemp[i];
longtemp[i]=mone;
m++;
if(m==5)m=0;
}
//第二次循环
m=0;
for(i=nlen;i>0;i--)
{
mone=longtemp[i]^temp2[m];
temp2[m]=longtemp[i];
longtemp[i]=mone;
m++;
if(m==5)m=0;
}
//第三次循环与第一次相同
m=0;
for(i=1;i<nlen+1;i++)
{
mone=longtemp[i]^temp3[m];
temp3[m]=longtemp[i];
longtemp[i]=mone;
m++;
if(m==5)m=0;
}
//第四次循环与第二次相同
m=0;
for(i=nlen;i>0;i--)
{
mone=longtemp[i]^temp4[m];
temp4[m]=longtemp[i];
longtemp[i]=mone;
m++;
if(m==5)m=0;
}
//第五个循环
m=0;
for(i=0;i<nlen;i++)
{
longtemp2[m]=longtemp2[m]+longtemp[i+1];
m++;
if(m==4)m=0;
}
//十六进制变成十进制
unsigned long longone=longtemp2[3]*pow(2,24)+longtemp2[2]*pow(2,16)+longtemp2[1]*pow(2,8)+longtemp2[0];
char strtwo[20];
for(i=0;longone>=1;i++)
{
strtwo[i]=longone%0xa+0x30;
longone=longone/0xa;
}
strtwo[i]='\0';
//转换数据
strrev(strtwo);
//得到注册码
strcpy(key,strtwo);
//返回注册码
SetDlgItemText(IDC_EDIT2,key);
}
例如:
用户名:ccbhcsy
注册码:703728050
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2007年01月16日 1:24:31
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法