学一学 hook 专题,今天是 virtual hook。
前置芝士
早就听说 C++ 因面向对象而闻名,然而我们平时在算法比赛中使用的 C++ 却几乎没有面向对象的内容,C++ 的类中有一个非常厉害的东西叫虚函数,C++中的虚函数的作用主要是实现了多态的机制,多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。
这是一种泛型技术,所谓泛型技术就是试图使用不变的代码来实现可变的算法。比如 平常的排序,排序的东西种类有很多,我们如果都为此写一个 sort 函数,那太过于麻烦,泛型技术就解决了这种麻烦,比如 std::sort,它可以通过自定义比较规则的方式来实现多种类型的数据都能使用这个 std::sort,只要你为数据类型重载了小于号即可。
test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <iostream>
using namespace std;
class Base { public: virtual void f() { cout << "Base::f()" << endl; } virtual void g() { cout << "Base::g()" << endl; } virtual void h() { cout << "Base::h()" << endl; } };
int main() { Base t; (((void(*)()) * ((int*)(*((int*)&t)) + 0))) (); (((void(*)()) * ((int*)(*((int*)&t)) + 1))) (); (((void(*)()) * ((int*)(*((int*)&t)) + 2))) (); return 0; }
|
运行结果
我们可以发现,t 所在的地址就填了这三个函数的地址,我们通过 call &t,call &t +1,call &t+2能调用到 Base::f(),Base::g(),Base::h()这三个函数。
这其实就已经是虚表了,Virtual hook 其实跟 IAT hook 差不多,就是改掉表的地址为我们自己的函数,然后尝试调用这个函数的时候就会劫持到我们的函数里面来。
More
单继承情况
单继承下的虚函数表很简单,若派生类具有和父类一样的函数名,则虚函数表中会调用子类的方法,若父类的函数名在派生类中没有出现,则虚函数表中会调用你父类的方法,虚函数表的顺序按照定义的顺序,若出现继承,则父类的虚函数在前,子类虚函数在后。
多继承情况
多继承的虚函数表中,我们也写一个 demo 去理解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include <iostream>
using namespace std;
class Base1 { public: virtual void f() { cout << "Base1::f()" << endl; } virtual void g() { cout << "Base1::g()" << endl; } }; class Base2 { public: virtual void f() { cout << "Base2::f()" << endl; } virtual void g() { cout << "Base2::g()" << endl; } }; class Base3 { public: virtual void f() { cout << "Base3::f()" << endl; } virtual void g() { cout << "Base3::g()" << endl; } virtual void h() { cout << "Base3::h()" << endl; } };
class B :public Base1, public Base2, public Base3 { public: virtual void f() { cout << "B::f()"<<endl; } }; int main() { B t; (((void(*)()) * ((int*)(*((int*)&t)) + 0))) (); (((void(*)()) * ((int*)(*((int*)&t)) + 1))) (); (((void(*)()) * ((int*)(*((int*)&t)) + 2))) (); return 0; }
|
我们不难发现,多继承情况下,对象有多张虚表。
至于这个也不再去深究了,感觉挺复杂。
hook
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #include <stdio.h> #include <Windows.h>
class MyClass { public: MyClass(); ~MyClass(); virtual void Print(); private:
};
MyClass::MyClass() { }
MyClass::~MyClass() { }
void MyClass::Print() { printf("hello\r\n"); }
void Print() { printf("xia0ji233\r\n"); }
int main() { MyClass obj; MyClass& vobj = obj; vobj.Print(); int nAddr = *(int*)&obj; DWORD dwOldProtect = 0; VirtualProtect((void*)nAddr, 0x100, PAGE_EXECUTE_READWRITE, &dwOldProtect); (*(int*)nAddr) = (int)Print; VirtualProtect((void*)nAddr, 0x100, dwOldProtect, &dwOldProtect); vobj.Print(); system("pause"); return 0; }
|
结果
这玩意感觉实用性没有特别强吧,mark 一下,遇到再补。