【吾爱2013CM大赛解答】-- CrackMe -- 苏紫方璇 详细分析
本帖最后由 JoyChou 于 2013-12-19 14:20 编辑作者:JoyChou
博客:http://joychou.sinaapp.com
日期:2013年12月16日
这个CM是VC6.0写的MFC程序。这次我就不用MFC按钮入口工具,试试手动来找找。
在Xp下可以这样:
首先用IDA找到CCmdTarget::OnCmdMsg函数的地址,这个CM是00401C6C
OD中,来到00401C6C这个地址,是一个jmpxxxx, enter进入,再向下翻,在push 下断,此时等于0x7DD491F4,
其实这个就是按钮的入口地址,只是有的时候会单步两步才能到处下断。单步两步后,来到入口点00401580
一来就构造了一个SEH的handler,先留意点,下面肯定有异常发生
00401580 .55 push ebp
00401581 .8BEC mov ebp,esp
00401583 .6A FF push -0x1
00401585 .68 10344000 push CrackMe.00403410
0040158A .68 F01D4000 push <jmp.&MSVCRT._except_handler3> ;SE Hander; SE 处理程序安装
0040158F .64:A1 0000000>mov eax,dword ptr fs: ;fs:
00401595 .50 push eax
00401596 .64:8925 00000>mov dword ptr fs:,esp
0040159D .81C4 A8FEFFFF add esp,-0x158
接着
00401742 > \6A 01 push 0x1
00401744 .8B8D C4FEFFFF mov ecx,dword ptr ss:
0040174A .E8 43060000 call <jmp.&MFC42.#CWnd::UpdateData_6334> ;获取用户名
0040174F .6A 00 push 0x0
00401751 .8B8D C4FEFFFF mov ecx,dword ptr ss:
00401757 .E8 36060000 call <jmp.&MFC42.#CWnd::UpdateData_6334> ;获取密码
0040175C .8B8D C4FEFFFF mov ecx,dword ptr ss:
00401762 .8B51 60 mov edx,dword ptr ds:
00401765 .8995 CCFEFFFF mov dword ptr ss:,edx
0040176B .8D85 E4FEFFFF lea eax,dword ptr ss:
00401771 .50 push eax
00401772 .8B8D CCFEFFFF mov ecx,dword ptr ss:
00401778 .51 push ecx
00401779 .E8 92FDFFFF call CrackMe.00401510 ;算法,此时,看堆栈,密文出来了
0040177E .83C4 08 add esp,0x8
堆栈
0018F650 00362DD0ASCII "JoyChou"
0018F654 0018F6B0ASCII "6FA6B5649CA6AF"
算法很简单,不是重点,就不写keygen了。
做到这其实这题已经差不多算做完了。不过,如果这只是我们想要的结果,那么分析还有什么意思呢?
接着继续看……
重新来一次,输入正确的一组用户名和密码,看看程序的总体流程。
一路单步,看到异常处
0040163C C745 FC 0000000>mov dword ptr ss:,0x0
00401643 8A15 00000000 mov dl,byte ptr ds: ; 访问0异常
00401649 8815 00000000 mov byte ptr ds:,dl
0040164F E8 0C020000 call CrackMe.00401860
这个时候在按钮入口的0040158Apush 0x401DF0的0x401DF0处下断
一路单步到7523DD32处F7
7523DD28 33DB xor ebx,ebx
7523DD2A 33C9 xor ecx,ecx
7523DD2C 33D2 xor edx,edx
7523DD2E 33F6 xor esi,esi
7523DD30 33FF xor edi,edi
7523DD32 FFD0 call eax ; F7
最后就可以看到成功的样子了,利用SetWindowTextA设置静态文本框为“破解成功”
00401714 50 push eax
00401715 68 EA030000 push 0x3EA
0040171A 8B8D C4FEFFFF mov ecx,dword ptr ss:
00401720 E8 79060000 call <jmp.&MFC42.#CWnd::GetDlgItem_3092>
00401725 8BC8 mov ecx,eax
00401727 E8 6C060000 call <jmp.&MFC42.#CWnd::SetWindowTextA_6199>
0040172C C745 FC FFFFFFF>mov dword ptr ss:,-0x1
上面就算基本了解程序流程,不过此CM还有几个地方值得学习。
0x1. 在MFC的OnInitDialog里面检测SetTimer函数的int断点,以及作者自己定义的函数int3 断点
call CrackMe.00401870 这个call就是检测int 3断点
00401870 55 push ebp
00401871 8BEC mov ebp,esp
00401873 51 push ecx
00401874 57 push edi
00401875 C745 FC 0000000>mov dword ptr ss:,0x0
0040187C FC cld
0040187D 8B7D 08 mov edi,dword ptr ss:
00401880 8B4D 0C mov ecx,dword ptr ss:
00401883 B0 CC mov al,0xCC
00401885 F2:AE repne scas byte ptr es:
00401887 75 07 jnz XCrackMe.00401890
00401889 C745 FC 0100000>mov dword ptr ss:,0x1
00401890 8B45 FC mov eax,dword ptr ss:
00401893 5F pop edi
00401894 8BE5 mov esp,ebp
00401896 5D pop ebp
00401897 C3 retn
0x2. 进程退出,都是利用call 00401860退出
分析的时候把这个call retn掉,这样程序就不会退出了。
7C92E510 > 8BD4 mov edx,esp
7C92E512 0F34 sysenter
7C92E514 >C3 retn
0x3. 在SetTimer的回调函数里检测硬件断点,检测到就退出
00401A68 33C0 xor eax,eax
00401A6A 8B50 20 mov edx,dword ptr ds:
00401A6D 8D4C24 18 lea ecx,dword ptr ss:
00401A71 51 push ecx
00401A72 52 push edx
00401A73 FF15 F4314000 call dword ptr ds:[<&USER32.SetWindowTextA>] ; USER32.SetWindowTextA
00401A79 8D4424 48 lea eax,dword ptr ss:
00401A7D C74424 48 3F000>mov dword ptr ss:,0x1003F
00401A85 50 push eax
00401A86 FF15 14304000 call dword ptr ds:[<&KERNEL32.GetCurrentThread>] ; kernel32.GetCurrentThread
00401A8C 50 push eax
00401A8D FF15 10304000 call dword ptr ds:[<&KERNEL32.GetThreadContext>] ; kernel32.Wow64GetThreadContext
00401A93 395C24 4C cmp dword ptr ss:,ebx
00401A97 75 12 jnz XCrackMe.00401AAB
00401A99 395C24 50 cmp dword ptr ss:,ebx
00401A9D 75 0C jnz XCrackMe.00401AAB
00401A9F 395C24 54 cmp dword ptr ss:,ebx
00401AA3 75 06 jnz XCrackMe.00401AAB
00401AA5 395C24 58 cmp dword ptr ss:,ebx
00401AA9 74 05 je XCrackMe.00401AB0
0x4. 利用QueryPerformanceCounter计算程序执行时间,大于2S就退出,达到反调试。
004018A0 .A1 FC404000 mov eax,dword ptr ds:
004018A5 .56 push esi
004018A6 .8B35 0C304000 mov esi,dword ptr ds:[<&KERNEL32.QueryPe>;kernel32.QueryPerformanceCounter
004018AC .85C0 test eax,eax
004018AE .7C 23 jl XCrackMe-.004018D3
004018B0 .7F 09 jg XCrackMe-.004018BB
004018B2 .A1 F8404000 mov eax,dword ptr ds:
004018B7 .85C0 test eax,eax
004018B9 .76 18 jbe XCrackMe-.004018D3
004018BB >A1 00414000 mov eax,dword ptr ds:
004018C0 .8B0D 04414000 mov ecx,dword ptr ds:
004018C6 .A3 F8404000 mov dword ptr ds:,eax
004018CB .890D FC404000 mov dword ptr ds:,ecx
004018D1 .EB 12 jmp XCrackMe-.004018E5
004018D3 >68 F0404000 push CrackMe-.004040F0 ; /pPerformanceFreq = CrackMe-.004040F0
004018D8 .FF15 08304000 call dword ptr ds:[<&KERNEL32.QueryPerfo>; \QueryPerformanceFrequency
004018DE .68 F8404000 push CrackMe-.004040F8
004018E3 .FFD6 call esi
004018E5 >6A 64 push 0x64 ; /Timeout = 100. ms
004018E7 .FF15 04304000 call dword ptr ds:[<&KERNEL32.Sleep>] ; \Sleep
004018ED .68 00414000 push CrackMe-.00404100
004018F2 .FFD6 call esi
004018F4 .8B15 00414000 mov edx,dword ptr ds:
004018FA .A1 F8404000 mov eax,dword ptr ds:
004018FF .8B35 FC404000 mov esi,dword ptr ds:
00401905 .2BD0 sub edx,eax
00401907 .A1 04414000 mov eax,dword ptr ds:
0040190C .6A 00 push 0x0
0040190E .1BC6 sbb eax,esi
00401910 .68 E8030000 push 0x3E8
00401915 .50 push eax
00401916 .52 push edx
00401917 .E8 94050000 call CrackMe-.00401EB0
0040191C .8B0D F4404000 mov ecx,dword ptr ds:
00401922 .51 push ecx
00401923 .8B0D F0404000 mov ecx,dword ptr ds:
00401929 .51 push ecx
0040192A .52 push edx
0040192B .50 push eax
0040192C .E8 CF040000 call CrackMe-.00401E00
00401931 .3D D0070000 cmp eax,0x7D0 ;判断事件是否小于2秒,大于就退出
00401936 .5E pop esi
00401937 .7E 05 jle XCrackMe-.0040193E
00401939 .E8 22FFFFFF call CrackMe-.00401860
0040193E >C2 1000 retn 0x10
成功截图
写文章好辛苦的说。 来顶下 大牛的破文
页:
[1]