背景:

我迷上了Windows COM对象。

使用的方法是vtable修改。说我们有一个接口实例,一个命名实例,它在接口中包含oldmethod,我替换为newmethod。但是,在我的newmethod中,我需要知道oldmethod的地址,以便在做完自己的事情后可以调用oldmethod。

将旧方法的地址存储在全局变量中是不安全的,因为接口A后面可能有多个实现,所以可以说有两个实现,即类A1和类A2。因此,我的newmethod需要存储A1-> oldmethod和A2-> oldmethod,并根据实例类型调用适当的函数。


实现此目的的一种方法是保留一个映射,该映射存储(vtable的地址-> oldmethod)。由于vtable的地址可以用作A1类和A2类的区分符。在我的newmethod中,将检查映射以查找当前实例的正确旧方法。但是,这将使程序每次都检查地图,这带来了成本,并且地图上的线程安全性将增加成本。
另一种方法是进行关闭,我分配了一块可执行内存,并在其中写入了我的newmethod的二进制代码(可以将其减小为最小大小,因此大小不是问题)。我为每个实例修改二进制代码中的oldmethod的地址。在这种情况下,不会搜索地图成本。


问题1:

第二种方法是这样做的安全方法,还是第一种方法更好?两者中是否都存在潜在的安全问题?

问题2:

第二种方式,我创建的闭包包含特定于类的数据,这是oldmethod指针。如果我需要在newmethod中存储实例特定的数据,除了保留(此指针->数据)映射之外,还有其他策略吗?我已尽力而为,找不到办法。

最佳答案

您可能没有类A1的源,但是您可以控制它何时实例化(通过“ new”,CoCreateInstance或其他一些工厂函数)吗?如果是这样,则只需实现一个实现接口A的类,然后将接口A上的所有调用转发到实际对象并拦截您关心的方法。

在下面的示例中,我们显示了一个替换示例

class InterfaceA : public IUnknown
{
public:

    virtual int M1() = 0;
    virtual int M2(int x, int y) = 0;
    virtual int M3() = 0;
};


class CMyWrapperClass : public InterfaceA
{
public:

    int _refcount;
    InterfaceA* _pInner;

    CSomeClass2(InterfaceA* pInner)
    {
        _pInner = pInner;
        _pInner->AddRef();
        _refcount = 1;
    }

    ~CSomeClass2()
    {
        _pInner->Release();
    }

    virtual int M1() {return _pInner->M1();}
    virtual int M2(int x, int y)  {printf("CSomeClass2::M2(x=%d, y=%d)\n", x, y); return _pInner->M2(x,y);  }
    virtual int M3() {return _pInner->M3();}

    // not shown - addRef, release, queryinterface
};


   // example instantiation
   hr = CoCreateInstance(CLSID_A1, NULL, CLXCTX_ALL, IID_InterfaceA, (void**)&pInterfaceA);

   // now do the wrap
   pInterfaceA = new CMyWrapperClass(pInterfaceA);


如果您无法控制要热补丁的类的实例化,那么我确实有共享的代码。但这显然要复杂一些。如果这不起作用,我将发布另一个与热修补COM vtable直接相关的答案。

10-08 08:13