zoukankan      html  css  js  c++  java
  • 数组、结构体、类的反汇编

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

  • 相关阅读:
    JAVA中的流-简介(二)
    JAVA中的流-简介(一)
    Java中内部类简介
    应用小练习-自定义栈
    集合知识点(二)
    集合知识点(一)
    JAVA中的正则表达式简介
    从头文件中学习sfr和sbit
    PCB中实现元器件旋转一个角度放置
    DXP中插入LOGO字体方法(2)
  • 原文地址:https://www.cnblogs.com/sld666666/p/1859819.html
Copyright © 2011-2022 走看看