C++成员不通过对象调用的直接调用写法

C++成员不通过对象调用(.或->方式)的另类(C式)调用写法

#include <iostream>
using namespace std;

/* 
我们知道,成员函数和普通函数最大的区别就是成员函数包含一个隐藏的参数this指针,用来表明成员函
数当前作用在那一个对象实例上。

根据调用约定(Calling Convention)的不同,成员函数实现this指针的方式也不同。

1.    如果使用__thiscall调用约定,那么this指针保存在寄存器ECX中,VC编译器缺省情况下就是这样的。
2.    如果是__stdcall或__cdecl调用约定,this指针将通过栈进行传递,且this指针是最后一个被压入栈
的参数,相当于编译器在函数的参数列表中最左边增加了一个this参数。 
*/
class Base {
public:
virtual void f() { cout << "Base::f()" << endl; }
virtual void g() { cout << "Base::g()" << endl; }
virtual void h() { cout << "Base::h()" << endl; }

virtual void foo(Base *pThis) { 
pThis->hello(this); 
}
virtual void hello(Base *pThis) { 
pThis->h();
}

//virtual void __stdcall hello(Base *pThis) {} //成员函数指定了__stdcall调用约定
};

int test()
{
typedef void(*Fun)();

Base *b = new Base;
cout << *(int*)(&b) << endl; //虚函数表的地址存放在对象最开始的位置

Fun funf = (Fun)(*(int*)*(int*)b);
Fun fung = (Fun)(*((int*)*(int*)b + 1));
Fun funh = (Fun)(*((int*)*(int*)b + 2));

/************************************************************************/
/*    调用内部无this参与的成员(包括变量和方法)的对象方法    */
/************************************************************************/

//如果下面三个方法里,没有用到与对象相关的成员可以不用为ecx赋值,否则会出错
funf();
fung();
funh();


/************************************************************************/
/* 调用内部有this参与的成员(包括变量和方法)的对象方法 */
/************************************************************************/

//少了__stdcall(注意位置),栈会不平衡了:本来c++默认是thiscall,如果不要,vs编译器会让调用者平衡栈,即多了一句 add esp, 4
typedef void(__stdcall *Fun_Base)(Base*);
Fun_Base foo = Fun_Base(*((int*)*(int*)b + 3));

//就是多增加这句,因为编译器对c++默认采用thiscall
_asm{
mov ecx, dword ptr[b]
}

foo(b);//里面用到this了,不给ecx赋值,this就不对


/************************************************************************/
/* 调用内部有this参与的成员(包括变量和方法)的对象方法,纯汇编版本    */
/************************************************************************/

//尝试调用虚函数表的第四个方法
_asm{
//同上,如果所call的方法里,没有用到与对象相关的成员可以不用为ecx赋值,否则会出错
mov ecx, dword ptr[b]

push ecx    //一个入参

//mov ecx, b或mov eax, [b],表达同一个意思,vs最后都是mov ecx, dword ptr[b];
//实质是mov eax, [EBP-04h],结果表现为ecx=b,即从栈上获取指针b的值,而不是*b(即*(int*)b == ptr_vftable)的值。
mov eax, [b]    //获取对象指针
mov eax, [eax]    //虚函数表首地址,即对象的开始处,ptr_vftable = [eax + 0] = [ecx] = [this_of_b] = [b]
call [eax + 0x0c]    //调用虚函数表的第四个,某虚函数表里第N个方法(x86),[ptr_vftable + (N - 1) * 4] = [[eax]+(N-1)*4]
}

/************************************************************************/
/* 正常的调用 */
/************************************************************************/

/*
00EF9886 8B 45 F4 mov eax, dword ptr[b]
00EF9889 50 push eax
00EF988A 8B 4D F4 mov ecx, dword ptr[b]
00EF988D 8B 11 mov edx, dword ptr[ecx]
00EF988F 8B 4D F4 mov ecx, dword ptr[b]
00EF9892 8B 42 0C mov eax, dword ptr[edx + 0Ch]
00EF9895 FF D0 call eax
*/
b->foo(b);

//http://blog.csdn.net/haoel/article/details/1948051/
/*
虚函数表的结束结点,就像字符串的结束符“/0”一样,其标志了虚函数表的结束。
这个结束标志的值在不同的编译器下是不同的。

1. 在WinXP+VS2003下,这个值是NULL。
2. 而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下,
2.1 这个值是如果1,表示还有下一个虚函数表,
2.2 如果值是0,表示是最后一个虚函数表。
*/
cout << (Fun)(*((int*)*(int*)b + 5)); // 最后一个位置为0,表明虚函数表的结束

return 0;
}

 

C++虚函数介绍

1. 先从C++看看下虚函数表和多态表现 http://blog.csdn.net/haoel/article/details/1948051/

2. 有虚函数的类的大小 http://blog.csdn.net/hackbuteer1/article/details/7883531

3. 再从汇编看,类的继承,虚函数表的形成,表放在哪个地址等 http://www.pediy.com/kssd/pediy10/60538.html

虚函数表的形成,是在类构造函数里,对实例对象首地址里存放的虚函数数组进行修改实现的。

class Child :public Base{
public:
virtual void vf1(){cout<<"I'm in sub Class.";}
virtual void vf2(){cout<<"I'm in sub Class.";}
//...
}

 

.text                     .rdata?
class Child内存           这个类的所有对象所共有的
+-------------+          +-----+-----+-----+-----+----+
| ptr_vftable | -------> | vf1 | vf2 | vf3 | ... |end?|
+-------------+          +-----+-----+-----+-----+----+
|             |
|other members|
|             |
+-------------+

 

逆向C++(中文版)
http://wenku.baidu.com/link?url=bjLVj2eqfe29_Edzi99MBGJeoCtVaHDXj-3r4s4lm771BAQnJ0WIUaQywPZgGq3Yz_uU9yh-B0V6q5SFMUhRo0t436BUnUdaHuhpwERvLvC

 

C++多态性:

GoF著作中未提到的设计模式(4):Double Dispatch
http://www.cnblogs.com/west-link/archive/2011/07/26/2116887.html
http://en.wikipedia.org/wiki/Double_dispatch

 

posted @ 2015-05-08 00:04  山岚的一缺  阅读(1001)  评论(0编辑  收藏  举报
喜欢
评论
收藏
顶部