COM 对象函数上的 API 挂钩?

2022-01-14 00:00:00 com hook c++ vtable api-hook

问候 StackOverflowians,

Greetings StackOverflowians,

正如此处所发现的,Windows 7 存在一个错误,在该错误中,Windows Explorer 实例不会触发 DISPID_BEFORENAVIGATE2 事件.此事件允许在导航即将发生时通知 shell 扩展,并且(对我来说最重要的是)有机会取消导航.很长一段时间以来,我一直在寻找一种解决方法,我想我找到了.但是,我想了解一下它的安全性.

As discovered here, Windows 7 features a bug in which the DISPID_BEFORENAVIGATE2 event does not fire for Windows Explorer instances. This event allows shell extensions to be notified when a navigation is about to take place, and (most importantly for me) have the opportunity to cancel the navigation. I've been looking for a workaround for quite some time, and I think I found one. But, I'd like to get some opinions on how safe it is.

我最近一直在使用 API 挂钩,并且我已经在使用它为我的扩展挂钩一些函数.我注意到 IShellBrowser 中有一个 函数 控制导航.起初我以为你不能钩这样的东西,但在阅读了 COM 对象的布局 我意识到这应该可以通过从任何活动实例的 vtable 中获取正确的函数指针来实现.果然,它就像一场梦.设置钩子后,所有资源管理器窗口中的所有导航都直接通过我的 detour 函数运行,我可以根据它们的目标 pidl 决定是否拒绝它们.

I've been playing with API hooking a lot lately, and I'm already using it to hook a few functions for my extension. I noticed that there is a function in IShellBrowser that controls navigation. At first I thought you couldn't hook something like that, but upon reading about the layout of a COM object I realized it should be possible by just grabbing the right function pointer out of the vtable of any active instance. Sure enough, it works like a dream. After the hook is set, all navigations in all Explorer windows run right through my detour function, and I can decide whether to reject them based on their target pidl.

所以我的问题是,我有什么理由不应该这样做吗?我从未听说过用于挂钩 COM 对象函数的 API 挂钩.有没有它不起作用的情况?危险吗?(至少比常规的 API 挂钩)

So my question is, is there any reason I should NOT do this? I've never heard of API hooking used to hook COM object functions. Are there circumstances it which it wouldn't work? Is it dangerous? (Any more than regular API hooking, at least)

相关代码如下.我正在使用 MinHook,这是一个使用久经考验的简约挂钩库- 真正的蹦床函数方法.

The relevant code follows. I'm using MinHook, a minimalistic hooking library that uses the tried-and-true method of trampoline functions.

typedef HRESULT (WINAPI *BROWSEOBJECT)(IShellBrowser*, PCUIDLIST_RELATIVE, UINT);
HRESULT WINAPI DetourBrowseObject(IShellBrowser* _this, PCUIDLIST_RELATIVE pidl, UINT wFlags);
BROWSEOBJECT fpBrowseObject = NULL;
BROWSEOBJECT ShellBrowser_BrowseObject = NULL;

bool Initialize() {
    if(MH_Initialize() != MH_OK) {
        return false;
    }

    // Get a reference to an existing IShellBrowser.  Any instance will do.
    // ShellBrowser enum code taken from The Old New Thing
    IShellWindows *psw;
    BOOL fFound = FALSE;
    if (SUCCEEDED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL, IID_IShellWindows, (void**)&psw))) {
        VARIANT v;
        V_VT(&v) = VT_I4;
        IDispatch  *pdisp;
        for (V_I4(&v) = 0; !fFound && psw->Item(v, &pdisp) == S_OK; V_I4(&v)++) {
            IWebBrowserApp *pwba;
            if (SUCCEEDED(pdisp->QueryInterface(IID_IWebBrowserApp, (void**)&pwba))) {
                IServiceProvider *psp;
                if (SUCCEEDED(pwba->QueryInterface(IID_IServiceProvider, (void**)&psp))) {
                    IShellBrowser *psb;
                    if (SUCCEEDED(psp->QueryService(SID_STopLevelBrowser,IID_IShellBrowser, (void**)&psb))) {
                        fFound = true;

                        // Grab the 11th entry in the VTable, which is BrowseObject
                        void** vtable = (*(void***)(psb));
                        ShellBrowser_BrowseObject = (BROWSEOBJECT)(vtable[11]);
                        psb->Release();
                    }
                    psp->Release();
                }
                pwba->Release();
            }
            pdisp->Release();
        }
        psw->Release();
    }

    if(fFound) {
        if(MH_CreateHook(ShellBrowser_BrowseObject, &DetourBrowseObject, reinterpret_cast<void**>(&fpBrowseObject)) != MH_OK) {
            return false;
        }
        if(MH_EnableHook(ShellBrowser_BrowseObject) != MH_OK) {
            return false;
        }
    }
    return true;
}

HRESULT WINAPI DetourBrowseObject(IShellBrowser* _this, PCUIDLIST_RELATIVE pidl, UINT wFlags) {
    if(NavigateIsOkay(pidl, wFlags)) {
        return fpBrowseObject(_this, pidl, wFlags);
    }
    else {
        return S_FALSE;
    }    
}

推荐答案

我从未听说过使用 API 挂钩挂钩 COM 对象函数.

I've never heard of API hooking used to hook COM object functions.

COM 对象的成员函数并没有太大的不同,如果您坚持通常的挂钩准则,实际上可以很好地挂钩.几年前,我不得不挂钩专有 CRM 解决方案的 COM 组件,以将其连接到数据库服务器.该应用程序运行良好,并且多年来一直运行相当稳定.

Member functions of COM Objects are not really that different and can actually be hooked just fine if you stick to the usual guidelines for hooking. A few years ago, I had to hook COM components of a proprietary CRM solution to connect it to a database server. The application worked fine and has been running quite stable for several years.

相关文章