上一篇介绍了gcc对成员函数指针做了thunk的处理,本篇介绍vc对成员函数指针如何处理,还有动态绑定相关的处理。

同样用回上一篇的例子:

struct point {float x,y;};
struct obj
{
virtual ~obj {}
void foo(int) {}
void foo(point) {}
virtual void vfoo() {}
};
struct objobj : public obj
{
virtual ~objobj {}
virtual void vfoo() {}
}; void main()
{
obj o;
objobj oo;
//void* pofp = (void*) (void(obj::*)(point))&obj::foo; // error C2440: “类型转换”: 无法从“void (__cdecl obj::* )(point)”转换为“void *”
void(obj::*pi)(int) = &obj::foo;
void(obj::*pp)(point) = &obj::foo;
void(objobj::*vp)() = &objobj::vfoo;
NOOP
((&oo)->*vp)();
NOOP
((&oo)->*pi)();
NOOP
((&o)->*pp)(pt);
}

成员函数指针定义以及调用的代码,所对应的反汇编:

`3f461159 488d05e6feffff  lea     rax,[test!ILT+(?fooobjQEAAXHZ) (`3f461046)]
`3f461160 48898424d8000000 mov qword ptr [rsp+0D8h],rax ; void(obj::*pi)(int) = &obj::foo;
`3f461168 488d05b4feffff lea rax,[test!ILT+(?fooobjQEAAXUpointZ) (`3f461023)]
`3f46116f 48898424e0000000 mov qword ptr [rsp+0E0h],rax ; void(obj::*pp)(point) = &obj::foo;
`3f461177 488d05b9feffff lea rax,[test!ILT+(??_9objobj$B7AA) (`3f461037)]
`3f46117e 48898424e8000000 mov qword ptr [rsp+0E8h],rax ; void(objobj::*vp)() = &objobj::vfoo;
`3f461186 488d8c2498000000 lea rcx,[rsp+98h]
`3f46118e ff9424e8000000 call qword ptr [rsp+0E8h] ; ((&oo)->*vp)(); test!ILT+50(??_9objobj$B7AA) (00000001`3f461037)
`3f461195 ba01000000 mov edx,
`3f46119a 488d8c2498000000 lea rcx,[rsp+98h]
`3f4611a2 ff9424d8000000 call qword ptr [rsp+0D8h] ; ((&oo)->*pi)(1); test!ILT+65(?fooobjQEAAXHZ) (00000001`3f461046)

上面有三处指针赋值,被赋地址分别信息分别如下:

test!ILT+(?fooobjQEAAXHZ):
`3f461046 e955020000 jmp test!obj::foo (`3f4612a0)
:> dt `3f4612a0
obj::foo
void test!obj::foo+(
int) test!ILT+(?fooobjQEAAXUpointZ):
`3f461023 e9a8020000 jmp test!obj::foo (`3f4612d0)
:> dt `3f4612d0
obj::foo
void test!obj::foo+(
point) test!ILT+(??_9objobj$B7AA):
`3f461037 e964050000 jmp test!objobj::`vcall'{8}' (`3f4615a0)
:> dt `3f4615a0
objobj::`vcall'{8}'
Symbol not found.
:> u `3f4615a0 L3
test!objobj::`vcall'{8}':
`3f4615a0 488b01 mov rax,qword ptr [rcx]
`3f4615a3 ff6008 jmp qword ptr [rax+] ; => jmp test!objobj::vfoo

函数的调用都经由一个间接跳转,这是hook的基础,天生带上了M属性,这不是本篇的主题。忽视这个间接跳转(或者看作短路),我们认为非虚的成员函数指针直接指向成员函数本体,但是虚函数指针指向的是一小块类似thunk的处理代码。虚函数是动态绑定的,thunk相关的处理是必要的。vc编译器在可以正确分析出绑定的情况下,将这段thunk处理内联到调用处罢了。

SDK中使用thunk的地方还有,atlthunk.h, olecall_.s, oledisp1.cpp, qithunk.s, stdcallthunk.s。这些使用thunk的地方大都与com相关,目的各不相同。例如,qithunk.s就是queryinterface thunk,也就是用于调试com, 别有用心加了一层模仿IUnknown调用虚函数的过程,使得com的方法被调用前都必须先经过qithunk的虚函数,从而可以被中断而不用知道执行的是哪种具体的com。当你不知道com的调试信息时,也可以中断到com的每个方法入口。qithunk是这里面我认为比较容易分析的,只要了解IUKnown接口和虚函数表就可以分析了。

又如IDispatch是用于实现动态绑定的接口,vbscript和jscript中使用到的对象都实现了这个接口。在script中调用对象的属性或方法时,是通过属性名或方法名来绑定com的执行函数。这种方式跟objc的消息调用在形式上有点像。window.getElementById("form"), 调用的是window.invoke(GETDISPID("getElementById"), ..., args("form"),...); 在objc中[window getElementById:"form"],调用是objc_sendMsg(window, "getElementById:", "form");
本篇浅略提及了thunk和动态绑定,有了感性认识后,分析objc中SEL的动态绑定就不会太陌生了。分析objc的文章也请在未来的日子关注。

最新文章

  1. javascript之一切皆为对象3
  2. Android UI控件----ExpandableListView的基本用法
  3. MWeb 1.4 新功能介绍二:静态博客功能增强
  4. DMA控制器
  5. php CLI 模式下的传参方法
  6. 迭代和递归 - leetcode 206. Reverse Linked List
  7. Android修改system只读权限
  8. javascript运行机制之执行顺序详解(转)
  9. 动一动手指,玩转 Kindle Paperwhite 2 (2015.7.13)
  10. Android 共享文件的 Runtime 权限
  11. UVALive - 3401 Colored Cubes
  12. 【LeetCode】- Valid Palindrome(右回文)
  13. SharePoint Solutions Deployment-PowerShell
  14. .Net Core下使用WCF
  15. python的布尔类型与流程控制
  16. 多态原理探究-从C++编译器角度理解多态的实现原理
  17. 基于vue-simple-uploader封装文件分片上传、秒传及断点续传的全局上传插件
  18. day02代码
  19. centos7/rhel7下配置PXE+Kickstart自动安装linux系统
  20. 修改Linux下的文件以及文件夹的权限

热门文章

  1. openssl生成密钥/证书
  2. POJ 3295 Tautology(构造法)
  3. The All-in-One Note
  4. .Net Core 3.0 IdentityServer4 快速入门02
  5. 解析 Microsoft.Extensions.DependencyInjection 2.x 版本实现
  6. Linux 修改网卡名
  7. 在docker中创建使用MySQL,并实现远程连接navicat
  8. 获得shell的几种姿势
  9. winform 数据库资料导出Excel方法(适用于资料数据较多加载慢,不用呈现至DatagridView)
  10. 爬虫之CrawlSpider简单案例之读书网