偶然看到了Ryosuke前辈关于happytown第九个crackme的破解一文(原文链接http://bbs.pediy.com/showthread.php?threadid=21531 ),现在我补上自己的一点研究心得,希望对大家有所帮助。
第一步去壳。od载入后按十几下F7,直到出现如下代码:
00459D2C 55 DB 55
00459D2D 8B DB 8B
00459D2E EC DB EC
00459D2F 83 DB 83
00459D30 C4 DB C4
00459D31 F0 DB F0
00459D32 B8 DB B8
00459D33 . 34 9B XOR AL,9B
00459D35 . 45 INC EBP
然后右键第一行选“分析”-->“从模块删除分析”,代码变为下面的样子:
00459D2C 55 PUSH EBP
00459D2D 8BEC MOV EBP,ESP
00459D2F 83C4 F0 ADD ESP,-10
00459D32 B8 349B4500 MOV EAX,CrackMe_.00459B34
00459D37 E8 0CC1FAFF CALL CrackMe_.00405E48
00459D3C A1 ECC04500 MOV EAX,DWORD PTR DS:[45C0EC]
00459D41 8B00 MOV EAX,DWORD PTR DS:[EAX]
接着用OD自带的ollydump就行了,查壳显示Borland Delphi 6.0 - 7.0
第二步去anti-debug。由于脱壳后的程序运行不弹出窗口,猜想可能直接退出了。
试着下断点TerminateProcess,这时运行脱壳后的程序会立刻断下,堆栈中显示为:
0012FCA0 004589C9 /CALL 到 TerminateProcess 来自 004589C4
0012FCA4 000000A0 |hProcess = 000000A0 (窗口)
0012FCA8 FFFFFFFF \ExitCode = FFFFFFFF (-1.)
0012FCAC 0012FE24 指向下一个 SEH 记录的指针
我们根据来自 004589C4这句,跳过去看看,发现代码如下:
004589A4 |. 50 |PUSH EAX ; /pProcessID
004589A5 |. 8B45 FC |MOV EAX,DWORD PTR SS:[EBP-4] ; |
004589A8 |. 50 |PUSH EAX ; |hWnd
004589A9 |. E8 72DDFAFF |CALL <JMP.&user32.GetWindowThreadProces>; \GetWindowThreadProcessId
004589AE |. 837D C8 00 |CMP DWORD PTR SS:[EBP-38],0
004589B2 |. 74 15 |JE SHORT CrackMe_.004589C9
004589B4 |. 6A FF |PUSH -1 ; /ExitCode = FFFFFFFF (-1.)
004589B6 |. 8B45 C8 |MOV EAX,DWORD PTR SS:[EBP-38] ; |
004589B9 |. 50 |PUSH EAX ; |/ProcessId
004589BA |. 6A 00 |PUSH 0 ; ||Inheritable = FALSE
004589BC |. 6A 01 |PUSH 1 ; ||Access = TERMINATE
004589BE |. E8 FDD7FAFF |CALL <JMP.&kernel32.OpenProcess> ; |\OpenProcess
004589C3 |. 50 |PUSH EAX ; |hProcess
004589C4 |. E8 47D8FAFF |CALL <JMP.&kernel32.TerminateProcess> ; \TerminateProcess
这里就是检测到OD进程并结束的地方。于是再往上看来到:
004586E4 /$ 55 PUSH EBP
004586E5 |. 8BEC MOV EBP,ESP
004586E7 |. B9 29000000 MOV ECX,29
004586EC |> 6A 00 /PUSH 0
004586EE |. 6A 00 |PUSH 0
004586F0 |. 49 |DEC ECX
004586F1 |.^ 75 F9 \JNZ SHORT CrackMe_.004586EC
004586F3 |. 51 PUSH ECX
004586F4 |. 33C0 XOR EAX,EAX
004586F6 |. 55 PUSH EBP
选中第一行004586E4 PUSH EBP,在提示窗口显示“局部调用来自 00459705”,那么再到那去看看,原来是这样:
004596EC /. 55 PUSH EBP
004596ED |. 8BEC MOV EBP,ESP
004596EF |. 83C4 F0 ADD ESP,-10
004596F2 |. 8955 F0 MOV DWORD PTR SS:[EBP-10],EDX
004596F5 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX
004596F8 |. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4]
004596FB |. E8 C420FEFF CALL CrackMe_.0043B7C4
00459700 |. A3 F4DF4500 MOV DWORD PTR DS:[45DFF4],EAX
00459705 |. E8 DAEFFFFF CALL CrackMe_.004586E4 // 这里就是结束OD的call必须nop掉
0045970A |. E8 51F8FFFF CALL CrackMe_.00458F60
0045970F |. E8 5CE9FFFF CALL CrackMe_.00458070 // 计算文件的校验值
00459714 |. 8945 F4 MOV DWORD PTR SS:[EBP-C],EAX //[EBP-C]= 当前文件的校验值
00459717 |. A1 E0DF4500 MOV EAX,DWORD PTR DS:[45DFE0] //注意45DFE0这个内存地址(exe文件偏移144处的dword取出来放这里)
0045971C |. 3B45 F4 CMP EAX,DWORD PTR SS:[EBP-C] //判断文件是否修改过
0045971F |. 74 05 JE SHORT sss.00459726 //相等文件未修改过继续运行 可以修改为JNZ SHORT 00459726
00459721 |. E8 CEA7FAFF CALL sss.00403EF4 //修改过就结束
00459726 |> 68 7C974500 PUSH sss.0045977C ; /ResourceType = "WAV"
0045972B |. 68 80974500 PUSH sss.00459780 ; |ResourceName = "BackSound"
00459730 |. A1 F0DF4500 MOV EAX,DWORD PTR DS:[45DFF0] ; |
00459735 |. 50 PUSH EAX ; |hModule => NULL
00459736 |. E8 3DC9FAFF CALL <JMP.&kernel32.FindResourceA> ; \FindResourceA
在脱壳后的程序中把 CALL 004586E4一行nop掉保存后运行发现仍然没有反应。但是运行到刚nop掉那行结束时是没有问题的,
那就从这里开始继续往后看,发现0045971F处不跳就结束了。显然是比较0045970F 处 CALL 00458070 的
返回值eax与DWORD PTR DS:[45DFE0]是否相等。那么CALL 00458070应该就是anti call了。
进去看一下发现:
0045809B |. 8945 E0 MOV DWORD PTR SS:[EBP-20],EAX
0045809E |. 6A 00 PUSH 0 ; /hTemplateFile = NULL
004580A0 |. 6A 20 PUSH 20 ; |Attributes = ARCHIVE
004580A2 |. 6A 03 PUSH 3 ; |Mode = OPEN_EXISTING
004580A4 |. 6A 00 PUSH 0 ; |pSecurity = NULL
004580A6 |. 6A 01 PUSH 1 ; |ShareMode = FILE_SHARE_READ
004580A8 |. 68 00000080 PUSH 80000000 ; |Access = GENERIC_READ
004580AD |. 8B45 E0 MOV EAX,DWORD PTR SS:[EBP-20] ; |
004580B0 |. 50 PUSH EAX ; |FileName
004580B1 |. E8 92DFFAFF CALL <JMP.&kernel32.CreateFileA> ; \CreateFileA
004580B6 |. 8945 EC MOV DWORD PTR SS:[EBP-14],EAX
004580B9 |. 6A 00 PUSH 0 ; /pFileSizeHigh = NULL
004580BB |. 8B45 EC MOV EAX,DWORD PTR SS:[EBP-14] ; |
004580BE |. 50 PUSH EAX ; |hFile
004580BF |. E8 0CE0FAFF CALL <JMP.&kernel32.GetFileSize> ; \GetFileSize
..............
00458101 |. B9 3C000000 MOV ECX,3C
00458106 |. 034D FC ADD ECX,DWORD PTR SS:[EBP-4]
00458109 |. 8B09 MOV ECX,DWORD PTR DS:[ECX]
0045810B |. 83C1 44 ADD ECX,44
0045810E |. 034D FC ADD ECX,DWORD PTR SS:[EBP-4]
00458111 |. 8B01 MOV EAX,DWORD PTR DS:[ECX]
00458113 |. A3 E0DF4500 MOV DWORD PTR DS:[45DFE0],EAX //取出exe文件偏移144h处的dword 用于文件校验
............
00458136 |. 8945 CC MOV DWORD PTR SS:[EBP-34],EAX
00458139 |. C745 D0 01000>MOV DWORD PTR SS:[EBP-30],1
00458140 |> 8B55 F8 /MOV EDX,DWORD PTR SS:[EBP-8]
00458143 |. 8A02 |MOV AL,BYTE PTR DS:[EDX]
00458145 |. 0FB6C0 |MOVZX EAX,AL
00458148 |. 8945 F4 |MOV DWORD PTR SS:[EBP-C],EAX
0045814B |. 42 |INC EDX
0045814C |. 8955 F8 |MOV DWORD PTR SS:[EBP-8],EDX
0045814F |. A1 2CBE4500 |MOV EAX,DWORD PTR DS:[45BE2C]
00458154 |. 3345 F4 |XOR EAX,DWORD PTR SS:[EBP-C]
00458157 |. 25 FF000000 |AND EAX,0FF
0045815C |. 8945 D4 |MOV DWORD PTR SS:[EBP-2C],EAX
0045815F |. A1 2CBE4500 |MOV EAX,DWORD PTR DS:[45BE2C]
00458164 |. C1E8 08 |SHR EAX,8
00458167 |. 25 FFFFFF00 |AND EAX,0FFFFFF
0045816C |. 8B55 D4 |MOV EDX,DWORD PTR SS:[EBP-2C]
0045816F |. 330495 E0DB45>|XOR EAX,DWORD PTR DS:[EDX*4+45DBE0]
00458176 |. A3 2CBE4500 |MOV DWORD PTR DS:[45BE2C],EAX
0045817B |. FF45 D0 |INC DWORD PTR SS:[EBP-30]
0045817E |. FF4D CC |DEC DWORD PTR SS:[EBP-34]
00458181 |.^ 75 BD \JNZ SHORT sss.00458140
00458183 |> \F715 2CBE4500 NOT DWORD PTR DS:[45BE2C]
00458189 |. A1 2CBE4500 MOV EAX,DWORD PTR DS:[45BE2C] //保存计算好的当前文件的校验值
总结一下: 1) 00459705 CALL 004586E4 // 这里就是结束OD的call必须nop掉
通过枚举得到窗口句柄-->进程ID-->进程句柄,再对特定的程序用TerminateProcess结束掉。
用到如下函数:
HWND GetWindow( //得到窗口句柄
HWND hWnd, // handle of original window
UINT uCmd // relationship flag
);
DWORD GetWindowThreadProcessId(//得到对应的ProcessId
HWND hWnd, // handle of window
LPDWORD lpdwProcessId // address of variable for process identifier
);
HANDLE OpenProcess( //the return value is the handle to the process
DWORD dwDesiredAccess, // access flag
BOOL bInheritHandle, // handle inheritance flag
DWORD dwProcessId // process identifier
);
BOOL TerminateProcess(
HANDLE hProcess, // handle to the process
UINT uExitCode // exit code for the process
);
2)去除文件校验
0045970F |. E8 5CE9FFFF CALL CrackMe_.00458070 // 计算文件的校验值
00459714 |. 8945 F4 MOV DWORD PTR SS:[EBP-C],EAX //[EBP-C]= 当前文件的校验值
00459717 |. A1 E0DF4500 MOV EAX,DWORD PTR DS:[45DFE0] //exe文件偏移144处的dword取出来放这里
0045971C |. 3B45 F4 CMP EAX,DWORD PTR SS:[EBP-C] //判断文件是否修改过
0045971F |. 74 05 JE SHORT sss.00459726 //未修改过跳走 修改为JNZ SHORT 00459726
00459721 |. E8 CEA7FAFF CALL sss.00403EF4 //修改过就结束
第三步就是找算法call。运行去除anti的程序,弹出窗口后随便输入name和serial,
然后在内存中搜到只有一处,然后在内存中的serial后下好内存断点。在刚才的serial
的后面接着输入,立即断下。
显然是文本输入框的OnChange事件。这时用dede和IDA查找都能发现txtSerialChange事件,他的位置就是004597EC。
004597EC /. 55 push ebp
004597ED |. 8BEC mov ebp, esp
004597EF |. B9 0B000000 mov ecx, 0B
004597F4 |> 6A 00 /push 0
004597F6 |. 6A 00 |push 0
004597F8 |. 49 |dec ecx
004597F9 |.^ 75 F9 \jnz short 004597F4
004597FB |. 53 push ebx
004597FC |. 56 push esi
004597FD |. 8955 C4 mov [ebp-3C], edx
00459800 |. 8945 FC mov [ebp-4], eax
00459803 |. 33C0 xor eax, eax
00459805 |. 55 push ebp
00459806 |. 68 6E9A4500 push 00459A6E
0045980B |. 64:FF30 push dword ptr fs:[eax]
0045980E |. 64:8920 mov fs:[eax], esp
00459811 |. 8D55 C0 lea edx, [ebp-40]
00459814 |. 8B45 FC mov eax, [ebp-4]
00459817 |. 8B80 04030000 mov eax, [eax+304]
0045981D |. E8 C6B7FDFF call 00434FE8 // GetText
00459822 |. 8B45 C0 mov eax, [ebp-40] // serail
我就补充到这,剩下的内容Ryosuke前辈讲得已经非常详细了。最后写了个注册机,
貌似happytown的crackme。最后感谢happytown和Ryosuke前辈,只有站在巨人的肩上,我们才能看得更远!
天易love
2010-03-26
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
上传的附件: