C源码级混乱的 CrackMe 调试过程
日期:2005年3月27日 调试人:csjwaman[DFCG]
―――――――――――――――――――――――――――――――――――――――――――
【软件名称】:C源码级混乱的 CrackMe
【下载地址】:http://bbs.pediy.com/showthread.php?s=&threadid=12217
【调试声明】:初学Crack,只是感兴趣,没有其它目的。失误之处敬请诸位大侠赐教!
【操作系统】:winxp sp1
【调试工具】:OD
【调试过程】:
这个CrackMe 是vcasm的,据称是“算法特别简单,不过可能源码不好跟踪”。到底是不是不好跟踪,我们来试试。
用OD载入后,忽略所有异常:
00402710 >/$ 55 push ebp////载入后停在这里。
00402711 |. 8BEC mov ebp,esp
00402713 |. 6A FF push -1
00402715 68 08344000 push 403408
0040271A 68 96284000 push 402896 ; SE handler installation
0040271F 64:A1 0000000>mov eax,dword ptr fs:[0]
00402725 50 push eax
00402726 64:8925 00000>mov dword ptr fs:[0],esp
F9运行,出现注册界面,输入用户名和验证码,点“注册”居然异常在:
00401647 56 db 56 ; CHAR 'V'
00401648 57 db 57 ; CHAR 'W'
00401649 8B db 8B
0040164A F9 db F9
0040164B CC int3////异常在这里!(把这里NOP掉是不行的,见后面分析。)
0040164C 9D db 9D
0040164D 42 db 42 ; CHAR 'B'
0040164E 48 db 48 ; CHAR 'H'
0040164F 2A db 2A ; CHAR '*'
异常后程序自动退出。往下看看发现有不少类似的代码!看来是CREACKME发现被跟踪了。那么程序是如何发现被跟踪的呢?我们一起来分析一下吧!
重新用OD载入后,忽略所有异常:
00402710 >/$ 55 push ebp////入口处。
00402711 |. 8BEC mov ebp,esp
00402713 |. 6A FF push -1
00402715 68 08344000 push 403408
0040271A 68 96284000 push 402896////SE handler 到402896处F2下断,不然程序跑飞!installation
0040271F 64:A1 0000000>mov eax,dword ptr fs:[0]////看到fs:[0]就应该联想到异常。
00402725 50 push eax
00402726 64:8925 00000>mov dword ptr fs:[0],esp
0040272D |. 83EC 68 sub esp,68
00402730 |. 53 push ebx
00402731 |. 56 push esi
00402732 |. 57 push edi
00402896 $- FF25 A4314000 jmp dword ptr ds:[4031A4]////这里下断!
0040289C $- FF25 A8314000 jmp dword ptr ds:[4031A8]
接着F9运行程序,等出现注册界面后输入用户名“aman”和验证码"12345678",点“注册”。
好!断下了,清除断点。F7跟进:
77C03EB0 > 55 push ebp
77C03EB1 8BEC mov ebp,esp
77C03EB3 83EC 08 sub esp,8
77C03EB6 53 push ebx
77C03EB7 56 push esi
77C03EB8 57 push edi
77C03EB9 55 push ebp
77C03EBA FC cld
77C03EBB 8B5D 0C mov ebx,dword ptr ss:[ebp+C]
77C03EBE 8B45 08 mov eax,dword ptr ss:[ebp+8]
77C03EC1 F740 04 0600000>test dword ptr ds:[eax+4],6
77C03EC8 0F85 82000000 jnz 77C03F50 ; 77C03F50
77C03ECE 8945 F8 mov dword ptr ss:[ebp-8],eax
77C03ED1 8B45 10 mov eax,dword ptr ss:[ebp+10]
77C03ED4 8945 FC mov dword ptr ss:[ebp-4],eax
77C03ED7 8D45 F8 lea eax,dword ptr ss:[ebp-8]
77C03EDA 8943 FC mov dword ptr ds:[ebx-4],eax
77C03EDD 8B73 0C mov esi,dword ptr ds:[ebx+C]
77C03EE0 8B7B 08 mov edi,dword ptr ds:[ebx+8]
77C03EE3 83FE FF cmp esi,-1
77C03EE6 74 61 je short 77C03F49 ; 77C03F49
77C03EE8 8D0C76 lea ecx,dword ptr ds:[esi+esi*2]
77C03EEB 837C8F 04 00 cmp dword ptr ds:[edi+ecx*4+4],0
77C03EF0 74 45 je short 77C03F37 ; 77C03F37
77C03EF2 56 push esi
77C03EF3 55 push ebp
77C03EF4 8D6B 10 lea ebp,dword ptr ds:[ebx+10]
77C03EF7 FF548F 04 call dword ptr ds:[edi+ecx*4+4] ////直接F4到这里,看看CALL的地址是哪里?
77C03EFB 5D pop ebp
77C03EFC 5E pop esi
77C03EFD 8B5D 0C mov ebx,dword ptr ss:[ebp+C]
77C03F00 0BC0 or eax,eax
原来是CALL到0040284E处。好,继续F7跟进:
0040284E |. 8B45 EC mov eax,dword ptr ss:[ebp-14]////来到这里。
00402851 |. 8B08 mov ecx,dword ptr ds:[eax]
00402853 |. 8B09 mov ecx,dword ptr ds:[ecx]
00402855 |. 894D 88 mov dword ptr ss:[ebp-78],ecx
00402858 |. 50 push eax
00402859 51 push ecx
0040285A E8 15000000 call 00402874 ; <jmp.&MSVCRT._XcptFilter>////关键!
0040285F 59 pop ecx
00402860 |. 59 pop ecx
00402861 \. C3 retn
在0040285A处用F7跟进:
0040286E $- FF25 B0314000 jmp dword ptr ds:[4031B0]
00402874 $- FF25 7C314000 jmp dword ptr ds:[40317C]////继续F7。
0040287A $- FF25 8C314000 jmp dword ptr ds:[40318C]
来到:
77C01269 > 55 push ebp////以下用F8走。
77C0126A 8BEC mov ebp,esp
77C0126C 51 push ecx
77C0126D 53 push ebx
77C0126E 56 push esi
77C0126F 57 push edi
77C01270 E8 DD680000 call 77C07B52 ; 77C07B52
77C01275 8B7D 08 mov edi,dword ptr ss:[ebp+8]
77C01278 8BF0 mov esi,eax
77C0127A 8B56 54 mov edx,dword ptr ds:[esi+54]
77C0127D A1 94A7C277 mov eax,dword ptr ds:[77C2A794]
77C01282 8BCA mov ecx,edx
77C01284 3939 cmp dword ptr ds:[ecx],edi
77C01286 74 0D je short 77C01295 ; 77C01295
77C01288 8D1C40 lea ebx,dword ptr ds:[eax+eax*2]
77C0128B 83C1 0C add ecx,0C
77C0128E 8D1C9A lea ebx,dword ptr ds:[edx+ebx*4]
77C01291 3BCB cmp ecx,ebx
77C01293 ^ 72 EF jb short 77C01284 ; 77C01284
77C01295 8D0440 lea eax,dword ptr ds:[eax+eax*2]////F4下来。
77C01298 8D0482 lea eax,dword ptr ds:[edx+eax*4]
77C0129B 3BC8 cmp ecx,eax
77C0129D 73 04 jnb short 77C012A3 ; 77C012A3
77C0129F 3939 cmp dword ptr ds:[ecx],edi
77C012A1 74 02 je short 77C012A5 ; 77C012A5
77C012A3 33C9 xor ecx,ecx
77C012A5 85C9 test ecx,ecx
77C012A7 0F84 12010000 je 77C013BF ////这里跳!
77C012AD 8B59 08 mov ebx,dword ptr ds:[ecx+8]
77C012B0 85DB test ebx,ebx
77C012B2 895D 08 mov dword ptr ss:[ebp+8],ebx
77C012B5 0F84 04010000 je 77C013BF ; 77C013BF
77C012BB 83FB 05 cmp ebx,5
77C012BE 75 0C jnz short 77C012CC ; 77C012CC
跳到:
77C013BF FF75 0C push dword ptr ss:[ebp+C]////跳到这里。
77C013C2 FF15 C011BE77 call dword ptr ds:[77BE11C0] ; kernel32.UnhandledExceptionFilter
77C013C8 5F pop edi
77C013C9 5E pop esi
77C013CA 5B pop ebx
77C013CB C9 leave
77C013CC C3 retn
跳到77C013BF后就可以发现开始调用UnhandledExceptionFilter了!!!
F7跟进:
77E730C0 > 68 00050000 push 500
77E730C5 68 F852E777 push 77E752F8
77E730CA E8 0972FEFF call 77E5A2D8 ; 77E5A2D8
77E730CF C745 E4 0400000>mov dword ptr ss:[ebp-1C],4
77E730D6 8B7D 08 mov edi,dword ptr ss:[ebp+8]
77E730D9 8B07 mov eax,dword ptr ds:[edi]
77E730DB BB 050000C0 mov ebx,C0000005
77E730E0 33F6 xor esi,esi
77E730E2 3918 cmp dword ptr ds:[eax],ebx
77E730E4 75 09 jnz short 77E730EF ; 77E730EF
77E730E6 3970 14 cmp dword ptr ds:[eax+14],esi
77E730E9 0F85 77DC0000 jnz 77E80D66 ; 77E80D66
77E730EF 8975 E0 mov dword ptr ss:[ebp-20],esi
77E730F2 56 push esi
77E730F3 6A 04 push 4
77E730F5 8D45 E0 lea eax,dword ptr ss:[ebp-20]
77E730F8 50 push eax
77E730F9 6A 07 push 7
77E730FB E8 B9B5FEFF call 77E5E6B9////调用GetCurrentProcess,返回hProcess。
77E73100 50 push eax
77E73101 FF15 AC10E477 call dword ptr ds:[77E410AC]////调用ntdll.ZwQueryInformationProcess。晕,调用好多次!
77E73107 85C0 test eax,eax////EAX=0则表示成功,否则失败。
77E73109 7C 09 jl short 77E73114
77E7310B 3975 E0 cmp dword ptr ss:[ebp-20],esi////[ebp-20]=-1,则表示有调试器,=0表示没调试器。
77E7310E 0F85 C5060000 jnz 77E737D9////这里不能跳,跳则表示有调试器。干脆NOP掉!
77E73114 A1 B473EB77 mov eax,dword ptr ds:[77EB73B4]
77E73119 3BC6 cmp eax,esi
77E7311B 74 15 je short 77E73132
77E7311D 57 push edi
77E7311E FFD0 call eax////F7进入!
77E73120 83F8 01 cmp eax,1
77E73123 0F84 E9030000 je 77E73512 ; 77E73512
把77E7310E处的跳转NOP掉后,在77E7311E处用F7跟进。进入后来到:
004012F0 . /EB 44 jmp short 00401336 ////来到这里。
004012F2 > |0F85 D6000000 jnz 004013CE ; 004013CE
004012F8 . |EB 2F jmp short 00401329 ; 00401329
004012FA |9A db 9A
004012FB > |EB 02 jmp short 004012FF ; 004012FF
004012FD |9A db 9A
用F7跟踪几步就可以看到:
004014A8 > \8A0A mov cl,byte ptr ds:[edx]
004014AA .^ E9 3EFFFFFF jmp 004013ED ; 004013ED
004014AF 3B db 3B ; CHAR ';'
004014B0 > 8AC8 mov cl,al
004014B2 .^ E9 79FFFFFF jmp 00401430 ; 00401430
004014B7 3B db 3B ; CHAR ';'
004014B8 > 803A CC cmp byte ptr ds:[edx],0CC////edx=40164B,[edx]=CC,比较后跳!
004014BB .^ E9 32FEFFFF jmp 004012F2 ; 004012F2
004014C0 3B db 3B ; CHAR ';'
004014C1 > 80C1 11 add cl,11
004014C4 .^ E9 32FEFFFF jmp 004012FB ; 004012FB
004014C9 3B db 3B ; CHAR ';'
看看40164B处的数据:
00401649 8B db 8B
0040164A F9 db F9
0040164B CC int3
0040164C 9D db 9D
0040164D 42 db 42 ; CHAR 'B'
0040164E 48 db 48 ; CHAR 'H'
跳到:
004012F2 > /0F85 D6000000 jnz 004013CE////跳到这里!
004012F8 . |EB 2F jmp short 00401329 ; 00401329
004012FA |9A db 9A
004012FB > |EB 02 jmp short 004012FF ; 004012FF
004012FD |9A db 9A
如果40164B处不是CC,则跳,然后会来到:
004014A8 > \8A0A mov cl,byte ptr ds:[edx]/////跳到这里。此时edx=40164B.
004014AA .^ E9 3EFFFFFF jmp 004013ED
004014AF 3B db 3B
如果40164B处是CC,则不跳,然后会来到:
00401329 > \42 inc edx ////跳到这里。
0040132A . E9 81000000 jmp 004013B0 ; 004013B0
0040132F 3B db 3B ; CHAR ';'
00401330 > E9 EF000000 jmp 00401424 ; 00401424
经过几个跳转后也会来到:
004014A8 > \8A0A mov cl,byte ptr ds:[edx]////来到这里。此时edx=40164C.
004014AA .^ E9 3EFFFFFF jmp 004013ED ; 004013ED
004014AF 3B db 3B ; CHAR ';'
004014B0 > 8AC8 mov cl,al
但004012F2处跳与不跳,在004014A8处的[edx]中的值是不一样的,分别是CC和9D。而这个数据通过变换以后计算出堆栈数据,所以是不能轻易NOP掉的。
现在搜索所有命令“push 404020”。为什么搜索这个命令,因为404020处保存着“注册成功”的字符串:)
搜索到:
0040228F . 44 inc esp
00402290 . 3BD9 cmp ebx,ecx////这里下断!
00402292 . 75 17 jnz short 004022AB ; 004022AB
00402294 . 6A 00 push 0
00402296 . 6A 00 push 0
00402298 . 68 20404000 push 404020////找到这里!
0040229D . 8BCF mov ecx,edi
0040229F . E8 14040000 call 004026B8 ; <jmp.&MFC42.#4224>
004022A4 . 5F pop edi
004022A5 . 5E pop esi
004022A6 . 5B pop ebx
004022A7 . 8BE5 mov esp,ebp
004022A9 . 5D pop ebp
004022AA . C3 retn
在00402290处下断后,F9运行程序会出现一个INT3异常,SHIFT+F9通过,接着OD会有好多“可疑中断点”的提示,因为程序在数据区中添加了好多CC在里面。全部点“否”。最后会断在00402290处。
断下后00402290处就是真假注册比较,ECX中保存的是验证码“12345678”倒置后的十六进制值,EBX中保存的“E9471”,换算成十进制后是“955505”。所以用户名“aman”的注册码是“505559”。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)