本帖最后由 xiangshen 于 2011-11-23 19:59 编辑
[文件标题:一个名为CrackMe02的CrackMe算法分析 [文章作者]:willjhw [作者邮箱]:466684954@qq.com [软件名称]:CrackMe02 [下载地址]:附件 [运行环境]:Windows xp [使用工具]:OD,PEID [作者声明]:
只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
[详细过程]:
马上要去实习了,自己也想做逆向方面的东西,所以最近加强的逆向方面的锻炼,最近搞到一个某高校某次比赛的程序,于是拿来分析下,感觉很有收获,在此总结过后与吾爱破解的各位分享下。
老规矩,先用PEID查壳,如下图:
很明显是一个vc++的程序,直接上od吧。 F9允许后发现是个控制台程序,我们在输入函数后的地方下断,就是40101e吧。 0040101E |. 8B8424 0C1000 MOV EAX,DWORD PTR SS:[ARG.1]
00401025 |. 83C4 08 ADD ESP,8
00401028 |. 83F8 01 CMP EAX,1
0040102B |. 75 20 JNE SHORT 0040104D
0040102D |. 8D4C24 00 LEA ECX,[LOCAL.1023]
00401031 |. 51 PUSH ECX
00401032 |. E8 D9000000 CALL 00401110
00401037 |. 83C4 04 ADD ESP,4
关键call就是401032这个call的地方,我们跟进去
00401110 /[ DISCUZ_CODE_31 ]nbsp
00401113 |. A1 A0A14000 MOV EAX,DWORD PTR DS:[40A1A0]
00401118 |. 8B0D A4A14000 MOV ECX,DWORD PTR DS:[40A1A4]
0040111E |. 53 PUSH EBX
0040111F |. 55 PUSH EBP
00401120 |. 8B6C24 18 MOV EBP,DWORD PTR SS:[ARG.1]
00401124 |. 56 PUSH ESI
00401125 |. 57 PUSH EDI
00401126 |. 894424 10 MOV DWORD PTR SS:[LOCAL.2],EAX
0040112A |. 894C24 14 MOV DWORD PTR SS:[LOCAL.1],ECX
0040112E |. 8BFD MOV EDI,EBP
00401130 |. 83C9 FF OR ECX,FFFFFFFF
00401133 |. 33C0 XOR EAX,EAX
00401135 |. F2:AE REPNE SCAS BYTE PTR ES:[EDI]
00401137 |. 8A15 A8A14000 MOV DL,BYTE PTR DS:[40A1A8]
0040113D |. F7D1 NOT ECX
0040113F |. 49 DEC ECX
00401140 |. 885424 18 MOV BYTE PTR SS:[LOCAL.0],DL
00401144 |. 83F9 08 CMP ECX,8
00401147 |. 75 62 JNE SHORT 004011AB
以后的汇编实现的功能就是将关键比较的字符串ADGNWKQU压入,然后在401144处进行判断你输入的长度是否为8,如果不是8则直接跳入失败的地方,所以这里说明输入的注册码的长度为8。 00401161 |> /8D741C 10 /LEA ESI,[EBX+ESP+10]
00401165 |. |B9 1A000000 |MOV ECX,1A
0040116A |. |0FBE042E |MOVSX EAX,BYTE PTR DS:[EBP+ESI]
0040116E |. |83E8 2F |SUB EAX,2F
00401171 |. |99 |CDQ
00401172 |. |F7F9 |IDIV ECX
00401174 |. |52 |PUSH EDX
00401175 |. |E8 E6010000 |CALL 00401360
0040117A |. |8A0E |MOV CL,BYTE PTR DS:[ESI]
0040117C |. |83C4 04 |ADD ESP,4
0040117F |. |3AC1 |CMP AL,CL
00401181 |. |75 28 |JNE SHORT 004011AB
00401183 |. |8D7C24 10 |LEA EDI,[LOCAL.2]
00401187 |. |83C9 FF |OR ECX,FFFFFFFF
0040118A |. |33C0 |XOR EAX,EAX
0040118C |. |43 |INC EBX
0040118D |. |F2:AE |REPNE SCAS BYTE PTR ES:[EDI]
0040118F |. |F7D1 |NOT ECX
00401191 |. |49 |DEC ECX
00401192 |. |3BD9 |CMP EBX,ECX
00401194 |.^\7C CB \JL SHORT 00401161
这个代码就是关键的算法处,里面有个call我们跟进去如下面: 00401360 /[ DISCUZ_CODE_33 ]nbsp
00401364 |. 85C0 TEST EAX,EAX
00401366 |. 7C 08 JL SHORT 00401370
00401368 |. 83F8 1A CMP EAX,1A
0040136B |. 7F 03 JG SHORT 00401370
0040136D |. 83C0 41 ADD EAX,41
00401370 \> C3 RETN
这个call就是对每次计算出来的值进行加‘A’也就是0x41。
我们再来看看上面的算法吧,就是一个循环与str=”ADGNWKQU”比较,只要有一个计算出来不等于就跳向错误。算法是:(输入 - 0x2f)% 0x1a + 0x41 = str;
于是我们用c语言实现下算法: #include <stdio.h>
int main (void)
{
char str1[8] = "ADGNWKQU";
char str2[8];
int i = 0;
printf ("\n其中一个字符串:\n")
for (i = 0; i < 8; i++)
{
str2[i] = str1[i] - 0x41 + 0x1a + 0x2f;
printf ("%c",str2[i]);
}
printf ("\n");
return 0;
}
因为上面有取余数的操作,所以导致注册码不唯一,我就给出一组吧ILOV_SY] 得到的结果如下图:
我们发现有个问题呀,它的答案是right?说明有问题呀。我们再看看这个程序的提示有个VERYEASY还有在以下汇编我们看看: 0040101E |. 8B8424 0C1000 MOV EAX,DWORD PTR SS:[ARG.1]
00401025 |. 83C4 08 ADD ESP,8
00401028 |. 83F8 01 CMP EAX,1
0040102B |. 75 20 JNE SHORT 0040104D
0040102D |. 8D4C24 00 LEA ECX,[LOCAL.1023]
00401031 |. 51 PUSH ECX
00401032 |. E8 D9000000 CALL 00401110
00401037 |. 83C4 04 ADD ESP,4
0040103A |. 68 30A04000 PUSH OFFSET 0040A030
0040103F |. E8 3C030000 CALL 00401380
00401044 |. 33C0 XOR EAX,EAX
00401046 |. 81C4 04100000 ADD ESP,1004
0040104C |. C3 RETN
0040104D |> 83F8 02 CMP EAX,2
00401050 |. 75 18 JNE SHORT 0040106A
如果这个程序不带参数运行,直接就不会跑到下面去,而下面才是真正的注册算法地方,于是我们用到OD的带参数调试,进入程序,发现40101e跳向了下面,参数是VERYEASY哦。关键算法4011c0处。 004011C0 /[ DISCUZ_CODE_36 ]nbsp
004011C3 |. 8B0D F4A14000 mov ecx, dword ptr [40A1F4]
004011C9 |. 53 push ebx
004011CA |. 55 push ebp
004011CB |. 56 push esi
004011CC |. 57 push edi
004011CD |. 894C24 1C mov dword ptr [esp+1C], ecx
004011D1 |. B9 09000000 mov ecx, 9
004011D6 |. BE C8A14000 mov esi, 0040A1C8
004011DB |. 8D7C24 24 lea edi, dword ptr [esp+24]
004011DF |. A1 F0A14000 mov eax, dword ptr [40A1F0]
004011E4 |. F3:A5 rep movs dword ptr es:[edi], dword p>
004011E6 |. 66:8B0D C4A14>mov cx, word ptr [40A1C4]
004011ED |. 8B6C24 50 mov ebp, dword ptr [esp+50]
004011F1 |. 66:A5 movs word ptr es:[edi], word ptr [esi>
004011F3 |. 894424 18 mov dword ptr [esp+18], eax
004011F7 |. A1 C0A14000 mov eax, dword ptr [40A1C0]
004011FC |. A4 movs byte ptr es:[edi], byte ptr [esi>
004011FD |. 894424 10 mov dword ptr [esp+10], eax
00401201 |. 66:894C24 14 mov word ptr [esp+14], cx
00401206 |. 8BFD mov edi, ebp
00401208 |. 83C9 FF or ecx, FFFFFFFF
0040120B |. 33C0 xor eax, eax
0040120D |. 8A15 F8A14000 mov dl, byte ptr [40A1F8]
00401213 |. F2:AE repne scas byte ptr es:[edi]
00401215 |. F7D1 not ecx
00401217 |. 885424 20 mov byte ptr [esp+20], dl
0040121B |. 8A15 C6A14000 mov dl, byte ptr [40A1C6]
00401221 |. 49 dec ecx
00401222 |. 885424 16 mov byte ptr [esp+16], dl
00401226 |. 83F9 08 cmp ecx, 8
00401229 |. 0F85 D5000000 jnz 00401304
如同上面一样,也是先进行了判断输入的字符长度,上面401229就是个关键跳,如果长度不等于8就会跳向输入错误。
00401289 |> /8B4C24 54 /mov ecx, dword ptr [esp+54]
0040128D |. |8D741C 18 |lea esi, dword ptr [esp+ebx+18]
00401291 |. |0FBE042E |movsx eax, byte ptr [esi+ebp]
00401295 |. |0FBE11 |movsx edx, byte ptr [ecx]
00401298 |. |2BC2 |sub eax, edx
0040129A |. |B9 1A000000 |mov ecx, 1A
0040129F |. |83C0 1A |add eax, 1A
004012A2 |. |99 |cdq
004012A3 |. |F7F9 |idiv ecx
004012A5 |. |52 |push edx
004012A6 |. |E8 B5000000 |call 00401360
004012AB |. |8A0E |mov cl, byte ptr [esi]
004012AD |. |83C4 04 |add esp, 4
004012B0 |. |3AC1 |cmp al, cl
004012B2 |. |75 50 |jnz short 00401304
004012B4 |. |8D7C24 18 |lea edi, dword ptr [esp+18]
004012B8 |. |83C9 FF |or ecx, FFFFFFFF
004012BB |. |33C0 |xor eax, eax
004012BD |. |43 |inc ebx
004012BE |. |F2:AE |repne scas byte ptr es:[edi]
004012C0 |. |F7D1 |not ecx
004012C2 |. |49 |dec ecx
004012C3 |. |3BD9 |cmp ebx, ecx
004012C5 |.^\7C C2 \jl short 00401289
关键算法的地方,里面有一个call也很重要,我们跟进去,如下图:
00401360 /[ DISCUZ_CODE_38 ]nbsp
00401364 |. 85C0 test eax, eax
00401366 |. 7C 08 jl short 00401370
00401368 |. 83F8 1A cmp eax, 1A
0040136B |. 7F 03 jg short 00401370
0040136D |. 83C0 41 add eax, 41
00401370 \> C3 retn
也就是对结果进行加1。
我们继续来看看上面的算法,其实和第一个也差不多,先定义了一个常量字符串str[]= ”ANLJSJWJ”,然后进行对输入的字符串str2进行操作:(str2 – 0x2f) % 0x1a + ‘A’ =str。
如果有一个不等于就跳向错误。
下面我简单的使用c写一个算注册码的吧,因为这里是用到了取余,同样结果不唯一
#include <stdio.h>
int main (void)
{
char str1[8] = "ANLJSJWJ";
char str2[8];
int i = 0;
printf ("\n其中一个字符串:\n");
for (i = 0; i < 8; i++)
{
str2[i] = str1[i] - 0x41 + 0x56;
printf ("%c",str2[i]);
}
printf ("\n");
return 0;
}
结果是Vca_h_1_(倒数第二个是小写的L哦) 运行成功的图例:
差不多分析这么多了吧,希望对自己有所提升,也希望和吾爱破解的众位一起学习,一起进步。 附件:
|