数组、结构体、类的反汇编

本文主要主要研究C++中数组,结构以及类的反汇编。

1.数组

数组在内存中式一块连续的区域。比如当声明char ch[100]的时候, 我们知道栈是向下增长的, 所以我们开辟地址空间的时候起地址就为[esp(esp会赋值给ebp) – 100, esp].我们可以看下如下的列子:

1 char buf[100]; 2  for (int i = 0; i < 100; i++) 3     buf[i] = i;

我们看下他的反汇编代码:

1     char buf[100];  2     for (int i = 0; i < 100; i++)  3 00BD13A8  mov         dword ptr [i],0   4 00BD13AF  jmp         wmain+3Ah (0BD13BAh)   5 00BD13B1  mov         eax,dword ptr [i]   6 00BD13B4  add         eax,1   7 00BD13B7  mov         dword ptr [i],eax   8 00BD13BA  cmp         dword ptr [i],64h   9 00BD13BE  jge         wmain+4Ch (0BD13CCh)  //到这里是一个典型的for循环标志了 10          buf[i] = i; 11 00BD13C0  mov         eax,dword ptr [i]  //把当前i的值赋予eax,这个eax是用来计算内存位置的 12  00BD13C3  mov         cl,byte ptr [i]       // 因为是char型,所以复制给cl, 这个是给内存赋值的 13  00BD13C6  mov         byte ptr [ebp+eax-6Ch],cl  //计算当前buf[i]在栈中的位置,并赋值 14  00BD13CA  jmp         wmain+31h (0BD13B1h)

从上面我们就可以知道buf[0] = [ebp-100]; buf[1] = [ebp -99]……

2.结构体

我们声明这样一个结构体

1 struct myStruct 2 { 3     int a; 4     char b; 5     float c; 6 };

然后调用之:

1 int _tmain(int argc, _TCHAR* argv[])  2 { 3     int n = sizeof(myStruct);  4     n = sizeof(float); 5     myStruct test;  6     test.a = 0;  7     test.b = 'a';  8     test.c = 1.0f; 9     return 0; 10 }

我们看下其内存是如何分布的:

1   int n = sizeof(myStruct);  2 00FB13A8  mov         dword ptr [ebp-0Ch],0Ch   3     n = sizeof(float);  4 00FB13AF  mov         dword ptr [ebp-0Ch],4  5     myStruct test;  6     test.a = 0;  7 00FB13B6  mov         dword ptr [ebp-20h],0   8     test.b = 'a';  9 00FB13BD  mov         byte ptr [ebp-1Ch],61h  10     test.c = 1.0f; 11 00FB13C1  fld1  12 00FB13C3  fstp        dword ptr [ebp-18h]

我们可以看出这个结构体的大小为OCH(12), float的大小为4, int为4, char为1, 考虑到内存对齐, 大小就得为4的整数倍, 所以为12。

test.a 在内存中的位置为[ebp-20h];

test.b 在内存中的位置为[ebp-2Ch];

test.C 在内存中的位置为[ebp-28h];

3. 类

C++中的类与结构体没有本质上的区别。都是在某一内存地址上开辟地址空间,存放和操作成员变量。我们可以看下如下的测试代码:

1 class myClass  2 {  3  private:  4     int m_a;  5     char m_b;  6     float m_c;  7  public:  8     myClass(){} 9     ~myClass(){}10     void SetA(int a) 11     { 12         m_a = a; 13     } 14     virtual void SetB(char b) 15     { 16         m_b = b; 17     } 18     virtual void SetC(float c) 19     { 20         m_c = c; 21     } 22 };

看下其反汇编的实现:

1   int n = sizeof(myClass);  2 0030144D  mov         dword ptr [ebp-14h],10h   3     myClass test;  4  00301454  lea         ecx,[ebp-2Ch]   5  00301457  call        myClass::myClass (301028h)   6 0030145C  mov         dword ptr [ebp-4],0   7     test.SetA(10);  8  00301463  push        0Ah   9  00301465  lea         ecx,[ebp-2Ch]  10  00301468  call        myClass::SetA (301014h)  11     test.SetB('a'); 12 0030146D  push        61h  13 0030146F  lea         ecx,[ebp-2Ch]  14  00301472  call        myClass::SetB (3011EFh)  15     test.SetC(1.0f); 16  00301477  push        ecx  17  00301478  fld1  18 0030147A  fstp        dword ptr [esp]  19 0030147D  lea         ecx,[ebp-2Ch]  20  00301480  call        myClass::SetC (301104h)  21     return 0; 22  00301485  mov         dword ptr [ebp-0F8h],0  23 0030148F  mov         dword ptr [ebp-4],0FFFFFFFFh  24  00301496  lea         ecx,[ebp-2Ch]  25  00301499  call        myClass::~myClass (3010BEh)  26 0030149E  mov         eax,dword ptr [ebp-0F8h]

这里先稍微解释下其意思:

0030144D 表明这个类的大小为16, 那位什么不是如上结构体的为12呢, 一问类中用到虚函数,所以就有一个指向虚函数列表的指针,其大小为4。

00301457 变量调用了该类的构造函数

00301468 调用了SetA

00301472 调用了SetB

00301499 调用了析构函数

我们分别来看下着四个函数内部的实现。

(1) 构造函数

1 00301550  mov         dword ptr [ebp-8],ecx  2  00301553  mov         eax,dword ptr [this]  //首先我们把this指针放入eax中 3 00301556  mov         dword ptr [eax],offset myClass::`vftable' (306744h)      //然后把虚函数列表的地址放入[eax]中 4 0030155C  mov         eax,dword ptr [this]

如上,我们可以看出此类的构造函数只是简单得把虚函数列表的指针指向[this]的地址。

(2) SetA

这个函数是一个的成员函数,我们看下其是如何实现的

1 003015E3  mov         eax,dword ptr [this]  2 003015E6  mov         ecx,dword ptr [a]  3 003015E9  mov         dword ptr [eax+4],ecx

如上,可以看出很简单,把参数a的值服务[this + 4]这块地址,而这块地址保存的是成员变量m_a的值。

(3) SetB

SetB是一个虚函数, 我们看下其实如何实现的:

1 00301633  mov         eax,dword ptr [this]  2 00301636  mov         cl,byte ptr [b]  3 00301639  mov         byte ptr [eax+8],cl

如上可以发现,虚函数的内部实现和普通的函数没有区别, 唯一不同的是虚函数的位置是放在一个类的虚函数列表里面的。

(4) 析构函数

最后我们看下此类的析构函数是怎么实现的

1 003015A0  mov         dword ptr [ebp-8],ecx  2 003015A3  mov         eax,dword ptr [this]  3 003015A6  mov         dword ptr [eax],offset myClass::`vftable' (306744h)

因为我们在析构函数中什么事情也没有干,所以此析构函数只是简单的吧虚函数列表的地址放到[this]中。

这里我总结下声明一个类其内存是如何分配的:

当写下myStruct test;的时候会在栈上开辟一个内存空间,其地址为[this]

如果此类有虚函数,就有有一个指向此类虚函数列表的指针,其地址为[this ]

m_a 的地址为[this +4]

m_b 的地址为[this + 8]

m_c 的地址为[this + 0Ch]

原文链接: https://www.cnblogs.com/sld666666/archive/2010/10/24/1859819.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/16509

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月7日 下午4:50
下一篇 2023年2月7日 下午4:51

相关推荐