MFC程序開(kāi)發(fā)所謂是非常簡(jiǎn)單,但是對(duì)于我們逆向人員來(lái)說(shuō),如果想要逆向MFC程序,那么我們就必須了解它背后的機(jī)制,這樣我們才能夠清晰地逆向出MFC程序,今天這篇文章就來(lái)帶領(lǐng)大家了解MFC的第一大機(jī)制:程序啟動(dòng)機(jī)制:
首先,我們創(chuàng)建一個(gè)單文檔架構(gòu)程序,我們來(lái)觀察一下:
這里我創(chuàng)建的項(xiàng)目名稱(chēng)為:MFCApplication1
我們發(fā)現(xiàn)一共有三個(gè)類(lèi):(這里有一些是MFC自動(dòng)為我們寫(xiě)好的類(lèi),類(lèi)名稱(chēng)通常為項(xiàng)目名稱(chēng),繼承了MFC庫(kù)中的一些類(lèi),我們只看MFC庫(kù)中的類(lèi))我們發(fā)現(xiàn)大致三個(gè)類(lèi):
CFrame類(lèi):這個(gè)類(lèi)是框架窗口類(lèi),封裝了框架窗口的操作
CWinApp類(lèi):這個(gè)類(lèi)是應(yīng)用程序類(lèi),封裝了流程的操作
CDocument類(lèi):這個(gè)類(lèi)是文檔類(lèi),封裝了數(shù)據(jù)的處理,例如存儲(chǔ),轉(zhuǎn)換等
CView類(lèi):這個(gè)類(lèi)是視圖類(lèi),封裝了視圖窗口的操作
我們主要講解的是CFrame類(lèi)和CWinApp類(lèi)
這里我創(chuàng)建好的項(xiàng)目,我們先來(lái)看看部分源代碼:
CMFCApplication1.h:
// MFCApplication1.h: MFCApplication1 應(yīng)用程序的主頭文件
//
#pragma once
#ifndef __AFXWIN_H__
#error "include 'pch.h' before including this file for PCH"
#endif
#include "resource.h" // 主符號(hào)
// CMFCApplication1App:
// 有關(guān)此類(lèi)的實(shí)現(xiàn),請(qǐng)參閱 MFCApplication1.cpp
//
class CMFCApplication1App : public CWinApp
{
public:
CMFCApplication1App() noexcept;
// 重寫(xiě)
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
// 實(shí)現(xiàn)
afx_msg void OnAppAbout();
DECLARE_MESSAGE_MAP()
};
extern CMFCApplication1App theApp;
MianFrm.h:
// MainFrm.h: CMainFrame 類(lèi)的接口
//
#pragma once
class CMainFrame : public CFrameWnd
{
protected: // 僅從序列化創(chuàng)建
CMainFrame() noexcept;
DECLARE_DYNCREATE(CMainFrame)
// 特性
public:
// 操作
public:
// 重寫(xiě)
public:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
// 實(shí)現(xiàn)
public:
virtual ~CMainFrame();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected: // 控件條嵌入成員
CToolBar m_wndToolBar;
CStatusBar m_wndStatusBar;
// 生成的消息映射函數(shù)
protected:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
DECLARE_MESSAGE_MAP()
};
看完這部分源碼之后,我們來(lái)自己實(shí)現(xiàn),不用MFC自動(dòng)幫我們實(shí)現(xiàn)的類(lèi),來(lái)加深我們的理解:
我們創(chuàng)建一個(gè)控制臺(tái)應(yīng)用,修改項(xiàng)目屬性:
- 常規(guī):MFC的使用:在靜態(tài)庫(kù)中使用MFC
- 連接器->系統(tǒng)->子系統(tǒng):窗口
接下來(lái),我們刪除掉main.cpp中的所有代碼,我們來(lái)自己仿照MFC寫(xiě)一個(gè)窗口:
#include <afxwin.h>
class CMyFrameWnd:public CFrameWnd{
public:
}
class CMyApp:public CWinApp{
public:
CMyApp(){};
//必須要重寫(xiě)虛函數(shù):
virtual BOOL InitInstance(){
CMyFrameWnd* pFrame = new CMyFrameWnd;
pFrame->Create(NULL,L"FirstMFC");
m_pMainWnd = pFrame;
pFrame.ShouWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
};
CMyWinApp theApp;
這里的CMyFrameWnd由于只是一個(gè)框架類(lèi),我們不需要做任何操作,只是繼承MFC的框架類(lèi)就可以了,狗仔函數(shù)會(huì)調(diào)用CFrameWnd中寫(xiě)好的構(gòu)造函數(shù)
接下來(lái)我們創(chuàng)建一個(gè)自己的應(yīng)用程序類(lèi),繼承MFC的CWinApp類(lèi),這里我們寫(xiě)一個(gè)空的構(gòu)造函數(shù),當(dāng)構(gòu)造的時(shí)候,就會(huì)調(diào)用父類(lèi)的構(gòu)造函數(shù)了
我們?cè)谧约旱膽?yīng)用程序類(lèi)中還必須重寫(xiě)虛函數(shù)InitInstance,在這個(gè)函數(shù)中,我們創(chuàng)建窗口,顯示窗口
最后,我們還需要一個(gè)應(yīng)用程序類(lèi)全局變量theApp
當(dāng)我們寫(xiě)好這些之后,我們運(yùn)行,發(fā)現(xiàn)窗口已經(jīng)神奇的創(chuàng)建好了:
我們?cè)趧?chuàng)建Win32應(yīng)用程序的時(shí)候,需要做的幾個(gè)步驟:
- 定義窗口類(lèi),創(chuàng)建窗口
- 注冊(cè)窗口
- 刷新窗口
- 顯示窗口
但是在我們這樣實(shí)現(xiàn)了MFC之后,我們貌似少做了很多操作,幾行代碼就完成了窗口的創(chuàng)建,甚至我們連WinMain函數(shù)都沒(méi)有看見(jiàn),那么MFC的程序啟動(dòng)機(jī)制到底是怎樣的呢?我們首先就來(lái)深究一下MFC的程序運(yùn)行機(jī)制:
我們知道,在C++中,全局變量的構(gòu)造是優(yōu)先于main函數(shù)的,那么我們少做的很多操作,肯定就是在這里全局變量的構(gòu)造函數(shù)中了
我們?cè)谖覀冏约旱膽?yīng)用程序類(lèi)的構(gòu)造函數(shù)上下斷點(diǎn),我們跟到CWinApp的構(gòu)造函數(shù)中來(lái)看看:
這里我就寫(xiě)偽代碼了:
我們應(yīng)用程序類(lèi)的父類(lèi):CWinApp的構(gòu)造函數(shù):
CWinApp::CWinApp(LPCTSTR lpszAppName){
//首先判斷l(xiāng)pszAppName是否為空,做了一些操作,我們沒(méi)必要關(guān)注
//AFX_MODULE_STATE這個(gè)結(jié)構(gòu)體是MFC中的 程序模塊狀態(tài)信息結(jié)構(gòu)體
//這里是獲取當(dāng)前應(yīng)用程序模塊狀態(tài)信息
AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();
//這里是獲取當(dāng)前程序線(xiàn)程狀態(tài)信息,可以看出來(lái),當(dāng)前程序狀態(tài)信息是模塊狀態(tài)信息中的一個(gè)成員
AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;
//這里是給線(xiàn)程信息的某個(gè)成員賦值,給的是this指針,也就是theApp的地址
pThreadState->m_pCurrentWinThread = this;
//這里是給theApp的成員賦值,我們自己的應(yīng)用程序類(lèi)沒(méi)有寫(xiě)成員,這里都是CWinApp中寫(xiě)好的,我們只是繼承過(guò)來(lái)了
//獲取當(dāng)前應(yīng)用程序線(xiàn)程句柄
m_hThread = ::GetCurrentThread();
//獲取當(dāng)前應(yīng)用程序線(xiàn)程ID
m_nThreadID = ::GetCurrentThreadId();
//給當(dāng)前應(yīng)用程序模塊狀態(tài)信息的某個(gè)成員賦值
//不難看出這里保存的是當(dāng)前應(yīng)用程序類(lèi)的地址,也就是theApp的地址
pModuleState->m_pCurrentWinApp = this;
//之后就是一些給類(lèi)中的其他成員賦初值,很多都是NULL,我們不再關(guān)注
}
但是跟完構(gòu)造函數(shù)后,我們也沒(méi)有找到WinMain,更沒(méi)有找到注冊(cè)窗口等操作
既然在應(yīng)用程序類(lèi)的構(gòu)造中我們沒(méi)有找到,我們來(lái)看看接下來(lái)我們寫(xiě)的pFrame->Create(NULL, L"FirstMFC");
這一段代碼,但是這一段代碼很明顯是框架類(lèi)的方法,我們猜測(cè)很多操作就在這里完成
我們?cè)谶@一段代碼上下斷點(diǎn),斷下來(lái)之后,我們F11跟進(jìn),然后查看調(diào)用對(duì)戰(zhàn),我們就發(fā)現(xiàn)了wWinMian和AfxWInMain,由于我們寫(xiě)過(guò)win32程序,我們知道底層就是wWinMain函數(shù),我們就跟到這個(gè)函數(shù)中來(lái)看:
這里還是寫(xiě)偽代碼
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)
{
//我們發(fā)現(xiàn)在wWinMain函數(shù)中,做了轉(zhuǎn)發(fā),而且根據(jù)名稱(chēng)來(lái)看,是MFC的wWimMain函數(shù),我們跟進(jìn)去看看:
//這里注意,有些人到這里就不會(huì)往進(jìn)去跟了,我們?cè)谶@行代碼上下斷點(diǎn),然后去掉之前的斷點(diǎn),重新調(diào)試,就能跟進(jìn)去了
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow){
//這里根據(jù)名稱(chēng)來(lái)看,是獲取當(dāng)前應(yīng)用程序線(xiàn)程信息,我們跟進(jìn)去看看:
CWinThread* pThread = AfxGetThread(){
//這里跟前面的應(yīng)用程序類(lèi)的構(gòu)造函數(shù)中一樣,獲取當(dāng)前應(yīng)用程序的模塊狀態(tài)信息
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
//取出得到的模塊狀態(tài)信息中的成員->當(dāng)前線(xiàn)程
CWinThread* pThread = pState->m_pCurrentWinThread;
//返回線(xiàn)程
return pThread;
}
//這里根據(jù)名稱(chēng)來(lái)看,是獲取了當(dāng)前應(yīng)用程序的應(yīng)用程序類(lèi)的地址,我們還是跟進(jìn)去看看
CWinApp* pApp = AfxGetApp(){
//這里afx開(kāi)頭的函數(shù)
return afxCurrentWinApp{
//這里可以肯定,是返回了應(yīng)用程序類(lèi)地址
return pResult = _afxBaseModuleState.GetData();
}
}
這里這兩步跟完之后,大家有沒(méi)有發(fā)現(xiàn),這里實(shí)現(xiàn)了C++的多態(tài)?
CWinThread,是爺類(lèi)指針,指向派生類(lèi)類(lèi)對(duì)象
CWinApp是父類(lèi)的指針,指向了派生類(lèi)對(duì)象,theApp是我們自己實(shí)現(xiàn)的應(yīng)用程序類(lèi)嘛
//下一步:執(zhí)行了這個(gè)函數(shù),在源代碼中,是在if的條件中實(shí)現(xiàn)的,這里直接拉出來(lái)了
AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow){
//根據(jù)名稱(chēng),這個(gè)是初始化的函數(shù)
pApp->m_hInstance = hInstance;
hPrevInstance; // Obsolete.
pApp->m_lpCmdLine = lpCmdLine;
pApp->m_nCmdShow = nCmdShow;
pApp->SetCurrentHandles();
}
程序啟動(dòng)初始化操作 虛函數(shù)我們可以重寫(xiě),貌似是關(guān)于文檔的一些初始化操作
pApp->InitApplication()
//接下來(lái)這個(gè)函數(shù)也是在if條件中進(jìn)行的,這里直接拉出來(lái)
pThread->InitInstance(){
這是虛函數(shù),我們?cè)谖覀冏约旱念?lèi)中已經(jīng)實(shí)現(xiàn)
}
}
}
代碼跟到這里,我們已經(jīng)解決了很多前面我們提出來(lái)的問(wèn)題:WinMain函數(shù)等但是還有一個(gè)問(wèn)題:消息循環(huán)呢?我們知道,如果沒(méi)有消息循環(huán),那么這個(gè)窗口根本運(yùn)行不了,那既然我們能夠關(guān)閉窗口等操作,那肯定是已經(jīng)寫(xiě)好了消息循環(huán),我們來(lái)接著上面的代碼來(lái)跟:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-631881.html
上面的函數(shù)緊接著,我們就看到了一行代碼:
這就是MFC的消息循環(huán),我們來(lái)跟進(jìn)去看一下:
pThread->Run(){
//這里也相當(dāng)于做了轉(zhuǎn)發(fā),在轉(zhuǎn)發(fā)之前有一些判斷,這里省略
int CWinApp::Run(){
//跟到這里之后,我們就發(fā)現(xiàn)了消息循環(huán):
for (;;)//死循環(huán)
{
//這里的PeekMessage是到消息列表中檢查是否有消息
while (bIdle &&!::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE))
{
//這里是說(shuō):如果沒(méi)有消息的話(huà),就會(huì)進(jìn)行空閑操作,比如刷新窗口,檢查定時(shí)器等等
if (!OnIdle(lIdleCount++))
bIdle = FALSE;
}
//死循環(huán)里面還有一個(gè)do——while循環(huán),我們來(lái)看看:
do
{
//這里,如果是WM_QUIT消息,才會(huì)返回false,才會(huì)進(jìn)去退出程序
if (!PumpMessage(){//在這個(gè)PumpMessage中,發(fā)現(xiàn)了翻譯消息,派發(fā)消息
if (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur)))
{
::TranslateMessage(&(pState->m_msgCur));
::DispatchMessage(&(pState->m_msgCur));
}
}
)
return ExitInstance();
if (IsIdleMessage(&(pState->m_msgCur)))
{
bIdle = TRUE;
lIdleCount = 0;
}
} while (::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE));
}
}
}
至此,我們的消息循環(huán)就跟蹤完了,這里,再總結(jié)一下MFC的程序啟動(dòng)機(jī)制:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-631881.html
- 首先是theApp,也就是我們自己的窗口類(lèi)(CMyApp)的構(gòu)造,也就是CWinApp的構(gòu)造,在構(gòu)造函數(shù)中:
- 將theApp的地址,保存到了當(dāng)前應(yīng)用程序線(xiàn)程狀態(tài)信息中
- 將theApp的地址,保存到了當(dāng)前應(yīng)用程序的模塊狀態(tài)信息中
- 然后進(jìn)入了WinMain函數(shù),在WinMain函數(shù)中:
- 利用全局函數(shù)AfxGetThread()獲取到了theApp的地址
- 利用theApp調(diào)用虛函數(shù) InitApplication,進(jìn)行文檔類(lèi)的一些初始化操作
- 利用theApp調(diào)用虛函數(shù) InitInstance,也就是我們重寫(xiě)的虛函數(shù),創(chuàng)建窗口
- 利用theApp調(diào)用虛函數(shù) Run 消息循環(huán)
- 在消息循環(huán)中,我們跟蹤到了獲取消息,翻譯消息,派發(fā)消息等
- 利用TheApp調(diào)用退出函數(shù)
到了這里,關(guān)于【MFC】05.MFC第一大機(jī)制:程序啟動(dòng)機(jī)制-筆記的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!