160个CrackMe之108 mfc程序 寻找按钮事件,代码还原(上)

 

·前言 

虽然网上已经有帖子写160个CrackMe,我个人还是以正向的思路来逆向一部分的crackme,还有一些 代码还原的小技巧,挑选出这160个CrackMe中由c,c++,汇编编写的程序来来写。vb,delphi现在用 的少些了就不拿来写了。 

·思路分析 

先判断该程序是啥语言写的用工具查看一下 

是vc6的mfc编写的现在先运行下程序 寻

 

找按钮Check的按钮事件 该程序是mfc编写的,我自己写个例子,来找按钮事件 vs2019创建mfc工程后增加个按钮事件

双击Button1 写一个弹出对话框的代码

mfc是怎么知道这个按钮事件的呢,实际是通过映射消息,代码为

  这些都是宏定义,按下F12进去看 

·把这些宏代码展开为 

PTM_WARNING_DISABLE \ const AFX_MSGMAP* theClass::GetMessageMap() const \  	{ return GetThisMessageMap(); } \ const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \ 
{ \ 
 	typedef theClass ThisClass; 	 	 	 	 	 	   \  	typedef baseClass TheBaseClass;  	 	 	 	   \ 
 	__pragma(warning(push))  	 	 	 	 	 	   \ 
 	__pragma(warning(disable: 4640))  \ 
 	static const AFX_MSGMAP_ENTRY _messageEntries[] =  \ 
 	{ 
 
            ON_WM_SYSCOMMAND() 
            ON_WM_PAINT() 
            ON_WM_QUERYDRAGICON()             ON_BN_CLICKED(IDC_BUTTON1, &CMFCApplication3Dlg::OnBnClickedButton1) 
 
 
 
}; \ 	    {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \ 
 	__pragma(warning(pop)) \
 	static const AFX_MSGMAP messageMap = \ 
 	{ &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \ 
 	return &messageMap; \ 
    } 	 	 	 	 	 	 	 	  \ 
PTM_WARNING_RESTORE 

· __pragma和#pragma之间有什么区别

 

_Pragma操作符具有与#pragma指令相同的功能 
#pragma本身就是一个预处理器指令;它不能在#define指令中使用。 
因此,这就是__pragma存在的原因:它提供了一种方法,可以从扩展使用它的宏的任何地方发出编译指示。 
 
这是一个非标准的编译器扩展(MSVC,Intel和一些C编译器在不同程度上支持它). 
 
#define PTM_WARNING_DISABLE \ 
 	__pragma(warning( push )) \
 	__pragma(warning( disable : 4867 )) 
 
#define PTM_WARNING_RESTORE \ 
 	__pragma(warning( pop )) 
 
__pragma(warning(push)) 等于 
#pragma warning(push)是保存当前的编译器警告状态;  
 
__pragma(warning(pop)) 等于 
#pragma warning(pop)是恢复原先的警告状态。  
 
__pragma(warning( disable : 4867 )) 
warning disable 作用:让编译器忽略指定编号的警告,跳过警告直接运行程序,可用来忽略一部分不重要的警告参考资料 
https://blog.csdn.net/qq_45481381/article/details/110875019 

·去掉不要的宏定义与代码

const AFX_MSGMAP* theClass::GetMessageMap() const   	{ return GetThisMessageMap(); } \ const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap()  
{  	 	 	 	    
 	static const AFX_MSGMAP_ENTRY _messageEntries[] =   
 	{ 
 	    ON_BN_CLICKED(IDC_BUTTON1, &CMFCApplication3Dlg::OnBnClickedButton1)  
            {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }  
};  	static const AFX_MSGMAP messageMap =  
 	{ &TheBaseClass::GetThisMessageMap, &_messageEntries[0] };   	return &messageMap;  
    }  

现在替换ON_BN_CLICKED宏

#define ON_BN_CLICKED(id, memberFxn) \ 
 	ON_CONTROL(BN_CLICKED, id, memberFxn) 
 
ON_BN_CLICKED(IDC_BUTTON1, &CMFCApplication3Dlg::OnBnClickedButton1) 等于 
ON_CONTROL(BN_CLICKED, IDC_BUTTON1, CMFCApplication3Dlg::OnBnClickedButton1) 
 
进一步替换 
#define ON_CONTROL(wNotifyCode, id, memberFxn) \ 
 	{ WM_COMMAND, (WORD)wNotifyCode, (WORD)id, (WORD)id, AfxSigCmd_v, \
 	 	(static_cast< AFX_PMSG > (memberFxn)) }, 
 
ON_CONTROL(BN_CLICKED, IDC_BUTTON1, CMFCApplication3Dlg::OnBnClickedButton1)等于 
{ WM_COMMAND, (WORD)BN_CLICKED, (WORD)IDC_BUTTON1, (WORD)IDC_BUTTON1, AfxSigCmd_v, \ 
 	 	(static_cast< AFX_PMSG > (CMFCApplication3Dlg::OnBnClickedButton1)) }, 

·替换后的代码 

const AFX_MSGMAP* theClass::GetMessageMap() const   	{ return GetThisMessageMap(); } \ const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap()  
{  	 	 	    
        static const AFX_MSGMAP_ENTRY _messageEntries[] =   
 	{ 
 
            { WM_COMMAND, (WORD)BN_CLICKED, (WORD)IDC_BUTTON1, (WORD)IDC_BUTTON1, AfxSigCmd_v, 
 	 	(static_cast< AFX_PMSG > (CMFCApplication3Dlg::OnBnClickedButton1)) }, 
            {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }  
        };   	static const AFX_MSGMAP messageMap =  
 	{ &TheBaseClass::GetThisMessageMap, &_messageEntries[0] };   	return &messageMap;  
    }  

·OnBnClickedButton1地址写在了_messageEntries数组这个结构体中,现在看看这个结构体定义

struct AFX_MSGMAP_ENTRY 
{ 
 	UINT nMessage;   // windows message 
 	UINT nCode;      // control code or WM_NOTIFY code 
 	UINT nID;        // control ID (or 0 for windows messages) 
 	UINT nLastID;    // used for entries specifying a range of control id's  	UINT_PTR nSig;       // signature type (action) or pointer to message # 
 	AFX_PMSG pfn;    // routine to call (or special value) 
}; 

·那我的按钮事件就为

AFX_MSGMAP_ENTRY btn; btn.nMessage=WM_COMMAND; btn.nCode=BN_CLICKED; btn.nID=IDC_BUTTON1; btn.nLastID=IDC_BUTTON1; btn.nSig=AfxSigCmd_v; 
btn.pfn=CMFCApplication3Dlg::OnBnClickedButton1; 
 
查看WM_COMMAND,BN_CLICKED,IDC_BUTTON1宏定义的值,btn结构体的值应该为 btn.nMessage=0x0111; btn.nCode=0; btn.nID=1000; btn.nLastID=1000; btn.nSig=AfxSigCmd_v; 
btn.pfn=CMFCApplication3Dlg::OnBnClickedButton1; 
 
因为static const AFX_MSGMAP_ENTRY _messageEntries[]是静态数组,会在exe的.data段 
那我直接在内存搜索结构体btn的二进制就行了 
 
btn的内存呈现形式为 
nMessage,nCode,nID,nLastID,nSig,pfn 
可以不用管nSig,pfn的值,只用搜索前四项就够了,等价替换为 
0x0111,  0,   1000,1000,nSig,pfn       等价替换为 
11 01 00 00, 00 00 00 00, e8 03 00 00, e8 03 00 00 ,nSig,pfn 等价替换为 
11 01 00 00 00 00 00 00 e8 03 00 00 e8 03 00 00  

·用x64dbg打开该程序,内存搜索 

·一定要用鼠标左键点击 开始的00280000地址,这样搜索就会从00280000开始查找,如果你从00c94000开始的话,00280000至00c94000这段内存就不会搜索 了 

跟过去看看 

00CA07FB就是OnBnClickedButton1函数地址