系统 : Windows xp
程序 : crackme1
程序下载地址 :http://pan.baidu.com/s/1gdY4wMJ
要求 : 编写注册机
使用工具 :OD
可在“PEDIY CrackMe 2007”中查找关于此程序的讨论,标题为“muckis's crakcme #1破解(检测OD)”。
前天分析了一个crackme,对其中的浮点汇编指令有点头疼,并没有写出注册机。
这两天上网查找了一些关于浮点汇编指令集的资料,补充完基础知识,我们再来分析一下这个crackme。
接着上次,我们来到计算余数的子程序处:
004010E0 /> 55 push ebp
004010E1 |. 8BEC mov ebp, esp
004010E3 |. 83EC 58 sub esp, 58
004010E6 |. 53 push ebx
004010E7 |. 56 push esi
004010E8 |. 57 push edi
004010E9 |. 8D7D A8 lea edi, dword ptr [ebp-58]
004010EC |. B9 16000000 mov ecx, 16
004010F1 |. B8 CCCCCCCC mov eax, CCCCCCCC
004010F6 |. F3:AB rep stos dword ptr es:[edi] ; 用CC填充一段内存
004010F8 |. C745 FC 00000>mov dword ptr [ebp-4], 0
004010FF |. C745 F8 01000>mov dword ptr [ebp-8], 1
00401106 |. C745 F4 00000>mov dword ptr [ebp-C], 0
0040110D |. C745 FC 0A000>mov dword ptr [ebp-4], 0A
00401114 |. EB 09 jmp short 0040111F
00401116 |> 8B45 FC /mov eax, dword ptr [ebp-4]
00401119 |. 83E8 01 |sub eax, 1 ; 循环变量递减
0040111C |. 8945 FC |mov dword ptr [ebp-4], eax
0040111F |> 837D FC 00 cmp dword ptr [ebp-4], 0 ; 小于0?
00401123 |. 7C 4F |jl short 00401174 ; 则退出循环
00401125 |. DB45 08 |fild dword ptr [ebp+8] ; 将传入的参数转化为浮点数,并压栈
00401128 |. DD5D E8 |fstp qword ptr [ebp-18] ; 注意!这里将累加结果转化为双精度浮点型
0040112B |. DB45 FC |fild dword ptr [ebp-4] ; 将循环变量转化为浮点数,并压栈
0040112E |. 83EC 08 |sub esp, 8 ; 开辟8个字节的内存空间
00401131 |. DD1C24 |fstp qword ptr [esp] ; 保存转化成浮点数的循环变量
00401134 |. 68 00002440 |push 40240000 ; 40240000入栈
00401139 |. 6A 00 |push 0
0040113B |. E8 C91E0000 |call 00403009
00401140 |. 83C4 10 |add esp, 10
00401143 |. DC7D E8 |fdivr qword ptr [ebp-18] ; 浮点反除
00401146 |. E8 AD210000 |call 004032F8 ; 将st的值转为整数存入eax
0040114B |. 8945 F0 |mov dword ptr [ebp-10], eax
0040114E |. 837D F0 00 |cmp dword ptr [ebp-10], 0 ; 商太小则直接进行累加
00401152 |. 7E 0F |jle short 00401163
00401154 |. 8B4D F8 |mov ecx, dword ptr [ebp-8]
00401157 |. 51 |push ecx
00401158 |. E8 DAFEFFFF |call 00401037 ; 变换数据
0040115D |. 83C4 04 |add esp, 4
00401160 |. 8945 F8 |mov dword ptr [ebp-8], eax
00401163 |> 8B55 F0 |mov edx, dword ptr [ebp-10] ; 取商
00401166 |. 0FAF55 F8 |imul edx, dword ptr [ebp-8] ; 乘以 变换数据
0040116A |. 8B45 F4 |mov eax, dword ptr [ebp-C] ; 取出累加的值
0040116D |. 03C2 |add eax, edx ; 累加
0040116F |. 8945 F4 |mov dword ptr [ebp-C], eax ; 保存
00401172 |.^ EB A2 jmp short 00401116
00401174 |> 8B45 F4 mov eax, dword ptr [ebp-C] ; 算出一个值
00401177 |. 99 cdq ; 把EDX的所有位都设成EAX最高位的值
00401178 |. B9 0A000000 mov ecx, 0A
0040117D |. F7F9 idiv ecx ; 除以0A
0040117F |. 8BC2 mov eax, edx ; 余数放入eax
00401181 |. 5F pop edi
00401182 |. 5E pop esi
00401183 |. 5B pop ebx
00401184 |. 83C4 58 add esp, 58
00401187 |. 3BEC cmp ebp, esp
00401189 |. E8 32210000 call 004032C0
0040118E |. 8BE5 mov esp, ebp
00401190 |. 5D pop ebp
00401191 . C3 retn
跟入403009:
00403009 $ 8D5424 0C lea edx, dword ptr [esp+C]
0040300D . E8 630B0000 call 00403B75
00403012 $ 8BC8 mov ecx, eax
00403014 . 50 push eax
00403015 . 9B wait
00403016 . D93C24 fstcw word ptr [esp] ; 保存控制字寄存器
00403019 . 66:813C24 7F0>cmp word ptr [esp], 27F
0040301F . 74 05 je short 00403026
00403021 . E8 1F0B0000 call 00403B45
00403026 > 81E1 0000F07F and ecx, 7FF00000
0040302C . 8D5424 08 lea edx, dword ptr [esp+8] ; 取一段内存
00403030 . 81F9 0000F07F cmp ecx, 7FF00000 ; 是7FF00000吗?
00403036 . 0F84 9D000000 je 004030D9
0040303C . E8 340B0000 call 00403B75
00403041 . 0F84 8E000000 je 004030D5
00403047 . A9 0000F07F test eax, 7FF00000
0040304C . 0F84 F6000000 je 00403148
00403052 > 8A4C24 0F mov cl, byte ptr [esp+F]
00403056 . 80E1 80 and cl, 80
00403059 . 0F85 61010000 jnz 004031C0
0040305F > D9F1 fyl2x
00403061 . E8 CA0A0000 call 00403B30 ; 生成除数
00403066 . 80F9 01 cmp cl, 1
00403069 . 75 02 jnz short 0040306D
0040306B . D9E0 fchs
0040306D > 833D B8DE4200>cmp dword ptr [42DEB8], 0
00403074 . 0F85 540B0000 jnz 00403BCE
0040307A . 8D0D B8CB4200 lea ecx, dword ptr [42CBB8]
00403080 . BA 1D000000 mov edx, 1D
00403085 . E9 8F0B0000 jmp 00403C19
0040308A > 833D B8DE4200>cmp dword ptr [42DEB8], 0
00403091 . 0F85 370B0000 jnz 00403BCE
00403097 . 8D0D B8CB4200 lea ecx, dword ptr [42CBB8]
0040309D . BA 1D000000 mov edx, 1D
004030A2 . E8 290A0000 call 00403AD0
004030A7 . 5A pop edx
004030A8 . C3 retn
跟入关键call:
00403B30 /$ D9C0 fld st
00403B32 |. D9FC frndint ; 对st取整
00403B34 |. DCE1 fsubr st(1), st ; st - st(1)
00403B36 |. D9C9 fxch st(1) ; 互换st和st(1)
00403B38 |. D9E0 fchs ; 取相反数
00403B3A |. D9F0 f2xm1
00403B3C |. D9E8 fld1 ; 将1.0压栈
00403B3E |. DEC1 faddp st(1), st ; 加法,结果存入st(1),弹出st
00403B40 |. D9FD fscale ; 这个指令是计算ST(0)*2的ST(1)次方之值,再把结果存入 ST(0) 里而 ST(1) 之值不变
00403B42 |. DDD9 fstp st(1) ; 将协处理器堆栈栈顶的数据传送到目标操作数中,并弹栈
00403B44 . C3 retn
那么403009子程序主要就是自动生出一个除数出来,第一次生成10^10,第二次生成10 ^ 9,以此类推。
再来看看4032F8:
004032F8 /$ 55 push ebp
004032F9 |. 8BEC mov ebp, esp
004032FB |. 83C4 F4 add esp, -0C
004032FE |. 9B wait
004032FF |. D97D FE fstcw word ptr [ebp-2] ; 取出FCW
00403302 |. 9B wait
00403303 |. 66:8B45 FE mov ax, word ptr [ebp-2] ; 存入ax
00403307 |. 80CC 0C or ah, 0C ; 按位或
0040330A |. 66:8945 FC mov word ptr [ebp-4], ax
0040330E |. D96D FC fldcw word ptr [ebp-4] ; 存回FCW
00403311 |. DF7D F4 fistp qword ptr [ebp-C] ; st转化为整数存入ebp-c,st出栈
00403314 |. D96D FE fldcw word ptr [ebp-2] ; 将原本的FCW保存回去
00403317 |. 8B45 F4 mov eax, dword ptr [ebp-C] ; 结果存入eax
0040331A |. 8B55 F8 mov edx, dword ptr [ebp-8]
0040331D |. C9 leave
0040331E . C3 retn
还有401037:
00401037 $ /E9 44000000 jmp 00401080
继续跟:
00401080 /> 55 push ebp
00401081 |. 8BEC mov ebp, esp
00401083 |. 83EC 44 sub esp, 44 ; 开辟内存空间
00401086 |. 53 push ebx
00401087 |. 56 push esi
00401088 |. 57 push edi
00401089 |. 8D7D BC lea edi, dword ptr [ebp-44] ; 取一段内存
0040108C |. B9 11000000 mov ecx, 11
00401091 |. B8 CCCCCCCC mov eax, CCCCCCCC
00401096 |. F3:AB rep stos dword ptr es:[edi] ; 用CC填充内存
00401098 |. C745 FC 07000>mov dword ptr [ebp-4], 7
0040109F |. 837D 08 07 cmp dword ptr [ebp+8], 7 ; 入栈数据是否等于7?
004010A3 |. 75 07 jnz short 004010AC
004010A5 |. C745 FC 03000>mov dword ptr [ebp-4], 3
004010AC |> 837D 08 03 cmp dword ptr [ebp+8], 3 ; 入栈数据是否等于3?
004010B0 |. 75 07 jnz short 004010B9
004010B2 |. C745 FC 01000>mov dword ptr [ebp-4], 1
004010B9 |> 8B45 FC mov eax, dword ptr [ebp-4] ; 变换数据之后存入eax
004010BC |. 5F pop edi
004010BD |. 5E pop esi
004010BE |. 5B pop ebx
004010BF |. 8BE5 mov esp, ebp
004010C1 |. 5D pop ebp
004010C2 . C3 retn
这样,整个子程序分析完毕。它的功能就是根据入栈的累加结果生成一个数,取其与10的余数并返回。
我们直接打开之前搭建的框架,并修改OnBtnDecrypt函数如下:
void CKengen_TemplateDlg::OnBtnDecrypt()
{
// TODO: Add your control notification handler code here
CString str;
GetDlgItemText( IDC_EDIT_NAME,str ); //获取用户名字串基本信息。
int len = str.GetLength();
if ( !str.IsEmpty() ){ //格式控制。
str.MakeUpper();
unsigned int sum = 0;
for ( int i = 0 ; i != len ; i++ ){
if ( str[i] == 0x20 ) //字符为空格则continue
continue;
sum += ( str[i] * 0x157A - 1 );
}
sum *= 0xA;
sum += GetRes( sum );
CString PassWord;
PassWord.Format( "%d",sum );
SetDlgItemText( IDC_EDIT_PASSWORD,PassWord );
}
else
MessageBox( "用户名格式错误!" );
}
并添加如下类成员函数:
int CKengen_TemplateDlg::GetRes(unsigned int Var)
{
double FVar; //这里一定要设置成为double,否则会损失精度
int ChangeVar = 1;
unsigned int sum = 0;
for ( int i = 0 ; i != 10 ; i++ ){
__asm{ //转换为浮点数
fild Var
fstp FVar
}
int temp = (int)( FVar / GetDR(i) ); //进行除法,保存商
if ( temp > 0 )
Change( ChangeVar ); //变换数据
sum += temp * ChangeVar; //累加
}
/*
CString PS;
PS.Format( "%x",sum );
MessageBox( PS );
*/
return sum % 10;
}
float CKengen_TemplateDlg::GetDR(int temp)
{
float res = 1;
for ( int i = 0 ; i != 10 - temp ; i++ )
res *= 10;
return res;
}
void CKengen_TemplateDlg::Change(int &Var)
{
int temp;
if ( Var == 1 )
temp = 7;
if ( Var == 7 )
temp = 3;
if ( Var == 3 )
temp = 1;
Var = temp;
}
再在OnInitDialog中添加此代码修改标题:SetWindowText(_T("crackme1_Keygen"));
运行效果:
一处细微的差异,会引起一连串连锁反应,最终导致结果的巨大差异。程序中,一处细小的设计失误,可能引起整个系统的崩溃,体现了计算机系统的高精度和复杂性。这就要求操作这个复杂系统的程序员要对自己的技术精益求精,刻意养成良好的习惯,这样一直不断堆砌,才能成就日后的巨大成功。