首页
社区
课程
招聘
[原创]探究C++之一:类
发表于: 2016-1-13 17:26 4115

[原创]探究C++之一:类

2016-1-13 17:26
4115
      逆向探究c++之类
一:前言

  自己总结的学习的三个过程:
  1:知道怎么用
  2:知道为什么这么用
  3:想怎么用就怎么用
  本文探究的知识比较基础,大神请飘过。  

二:正文
1:起因
     在工作中分析一个进程退出时的崩溃dump时,调查原因发现是子类在调用虚函数时父类已经被释放引起的崩溃,虽然已经解决了此问题,但因此起了对类的继承虚表等特性的探究的想法。

2:探究
    首先构建了三个类 TestClass:public FatherTwo :public Fatherone,然后通过od和ida对类的析构和类的结构进行了一些探究。

      Class :FathOne(父类)FatherTwo(父类)TestClass(子类)
#ifndef _CLASS_FATHERONE_
#define _CLASS_FATHERONE_

class FatherOne
{
public:
    FatherOne();
    ~FatherOne();

public:

    virtual void TestVirFunOne();

private:

    int iOne;
};
#endif  // _CLASS_FATHERONE_


#include "stdafx.h"
#include "fatherOne.h"
#include <stdio.h>

FatherOne::FatherOne():
        iOne(0)
{
    printf("FatherOne Init\n");
}

FatherOne::~FatherOne()
{
    printf("FatherOne unInit\n");
}

//virtual function
void FatherOne::TestVirFunOne()
{
    iOne = 1;
    printf("Virtual Function From One\n");
}


#ifndef _CLASS_FATHERTWO_
#define _CLASS_FATHERTWO_

#include "fatherOne.h"

class FatherTwo :
    public FatherOne
{
public:
    FatherTwo();
    ~FatherTwo();

public:

    virtual void TestVirFunTwo();

private:

    int iTwo;
};

#endif
  


#include "stdafx.h"
#include "fatherTwo.h"
#include <stdio.h>

FatherTwo::FatherTwo():
        iTwo(0)
{
    printf("FatherTwo Init\n");
}

FatherTwo::~FatherTwo()
{
    printf("FatherTwo UnInit\n");
}

// virtual function
void FatherTwo::TestVirFunTwo()
{
    iTwo = 1;
    printf("Virtual Function From Two\n");
}

#ifndef _ClASS_TEST_ 
#define _ClASS_TEST_

#include "stdafx.h"
#include "fatherTwo.h"

class TestClass :
    public FatherTwo
{
public:
    TestClass();
    ~TestClass();

public:

    virtual void TestVirFun();

private:

    int iTest;
};

#endif  


#include "stdafx.h"

#include "test.h"
#include <stdio.h>

TestClass::TestClass():
    iTest(0)
{
    printf("TestClass Init\n");
}

TestClass::~TestClass()
{
    printf("TestClass UnInit\n");
}

// virtual function
void TestClass::TestVirFun()
{
    iTest = 1;
    printf("Virtual Function From Test\n");
}

        
      TestClass继承自FatherTwo,FatherTwo继承自FatherOne,三个类的结构类似:都包含一个虚函数和一个私有属性。
      
      对以下代码进行调试测试:
int _tmain(int argc, _TCHAR* argv[])
{
    TestClass test;
    test.TestVirFun();

    // stop the pro to get the result
    int getChar = 0;
    scanf_s("%d", &getChar);
  return 0;
}


      具体的od分析 (只贴出了main的反汇编代码,其他的非常简单,在这里略去。有兴趣可以自行研究)
00EB18D0 >  55                        push ebp                                 ; main函数
00EB18D1    8BEC                      mov ebp,esp
00EB18D3    6A FF                     push -0x1
00EB18D5    68 E853EB00               push TestClas.00EB53E8
00EB18DA    64:A1 00000000            mov eax,dword ptr fs:[0]
00EB18E0    50                        push eax
00EB18E1    81EC F4000000             sub esp,0xF4
00EB18E7    53                        push ebx
00EB18E8    56                        push esi
00EB18E9    57                        push edi
00EB18EA    8DBD 00FFFFFF             lea edi,dword ptr ss:[ebp-0x100]
00EB18F0    B9 3D000000               mov ecx,0x3D
00EB18F5    B8 CCCCCCCC               mov eax,0xCCCCCCCC
00EB18FA    F3:AB                     rep stos dword ptr es:[edi]
00EB18FC    A1 00A0EB00               mov eax,dword ptr ds:[__security_cookie]
00EB1901    33C5                      xor eax,ebp
00EB1903    8945 F0                   mov dword ptr ss:[ebp-0x10],eax
00EB1906    50                        push eax
00EB1907    8D45 F4                   lea eax,dword ptr ss:[ebp-0xC]
00EB190A    64:A3 00000000            mov dword ptr fs:[0],eax
00EB1910    8D4D DC                   lea ecx,dword ptr ss:[ebp-0x24]          ; 首先分配空间,调用构造函数
00EB1913    E8 C3F8FFFF               call TestClas.00EB11DB                   ; TestClas.TestClass::TestClass
00EB1918    C745 FC 00000000          mov dword ptr ss:[ebp-0x4],0x0
00EB191F    8D4D DC                   lea ecx,dword ptr ss:[ebp-0x24]
00EB1922    E8 E2F7FFFF               call TestClas.00EB1109                   ; TestClas.TestClass::TestVirFun
00EB1927    C745 D0 00000000          mov dword ptr ss:[ebp-0x30],0x0
00EB192E    8BF4                      mov esi,esp
00EB1930    8D45 D0                   lea eax,dword ptr ss:[ebp-0x30]
00EB1933    50                        push eax
00EB1934    68 7079EB00               push TestClas.00EB7970                   ; ASCII "%d"
00EB1939    FF15 1CB1EB00             call dword ptr ds:[<&MSVCR120D.scanf_s>] ; MSVCR120.scanf_s
00EB193F    83C4 08                   add esp,0x8
00EB1942    3BF4                      cmp esi,esp
00EB1944    E8 2EF8FFFF               call TestClas.00EB1177
00EB1949    C785 04FFFFFF 00000000    mov dword ptr ss:[ebp-0xFC],0x0
00EB1953    C745 FC FFFFFFFF          mov dword ptr ss:[ebp-0x4],-0x1
00EB195A    8D4D DC                   lea ecx,dword ptr ss:[ebp-0x24]
00EB195D    E8 BBF7FFFF               call TestClas.00EB111D                   ; TestClas.TestClass::~TestClass
00EB1962    8B85 04FFFFFF             mov eax,dword ptr ss:[ebp-0xFC]
00EB1968    52                        push edx
00EB1969    8BCD                      mov ecx,ebp
00EB196B    50                        push eax
00EB196C    8D15 A419EB00             lea edx,dword ptr ds:[0xEB19A4]
00EB1972    E8 1FF7FFFF               call TestClas.00EB1096

      

      这里可以得到几个要点:
      1: 构造函数的调用图为


         
        析构函数的调用顺序相同。
      
2:子类中的虚函数表中虚函数的顺序为先最上层的父类依次下排,最后为子类的虚函数指针,其私有属性的排序也相同,如下图


3:每个类的虚函数表在编译之后是固定的,其存储在PE文件中的rdata段或者data段,从pe的角度可以理解为全局的,如果要进行虚函数hook可以直接从基址+偏移的思路去进行xx。
      
4:子类和父类的虚函数表并不是连续放在一起的,而是独立的,可能有RTTI干扰的原因(其位于虚函数表指针-4的位置)。
5:从代码中可以看出,我们并没有显示的调用析构函数,但在内存中却看到了析构的代码,这里要感谢编译器的强大功能。(其实很多语法是由编译器的,从程序的本质 (代码+数据)的角度,语法其实是编译器对于一些规则的限定而已),虽然是这样,但我们在使用类的时候,要自动脑补编译器为我们添加的代码,这样才可以更好的把握住我们写的代码。
6:当无虚函数时,类偏移0(也就是原来虚函数表地址)的值为空,所以也无法确定RTTI的地址(通过RTTI可以获取很多信息,对逆向用处比较大的是类的继承关系和类的链接符号名称),故通过RTTI进行typeid和dynamic_cast时是错误的,当然这个编译器会自动报错提醒。(编译器真的很强大)

第一次写排版不太好,如果感兴趣请看附件吧。

[课程]Android-CTF解题方法汇总!

上传的附件:
收藏
免费 3
支持
分享
最新回复 (2)
雪    币: 1392
活跃值: (4872)
能力值: ( LV13,RANK:240 )
在线值:
发帖
回帖
粉丝
2
想听一下 什么时候会出现 子类析构的时候,父类被释放了?
2016-1-13 17:39
0
雪    币: 274
活跃值: (30)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
3
非子类析构。
进程退出时,UI在多线程退出时发生崩溃
2016-1-13 17:58
0
游客
登录 | 注册 方可回帖
返回
//