这个CrackMe带有一个校验文件Crackme.atom,我感觉它里面的内容作用无非就是提供一些数据参与计算,或者保存文件的校验信息防止crackme被修改之类。故OD调试得到信息如下:
00401D58 894C24 14 mov dword ptr [esp+0x14], ecx
00401D5C C68424 6C010000>mov byte ptr [esp+0x16C], 0x2
00401D64 894C24 18 mov dword ptr [esp+0x18], ecx
00401D68 894C24 1C mov dword ptr [esp+0x1C], ecx
00401D6C 894C24 20 mov dword ptr [esp+0x20], ecx
00401D70 884C24 24 mov byte ptr [esp+0x24], cl
00401D74 8D4C24 48 lea ecx, dword ptr [esp+0x48]
00401D78 E8 B30E0000 call 00402C30
00401D7D 68 B0514000 push 004051B0 ; ASCII "rb"
00401D82 68 A0514000 push 004051A0 ; ASCII "CrackMe.Atom"
00401D87 FF15 DC414000 call dword ptr [<&MSVCRT.fopen>] ; msvcrt.fopen
00401D8D 8BF8 mov edi, eax
00401D8F 83C4 08 add esp, 0x8
00401D92 85FF test edi, edi
00401D94 75 10 jnz short 00401DA6
00401D96 8B06 mov eax, dword ptr [esi]
00401D98 8BCE mov ecx, esi
00401D9A FF90 D0000000 call dword ptr [eax+0xD0]
00401DA0 8B6C24 0C mov ebp, dword ptr [esp+0xC]
00401DA4 EB 1C jmp short 00401DC2
00401DA6 57 push edi
00401DA7 6A 11 push 0x11
00401DA9 8D4C24 38 lea ecx, dword ptr [esp+0x38]
00401DAD 6A 01 push 0x1
00401DAF 51 push ecx
00401DB0 FF15 E0414000 call dword ptr [<&MSVCRT.fread>] ; msvcrt.fread
返回长度是0x11个字符。
0013F6A0 1F F5 35 57 9A 9E 8E B6 03 BC 5E E0 4B 77 F8 1B ?W殲幎糬郖w?
0013F6B0 00 FF FF FF 10 47 40 00 A0 8E D2 77 00 00 00 00 .G@.爭襴....
00401DB6 57 push edi
00401DB7 8BE8 mov ebp, eax
00401DB9 FF15 E4414000 call dword ptr [<&MSVCRT.fclose>] ; msvcrt.fclose
00401DBF 83C4 14 add esp, 0x14
00401DC2 83FD 11 cmp ebp, 0x11
00401DC5 74 0A je short 00401DD1
00401DC7 8B16 mov edx, dword ptr [esi]
00401DC9 8BCE mov ecx, esi
00401DCB FF92 D0000000 call dword ptr [edx+0xD0]
00401DD1 8D4424 10 lea eax, dword ptr [esp+0x10] ; 缓冲区,用来存放从CheckMe.exe中读取的数据
00401DD5 8D4C24 60 lea ecx, dword ptr [esp+0x60]
00401DD9 50 push eax
00401DDA 51 push ecx
00401DDB 8D4C24 2C lea ecx, dword ptr [esp+0x2C]
00401DDF E8 2C140000 call 00403210 ; 这里打开了程序本身,应该是做了校验
读取出来的数据如下:
0013F680 1F F5 35 57 9A 9E 8E B6 03 BC 5E E0 4B 77 F8 1B ?W殲幎糬郖w?
0013F690 00 00 00 00 3C 47 40 00 00 00 00 00 FF FF FF FF ....<G@.....
00401DE4 8D7C24 30 lea edi, dword ptr [esp+0x30] ; 取出从CrackMe.Atom中读取到的数据
00401DE8 8D4424 10 lea eax, dword ptr [esp+0x10] ; 取出从CrackMe.exe中读取到的数据
00401DEC 8A10 mov dl, byte ptr [eax] ; 应该是开始做校验了,可能是用来防爆破的。
00401DEE 8ACA mov cl, dl ; 所以,我们可以吧修改之后从EXE中取到的内容填到ATOM文件中,来绕过校验
00401DF0 3A17 cmp dl, byte ptr [edi]
00401DF2 75 1C jnz short 00401E10
如上所述,atom文件的问题解决,我们来看一下它用户名和序列号的处理方法。
关于绕过时间的校验
通过分析它处理用户名和序列号的代码发现:
0040250E B9 58544000 mov ecx, 00405458
00402513 E8 E8060000 call 00402C00
{
00402C00 51 push ecx
00402C01 8D4424 00 lea eax, dword ptr [esp]
00402C05 50 push eax
00402C06 FF15 D0414000 call dword ptr [<&MSVCRT.time>] ; msvcrt.time
00402C0C 8D4C24 04 lea ecx, dword ptr [esp+0x4]
00402C10 51 push ecx
00402C11 FF15 D4414000 call dword ptr [<&MSVCRT.localtime>] ; msvcrt.localtime
00402C17 8B48 04 mov ecx, dword ptr [eax+0x4]
00402C1A 8B00 mov eax, dword ptr [eax]
00402C1C 8D0C49 lea ecx, dword ptr [ecx+ecx*2]
00402C1F 8D1489 lea edx, dword ptr [ecx+ecx*4]
00402C22 8D0490 lea eax, dword ptr [eax+edx*4]
00402C25 83C4 0C add esp, 0xC
00402C28 C3 retn
}
00402518 2BC6 sub eax, esi
0040251A 83F8 01 cmp eax, 0x1
0040251D 7E 07 jle short 00402526
0040251F 8BCD mov ecx, ebp
00402521 E8 9C120000 call <jmp.&MFC42.#CDialog::OnCancel_4>; 退出应用程序了
00402526 803B 6D cmp byte ptr [ebx], 0x6D
00402529 75 19 jnz short 00402544
0040252B 43 inc ebx
这样的校验非常多,影响我们的调试分析,所以我将代码改成如下样子,绕过了这个时间的校验:
00402C00 51 push ecx
00402C01 8D4424 00 lea eax, dword ptr [esp]
00402C05 50 push eax
00402C06 FF15 D0414000 call dword ptr [<&MSVCRT.time>] ; msvcrt.time
00402C0C 8D4C24 04 lea ecx, dword ptr [esp+0x4]
00402C10 51 push ecx
00402C11 FF15 D4414000 call dword ptr [<&MSVCRT.localtime>] ; msvcrt.localtime
00402C17 8B48 04 mov ecx, dword ptr [eax+0x4]
00402C1A 8B00 mov eax, dword ptr [eax]
00402C1C 8D0C49 lea ecx, dword ptr [ecx+ecx*2]
00402C1F B8 00000000 mov eax, 0x0 ;修改了它的返回值,这样下面所有的时间校验就都报废了
00402C24 90 nop
00402C25 83C4 0C add esp, 0xC
00402C28 C3 retn
以上代码不改也可以的,当时是为了调试方便才改的。
我们继续分析用户名和序列号的校验代码,根据以前看雪坛子上的前辈留下的找MFC事件处理代码的方法,我们来到如下的地方:
00401FF0 /. 55 push ebp
00401FF1 |. 8BEC mov ebp, esp
00401FF3 |. 6A FF push -0x1
00401FF5 |. 68 B33A4000 push 00403AB3 ; SE handler installation
00401FFA |. 64:A1 0000000>mov eax, dword ptr fs:[0]
00402000 |. 50 push eax
00402001 |. 64:8925 00000>mov dword ptr fs:[0], esp
00402008 |. 81EC AC000000 sub esp, 0xAC
0040200E |. 53 push ebx
0040200F |. 8BD9 mov ebx, ecx
00402011 |. 56 push esi
00402012 |. 57 push edi
00402013 |. 8D8D 48FFFFFF lea ecx, dword ptr [ebp-0xB8]
00402019 |. E8 32F9FFFF call 00401950
0040201E |. 8D4D E4 lea ecx, dword ptr [ebp-0x1C]
00402021 |. C745 FC 00000>mov dword ptr [ebp-0x4], 0x0
00402028 |. E8 B3100000 call 004030E0
0040202D |. 8D4D A8 lea ecx, dword ptr [ebp-0x58]
00402030 |. C645 FC 01 mov byte ptr [ebp-0x4], 0x1
00402034 |. E8 27F3FFFF call 00401360
00402039 |. 90 nop
0040203A |. 6A 00 push 0x0 ; /pThreadId = NULL
0040203C |. 6A 00 push 0x0 ; |CreationFlags = 0
0040203E |. 53 push ebx ; |pThreadParm
0040203F |. 68 D01F4000 push 00401FD0 ; |ThreadFunction = CrackMe.00401FD0
00402044 |. 6A 00 push 0x0 ; |StackSize = 0
00402046 |. 6A 00 push 0x0 ; |pSecurity = NULL
00402048 |. FF15 08404000 call dword ptr [<&KERNEL32.CreateThre>; \CreateThread
这里以挂起的方式起了一个线程,由于没有执行,所以没分析到它的所用,估计也是一个校验的代码,先不管它。
继续向下,我们看到了用户名和序列号的规则如下:
004020A1 |. 8B4B 60 mov ecx, dword ptr [ebx+0x60] ; 取出用户名
004020A4 |. 8B41 F8 mov eax, dword ptr [ecx-0x8] ; 用户名的长度 6~20
004020A7 |. 83F8 06 cmp eax, 0x6
004020AA |. 0F8C 49020000 jl 004022F9
004020B0 |. 83F8 20 cmp eax, 0x20
004020B3 |. 0F8F 40020000 jg 004022F9
004020B9 |. 8B53 64 mov edx, dword ptr [ebx+0x64] ; 取出密码
004020BC |. 8B52 F8 mov edx, dword ptr [edx-0x8] ; 密码的长度 6~A0
004020BF |. 83FA 06 cmp edx, 0x6
004020C2 |. 0F8C 31020000 jl 004022F9
004020C8 |. 81FA A0000000 cmp edx, 0xA0
004020CE |. 0F8F 25020000 jg 004022F9
004020F2 |. BF 34534000 mov edi, 00405334 ; 取出用户名及拼凑字符(将用户名凑够了32位),开始运算了
004020F7 |. 83C9 FF or ecx, -0x1
004020FA |. 33C0 xor eax, eax
004020FC |. 83C4 18 add esp, 0x18
004020FF |. 33F6 xor esi, esi
00402101 |. 33D2 xor edx, edx
00402103 |. F2:AE repne scas byte ptr es:[edi]
00402105 |. F7D1 not ecx
00402107 |. 49 dec ecx
00402108 |. 74 39 je short 00402143
0040210A |> 8A82 34534000 /mov al, byte ptr [edx+0x405334] ; 判断用户名是不是合法的数字及大小写字母
00402110 |. 3C 30 |cmp al, 0x30
00402112 |. 7C 04 |jl short 00402118
00402114 |. 3C 39 |cmp al, 0x39
00402116 |. 7E 10 |jle short 00402128
00402118 |> 3C 61 |cmp al, 0x61
0040211A |. 7C 04 |jl short 00402120
0040211C |. 3C 7A |cmp al, 0x7A
0040211E |. 7E 08 |jle short 00402128
00402120 |> 3C 41 |cmp al, 0x41
00402122 |. 7C 1A |jl short 0040213E
00402124 |. 3C 5A |cmp al, 0x5A
00402126 |. 7F 16 |jg short 0040213E
00402128 |> BF 34534000 |mov edi, 00405334 ; ASCII "}esterCheneghmfnopqrijklstuvabcd"
0040212D |. 83C9 FF |or ecx, -0x1
00402130 |. 33C0 |xor eax, eax
00402132 |. 42 |inc edx
00402133 |. F2:AE |repne scas byte ptr es:[edi]
00402135 |. F7D1 |not ecx
00402137 |. 49 |dec ecx
00402138 |. 3BD1 |cmp edx, ecx
0040213A |.^ 75 CE \jnz short 0040210A
00402143 |> \BF 68534000 mov edi, 00405368 ; 取出密码
00402148 |. 83C9 FF or ecx, -0x1
0040214B |. 33C0 xor eax, eax
0040214D |. 33D2 xor edx, edx
0040214F |. F2:AE repne scas byte ptr es:[edi]
00402151 |. F7D1 not ecx
00402153 |. 49 dec ecx
00402154 |. 74 32 je short 00402188
00402156 |> 8A82 68534000 /mov al, byte ptr [edx+0x405368]
0040215C |. 3C 30 |cmp al, 0x30
0040215E |. 7C 04 |jl short 00402164
00402160 |. 3C 39 |cmp al, 0x39
00402162 |. 7E 10 |jle short 00402174
00402164 |> 3C 61 |cmp al, 0x61
00402166 |. 7C 04 |jl short 0040216C
00402168 |. 3C 7A |cmp al, 0x7A
0040216A |. 7E 08 |jle short 00402174
0040216C |> 3C 41 |cmp al, 0x41
0040216E |. 7C 1D |jl short 0040218D
00402170 |. 3C 5A |cmp al, 0x5A
00402172 |. 7F 19 |jg short 0040218D
00402174 |> BF 68534000 |mov edi, 00405368 ; ASCII "14725lmn858963efghi369jk2741abcd"
00402179 |. 83C9 FF |or ecx, -0x1
0040217C |. 33C0 |xor eax, eax
0040217E |. 42 |inc edx
0040217F |. F2:AE |repne scas byte ptr es:[edi]
00402181 |. F7D1 |not ecx
00402183 |. 49 |dec ecx
00402184 |. 3BD1 |cmp edx, ecx
00402186 |.^ 75 CE \jnz short 00402156
00402188 |> 83FE 01 cmp esi, 0x1
0040218B |. 75 0B jnz short 00402198
0040218D |> 8B45 F0 mov eax, dword ptr [ebp-0x10]
00402190 |. 6A 00 push 0x0
00402192 |. 50 push eax
00402193 |. E9 64010000 jmp 004022FC
004021B3 |> \BF 68534000 mov edi, 00405368 ; 取出密码
004021B8 |. 83C9 FF or ecx, -0x1
004021BB |. 33C0 xor eax, eax
004021BD |. F2:AE repne scas byte ptr es:[edi]
004021BF |. F7D1 not ecx ; 取出密码的长度然后减1的值必须大于等于0x20
004021C1 |. 49 dec ecx
004021C2 |. 83F9 20 cmp ecx, 0x20
004021C5 |. 73 0A jnb short 004021D1 ; 只要不大于0x20,就结束线程停止校验
可见这个CrackMe的规矩有很多哦……
00402610 /$ 56 push esi
00402611 |. 8BF1 mov esi, ecx
00402613 |. 6A 03 push 0x3
00402615 |. E8 66FEFFFF call 00402480
0040261A |. 83F8 01 cmp eax, 0x1
0040261D |. 74 04 je short 00402623
0040261F |. 33C0 xor eax, eax
00402621 |. 5E pop esi
00402622 |. C3 retn
00402623 |> 8BCE mov ecx, esi
00402625 |. E8 8E120000 call <jmp.&MFC42.#CDialog::DoModal_2514>
0040262A |. 5E pop esi
0040262B \. C3 retn
根据CrackMe作者的注册成功界面提示,可以想象出来,这个成功界面应该是个模式对话框,不是简单的AfxMessageBox,跟进00402480这个函数看看条件是什么……
00402480 /$ 53 push ebx
00402481 |. 55 push ebp
00402482 |. 8BE9 mov ebp, ecx
00402484 |. 56 push esi
00402485 |. 57 push edi
00402486 |. B9 58544000 mov ecx, 00405458
0040248B |. E8 70070000 call 00402C00
00402490 |. 8B4C24 14 mov ecx, dword ptr [esp+0x14]
00402494 |. 8BF0 mov esi, eax
00402496 8D41 FD lea eax, dword ptr [ecx-0x3] ; 把EAX值改成4,来跳到0040254D
00402499 83F8 04 cmp eax, 0x4
0040249C 0F87 A2000000 ja 00402544
004024A2 |. 8BF8 mov edi, eax
004024A4 |. 8D99 68534000 lea ebx, dword ptr [ecx+0x405368]
004024AA |> FF2485 782540>/jmp dword ptr [eax*4+0x402578] ; 这里要调到关键的函数,需要让EAX=4
004024B1 |> B9 58544000 |mov ecx, 00405458
004024B6 |. E8 45070000 |call 00402C00
004024BB |. 2BC6 |sub eax, esi
004024BD |. 83F8 01 |cmp eax, 0x1
004024C0 |. 7E 07 |jle short 004024C9
004024C2 |. 8BCD |mov ecx, ebp
004024C4 |. E8 F9120000 |call <jmp.&MFC42.#CDialog::OnCancel_4376>
004024C9 |> 803B 41 |cmp byte ptr [ebx], 0x41
004024CC |. 75 76 |jnz short 00402544
004024CE |. EB 5B |jmp short 0040252B
...
00402544 |> 5F pop edi
00402545 |. 5E pop esi
00402546 |. 5D pop ebp
00402547 |. 33C0 xor eax, eax
00402549 |. 5B pop ebx
0040254A |. C2 0400 retn 0x4
0040254D |> B9 58544000 mov ecx, 00405458
00402552 |. E8 A9060000 call 00402C00
00402557 |. 2BC6 sub eax, esi
00402559 |. 83F8 01 cmp eax, 0x1
0040255C |. 7E 07 jle short 00402565
0040255E |. 8BCD mov ecx, ebp
00402560 |. E8 5D120000 call <jmp.&MFC42.#CDialog::OnCancel_4376>
00402565 |> 6A 0E push 0xE
00402567 |. 8BCD mov ecx, ebp
00402569 |. E8 22000000 call 00402590 ; 这里面是关键
0040256E |. 5F pop edi
0040256F |. 5E pop esi
00402570 |. 5D pop ebp
00402571 |. 5B pop ebx
00402572 \. C2 0400 retn 0x4
进入关键的函数,得到信息如下:
00402590 /$ 8B4424 04 mov eax, dword ptr [esp+0x4]
00402594 |. 56 push esi
00402595 |. 83F8 1D cmp eax, 0x1D
00402598 |. 74 2C je short 004025C6
0040259A |. B9 34534000 mov ecx, 00405334 ; ASCII "}esterCheneghmfnopqrijklstuvabcd"这个是根据用户名算出来的值
0040259F |. 81E9 68534000 sub ecx, 00405368 ; ASCII "14725lmn858963efghi369jk2741abcd"这个是根据密码算出来的值
004025A5 |. 83F8 1D cmp eax, 0x1D
004025A8 |> 7F 30 /jg short 004025DA
004025AA |. 0FBE9401 6853>|movsx edx, byte ptr [ecx+eax+0x405368] ; ECX=FFFFFFCC,eax=E
004025B2 |. 0FBEB0 685340>|movsx esi, byte ptr [eax+0x405368]
004025B9 |. 2BD6 |sub edx, esi
004025BB |. 83FA 01 |cmp edx, 0x1
004025BE |. 75 1A |jnz short 004025DA
004025C0 |. 40 |inc eax
004025C1 |. 83F8 1D |cmp eax, 0x1D
004025C4 |.^ 75 E2 \jnz short 004025A8
004025C6 |> 0FBE88 345340>movsx ecx, byte ptr [eax+0x405334]
004025CD |. 0FBE80 685340>movsx eax, byte ptr [eax+0x405368]
004025D4 |. 2BC1 sub eax, ecx 相减如果等于1,则就可以满足上面的条件,调用Domodal函数显示界面了。
004025D6 |. 5E pop esi
004025D7 |. C2 0400 retn 0x4
004025DA |> 33C0 xor eax, eax
004025DC |. 5E pop esi
004025DD \. C2 0400 retn 0x4
004025E0 . 56 push esi
004025E1 . 8BF1 mov esi, ecx
004025E3 . 6A 07 push 0x7
004025E5 . E8 66FDFFFF call 00402350
004025EA . 85C0 test eax, eax
004025EC . 8BCE mov ecx, esi
004025EE . 75 09 jnz short 004025F9
004025F0 . E8 CD110000 call <jmp.&MFC42.#CDialog::OnCancel_4376>
004025F5 . 33C0 xor eax, eax
004025F7 . 5E pop esi
004025F8 . C3 retn
004025F9 > E8 B4120000 call <jmp.&MFC42.#CDialog::OnInitDialog_4710>
004025FE . B8 01000000 mov eax, 0x1
00402603 . 5E pop esi
00402604 . C3 retn
由此可知,由用户名算出来的值中中间一部分的每个字符都要比由序列号算出来的值大1,这样就可以通过校验了比如ccfer老大提供的名和序列号:
eT_oxnldviuotv qqpqhkr{vkpujmnv ms
gTmAtomfwhumlv ppopgjqzujotilmw ms
这样我们就明白了一个道理:
用户名的长度应该是32位,否则程序自己按照字母表填出起来得到的字符串减一再逆出来的序列号不会满足我们在上面贴出来的序列号的规则,所以,用户名需要枚举。
先不管这些了,先爆破了它:
把:
0040261D |. 74 04 je short 00402623
改成:
0040261D /EB 04 jmp short 00402623
这样,不管注册对错,都应该弹出一个模式对话框。修改了Crackme.atom文件以后,运行发现没有对话框弹出,先后比较过多次,传入的this指针什么的都是一样的,CPU上下文也一样的时候还是不能弹出来,所以,我估计
应该是做了处理,这个crackme无数次的调用OnCancel,我觉得这次模式对话框没有出现也应该是它搞的鬼,所以就在OnCancel上下了个断点,来到如下地方:
004025E0 56 push esi ; Decoded as <WinProc>
004025E1 . 8BF1 mov esi, ecx
004025E3 . 6A 07 push 0x7
004025E5 . E8 66FDFFFF call 00402350
004025EA . 85C0 test eax, eax
004025EC . 8BCE mov ecx, esi
004025EE EB 09 jnz short 004025F9 ; 这里改成JMP就可以了应该。
004025F0 . E8 CD110000 call <jmp.&MFC42.#CDialog::OnCancel_4>
004025F5 . 33C0 xor eax, eax
004025F7 . 5E pop esi
004025F8 . C3 retn
004025F9 > E8 B4120000 call <jmp.&MFC42.#CDialog::OnInitDial>
004025FE . B8 01000000 mov eax, 0x1
00402603 . 5E pop esi
00402604 . C3 retn
把004025EE处的JNZ改成JMP,如: EB 09 jmp short 004025F9,这样,就爆破完成了!
经过观察,发现,这里提示失败,但是只要让窗口重新刷新一下,比如找个别的窗口遮盖一下,再露出来就提示成功了。
我猜测应该是在一些刷新消息的响应函数里有个暗装在搞怪的原因,我看了这个crackme的资源,它的注册失败和成功是两个标签资源,我想他应该是通过修改资源的显示来动的手脚或者修改的资源的Z轴。
但是我下断ShowWindow,和一些我知道的消息断点比如WM_PAINT,OnPaint()这些函数都没有找到可疑代码。所以就提交了这个不是没成功的破解文件。希望能通过审核。
另外,我会继续找这个暗装到底是在哪里出的问题,明天有时间的话给出用户名和注册码的算法部分……