背景:
我钩住窗户COM对象。
所用的方法是虚函数表的修改。 说我们有命名实例 接口A的一个实例,它包含oldmethod在界面上,我替换newmethod。 然而,在我newmethod我需要知道oldmethod的地址,以便我可以做我自己的事情后打电话oldmethod。
它是不是安全的oldmethod地址存储在一个全局变量,因为有可能是一个界面背后不止一个实现,比方说有两种实现方式,A1级和A2级 。 因此,我newmethod需要同时存储A1-> oldmethod和A2-> oldmethod,并调用基于实例类型相应的功能。
做到这一点的方法之一是,我一直在地图上,存储(虚表的地址 - > oldmethod)。 由于虚函数表的地址可以作为类A1和A2级的区分器起作用。 在我newmethod,地图检查的正确oldmethod当前实例。 然而,这将使程序查地图每一次,这带来成本和线程在地图上安全性会增加成本。
另一种方式是使封闭的,我分配可执行存储器的块,和写我newmethod内的二进制码(可以被减小到最小尺寸,所以大小是没有问题的)。 我修改的oldmethod地址在每个实例的二进制代码。 在这种情况下,是在地图上的成本无觅处。
问题1:
是第二个办法做到这一点安全的方式,或者是第一种方式更好? 是否有任何人任何潜在的安全问题?
问题2:
在第二种方式中,我创建封闭包含类特定的数据,这是oldmethod指针。 如果我需要存储在我的newmethod特定实例数据有什么策略比保持(this指针- >数据)其他地图? 我尽我所能,无法找到一种方法。
您可能没有源A1级,但是当它被实例化(通过“新”,CoCreateInstance的,或其他一些工厂函数),你控制? 如果是这样,那么就实现实现了接口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);
如果你没有你想的hotpatch类的实例化的控制,我有代码共享为。 但它是obivously有点复杂。 如果这也不行,我会发布另一个答案直接关系到一个热修补功能COM虚函数表。
我花了一段时间来理解这个问题。 其实我写出来的代码块好,我认为来演示如何修补虚函数表,然后调用的包装类的原始方法。 修补虚函数表很容易。
然后我发现你指的是这个问题。 那就是当打补丁的vtable方法称为(newmethod),即使它在另一个类中定义的,“本”是原来的对象,你没有这些被调用实例的任何方面。 所以,你不能轻易地只是参考的一个成员变量要回了“oldmethod”你救了。
所以经过一番思考,我认为全球地图是这样做的最安全的方法。 您可能只需要一个锁(CRITICAL_SECTION)守卫插入,删除,或查找地图中的函数指针。 你可能不会需要在调用旧方法你已经安全地从地图上检索后,持有该锁。 因此,这样的操作的运行时开销是非常微不足道的。