功能
-
列出
MFC
程序指定窗口的继承关系,消息响应,虚函数
MFC
中用户处理程序功能的一般是消息响应,再有就是虚函数。实现以下的功能,首先需要的是窗口的对象。
继承关系
MFC
中每个类都有个RuntimeClass
以及GetRuntimeClass
,调用这个函数就可以获取到类的继承关系。
消息响应
类中的GetMessageMap
,可以获取到类中所有的消息响应映射表。
虚函数
如何获取对象
通过CWnd::FromHandlePermanent
可以获取到窗口对应的类。于是如何获取CWnd::FromHandlePermanent
函数的位置就是一个问题。在每个窗口的过程函数中,首个调用的就是CWnd::FromHandlePermanent
这个函数。
步骤
-
注入
DLL
-
获取窗口过程函数
-
判断是否是DEBUG版本的程序
-
获取
CWnd::FromHandlePermanent
函数地址由于
MFC
中的数据存储在TLS
中,数据只能在UI
线程中获取,那么就需要替换一下过程函数了.void GetFromHandleAddr() { //动态库版 HMODULE hMod = GetModuleHandle("mfc140d.dll"); if (hMod != NULL) { g_pfnFromHandle = (PFN_FROMHANDLE)GetProcAddress(hMod, MAKEINTRESOURCE(5830)); g_bDll = TRUE; return; } //获取CWnd::FromHandlePermanent(hWnd)的函数地址 //循环,查找过程函数中的第一个call LPBYTE pCode = (LPBYTE)g_pfnWndProcDst; if (*pCode == 0xe9) { //说明是debug版,有调表 DWORD dwOff = *(LPDWORD)(pCode + 1); pCode = pCode + dwOff + 5; } while (*pCode != 0xe8) { ++pCode; } DWORD dwOff = *(LPDWORD)(pCode + 1); g_pfnFromHandle = (PFN_FROMHANDLE)(pCode + dwOff + 5); g_bDll = FALSE; } void ReplaceWndProc() { //判断是否是目标进程在加载自己 DWORD dwId; GetWindowThreadProcessId(g_hDstWnd, &dwId); if (dwId != GetCurrentProcessId()) { return; } //获取Fromhandle的地址 GetFromHandleAddr(); //获取窗口的过程函数 if (IsWindowUnicode(g_hDstWnd)) { g_pfnWndProcDst = (WNDPROC)GetWindowLongW(g_hDstWnd, GWL_WNDPROC); } else { g_pfnWndProcDst = (WNDPROC)GetWindowLongA(g_hDstWnd, GWL_WNDPROC); } if (IsWindowUnicode(g_hDstWnd)) { SetWindowLongW(g_hDstWnd, GWL_WNDPROC, (LONG)MyAfxWndProc); } else { SetWindowLongA(g_hDstWnd, GWL_WNDPROC, (LONG)MyAfxWndProc); } }
-
通过
CWnd::FromHandlePermanent
获取CWnd*
-
调用
GetruntimeClass
获取类名.在调用
CWnd*
类的GetruntimeClass
时,可以简单声明一下GetruntimeClass
的函数方法.并且之后用到的GetMessageMap
也需要定义一下
GetRuntimeClass
在虚表的第一项,所以如果CWnd
类中只声明一个函数也是可以调用的.
兼容性的问题
-
由于
MFC
中的数据存储在TLS
中,数据只能在UI
线程中获取,那么就需要替换一下过程函数了. -
DEBUG
版跟Release
版本的代码不同,各动态库跟静态库产生的代码也不同. -
静态库中
RuntimeClass
的父类指针存储着父类RuntimeClass
的地址,动态库中RuntimeClass
存储的是GetRuntimeClass
的函数指针.