大家在看了文章1后以经对取得虚函数地址有所了解,但用它作回调函数还是有一点问题,因为要使成员函数正确运行,我们必须每次在调用这前传一个this给ecx(对应的成员函数中没有操作类成员除外,原因见(文章1)),作为回调函数,它可不给你这个机会,那怎么实现呢?
下面就是一种实现方法:
我们可以在数据段中开一个数组,在这个数组里存放一些特殊的数据,然后将这个数组的地址作为回调函数的地址,然后这个数组将被作为代码运行。那么这个数组对应的代码又做些什么呢?很简单的:将对象的地址传入ecx, 然后jmp到真正的回调函数。
下面是个例子:
主要部分也就是ThunkInit(ThunkData t, void *This, int VirFucID);
传入一个数组指针,一个对象的指针,一个对应第几个虚函数。然后这个函数将生成对应的可执行代码:将对象的地址传入ecx, 最后jmp到真正的回调函数。
其次,就是用数组的地址作为函数地址,调用对应的函数时,先执行数组里的代码,这样就保证了函数内部的this指针的正确性。
例3
class CCC
{
public:
CCC() {m_data1=50;}
~CCC() {};
virtual void print1() { ::printf("m_data1=%dn", m_data1); }
virtual void print2() { ::printf("m_data1+10=%dn", m_data1+10); }
virtual void print3(int num) { ::printf("m_data1+%d=%dn", num, m_data1+num); }
private:
int m_data1;
};
typedef unsigned char ThunkData[14];
void inline ThunkInit(ThunkData t, void *This, int VirFucID)
{ // begin ThunkInit
t[0] = 0xB9; // mov ecx,
*((long *)(t+1)) = (long) This; // this
*((short int *)(t+5)) = 0x018B; // mov eax, [ecx]
t[7] = 0x5; // add eax,
*((long *)(t+8)) = VirFucID*4; // VirFucID*4
*((short int *)(t+12)) = 0x20FF; // jmp [eax]
} // end ThunkInit
void print5Times1(long fucAddr)
{
int i=5;
while(i--)
((void (__stdcall *)(void))fucAddr)();
}
void print5Times2(long fucAddr, int num)
{
int i=5;
while(i--)
((void (__stdcall *)(int))fucAddr)(num);
}
void main()
{
CCC cc;
ThunkData fucAddr;
//不做回调函数
ThunkInit(fucAddr, &cc, 0);
((void (__stdcall *)(void))(long)fucAddr)();
ThunkInit(fucAddr, &cc, 1);
((void (__stdcall *)(void))(long)fucAddr)();
ThunkInit(fucAddr, &cc, 2);
((void (__stdcall *)(int))(long)fucAddr)(100);
//做回调函数
ThunkInit(fucAddr, &cc, 0);
print5Times1((long)fucAddr);
ThunkInit(fucAddr, &cc, 1);
print5Times1((long)fucAddr);
ThunkInit(fucAddr, &cc, 2);
print5Times2((long)fucAddr, 100);
}
注意:
1. ((void (__stdcall *)(void))(long)fucAddr)();是将fucAddr的地址转换为long型,然后再将它转化为(void (__stdcall *)(void))类型函数(如有不明白,请找有关资料),最后调用此函数。 2. ThunkInit函数可能较难理解,下面是进一步的讲解: t[0] = 0xB9; //这个操作码为 mov ecx, 它将把接下来的long立即数,送入ecx *((long *)(t+1)) = (long) This; // this, 对应的对象的地址,在运行是将作立即数,送入ecx *((short int *)(t+5)) = 0x018B; // mov eax, [ecx] 取得虚函数表的起始位置 t[7] = 0x5; // add eax, *((long *)(t+8)) = VirFucID*4; //VirFucID*4 计算出存放对应虚函数地址的地址 *((short int *)(t+12)) = 0x20FF; // jmp [eax] 调转到以应的虚函数
最后我想加几句题外话,这些例子都只是简单的用应,文章1中两个例子没有实际意义,旨在为例3做铺垫。至于有没有用,我也不知,只是我在一些程序中应用了而己,刚开始只是从其它原码中拿过来用,也不解其中的原理。但一次在图书管中看了<编程深入引导>>(中国水利水电出版社)后,由于它详细说明了类的实现,并附了对应的汇编源码,让我豁然开朗,经过几翻测试,终于弄懂了它的原理,觉得做法挺不错,也就写了上面这些,文笔较差,还不知能否让大家看懂。
不过这种做法有点费涩难懂,在附件中的工程DD,演示了“静态成员函数+对象的静态指针”做回调函数,不过在这里得注意一点,后面这种做法,整个类只能定义一个对象(因为对象的静态指针)。
|
相关推荐
行业分类-设备装置-FPC吸附胶纸转贴组件.zip
电脑故障维修判断指导大全(联想内部文件) 第一部分---总则 ...§1.1 进行电脑维修应遵循的基本原则: 一、 进行维修判断须从最简单的事情做起 简单的事情,一方面指观察,另一方面是指简捷的环境。
行业资料-电子功用-导电胶配对模切对半转贴加工方法
电子政务-导电泡棉转贴装置.zip
博文链接:https://tj007-bo.iteye.com/blog/128169
Struts-menu源码分析(转贴).rar
此外,Struts2与Spring框架的集成非常紧密,能够利用Spring的依赖注入功能,简化Action类的管理,同时方便与其他框架如Hibernate、iBatis等进行集成。它支持多种结果类型,除了JSP外,还包括JasperReports报表、...
行业文档-设计装置-木器、玻璃用贴花纸生产及转贴方法.zip
总的来说,实现jQuery的转贴功能需要对DOM操作、事件处理、Ajax请求以及不同社交网络的API有深入理解。通过合理地组织代码和利用jQuery的功能,可以创建一个高效且易于维护的分享系统,提升网站的互动性和用户参与度...
动网转贴
行业资料-电子功用-全自动导电布成型转贴穿管设备及工艺的介绍分析.rar
[转贴]Symbian编程VC开发环境设置 (方便个人学习用,转载自 rocklys的专栏,转贴请搜索原作者) - waferham的专栏
<br> 这个工具的使用方法与通常的转贴工具完全相同,在浏览器中选中需要转换的内容并复制,然后在本程序窗口中点击 HTML->UBB 按钮即可完成转换,右键菜单中可使用 追加模式 覆盖模式 插入模式进行转换。...
**Axis学习笔记(网页转贴)** Axis是一个开源的Java库,主要用于创建和使用Web服务。它是Apache软件基金会的一部分,广泛应用于开发基于SOAP(简单对象访问协议)的Web服务。本学习笔记将深入探讨Axis在Web服务开发...
NULL 博文链接:https://tianjun309.iteye.com/blog/887257
用户只需下载并安装工具,然后在动易系统的论坛中找到想要转贴的帖子,复制帖子内容,打开工具进行转换,最后将转换后的UBB代码粘贴到目标论坛即可。整个过程快捷高效,极大地提高了内容分享的效率。 总的来说,...
4. 用户友好界面:为了方便普通用户使用,工具很可能设计有直观易懂的操作界面,让用户无需复杂的步骤就能完成转贴。 5. 安全性:考虑到论坛的安全和个人隐私,工具应确保在处理数据时不会泄露用户的敏感信息,也...
易语言动网转贴.rar 易语言动网转贴.rar 易语言动网转贴.rar 易语言动网转贴.rar 易语言动网转贴.rar 易语言动网转贴.rar