C++浅析——虚表和虚表Hook
为了探究虚表的今生前世,先来一段测试代码
虚函数类:
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的更多相关文章
- 逆向实用干货分享,Hook技术第二讲,之虚表HOOK
逆向实用干货分享,Hook技术第二讲,之虚表HOOK 正好昨天讲到认识C++中虚表指针,以及虚表位置在反汇编中的表达方式,这里就说一下我们的新技术,虚表HOOK 昨天的博客链接: http://www ...
- C++学习 - 虚表,虚函数,虚函数表指针学习笔记
http://blog.csdn.net/alps1992/article/details/45052403 虚函数 虚函数就是用virtual来修饰的函数.虚函数是实现C++多态的基础. 虚表 每个 ...
- C++ 继承与接口 知识点 小结(一)
[摘要] 要求理解覆盖.重载.隐藏的概念与相互之间的差别.熟记类继承中对象.函数的訪问控制:掌握虚函数.虚函数表.虚函数指针的联系:理解区分虚函数和虚继承在虚方法.虚指针在空间分配上的重点与难点:熟练 ...
- c++ 虚函数和纯虚函数
在你设计一个基类的时候,如果发现一个函数需要在派生类里有不同的表现,那么它就应该是虚的.从设计的角度讲,出现在基类中的虚函数是接口,出现在派生类中的虚函数是接口的具体实现.通过这样的方法,就可以将对象 ...
- Oracle数据库基础知识2
字符操作相关_1 1.CONCAT关键字作用:连接字符串语法:CONCAT(字串1, 字串2)例如: CONCAT('hello','world') FROM DUAL; 注意:Oracle的CONC ...
- C++学习笔记 封装 继承 多态 重写 重载 重定义
C++ 三大特性 封装,继承,多态 封装 定义:封装就是将抽象得到的数据和行为相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成类,其中数据和函数都是类的成员,目的在于将对 ...
- C++类所占内存大小计算
C++类所占内存大小计算 说明:笔者的操作系统是32位的. class A {}; sizeof( A ) = ? sizeof( A ) = 1明明是空类,为什么编译器说它是1呢? 空类同样可以实例 ...
- OD: Windows Security Techniques & GS Bypassing via C++ Virtual Function
Windows 安全机制 漏洞的万源之本在于冯诺依曼设计的计算机模型没有将代码和数据进行区分——病毒.加壳脱壳.shellcode.跨站脚本攻击.SQL注入等都是因为计算机把数据和代码混淆这一天然缺陷 ...
- [置顶] 【C/C++学习】之十三、虚函数剖析
所谓虚函数,虚就虚在“推迟联编”或者“动态联编”上,一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的.由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被称为“ ...
随机推荐
- jQuery超酷下拉插件6种效果演示
原始的下拉框很丑啦, 给大家一款jQuery超酷下拉插件6种效果 效果预览 下载地址 实例代码 <div class="container"> <section ...
- 小清新的jQuery ck-slide 图片轮播
ck_slide 是一款小清新的jQuery 幻灯片插件,它非常小巧,压缩后仅 3KB,基本功能可以满足.它支持淡入淡出/左右滚动.箭头/圆点控制.自动播放. 在线实例 默认(淡入淡出) 左右滚动 自 ...
- mongodb系列3 mongo mongoskin 连接以及连接数的问题进阶
1)使用mongodb连接mongo var mongo = require('mongodb'), //引入mongodb dbHost = '127.0.0.1', dbPort = 27017; ...
- 当jquery ajax遇上401请求
jquery ajax是个很常用接口,而在请求时候,可能存在响应401的情况(身份认证过期或未登录),比较容易出现在混合应用上,如何进行身份认证,重发失败请求,还是值得注意的. ajax请求有两种方式 ...
- 如何将List<string>转化为string
Convert List, string. A List can be converted to a string. This is possible with the ToArray method ...
- CRM 2015 快速抓取表单上字段
var table='<table><tr><td>序号</td><td>字段名</td><td>Schema名称& ...
- 为Autodesk Viewer添加自定义工具条
如果你参加过我们近期的活动,你就会频繁的听到我们现在正在做的Autodesk Viewer大模型浏览器,这是一个不需要下载任何插件,基于WebGL技术的浏览器,可以支持几十种数据格式.同时viewer ...
- C C++ TDD单元测试非常好的书
http://product.china-pub.com/199003 测试驱动的嵌入式C语言开发 Test Driven Development for Embedded C <测试驱动的嵌入 ...
- 为什么relativelayout.layoutParams的width为-1
源码里看下就知道了.. -1不代表宽度,代表MATCH_PARENT常量的值public static final int FILL_PARENT = -1; public static final ...
- (20160602)开源第三方学习之SDWebImage
这个类库提供一个UIImageView类别以支持加载来自网络的远程图片.具有缓存管理.异步下载.同一个URL下载次数控制和优化等特征. 地址:https://github.com/rs/SDWebIm ...