2012-07-11 78 views
2

我一直在使用與Microsoft Detours相同的方法練習彎路(用jmp和地址替換前五個字節)。最近我一直在通過修改虛擬表來閱讀關於繞行的內容。如果有人能夠通過提及這種方法的一些優點和缺點,與前面提到的方法相比,我會很感激的。有關繞行虛擬表的問題

我也想問一下堆棧上的vatch和對象。考慮以下情況:

// 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)法)。這是因爲如果可能的話,編譯器會在編譯時嘗試決定任何虛擬調用(如果我錯了,請糾正我)。這是否意味着你是否修補虛擬表並不重要,只要你使用棧上的對象(不是堆分配)?

回答

2

堆棧與堆無關緊要 - 重要的是編譯器在編譯時知道對象的類型。下面可能會產生相同的結果,除非優化器是很聰明:

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

由於foo2類型是已知的,編譯器可以調用該函數直接不看任何虛函數表。它不能爲foo做同樣的事情,因爲指針也可以指向派生對象。

0

當您使用foo2.call()編譯器不應該使用vtable來確定要調用哪個函數時,它會在不調用vtable的情況下在類中調用函數。