2

我一直在使用与 Microsoft Detours 相同的方法练习 detours(将前五个字节替换为 jmp 和地址)。最近,我一直在阅读有关通过修改虚拟表来绕行的信息。如果有人能通过提及与前面提到的方法相比的一些优点和缺点来阐明这个主题,我将不胜感激!

我还想询问有关堆栈上已修补的 vtable 和对象的信息。考虑以下情况:

// Class definition
struct Foo
{
 virtual void Call(void) { std::cout << "FooCall\n"; }
};

// If it's GCC, 'this' is passed as the first parameter
void MyCall(Foo * object)
{
 std::cout << "MyCall\n";
}

// In some function
Foo * foo = new Foo; // Allocated on the heap
Foo foo2; // Created on the stack

// Arguments: void ** vtable, uint offset, void * replacement
PatchVTable(*reinterpret_cast<void***>(foo), 0, MyCall);

// Call the methods
foo->Call(); // Outputs: 'MyCall'
foo2.Call(); // Outputs: 'FooCall'

在这种情况下foo->Call(),最终会在调用原始函数(即方法)MyCall(Foo * object)时调用。这是因为如果可能,编译器将尝试在编译期间决定任何虚拟调用(如果我错了,请纠正我)。这是否意味着您是否修补虚拟表并不重要,只要您使用堆栈上的对象(不是堆分配的)?foo2.Call()Foo::Call(void)

4

2 回答 2

2

堆栈与堆无关紧要 - 重要的是编译器在编译时知道对象的类型。除非优化器非常聪明,否则以下可能会产生相同的结果:

Foo foo2; // Created on the stack
Foo * foo = &foo2; // Also on the stack, in fact the same object

由于类型foo2是已知的,编译器可以直接调用函数而无需查看任何 vtable。它不能做同样的事情,foo因为指针也可以指向派生对象。

于 2012-07-11T15:38:35.180 回答
0

当您使用 foo2.call() 编译器时,不应使用 vtable 来确定要调用哪个函数,它会在类中调用函数而不调用 vtable。

于 2012-07-11T15:13:49.943 回答