160個CrackMe之108 mfc程式 尋找按鈕事件,程式碼還原(上)

極安御信發表於2022-03-10

·前言 

雖然網上已經有帖子寫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函式地址


相關文章