我是靠谱客的博主 淡然万宝路,最近开发中收集的这篇文章主要介绍C++——Hook教程[1]:虚函数表(VMT)Hook前言虚函数表(VMT)VMT Hook的实现封装总结,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

前言

虚函数表(VMT)Hook,又叫指针重定向,是一种常见的Hook技术,在游戏外挂程序中最常见。例如,使用VMTHook在Direct3D / OpenGL引擎游戏里实现内置叠加层。

虚函数表(VMT)

本文中VMT就代指虚函数表。

虚函数表是C++实现多态的一种方式。

每一个有虚函数的类(或有虚函数类的派生类)都有一个VMT,VMT本质上就是一个函数指针数组,通常位于对象内存布局的开头或结尾。每当C++类声明虚(virtual)函数时,编译器都会增加一个条目到VMT中。

例如,在x86系统上使用VS2019编译以下代码:

class Base
{
public:
    Base() { std::cout << "-  Base::Basen"; }
    virtual ~Base() { std::cout << "-  Base::~Basen"; }

    void A() { std::cout << "-  Base::An"; }
    virtual void B() { std::cout << "-  Base::Bn"; }
    virtual void C() { std::cout << "-  Base::Cn"; }
};

class Derived final : public Base
{
public:
    Derived() { std::cout << "-  Derived::Derivedn"; }
    ~Derived() { std::cout << "-  Derived::~Derivedn"; }

    void B() override { std::cout << "-  Derived::Bn"; }
    void C() override { std::cout << "-  Derived::Cn"; }
};

Base类有三个虚函数:~BaseBC.
Derived类派生自Base,并重写了两个虚函数Bh和C.

这里我们创建三个实例

Base base;
Derived derived;
Base* pBase = new Derived();

我们把代码跑起来,用调试器观察,发现Base实例的VMT包含了~BaseBC
Base
而两个Derived实例的VMT包含了~DerivedBC。但VMT里的函数地址与Base实例中的不一样(见下图)
DerivedDerived

那么应该如何使用这些函数呢?

以一个函数为例,该函数获取一个指向Base的指针并调用函数ABC

void Invoke(Base* const pBase)
{
    pBase->A();
    pBase->B();
    pBase->C();
}

以以下方式调用:

    Invoke(&base);
    Invoke(&derived);
    Invoke(pBase);

Invoke函数反汇编,看看在汇编层面,VMT内的函数是如何被调用的:

可以将RTC关闭(项目属性->C/C+±>代码生成->基本运行时检查->默认值),省去__RTC_CheckEsp等代码,让反汇编代码更简洁

asm
对于B的调用,编译器将pBase也就是对象的地址移入EAX寄存器,然后间接获取VTM的基地址,并将其存储在EDX寄存器中。通过EDX作为索引+4将函数地址存储在EAX寄存器中,然后调用EAX

C的调用如出一辙,只是VMT中函数地址的偏移量为8。

由此可见,VMT的底层实现就是一个函数指针数组

明白了VMT调用的原理,我们就可以很轻松的写一个函数来打印VMT:

void PrintVTable(Base* const pBase)
{
    auto pVTableBase = *reinterpret_cast<void***>(pBase);
    printf("First: %pn"
        "Second: %pn"
        "Third: %pn",
        pVTableBase[0] , pVTableBase[1], pVTableBase[2]);
}

VMT Hook的实现

明白了VMT调用的原理,当然也就可以轻松的实现Hook了。

我们只要覆盖掉需要Hook的函数在VMT中的地址即可,这也解释了为什么VMT Hook也叫指针重定向。

void HookVMT(Base* const pBase)
{
    auto pVTableBase = *reinterpret_cast<void***>(pBase);

    SIZE_T ulOldProtect = 0;
    VirtualProtect(&pVTableBase[1], sizeof(void*), PAGE_EXECUTE_READWRITE, &ulOldProtect);
    pVTableBase[1] = VMTHookFnc;
    VirtualProtect(&pVTableBase[1], sizeof(void*), ulOldProtect, &ulOldProtect);
}

VMTHook定义:

void __fastcall VMTHookFnc(void* pEcx, void* pEdx)
{
    Base* pThisPtr = (Base*)pEcx;

    std::cout << "In VMTHookFncn";
}

这里利用__fastcall调用约定用来获取this指针

成功Hook虚函数B
output
利用调试器,进入Hook函数中,可以看到this指针的B已经被替换成了VMTHookFuc
hook

封装

剩下的就是封装了

这里的命名规则遵循STL标准库的小写规则

这里的实现是整个VMT替换,这样也可以方便的实现获取原函数。
vmt_hook.h

#include<memory>

class vmt_hook
{
public:
	vmt_hook(void* obj, size_t num_funcs);
	
	void hook(size_t index, void* func);
	void unhook(size_t index);

	template <typename T>
	T get_original(size_t index);

	void enable();
	void disable();
private:
	void*** m_object;
	size_t m_num_funcs;

	void** m_original_table;
	std::unique_ptr<void*[]> m_new_table;
};

template<typename T>
inline T vmt_hook::get_original(std::size_t index)
{
	return static_cast<T>(m_original_table[index]);
}

vmt_hook.cpp

#include "vmt_hook.h"

vmt_hook::vmt_hook(void* obj, size_t num_funcs) :
	m_object(static_cast<void***>(obj)),
	m_num_funcs(num_funcs + 1),
	m_original_table(*m_object),
	m_new_table(std::make_unique<void*[]>(m_num_funcs))
{
	std::copy_n(m_original_table - 1, m_num_funcs, m_new_table.get());
}

void vmt_hook::hook(size_t index, void* func)
{
	m_new_table[index + 1] = func;
}

void vmt_hook::unhook(size_t index)
{
	m_new_table[index + 1] = m_original_table[index];
}

void vmt_hook::enable()
{
	*m_object = m_new_table.get() + 1;
}

void vmt_hook::disable()
{
	*m_object = m_original_table;
}

总结

  • 执行速度:10
  • 编写难度:3-5
  • 检测率:3

VMT Hook是最好的Hook方法之一,因为没有API或者检测这类Hook的通用方法。
但大多数反作弊引擎都会在D3D渲染引擎上检测VMT Hook。当然,只要你有经验,你的Hook就不会被检测

最后

以上就是淡然万宝路为你收集整理的C++——Hook教程[1]:虚函数表(VMT)Hook前言虚函数表(VMT)VMT Hook的实现封装总结的全部内容,希望文章能够帮你解决C++——Hook教程[1]:虚函数表(VMT)Hook前言虚函数表(VMT)VMT Hook的实现封装总结所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(40)

评论列表共有 0 条评论

立即
投稿
返回
顶部