由 BCG 谈到 API Hook

来源:互联网 发布:jy外设淘宝店网址 编辑:程序博客网 时间:2024/04/29 02:26

BCG 就无需介绍了,业界相当有名的 Visual C++ 换肤专家,能完美模拟 Outlook、Office、Visual Studio 的界面,连 Microsoft 也是他们的客户,其实力可见一斑。

来张界面,这是他自己的 Outlook Demo 编译之后运行的效果:

截图1

 

上面那个 SysCommand 我用红色圈出来了,这么漂亮的一个 UI,居然还是那么老土的菜单(这个菜单在 MFC 的基本控件,比如 CEdit 等,点右键时也会出来),本来 VS2008 sp1 已经内置 BCG 的 CMFCPopupMenu 了,可是在这些地方没有换过来,算是点小瑕疵吧。于是我想给他换过来,和整体 UI 界面风格保持一致。

要达到这样的效果,基本思路有两个:

1、安装消息钩子,处理 WM_INITMENUPOPUP 和 WM_UNINITMENUPOPUP,自绘。一般采用 CMenu 的派生类;

2、Hook 系统 API 函数,处理 TrackPopupMenu 和 TrackPopupMenuEx,用自定义的其他菜单类完成显示。

 

像常见的一些第三方 skin 包,比如 Skin++,采用的就是第一种方式。

这里我想换成 CMFCPopupMenu,而 CMFCPopupMenu 并非派生于 CMenu,而是派生于 CWnd,所以第一条路行不通,走第二条路。

 

提到 API Hook,也是个老生常谈的话题了。要实现它,也有两种思路:

一种是找到该API在内存的入口地址,修改其入口前八字节数据为了一汇编的 jmp xxxxxxxx 无条件跳转指令,使其调用该API时会跳到指定的另一处函数入口处,即与这个API有着相同的参数与返回值的回调处理程序。
      第二种是在进程的输入表(IAT 表)找到保存在这里的要调用的那个API地址,修改这个地址,使程序在输入表里找该API地址时取的是我们修改后的回调程序入口地址。

关于第一种方法,有例子:http://www.vckbase.com/document/viewdoc/?id=1378

 

两种方法的利弊也是显而易见的:采用第一种方式,功能上说可以确保万无一失,但是需要频繁的 hook 和 unhook,于效率和稳定性上有所欠缺;第二种在由于只修改一次 IAT 表,所以在效率很稳定性上很有优势,但是缺点就是:某些程序要调用某个 API 可以不通过输入表,造成 hook 不到。

好在 Microsoft 的核心 API 函数库 kernel32、user32、ntdll 好像并没有采用(或者是我理解有问题,至少我们这里 TrackPopupMenu 肯定不会漏),所以我还是采用第二种方式,通过修改进程每个模块的输入表来实现(用 Psapi 枚举进程模块就没必要细说了,都会)。

 

于是创建新类(比如 CXMenuHooker),用我以前写好的 Hook API 包,添加了两个公开的静态入口函数:

    public:
        static void HookPopupMenu();
        static void RestorePopupMenu();

以便于安装和卸载 API 钩子。然后再给 Menu 加上自定义的弹出菜单函数:

    protected:
        static BOOL WINAPI TrackPopupMenu(HMENU hMenu, UINT uFlags, int x, int y, int nReserved, HWND hWnd, CONST RECT* prcRect);
        static BOOL WINAPI TrackPopupMenuEx(HMENU hmenu, UINT fuFlags, int x, int y, HWND hwnd, LPTPMPARAMS lptpm);
注意参数需要和 SDK 的一致就是了。

 

关键函数:

这些代码工作的很好,成功 Hook 了 TrackPopupMenu 和 TrackPopupMenuEx 两个 API 函数,弹出了我想要的 CMFCPopupMenu,看图:

 

截图2

 

可是,等等,似乎有了新问题,这里的 CMFCPopupMenu 不能自动关闭,当我在其他地方点鼠标的时候应该要关闭这个菜单的,仔细翻了一遍 CMFCPopupMenu 的源码,也没发现我这里调用有啥不对的,极其郁闷中...

 

回头看看最上面的第一种方案,处理 WM_INITMENUPOPUP 的,可行倒是可行,但是自绘的话,效果没办法和 CMFC... 一套东西很好的保持一致,除非派生 CMenu 实现 2000/XP/Office2003/VisualStudio2005 的各种效果,这个代价就太大了,时间不够用啊...

 

所以最后,这个暂时就只有先搁置下来了,等以后有时间了再来完善了...

原创粉丝点击