大三暑假莫邪以内联汇编_4
1-3篇,我大概的介绍了__cdecl __stdcall 堆栈与 函数 的关系与处理过程。发现现在****上,写C++文章的真少。今天的主题,我想带大家揭开__thiscall 调用约定以及类的 this指针的面貌。
C++是一个庞大的东西,今天我们主要来看下 this与成员变量的关系(成员函数除外),C++的一个概貌。今天的演示程序如下:
#include<iostream> class CExp { public: explicit CExp(size_t _value , size_t _value2 = 0); ~CExp(); public: CExp(const CExp& _copy); CExp& operator = (const CExp& _copy); public: size_t GetExp()const; private: size_t m_value; size_t m_value2; }; CExp::CExp(size_t _value, size_t _value2):m_value(_value) ,m_value2(_value2) { } CExp::~CExp() { } CExp::CExp(const CExp& _copy) { m_value = _copy.m_value; } CExp& CExp::operator = (const CExp& _copy) { if(this != &_copy) { m_value = _copy.m_value; } return *this; } inline size_t CExp::GetExp()const { return m_value2; } int main() { try { CExp Gamer(99,1);//设计两个参数,主要是看入栈顺序 size_t value = Gamer.GetExp(); std::cout<<value<<std::endl; std::cout<<&Gamer<<std::endl; std::cout<<static_cast<int>(*(reinterpret_cast<unsigned char*>(&Gamer)))<<std::endl; //C风格为(int)(*((unsigned char*)&Gamer)) } catch(...) { std::cout<<"Unknown Error"<<std::endl; } return 0; }
在 这里,我们的编译器是古老的 VC++6.0,写文章前我也搜过网上关于thiscall的文档,其中发现存放this指针的寄存器也是根据不同编译器的情况而不同。我们来看看VC上this的情况。 老规矩 我们启动反汇编过程。(假如,你不知道操作,请看内联__2)
图一
我们发现 prolog的工作 比较我们之前的文章,多出来点有趣的东西 比如__ehhandler&_main
这是什么呢?通过我自己查找相关资料,发现 这和 windows 上SEH 的 异常处理有关(我们知道C++是有它的异常机制的,)想深入了解,请大家搜索
Windows SEH 技术, 看来我们的prolog工作 和前几天 分析差不多 我们来看看 函数调用部分
图二
我们发现 这里的try 竟然会让main的第一个local对象初始化为0
之后是构造函数的调用,我们发现参数是从右到左入栈,那this指针呢?
我们看到 lea ecx,[Gamer], 看了this指针并没有push,而是把Gamer对象的地址存在了ecx里 (好家伙,没反汇编钱,我的第一篇文章就以后是push了,哎这里改过)
调试技巧提示:当运行到断点处的时候,并没有执行断点处的指令,如果此时去查看寄存器里的内容,这可不是一个明智的作为。
我们发现Call以后 右把ebp-4 初始化为1个字节 (注意:前面是dword 而这里是byte)
让我们看下构造函数是怎么处理的。
图三
看来thiscall 和 我们之前调试的__cdecl __stdcall 差不多
我们看到 mov dword ptr[ebp-4],ecx pop后的ecx 就是我们原先存的 对象的地址, 这时候 我们发现 构造函数把它 当做了 第一个local对象, 之后看看
他把 第一个参数(非进栈顺序) [ebp+8]放到了 ecx处(等执行完构造函数,我们用memory去看看ecx的内存内容),之后的第二个参数[ebp+0ch]也放进了ecx+4处
之后是 epilog 工作, 我们发现 他并没有 add esp 恢复栈平横
执行完构造函数,我们来看看 ecx的内存存储情况
图四
ecx 就是 Gamer的地址, 我们程序里最后会输出 Gamer的地址来比对下, 我们发现ecx的双字部分 存的 是0x00000063 正是我们的十进制99, 而ecx+4处便是我们的存的1,看来 我们类的this指针就是对象的地址,而它的成员变量,就是从this的地址开始存的。(这里我们先不分析虚函数表,其实图片上的红色信息表示寄存器的前后改动信息, 我们发现最后一行有个01 该怎么解释呢?:))
继续看看GetExp() 一个inline函数 是怎么做的
图五
既然我们知道了 this指针存在ecx 而 ecx 又作为成员函数的第一个local对象处理, 下面的问题就迎刃而解了。[ebp-4]此时是this地址 mov给了 eax,而eax+4 实际上就是this+4 结果把 值放到eax 然后返回。 epilog 依旧没有恢复堆栈平衡
不知道为什么?看了下析构也没有?在这里,请教下懂的人,为什么会这样呢?我很不解。
最后我写了一个 验证this指针 及存储成员数据的 代码
C++的风格 可能对于C程序员来说看的吃力, 下面有C风格的注释
指针也就那点好处呵呵,喜欢操作内存的感脚儿
贴下运行结果吧~
怎么样?ecx没骗你吧?:)
- 2楼template_cplus昨天 17:33
- 我私下把try catch 去了 汇编的结果 和 上面的不同, 主要还是ecx 的处理不同。具体情况大家可以自己试下
- 1楼template_cplus昨天 17:00
- 我傻掉了, 一个结构体有什么好分析的。有删掉的冲动~