【问题标题】:C++ COM Object Hotpatching?C++ COM 对象热补丁?
【发布时间】:2013-02-17 09:20:45
【问题描述】:

背景:

我正在连接 Windows COM 对象。

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

oldmethod的地址存储在全局变量中是不安全的,因为在interface A后面可能有多个实现,假设有两个实现、A1 级A2 级。因此我的newmethod需要同时存储A1->oldmethodA2->oldmethod,并根据实例类型调用相应的函数。 p>

  • 实现此目的的一种方法是我保留一个地图,其中存储(vtable 的地址 -> oldmethod)。由于 vtable 的地址可以作为 A1 类和 A2 类的区分符。在我的 newmethod 中,会检查地图以查找当前实例的正确 oldmethod。但是这样会让程序每次都要检查map,这会带来成本,而且map上的线程安全会增加成本。

  • 1234563不是问题)。我在每个实例的二进制代码中修改 oldmethod 的地址。在这种情况下,地图成本上没有搜索。

问题 1

第二种方法是否安全,还是第一种方法更好?是否存在安全隐患?

问题 2

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

【问题讨论】:

  • 是否有某些特定原因,您不只是从现有实现派生一个 coclass,而是用“newmethod”虚拟覆盖“oldmethod”签名必须匹配,否则你就是,根据定义,违反了 COM 对已发布 IID 和 pin 的接口的合同。当然,除非这不是您的代码(旧方法)并且您基本上是在尝试挂钩其他人的 coclass。您也可以使用 coclass-alias,但听起来您更喜欢偷偷地这样做。
  • 我正在挂钩现有的 COM 对象以修改其行为,我没有实现。
  • @WhozCraig 而且我没有直接调用方法,我也没有调用者的代码。
  • “但是,这会让程序每次都检查地图,这会带来成本,而地图上的线程安全会增加成本。” - 这是一个非常假设无效。
  • 我不明白为什么这个无效,你的意思是成本可以忽略不计?

标签: c++ com closures hook


【解决方案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 直接相关的答案。

【讨论】:

  • 我考虑过这个,但我认为它不安全,在调用者知道的不仅仅是接口中的函数的情况下。在这种情况下,包装器将失败。我正在尝试找到一种可以处理所有情况并避免危险的通用方法。我对你的其他解决方案很感兴趣。
  • 给我看一个上面“不安全”的例子。
  • 类ClassA:接口A。 ClassA 具有 InterfaceA 具有的所有虚函数。但是ClassA多了一个方法M4(),软件作者可能知道M4(),在某处调用了M4(),但是提供的头文件可能不包含M4,它是私有的。我知道这是一个不好的做法,在编写自己的程序时我不会这样做。我说我正在尝试找到一种完全安全的方法,该程序在挂钩之前和之后都可以正常工作。
  • 换句话说,您担心您尝试破解的软件的内部结构可能违反 COM 的一般原则,并且不通过 QueryInterface 将接口指针不安全地向下转换回它的内部类?这似乎有点牵强,但我会发布另一个关于修补 vtable 的答案。
  • @MinLin:你不能处理所有的滥用案例,因为它们的数量是无穷无尽的。例如。任何inlined 电话也可能破坏。
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-26
    • 1970-01-01
    • 2022-01-02
    • 1970-01-01
    • 2021-12-20
    • 2019-01-22
    • 2021-01-10
    • 2012-06-16
    相关资源
    最近更新 更多