0

Background:

I'm hooking on windows COM object.

The method used is vtable modification. say we have an instance of interface A named instance, it contains oldmethod in the interface, I replaced with newmethod. However, in my newmethod I need to know the address of oldmethod so that I can call oldmethod after doing my own thing.

It is not safe to store the address of oldmethod in a global variable, since there might be more than one implementation behind interface A, let's say there are two implementations, class A1 and class A2. Thus my newmethod needs to store both A1->oldmethod and A2->oldmethod, and call appropriate function based on the instance type.

  • One way to accomplish this is that I keep a map, which stores the (address of vtable -> oldmethod). Since the address of vtable can act as a distinguisher of class A1 and class A2. In my newmethod, the map is checked for the correct oldmethod for current instance. However, this will make the program check the map every time, which imposes a cost, and thread safety on the map will increase the cost.

  • Another way is to make a closure, I allocate a chunk of executable memory, and write the binary code of my newmethod inside(which can be reduced to the minimum size, so size is not a problem). I modify the address of oldmethod in the binary code for each instance. In this case there is no searching on the map cost.

Question 1:

Is the second way a safe way to do this, or is the first way better? Is there any potential safety problems in either of them?

Question 2:

In the second way, the closure I created contains class specific data, which is the oldmethod pointer. If I need to store instance specific data in my newmethod is there any strategy other than keeping a (this pointer -> data) map? I tried my best and couldn't find a way.

4

2 回答 2

1

您可能没有 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 直接相关的答案。

于 2013-02-17T11:04:07.140 回答
0

我花了很长时间才理解这个问题。实际上,我写了一大段代码来演示如何修补 vtable,然后在包装类中调用原始方法。修补 vtable 很容易。

然后我发现了你所指的问题。那就是调用修补的 vtable 方法(newmethod)时,即使它在另一个类中定义,“this”也是原始对象,并且您没有任何关于哪个实例被调用的上下文。所以你不能轻易地引用一个成员变量来回到你保存的“旧方法”。

所以经过一番思考,我认为全球地图是最安全的方法。您可能只需要一个锁 (critical_section) 来保护在映射中插入、删除或查找函数指针。从地图中安全地检索到旧方法后,您可能不需要在调用旧方法时持有锁。因此,执行此操作的运行时开销可以忽略不计。

于 2013-02-17T23:02:09.730 回答