簡單介紹如何在別人的MFC程式中新增一個自己功能按鈕。

看雪資料發表於2015-11-15

1.先檢查目標程式是不是有保護殼,如果有就先進行脫殼處理;

2.脫殼後修改目標程式的資源,在對話方塊中新增一個自己的按鈕,並賦予一個新的控制元件ID
 可以使用ResHacker或者VC++對資源進行修改;這樣就有一個我們自己的功能按鈕了,接下來要新增一個訊息對映和響應函式

3.通常我們會在一個已經存在的對話方塊上新增一個按鈕,因此我們先摸索一下原來對話方塊的一些功能按鈕,最好有MessageBox
 呼叫的函式,這樣方便設斷點。

4.根據對MFC的程式碼,知道MFC程式有個訊息響應函式CCmdTarget::OnCmdMsg(),繼而定位到訊息查詢匹配函式_AfxFindMessageEntry
 被呼叫的地方。

5.假如找到一個功能按鈕並能彈出MessageBox的。在按下該按鈕前我們先在_AfxFindMessageEntry被呼叫的地方設上斷點,然後按下按鈕
 可能會有很多訊息進入斷點,因此我們可以設定一個條件斷點,只攔截WM_COMMAND(0x111h)訊息

6.如果一切順利的話,我們已經在斷點出攔截到功能按鈕的呼叫了。
 下面是CCmdTarget::OnCmdMsg()的程式碼片斷

CCmdTarget::OnCmdMsg()
PUSH    EBP
MOV     EBP,ESP
MOV     EAX,DWORD PTR [EBP+C]
PUSH    EBX
PUSH    ESI                           ;  Sample.00402308
PUSH    EDI
CMP     EAX,-2                        ;  Switch (cases FFFFFFFD..FFFFFFFF)
MOV     EDI,ECX
JE      6BC9E571                      ;  MFC42.6BC9E571
CMP     EAX,-3
JE      6BC9E592                      ;  MFC42.6BC9E592
CMP     EAX,-1
JNZ     SHORT 6BC42287                ;  MFC42.6BC42287
MOV     EBX,111                       ;  Case FFFFFFFF of switch 6BC42245
MOV     EAX,DWORD PTR [EDI]           ;  Sample.00402310
MOV     ECX,EDI
CALL    NEAR DWORD PTR [EAX+30]       ;  MFC42.GetMessageMap()
MOV     ESI,EAX                       ;  Sample.00402308
TEST    ESI,ESI                       ;  Sample.00402308
JE      SHORT 6BC422B0                ;  MFC42.6BC422B0
PUSH    DWORD PTR [EBP+8]             ; /Arg4 = 000003E8
PUSH    DWORD PTR [EBP+C]             ; |Arg3 = 00000000
PUSH    EBX                           ; |Arg2 = 00000111
PUSH    DWORD PTR [ESI+4]             ; |ESI + 4 == lpEntris, You Can Modify it to Chang MsgMap
CALL    6BC42088                      ; \#1145_AfxFindMessageEntry <----- 斷點停在這裡
TEST    EAX,EAX                       ;  Sample.00402308

7.現在有必要介紹一下比較重要的兩個資料結構

第一個是“訊息對映結構”
struct AFX_MSGMAP
{
#ifdef _AFXDLL
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
#else
const AFX_MSGMAP* pBaseMap;
#endif
const AFX_MSGMAP_ENTRY* lpEntries;
};
在這個結構中,我們只關心lpEntries這個值,因為這個值指向“訊息處理對映入口”陣列的首地址

第二個是“訊息處理對映入口”資料結構

AFX_MSGMAP_ENTRY
{
 nMessage;
 nCode   ;
 nID  ;
 nLastID ;
 nSig    ;
 lpEntry ;
}


8.檢視一下MFC關於呼叫_AfxFindMessageEntry的原始碼。
這個函式作用是,輪詢查詢和nMsg、nCode、nID匹配的AFX_MSGMAP_ENTRY結構,並從中提取出處理函式的入口地址
MS為了提高效能,所以這個函式是用匯編寫程式的。

AFX_MSGMAP *pMessageMap;
pMessageMap = GetMessageMap()
lpEntry = _AfxFindMessageEntry(pMessageMap->lpEntries, nMsg, nCode, nID);

我們可以發現pMessageMap指向我們需要的AFX_MSGMAP資料結構,因此在彙編程式碼中“ESI + 4”就是指向lpEntries的地址

9.我們現在所要做的就是,找到資料段空白的地方,然後將原來的“訊息處理對映入口”陣列複製到這裡來,然後用新的
陣列入口地址改寫“ESI + 4”所指向的資料。(因為沒有空間,所以不在原來的陣列後面新增)

10.現在我們可以在新的陣列中新增一個我們自己的“訊息處理對映入口”結構了。
下面是處理按鈕訊息的例子
AFX_MSGMAP_ENTRY
{
 nMessage = 0x111 WM_COMMAND
 nCode    = 0
 nID   = (ID) 按鈕控制元件的ID
 nLastID  = (ID) 按鈕控制元件的ID
 nSig     = 0xC  
 lpEntry (訊息響應函式入口)
}

11.在程式碼段的空白地方寫入自己的訊息響應函式,然後把這個函式的入口地址寫入到我們自己構造的訊息對映入口結構
的成員lpEntry中

12.基本上大致的流程就是這樣了。

相關文章