【问题标题】:Virtual functions and detouring虚函数和绕道
【发布时间】:2012-05-08 12:23:09
【问题描述】:

我最近一直在绕弯路编程,以及随之而来的一切。我绕了很多不同的功能; thiscall、stdcall、cdecl、虚函数等。但是有一件事我没有管理(这甚至可能是不可能的),那就是挂钩一个基类虚函数。例如;有一个 Car 类声明了一个虚函数(空)Drive。然后还有其他 3 个汽车类继承了汽车并实现了Drive。

如果我钩住汽车的(基类)Drive 函数(使用简单的“jmp”钩子),它会在Car 的后代触发Drive 时触发,如果他们这样做没有调用基函数?

解释得更彻底:

class Car
{
   virtual void Drive(void) { } // Empty virtual function
}

class Lamborghini : public Car
{
   void Drive(void) { // does lots of stuff, but does NOT call base function }
}

所以我想知道是否调用了基本方法 get 或者它是否可以以某种方式挂钩?函数执行是直接跳转到Lamborghini::Drive 还是以某种方式通过Car 类,以便在后代调用Drive 时可以检测到?

编辑:如果基类函数为空,是否甚至可以挂钩它,因为它需要 5 个字节的空间?

【问题讨论】:

标签: c++ gcc virtual-functions detours


【解决方案1】:

不,基本方法不会自动调用。动态调度机制将检测它需要调用哪个 override 并且这将是被调用的函数。这通常通过一个虚拟表 (vtable) 来实现,该表存储指向类中每个虚拟函数的 最终覆盖器 的指针。使用动态分派时,编译器会通过该表注入间接调用并跳转到正确的函数。

请注意,vtable 实际上包含指向 thunks 或 trampolines 的指针,这些指针可能会在转发调用之前修改 this(隐式第一个参数)。使用这种方式的好处是,如果this不需要更新,编译器可以直接跳转到最终的覆盖器。无论如何,您可以利用此功能并修改 vtable 以指向您自己的代码(即,您可以更新每个 vtable 中的指针 -- 每个类型一个 -- 以引用您自己的 thunk 或 函数)

【讨论】:

  • 只是为了避免任何误解;有可能,我需要做的就是修改虚拟表?如果是这样的话,我还想问一下(由于缺乏知识),vtable是否只包含4字节的指针?还是我现在应该做的其他事情(即只是找到 vtable 和偏移量,然后替换该指针以指向我的“钩子”)?
  • @ElliottDarfink 是的,您只需要找到 vtable 并替换指针。它也可能是 4 个字节,但可能与您计算机的体系结构无关。
  • @ElliottDarfink 还确保您意识到应该用 成员函数 替换它,这样该函数就不会认为它可以使用 ecx 寄存器并丢失 this指针。
  • 虚拟表是编译器定义的。修改它是一个非常糟糕的主意:代码膨胀,甚至不能跨同一个编译器的不同版本移植......
  • @Synxis 幸运的是我只关心 GCC 和 Linux 32bit :)
【解决方案2】:

如果我没有正确回答您的问题,您的 Lamborghini 类中的 Drive 方法将根据虚函数表被调用。如果你想调用基类的Drive方法,你必须写Car::Drive;之类的东西。由于 VTBL,基类需要一些空间。希望我没有回答您的问题。

【讨论】:

    【解决方案3】:

    如果我理解正确,您希望每次调用Lamborghini::Drive 时都调用Car::Drive,即使Lamborghini:Drive 不直接调用基函数?

    为此,最简单的方法是使用“内部”函数,该函数将是虚拟的(且受保护的),而初始方法将是非虚拟的并会路由调用。 这是一个例子:

    class Car
    {
        void Drive(void)
        {
            // ...
            Car::innerDrive(); // Base function call
            // ...
            this->innerDrive(); // 'Derived' function call
            // ...
        }
    protected:
        virtual void innerDrive(void) { } // Empty virtual function
    }
    
    class Lamborghini : public Car
    {
    protected:
        void innerDrive(void) { // does lots of stuff, but does NOT call base function }
    }
    

    【讨论】:

    • 问题是,我正在使用预编译的代码,这就是我使用 detours 的原因:)
    猜你喜欢
    • 2019-08-12
    • 2015-09-17
    • 2012-01-28
    • 1970-01-01
    • 2011-04-16
    • 1970-01-01
    • 2021-06-01
    • 2018-06-07
    • 2016-11-29
    相关资源
    最近更新 更多