2009-10-09 69 views
11

我想知道如何更改虚拟表中Test的地址与HackedVTable的地址。如何破解虚拟表?

void HackedVtable() 
{ 
    cout << "Hacked V-Table" << endl; 
} 

class Base 
{  
public: 
    virtual Test() { cout <<"base"; } 
    virtual Test1() { cout << "Test 1"; } 
    void *prt; 
    Base(){} 
}; 

class Derived : public Base 
{ 
public: 
    Test() 
    { 
     cout <<"derived"; 
    } 
}; 

int main() 
{  
    Base b1; 

    b1.Test(); // how to change this so that `HackedVtable` should be called instead of `Test`? 

    return 0; 
} 

答案将不胜感激。

在此先感谢。

+5

我不在乎你为什么要这样做。这是一个坏主意。不要这样做。 – abelenky 2009-10-09 06:36:29

+5

@abelenky,这仅仅是为了教育目的。我想知道事情是如何工作的。 :) – mahesh 2009-10-09 06:38:05

+0

@ Martin的评论是您可以为教育目的获得的最佳建议。否则 - 只要放弃,编译器对这个工作更好。 – LiraNuna 2009-10-09 06:54:10

回答

15

这适用于32位MSVC构建(这是一些生产代码的一个非常简化版本,已使用了一年多)。请注意,您的替换方法必须明确指定this参数(指针)。

// you can get the VTable location either by dereferencing the 
// first pointer in the object or by analyzing the compiled binary. 
unsigned long VTableLocation = 0U; 
// then you have to figure out which slot the function is in. this is easy 
// since they're in the same order as they are declared in the class definition. 
// just make sure to update the index if 1) the function declarations are 
// re-ordered and/or 2) virtual methods are added/removed from any base type. 
unsigned VTableOffset = 0U; 
typedef void (__thiscall Base::*FunctionType)(const Base*); 
FunctionType* vtable = reinterpret_cast<FunctionType*>(VTableLocation); 

bool hooked = false; 
HANDLE process = ::GetCurrentProcess(); 
DWORD protection = PAGE_READWRITE; 
DWORD oldProtection; 
if (::VirtualProtectEx(process, &vtable[VTableOffset], sizeof(int), protection, &oldProtection)) 
{ 
    vtable[VTableOffset] = static_cast<FunctionType>(&ReplacementMethod); 

    if (::VirtualProtectEx(process, &vtable[VTableOffset], sizeof(int), oldProtection, &oldProtection)) 
     hooked = true; 
} 
+0

非常感谢。 :) – mahesh 2009-10-09 07:04:59

+1

NP。我不得不用它来纠正在第三方应用程序中出现在我的机器配置上的错误参数验证,每次尝试使用该程序时都会导致崩溃。 – 2009-10-09 07:08:21

8

V-Table是一个实现细节。

编译器不需要使用它(它恰好是实现虚函数的最简单方法)。但是说每个编译器都可以(而且确实)以稍微不同的方式实现它,结果没有回答你的问题。

如果你问我怎么破解一个虚函数表的内置程序:

编译< X>版本< Y>构建<ž>

然后有人会知道答案。

+3

那么说吧。此外,即使使用V-Table的编译器有时也会产生带有静态链接的代码,如果程序的语义允许的话。 – mjv 2009-10-09 06:35:48

2

我不认为有一种便携的方式。主要是因为编译器优化和每个目标之间的不同架构ABI。

但是C++为您提供了完全相同的功能,为什么不使用它?

void HackedVtable() 
{ 
    cout << "Hacked V-Table" << endl; 
} 

class Base 
{ 
public: 
     virtual Test() { cout <<"base"; } 
     virtual Test1() { cout << "Test 1"; } 
     void *prt; 
     Base(){} 
}; 

class Derived : public Base 
{ 
    public: 
      Test() 
      { 
       HackedVtable(); // <-- NOTE 
      } 
}; 

int main() 
{ 
    Derived b1; // <-- NOTE 

    b1.Test(); 

    return 0; 
} 
+0

我想知道更改vtable – mahesh 2009-10-09 06:39:29

+2

中的地址您不想知道。即使为了教育目的,理论在这种情况下也是很好的。编译器对你的程序的了解比你想象的要多得多,所以搞乱一个可能存在或不存在的表(在这种情况下,从不,因为它没有被使用)是一个坏主意,只会让你思考你是一个糟糕的程序员,因为他们太多了。别。 – LiraNuna 2009-10-09 06:52:04

0

以及它很容易弄清楚。查找hte VTAble指针(在Visual Studio中,它的第一个4/8字节)。然后进入Test的正常调用(进入汇编程序),你会看到它跳转到Vtable,然后跳转到你的测试函数。要覆盖测试,只需将指针从VTable中跳出即可。

+0

你的意思是MSVC不会优化明显的覆盖(例如,如果不使用,例如)? – LiraNuna 2009-10-09 06:36:50

7
void HackedVtable() 
{ 
    cout << "Hacked V-Table" << endl; 
} 

class Base 
{ 

public: 
     virtual Test() { cout <<"base"; } 
     virtual Test1() { cout << "Test 1"; } 
     void *prt; 
     Base(){} 
}; 

class Derived:public Base 
{ 
    public: 
      Test() 
      { 
        cout <<"derived"; 
      } 
}; 

typedef void (*FUNPTR)(); 
typedef struct 
{ 
    FUNPTR funptr; 
} VTable; 


int main() 
{ 

    Base b1; 
    Base *b1ptr = &b; 

    VTable vtable; 
    vtable.funptr = HackedVtable; 

    VTable *vptr = &vtable; 
    memcpy (&b1, &vptr, sizeof(long)); 

    b1ptr->Test(); 

    //b1.Test(); // how to change this so that HackedVtable() should be called instead of Test() 

    return 0; 
} 
+0

@Ganesh,非常感谢你..简单而优雅。 :) – mahesh 2009-10-10 07:02:25

+1

@mahesh,这是*完全*不同的结果功能比我的。在此,它将替换单个实例的功能(b1)。在我的程序中,它取代了特定类型的* all *实例的函数。 – 2009-10-10 07:32:41

1

另一种方式来达到同样的事情是cheking类似的代码:

的GObject:

http://en.wikipedia.org/wiki/Gobject

的GLib:

http://en.wikipedia.org/wiki/GLib

瓦拉:

http://en.wikipedia.org/wiki/Vala_%28programming_language%29

那些家伙想用对象和类面向对象编程语言,但“C++”的工作,不适合他们的必需品。然后,他们拿出“普通的C”,并模拟包含n个记录指针的对象,包括虚拟方法表。最终得到了一种类似的语言“Vala”,他们自己的“C++”相似的语言(与他们自己的V.M.T.)。

另一个相关链接:

http://en.wikipedia.org/wiki/Virtual_method_table

http://www.artima.com/insidejvm/ed2/jvmP.html

干杯。

1

在Mac OS X 10.10.3 + gcc 4.8.3下,以下代码运行良好。

void HackedVtable() 
{ 
    cout << "Hacked V-Table" << endl; 
} 

class Base 
{  
public: 
    virtual void Test() { cout << "base" << endl; } 
    virtual void Test1() { cout << "Test 1" << endl; } 
    void *prt; 
    Base(){} 
}; 

class Derived : public Base 
{ 
public: 
    void Test() 
    { 
     cout << "derived" << endl; 
    } 
}; 

int main() 
{  
    Base b1; 
    Base* pb1 = &b1; 

    *(*(void***)pb1) = (void*) HackedVtable; 
    pb1->Test(); 

    //It works for all the Base instance 
    Base b2; 
    Base* pb2 = &b2; 
    pb2->Test(); 

    //But Derived's virtual function table is separated from Base's 
    Derived d1; 
    Derived* pd1 = &d1; 
    pd1->Test(); 
    *(*(void***)pd1) = (void*) HackedVtable; 
    pd1->Test(); 

    return 0; 
} 

其输出:

$ g++ h.cpp; ./a.out 
Hacked V-Table 
Hacked V-Table 
derived 
Hacked V-Table 

我的Ubuntu 12.04 + G ++ 4.9.0下测试相同的代码。但是,它不起作用并出现分段错误。 看来Linux将虚拟功能表分配在只读区域(例如rodata)中以禁止黑客入侵。

-1

我不认为vTable是在只读区域,因为它是动态填充的。编译器绝对确定在编译时调用哪个实现并跳过使用直接函数调用(去虚拟化)的vTable查找时,唯一可能失败的方法是。

+0

对不起,但仍不能发表评论。如果它没有用,请随时删除这篇文章... – 2015-06-07 22:49:36

0

这通常被称为“虚拟表钩”或类似的东西。 如果你打算使用它,那么我建议使用SourceHook库。它的开发是为了在游戏mod中对黑客源代码游戏引擎进行黑客攻击。例如,在idTech4成为开源软件之前,它被用于The Dark Mod