为了探究虚表的今生前世,先来一段测试代码

虚函数类:

 class    CTest
{
public:
int m_nData; virtual void PrintData()
{
printf("Data = 0x%x\n", m_nData);
}
}; class CBase1
{
public:
int m_nData; virtual void PrintData1() = ;
}; class CBase2
{
public:
int m_nData; virtual void PrintData2() = ;
}; class CBaseTest : public CBase1, public CBase2
{
public:
void PrintData1()
{
printf("Data = 0x%x\n", CBase1::m_nData);
} void PrintData2()
{
printf("Data = 0x%x\n", CBase2::m_nData);
}
};

测试代码:

 void    Test()
{
CTest oCTest;
CTest* pCTest = new CTest(); pCTest->m_nData = 0x8888;
pCTest->PrintData(); oCTest.m_nData = ;
oCTest.PrintData(); delete pCTest;
} void BaseTest()
{
CBaseTest oCBaseTest; oCBaseTest.PrintData1();
}

1、虚表位于何处?

WinDbg显示虚表的地址的属性:Usage    RegionUsageImage(代表此地址区域被映射到二进制文件的镜像),为只读属性。

2、同一个类对象的虚表位置相同吗?

同一个类对象的虚表位置相同。

加载模块的内存位置:0x00380000

虚表的VA = 0x003FDF2C - 0x00380000 = 0x0007DF2C

3、虚表需要在加载后进行初始化吗?

否,虚表的位置在PE文件的 .rdata 节中,.rdata 是存放程序常量的地方,属性为只读

从2的截图中可以看出,虚表第一个函数(CTest::PrintData)的地址 = 0x003A9EFB
其VA =  0x003A9EFB- 0x00380000 = 0x00029EFB
由于该PE文件的基址 = 0x00040000(默认情况下)
故其未重定位前的虚拟地址 = 0x00040000 + 0x00029EFB = 0x00429EFB
由于x86平台文件都是小尾储存,倒过来写就是 FB 9E 42 00,即如图左上红圈所示
说明PE文件在编译好后已经将虚表和虚表中的虚函数地址填写完毕并写入.rdata区

4、多父类继承的虚表如何存放?

多重继承的子类对象实际上是将每个父类的完整数据按顺序依次排布,所以拥有每个父类的虚表,父类每个虚表的位置同样在每个父类的起始位置。

oCBaseTest对象内存分布

5、何为虚表Hook?
通过以上对虚表的来龙去脉的分析,有IAT Hook基础的同学可以很容易的想到如何进行虚表Hook了。
5.1 改PE文件
    直接改掉PE文件虚表位置的函数指针,指向自己伪造的虚表地址(当然必须保证自己的虚表函数指针有效,且函数调用形式和参数个数一致)
5.2 内存中Hook
    当PE文件加载后,直接在内存中修改虚表函数指针(注意要去写保护,当然调试工具是没问题的)。

5.3 Hook测试(内存Hook的方式)

测试代码:

 class    CVFHook
{
public:
typedef void (CVFHook::* MemberFunctionPtr)(); public:
static BOOL Hook(void* pVirtrulFunctionVA); private:
void PrintData();
};
BOOL CVFHook::Hook(void* pVirtrulFunctionVA)
{
HMODULE hHookedModule = ::GetModuleHandle(NULL); if (NULL == hHookedModule)
{
_tprintf(_T("Get module handle fail\n"));
return FALSE;
} //取重定位后的虚表地址
MemberFunctionPtr* pMemberFunctionPtr = (MemberFunctionPtr*)((INT64)pVirtrulFunctionVA + (INT64)hHookedModule); //去除读写保护
DWORD dwOldProtect = ;
::VirtualProtect(pMemberFunctionPtr, sizeof(pMemberFunctionPtr), PAGE_READWRITE, &dwOldProtect);
*pMemberFunctionPtr = &CVFHook::PrintData; return TRUE;
} void CVFHook::PrintData()
{
printf("PrintData is facked!!!\n");
}

然后再dll的加载位置传入虚表的VA参数调用即可:

 BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
CVFHook::Hook((void*)0x0007DF2C);
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

注意:这里我们伪造的虚函数 CVFHook::PrintData() 我直接用的是与要 Hook 的虚函数相同的成员函数形式,由于Release版的代码可能会被编译器优化掉函数调用形式,如果如此,需要我们手动编写汇编形式的虚函数代码。

启动测试程序 Console.exe ,然后用 APIMonitor 工具将 VirtrualFunctionHook.dll 注入,然后按任意键观察结果如下:

我们发现堆对象被成功Hook了,但局部对象没有被Hook掉,这是为什么呢?难道上面的分析错了?

我们来对前面的测试代码进行反汇编后观察:

发现对象调用PrintData时并没有通过虚表调用,而是直接使用普通成员函数的调用方式,以节约开销,只有使用对象指针或引用来调用虚函数时才会使用虚表调用的方式,故虚表Hook的方式失效.

C++浅析——虚表和虚表Hook的更多相关文章

  1. 逆向实用干货分享,Hook技术第二讲,之虚表HOOK

    逆向实用干货分享,Hook技术第二讲,之虚表HOOK 正好昨天讲到认识C++中虚表指针,以及虚表位置在反汇编中的表达方式,这里就说一下我们的新技术,虚表HOOK 昨天的博客链接: http://www ...

  2. C++学习 - 虚表,虚函数,虚函数表指针学习笔记

    http://blog.csdn.net/alps1992/article/details/45052403 虚函数 虚函数就是用virtual来修饰的函数.虚函数是实现C++多态的基础. 虚表 每个 ...

  3. C++ 继承与接口 知识点 小结(一)

    [摘要] 要求理解覆盖.重载.隐藏的概念与相互之间的差别.熟记类继承中对象.函数的訪问控制:掌握虚函数.虚函数表.虚函数指针的联系:理解区分虚函数和虚继承在虚方法.虚指针在空间分配上的重点与难点:熟练 ...

  4. c++ 虚函数和纯虚函数

    在你设计一个基类的时候,如果发现一个函数需要在派生类里有不同的表现,那么它就应该是虚的.从设计的角度讲,出现在基类中的虚函数是接口,出现在派生类中的虚函数是接口的具体实现.通过这样的方法,就可以将对象 ...

  5. Oracle数据库基础知识2

    字符操作相关_1 1.CONCAT关键字作用:连接字符串语法:CONCAT(字串1, 字串2)例如: CONCAT('hello','world') FROM DUAL; 注意:Oracle的CONC ...

  6. C++学习笔记 封装 继承 多态 重写 重载 重定义

    C++ 三大特性 封装,继承,多态 封装 定义:封装就是将抽象得到的数据和行为相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成类,其中数据和函数都是类的成员,目的在于将对 ...

  7. C++类所占内存大小计算

    C++类所占内存大小计算 说明:笔者的操作系统是32位的. class A {}; sizeof( A ) = ? sizeof( A ) = 1明明是空类,为什么编译器说它是1呢? 空类同样可以实例 ...

  8. OD: Windows Security Techniques & GS Bypassing via C++ Virtual Function

    Windows 安全机制 漏洞的万源之本在于冯诺依曼设计的计算机模型没有将代码和数据进行区分——病毒.加壳脱壳.shellcode.跨站脚本攻击.SQL注入等都是因为计算机把数据和代码混淆这一天然缺陷 ...

  9. [置顶] 【C/C++学习】之十三、虚函数剖析

    所谓虚函数,虚就虚在“推迟联编”或者“动态联编”上,一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的.由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被称为“ ...

随机推荐

  1. 用css计算选中的复选框有几个

    上代码: <!DOCTYPE html> <html> <head> <meta charset='UTF-8'> <title>计数< ...

  2. Muzli – 所有你需要的设计灵感都在这

    曾几何时,三个家伙一起工作,他们分享各种令人振奋的资源链接.然后,其中一个人有一个想法.一个绝妙的主意,是否可以有一种方式来与他人分享这些链接?因此, Muzli 诞生了.所有你需要的设计灵感这里都有 ...

  3. Method Draw – 很好用的 SVG 在线编辑器

    Method Draw 是一款在线 SVG 编辑器,是 SVG Edit 的一个分支.Method Draw 的目的是改进 SVG Edit 的可用性和用户体验.它移除了 line-caps/corn ...

  4. angularjs——插值字符串

    一.何为插值字符串? 其实插值字符串的意思就是:拥有字符插值标记的字符串.如: hello,{{ to }}....字符插值标记:相当于我们平时在字符串替换中使用到的占位符.上面的例子中的{{to}} ...

  5. windows 端口 任务

  6. CSS3 学习笔记

    border-radius 圆角是做网页永远绕不过的话题,以前基本是通过背景图片做的,有了 CSS3 以后通过属性就 能够搞定,我们可以通过 border-radius 设置元素的圆角半径. bord ...

  7. Microsoft Dynamics CRM MVP

    MVP 一个响亮的名称, 不是 Kobe Bryant 不是 LeBron Raymone James 这是 Microsoft Dynamics CRM MVP 求证Link: http://mvp ...

  8. 关于一些网络代理实现智能流量分流的研究(PAC脚本介绍及利用)

    因为工作原因,需要访问一些国外的网站(科学上网),但直接FQ并不方便,于是研究了一些代理软件,比如Nydus,Green等, 在Nydus的Proxy版本中发现了实现国内国外流量的智能分流的办法,通过 ...

  9. SP2013 SP1(kb28805502)补丁安装测试初体验

    安装完SP1(kb28805502)第一印象是整体页面加载浏览速度非常快了,在笔记本建立的虚拟机能达到肉眼感觉不到卡顿真的是非常快了. 1.新添加了页面个性化设置功能菜单 3.默认访问网站的页面显示, ...

  10. IOS圆头像

    前言 随着腾讯QQ的普及,现在越来越多的社交类APP在显示好友头像时,都选择用圆形头像,效果如下(不包括黑底): 在ios开发中,大致有以下三种方案来实现圆形头像效果. 方案一:用Quartz2D绘制 ...