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

    本文主要主要研究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]

你可能感兴趣的:(结构体)