[软件名称]: CRECKME 0 CRECKME 0 程序和分析源码.zip
[应用平台]:Win9X/NT/2000/XP
[软件大小]:8K
[破解声明]:为了检验自己的逆向破解能力,请求斑竹给个邀请码,鼓励我继续写下去。顺便感谢一下党,感谢国家,感谢我们家的曾曾。
[破解工具]:PEiD,IDA 5.5,PE Explore
[备注] 破解软件来源于看雪论坛中的帖子 (本人原创CRACKME系列 难度由0--9,看你属于哪一级)
[作者]:INightElf
[破解日期]:2012-08-06
前言:
之前一段时间都在破解MASM32 / TASM32之类的软件,由于比较简单,很快就无法引起我的兴趣,在看雪论坛闲逛,发现有网友发(原创CRACKME系列 难度由0--9,看你属于哪一级)帖子,该破解软件由MFC 编译的,刚好用来检验本人掌握的MFC原理是否扎实,也来检验自己的破解水平。
言归正传!下面开始我们的破解之旅
1.查壳,用PEid 检测,编译器为编译器Microsoft Visual C++ 7.0 Method2 [调试],无壳。
2.用PE Explore 查看软件的资源,如图。可见该软件有三个对话框,其中编号为129的对话框为主要窗口, 验证输入的Name 和Serial是否正确。请记下Name控件的ID 为1000,Serial 控件的ID 为1001,确定按钮控件ID为1,取消按钮ID 为2。当输入的Name和Serial 正确,则弹出编号为102的对话框。编号为100则是关于对话框.请记下上面描述的ID号。
这是为了后面的反汇编代码中处理按钮消息做准备,请注意这里的ID 号是10进制
3.用IDA 反汇编CrackMe0,得到CrackMe0反汇编清单,熟悉MFC编程的人都知道,一般程序都会把初始化放到CXXXApp::InitInstance和CXXXApp::IInitApplication 这两个函数,而CXXXApp则继承了CWinApp,并在CXXXApp::InitInstance 中调用父类的CWinApp::InitInstance,在CXXXApp:: IInitApplication 中调用父类的CWinApp:: IInitApplication。所以逆向的一步,则是找到CWinApp::InitInstance和CWinApp:: IInitApplication这两个函数,然后通过交叉引用注释,查找到CXXXApp::InitInstance和CWinApp:: IInitApplication函数所在地址。
由于IDA强大的反汇编库函数,只要对Function Name 列表进行排序,很容易就找到了,在CrackMe0函数中,程序的初始化放到CXXXApp::InitInstance中。导航到交叉引用函数sub_401090,该函数就是CXXXApp::InitInstance。并对该函数进行重命名为CXXXWinApp::InitInstance。
4.在CXXXWinApp::InitInstance函数中仔细观察,看到了地址0x004010EE 调用了CDialog::DoModal(void),表明在该地址前面创建了对话框。在text:004010DA行有一个Call 函数指令,表明了该函数创建了对话框
.text:004010EE call?DoModal@CDialog@@UAEHXZ ; CDialog::DoModal(void)
导航进去该函数查看。果然验证了之前的推测. .text:004014CE 地址的ID为129,根据步骤2的ID比较,可以断定创建的窗口为即将破解的主窗口, 其中CrackMeMainDialogVirtualFunctionTable为主窗口的虚函数表
.text:004014EC mov dword ptr [esi], offset CrackMeMainDialogVirtualFunctionTable ; 虚函数表格
CrackMeMainDialogVirtualFunctionTable 函数名为本人重新命名的,请注意。为什么在这里要特别强调主窗口的虚函数表呢?这是非常关键的一步。猜猜!嘻嘻。
当当当……….谜底是(为了获取确定按钮的消息处理函数) 。是不是觉得我很无聊呢。 .text:004014B0 CreateCrackMeMainDialog proc near ; CODE XREF: CXXXWinApp::InitInstance(void)+4A p
.text:004014B0
.text:004014B0 var_10 = dword ptr -10h
.text:004014B0 var_C = dword ptr -0Ch
.text:004014B0 var_4 = dword ptr -4
.text:004014B0 arg_0 = dword ptr 4
.text:004014B0
.text:004014B0 push 0FFFFFFFFh
.text:004014B2 push offset SEH_4014B0
.text:004014B7 mov eax, large fs:0
.text:004014BD push eax
.text:004014BE mov large fs:0, esp
.text:004014C5 push ecx
.text:004014C6 mov eax, [esp+10h+arg_0]
.text:004014CA push esi
.text:004014CB push eax
.text:004014CC mov esi, ecx
.text:004014CE push 129 ; hDlgTemplateId 对话框模板ID,在这里也是破解的主窗口
.text:004014D3 mov [esp+1Ch+var_10], esi
.text:004014D7 call ??0CDialog@@QAE@IPAVCWnd@@@Z ; CDialog::CDialog(uint,CWnd *)
.text:004014DC push offset unk_403940
.text:004014E1 lea ecx, [esi+74h]
.text:004014E4 mov [esp+18h+var_4], 0
.text:004014EC mov dword ptr [esi], offset CrackMeMainDialogVirtualFunctionTable ; 虚函数表格
.text:004014F2 call ds:??0?$CStringT@DV?$StrTraitMFC_DLL@DV?$ChTraitsCRT@D@ATL@@@@@ATL@@QAE@PBD@Z ; ATL::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char>>>::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char>>>(char const *)
.text:004014F8 mov ecx, [esp+14h+var_C]
.text:004014FC mov dword ptr [esi+78h], 0
.text:00401503 mov eax, esi
.text:00401505 pop esi
.text:00401506 mov large fs:0, ecx
.text:0040150D add esp, 10h
.text:00401510 retn 4
.text:00401510 CreateCrackMeMainDialog endp 5.导航到CrackMeMainDialogVirtualFunctionTable 虚函数表格。
在地址
.rdata:0040380C dd offset ?GetTypeLib@CCmdTarget@@UAEJKPAPAUITypeLib@@@Z ; CCmdTarget::GetTypeLib(ulong,ITypeLib * *)
和地址
.rdata:00403814 dd offset ?GetCommandMap@CCmdTarget@@MBEPBUAFX_OLECMDMAP@@XZ ; CCmdTarget::GetCommandMap(void)
之间的函数就是主窗口消息栈区函数。请注意该函数已经被本人重新命名过。
.rdata:00403810 dd offset j_?GetMessageMap@CrackMeMainDialog@@MBEPBUAFX_MSGMAP@@XZ ; CrackMeMainDialog::GetMessageMap(void)
6.导航到CrackMeMainDialog::GetMessageMap(void) 函数,在该函数里面的地址.text:00401545 设置断点,并执行程序。
text:00401540 ; protected: virtual struct AFX_MSGMAP const * __thiscall CrackMeMainDialog::GetMessageMap(void)const
.text:00401540 j_?GetMessageMap@CrackMeMainDialog@@MBEPBUAFX_MSGMAP@@XZ proc near
.text:00401540 mov eax, offset off_403780
.text:00401545 retn
.text:00401545 j_?GetMessageMap@CrackMeMainDialog@@MBEPBUAFX_MSGMAP@@XZ endp
则程序中断在地址text:00401545,查看此时的EAX寄存器值,[EAX+4] == 0x00403784 的值就是MFC 对话框的消息处理函数列表。
在栈区Jump到地址0x00403784
在栈区Jump到地址0x00403788
在这里必须说明以下MFC消息处理函数的结构,定义如下
#define ON_CONTROL(wNotifyCode, id, memberFxn) \
{ WM_COMMAND, (WORD)wNotifyCode, (WORD)id, (WORD)id, AfxSigCmd_v, \
(static_cast< AFX_PMSG > (memberFxn)) },
#define WM_COMMAND 0x0111 发送命令消息,一般控件按钮消息都是通过这个命令发送的。
Id 就是按钮的ID号, memberFxn 就是按钮的消息处理函数。
从上图和步骤二可以知道,确定按钮的ID号为1,所以地址0x004015A0 为确定按钮的消息处理函数。本人把该函数重新命名为BtnOK
7. 十万长征终于完成了一半,导航到函数地址,剩下的工作就是分析算法了。
.text:004015A0 ; 确定按钮事件
.text:004015A0
.text:004015A0 BtnOK proc near
.text:004015A0
.text:004015A0 ; FUNCTION CHUNK AT .text:004015D5 SIZE 00000034 BYTES
.text:004015A0
.text:004015A0 push esi
.text:004015A1 push edi
.text:004015A2 push 1
.text:004015A4 mov esi, ecx
.text:004015A6 call ?UpdateData@CWnd@@QAEHH@Z ; CWnd::UpdateData(int)
.text:004015AB lea edi, [esi+74h] ; 获取Name 字段的内容
.text:004015AE mov ecx, edi
.text:004015B0 call ds:?GetLength@?$CSimpleStringT@D$00@ATL@@QBEHXZ ; ATL::CSimpleStringT<char,1>::GetLength(void)
.text:004015B6 cmp eax, 6 ; name 字段的字符串的长度必须大于等于6
.text:004015B9 jge short loc_4015D5
.text:004015BB cmp dword ptr [esi+78h], 186A0h
.text:004015C2 jge short loc_4015D5
.text:004015C4 push 0 ; unsigned int
.text:004015C6 push 0 ; unsigned int
.text:004015C8 push offset aNameOrSerialIs ; "Name or Serial is too short!"
.text:004015CD call ?AfxMessageBox@@YGHPBDII@Z ; AfxMessageBox(char const *,uint,uint)
.text:004015D2
.text:004015D2 loc_4015D2:
.text:004015D2 pop edi
.text:004015D3 pop esi
.text:004015D4 retn
.text:004015D4 BtnOK endp
.text:004015D4
.text:004015D5 ; ---------------------------------------------------------------------------
.text:004015D5 ; START OF FUNCTION CHUNK FOR BtnOK
.text:004015D5
.text:004015D5 loc_4015D5: ; Name 字段的内容与"IndolentAfternoon" 字符串比较,
.text:004015D5 push offset aIndolentaftern ; 一致则继续比较Serial,从这里就说明了Name 必须=='IndolentAfternoon'
.text:004015DA mov ecx, edi
.text:004015DC call ds:?Compare@?$CStringT@DV?$StrTraitMFC_DLL@DV?$ChTraitsCRT@D@ATL@@@@@ATL@@QBEHPBD@Z ; ATL::CStringT<char,StrTraitMFC_DLL<char,ATL::ChTraitsCRT<char>>>::Compare(char const *)
.text:004015E2 test eax, eax
.text:004015E4 jnz short loc_4015D2
.text:004015E6 cmp dword ptr [esi+78h], 5687255 ; 获取Serial 字符串与5687255 常量比较,一致则说明了输入的Serial
.text:004015E6 ; 是正确的,并给出成功的提示符,证明了 Serial ==5687255
.text:004015ED jnz short loc_4015D2
.text:004015EF push 0 ; unsigned int
.text:004015F1 push 0 ; unsigned int
.text:004015F3 push offset aCongratulation ; "Congratulation! Correct Serial Num,do n"...
.text:004015F8 call ?AfxMessageBox@@YGHPBDII@Z ; AfxMessageBox(char const *,uint,uint)
.text:004015FD mov eax, [esi]
.text:004015FF pop edi
.text:00401600 mov ecx, esi
.text:00401602 pop esi
.text:00401603 jmp dword ptr [eax+154h]
.text:00401603 ; END OF FUNCTION CHUNK FOR BtnOK
8.前面分析了那么多步骤只是为了获取按钮消息事件而已,感觉有点学院派的作风,其实可以用很多简单的方法来获取。但是就无法理解MFC消息处理机制了。总结前面的步骤
第1步获取: CWinApp::InitInstance函数地址
第2步获取: CXXXWinApp::InitInstance函数地址
第3步获取: 主对话框的虚函数表CrackMeMainDialogVirtualFunctionTable
第4步获取: 主对话框的CrackMeMainDialog::GetMessageMap(void)const 函数
第5步获取: 获取确定按钮事件.text:004015A0 BtnOK
很多MFC程序都可以用前面的5个步骤来获取按钮的消息,这个是本人总结出来的宝贵经验,现在无偿奉献给大家。明天把算法的步骤补上,其实该算法特别简单。但是写到现在我已经没有精力写下去了。累!
9.算法太简单了,在BtnOK函数中直接给出明文比较,通过分析可以得出 Name ==” IndolentAfternoon” ,Serial == 5687255 不需要注册机。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
上传的附件: