crackme下载地址:
https://files.cnblogs.com/tk091/crackme2.7z
很习惯的,首先查壳,ASPack 2.12 -> Alexey Solodovnikov,很简单,一个esp定律就搞定,dump后再说。
dump后的文件下载:
https://files.cnblogs.com/tk091/dump.rar
运行后发现这个程序是反虚拟机的,反虚拟机的方式有很多,比如,通过特权指令来检测、利用IDT基址检测、利用LDT和GDT的检测方法、基于STR(Store Task Register)的检测方法、通过注册表检测虚拟机(是否安装了tools工具等)、基于时间差的检测方式(通过RDTSC)、利用虚拟硬件指纹检测(对MAC地址检查等)等。
通过分析发现,这个crackme是通过特权指令来检测虚拟机的,有两种方式去除,一种是不让它检测,一种是检测后什么都不干(默认会显示提示并两秒后自动退出)。
我们用IDA来看看程序大致的流程,其中有一部分是:
瞬间就看到了dwMilliseconds,毫秒嘛,那么就是检测到虚拟机后的动作了,我们让它跳过这个,也就是不处理这段程序。
进入main函数的起始:
1 0040C6D0 /> \55 push ebp
找到关键跳:
1 0040CACF |. /75 2C jnz short 0040CAFD 2 0040CAD1 |. |8D8D 0CFBFFFF lea ecx, dword ptr [ebp-4F4] 3 0040CAD7 |. |51 push ecx 4 0040CAD8 |. |68 C80D4200 push 00420DC8 ; ASCII "%s",LF 5 0040CADD |. |E8 024EFFFF call 004018E4 6 0040CAE2 |. |83C4 08 add esp, 8 7 0040CAE5 |. |8BF4 mov esi, esp 8 0040CAE7 |. |68 D0070000 push 7D0 ; /Timeout = 2000. ms 9 0040CAEC |. |FF15 F4624200 call dword ptr [<&kernel32.Sleep>] ; \Sleep 10 0040CAF2 |. |3BF4 cmp esi, esp 11 0040CAF4 |. |E8 134AFFFF call 0040150C 12 0040CAF9 |. |33C0 xor eax, eax 13 0040CAFB |. |EB 79 jmp short 0040CB76 ; 检测到虚拟机,跳转到程序退出的地方
上面的第一行就是关键跳,即是跳转到正常的代码,直接修改为jmp,然后保存即可。这样,程序执行到这里的时候就会直接跳过。
下面是去掉反虚拟机后的程序:
https://files.cnblogs.com/tk091/Anti-Anti-virtual.rar
下面是完整的反虚拟机的汇编代码:
1 00401400 > \55 push ebp 2 00401401 . 8BEC mov ebp, esp 3 00401403 . 6A FF push -1 4 00401405 . 68 68004200 push 00420068 5 0040140A . 68 E4204000 push 004020E4 ; SE 处理程序安装 6 0040140F . 64:A1 0000000>mov eax, dword ptr fs:[0] 7 00401415 . 50 push eax 8 00401416 . 64:8925 00000>mov dword ptr fs:[0], esp 9 0040141D . 83C4 B4 add esp, -4C 10 00401420 . 53 push ebx 11 00401421 . 56 push esi 12 00401422 . 57 push edi 13 00401423 . 8965 E8 mov dword ptr [ebp-18], esp 14 00401426 . 8D7D A4 lea edi, dword ptr [ebp-5C] 15 00401429 . B9 11000000 mov ecx, 11 16 0040142E . B8 CCCCCCCC mov eax, CCCCCCCC 17 00401433 . F3:AB rep stos dword ptr es:[edi] 18 00401435 . C645 E4 01 mov byte ptr [ebp-1C], 1 19 00401439 . C745 FC 00000>mov dword ptr [ebp-4], 0 20 00401440 . 52 push edx 21 00401441 . 51 push ecx 22 00401442 . 53 push ebx 23 00401443 . B8 68584D56 mov eax, 564D5868 24 00401448 . BB 00000000 mov ebx, 0 25 0040144D . B9 0A000000 mov ecx, 0A 26 00401452 . BA 58560000 mov edx, 5658 27 00401457 . ED in eax, dx 28 00401458 . 81FB 68584D56 cmp ebx, 564D5868 29 0040145E . 0F9445 E4 sete byte ptr [ebp-1C] 30 00401462 . 5B pop ebx 31 00401463 . 59 pop ecx 32 00401464 . 5A pop edx 33 00401465 . C745 FC FFFFF>mov dword ptr [ebp-4], -1 34 0040146C . EB 14 jmp short 00401482 35 0040146E . B8 01000000 mov eax, 1 36 00401473 . C3 retn
至于后面的分析,就难了,我加了些注释,希望能够继续分析
1 0040CAFD BA 01000000 mov edx, 1 2 0040CB02 |. 85D2 |test edx, edx 3 0040CB04 |. 74 6E |je short 0040CB74 ; 跳到 结束 4 0040CB06 |. 8D85 5CFBFFFF |lea eax, dword ptr [ebp-4A4] 5 0040CB0C |. 50 |push eax ; 0012FADC please input password: 6 0040CB0D |. 68 74004200 |push 00420074 ; ASCII "%s " 7 0040CB12 |. E8 CD4DFFFF |call 004018E4 8 0040CB17 |. 83C4 08 |add esp, 8 ; 恢复堆栈 9 0040CB1A |. 8D8D FCFBFFFF |lea ecx, dword ptr [ebp-404] 10 0040CB20 |. 51 |push ecx ; 输入scanf 11 0040CB21 |. 68 6C0D4200 |push 00420D6C ; ASCII "%s" 12 0040CB26 |. E8 894DFFFF |call 004018B4 ; 输入的信息保存在堆栈中 13 0040CB2B |. 83C4 08 |add esp, 8 14 0040CB2E |. 8D95 FCFBFFFF |lea edx, dword ptr [ebp-404] 15 0040CB34 |. 52 |push edx 16 0040CB35 |. E8 CB44FFFF |call 00401005 ; 可能的关键call 17 0040CB3A |. 83C4 04 |add esp, 4 18 0040CB3D |. 8945 FC |mov dword ptr [ebp-4], eax ; 这两句相当于test eax,eax 19 0040CB40 837D FC 00 cmp dword ptr [ebp-4], 0 20 0040CB44 75 16 jnz short 0040CB5C ; 进行判断 21 0040CB46 |. 8D85 C4FBFFFF |lea eax, dword ptr [ebp-43C] 22 0040CB4C |. 50 |push eax 23 0040CB4D |. 68 C80D4200 |push 00420DC8 ; ASCII "%s",LF 24 0040CB52 |. E8 8D4DFFFF |call 004018E4 ; 调用printf函数 25 0040CB57 |. 83C4 08 |add esp, 8 26 0040CB5A |. EB 16 |jmp short 0040CB72 27 0040CB5C |> 8D8D 90FBFFFF |lea ecx, dword ptr [ebp-470] ; 这里是正确信息保存的位置 28 0040CB62 |. 51 |push ecx 29 0040CB63 |. 68 C80D4200 |push 00420DC8 ; ASCII "%s",LF 30 0040CB68 |. E8 774DFFFF |call 004018E4 31 0040CB6D |. 83C4 08 |add esp, 8 32 0040CB70 |. EB 02 |jmp short 0040CB74 ; 结果正确,跳出循环 33 0040CB72 |>^ EB 89 \jmp short 0040CAFD ; 重新开始判断
算法分析始终是个大难题。
程序的分析就大致这样吧。
勿在浮沙筑高台,还是回去好好学基础吧。
-----2012-5-2-22:10---------继续分析----------
通过52的几个大牛的帮助,搞到了真码(真码为:lEpynapmoi),任何我用IDA继续调试
1 int __cdecl sub_40BA10(const char *szKey) 2 { 3 char v2; // [sp+Ch] [bp-50h]@1 4 int v3; // [sp+4Ch] [bp-10h]@2 5 int i; // [sp+50h] [bp-Ch]@11 6 int v5; // [sp+54h] [bp-8h]@7 7 size_t ilength; // [sp+58h] [bp-4h]@1 8 9 memset(&v2, 0xCCCCCCCCu, 0x50u); 10 ilength = strlen(szKey); 11 if ( ilength == 10 ) 12 { 13 if ( szKey[1] == 69 ) 14 { 15 if ( *szKey == SomeString[(signed int)ilength % 24] ) 16 { 17 v5 = 2 * (signed int)ilength % 24; 18 if ( v5 > 24 ) 19 v5 -= 24; 20 if ( szKey[2] == SomeString[v5] ) 21 { 22 v5 = 0; 23 for ( i = 3; i <= 8; ++i ) 24 { 25 v5 = szKey[2] + v5 - 65; 26 if ( v5 > 24 ) 27 v5 %= 24; 28 if ( szKey[i] != SomeString[v5] ) 29 { 30 printf("%c", SomeString[v5]); 31 v3 = 0; 32 return unknown_libname_1(); 33 } 34 } 35 v5 = 0; 36 for ( i = 0; i < 9; ++i ) 37 v5 += szKey[i]; 38 v5 /= 9; 39 v3 = szKey[9] == (char)v5; 40 } 41 else 42 { 43 v3 = 0; 44 } 45 } 46 else 47 { 48 v3 = 0; 49 } 50 } 51 else 52 { 53 v3 = 0; 54 } 55 } 56 else 57 { 58 v3 = 0; 59 } 60 return unknown_libname_1(); 61 }
这个crackme没有上面算法,只有上面的这个,IDA显示SomeString为: UIT_SYC_Flowers_Company
这样,分析就完成了。