• 标 题:简单介绍如何在别人的MFC程序中添加一个自己功能按钮。 
  • 作 者:footfish2002
  • 时 间:2003/05/07 04:19pm
  • 链 接:http://bbs.pediy.com

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.基本上大致的流程就是这样了。