當前位置:編程學習大全網 - 熱門推薦 - c++反匯編與逆向分析技術揭秘 crackme怎麽無法運行

c++反匯編與逆向分析技術揭秘 crackme怎麽無法運行

壹、單類繼承

在父類中聲明為私有的成員,子類對象無法直接訪問,但是在子類對象的內存結構中,父類私有的成員數據依然存在。C++語法規定的訪問限制僅限於編譯層面,在編譯過程中進行語法檢查,因此訪問控制不會影響對象的內存結構。

子類未提供構造函數或析構函數,而父類卻需要構造函數與析構函數時,編譯器會為子類提供默認的構造函數與析構函數。但是子類有構造函數,而父類不存在構造函數,且沒有虛函數,則編譯器不會為父類提供默認的構造函數。

1. 內存結構:

①先安排父類的數據

②後安排子類新定義的數據

說明:基於上述的內存排列方法,即父類數據成員被安排前面。不管是父類的對象,還是子類的對象,父類的數據成員在內存中相對於對象的首地址的偏移值都是壹樣的。而且成員數據訪問都是基於this指針間接尋址的。所以,對於子類對象而言,使用父類指針或者子類指針都可以正確訪問其父類數據。

2. 虛表:

虛表的排列順序是按虛函數在類繼承層次中首次聲明的順序依次排列的。只要繼承了父類,其派生類的虛函數表中的父類部分的排列就與父類壹樣。子類新定義的虛函數會按照聲明順序緊跟其後。

3. 構造函數:

①先調用父類構造函數

②然後按照聲明順序調用成員數據變量的構造函數和初始化列表中指定的成員

③最後再執行子類構造函數的函數體。

說明:

①父類構造函數,虛表指針修改為指向父類的虛表,所以在父類構造函數內調用虛函數,調用的是父類的虛函數。

②子類構造函數,虛表指針修改為指向子類的虛表

4. 析構函數:

①先調用子類析造函數

②然後成員對象的析構函數,按照聲明的順序以倒序方式依次調用成員對象的析構函數。

③再執行父類構造函數

說明:

析構函數執行會首先設置虛表指針為自身虛表,再調用自身的析構函數。防止父類析構函數內調用子類對象的虛函數。

類有派生與繼承關系,需要聲明析構函數為虛函數。若析構函數不是虛函數時,當使用父類指針指向堆中的子類對象,並使用delete釋放對象空間時,編譯器會按照指針類型調用父類的析構函數,從而引發錯誤。

識別類之間的關系:

先定位構造函數,根據構造先後順序得到與之相關的其他類。

再根據虛表,利用IDA中使用引用參考功能可得到所有的構造和析構函數。

二、多重繼承

1. 內存排列:

數據成員的排列順序由繼承父類的先後順序所決定,從左向右依次排列。

子類虛表指針的個數取決於所繼承的父類的個數,有幾個父類便對應幾個虛表指針(虛基類除外)。

將壹個子類對象賦值給某個父類指針時,該父類指針便指向該父類所對應的虛表指針。

三、單類繼承與多重繼承比較:

單繼承類

在類對象占用的內存空間中,只保存壹份虛表指針

只有壹個虛表指針,對應的也只有壹個虛表

虛表中各項保存了類中各虛函數的首地址

構造時先構造父類,再構造自身,並且只調用壹次父類構造函數

析構時限析構自身,再析構父類,並且只調用壹次父類析構函數

多重繼承類

在類中所占用的內存空間中,根據繼承父類的個數保存對應的虛表指針

根據所保存的虛表指針的個數,對應產生相應個數的虛表。

轉換父類指針時,需要跳轉到對象的首地址。

構造時需要按照繼承順序調用多個父類構造函數。

析構時先析構自身,然後以與構造函數相反的順序調用所有父類的析構函數

當對象作為成員時,整個類對象的內存結構和多重繼承相似。當類中無虛函數時,整個類對象內存結構和多重繼承完全壹樣,可酌情還原;當父類或成員對象存在虛函數時,通過過觀察虛表指針的位置和構造函數、析構函數中填寫虛表指針的數目及目標地址,來還原繼承或成員關系。

四、 示例

1. 單類繼承:

C++源碼

1 #include <iostream>

2 using namespace std;

3

4 class Base {

5 public:

6 Base(){ nBase= 1;printf("CBase"); }

7 ~Base(){ printf("~CBase"); }

8 virtual void f() { printf("Base:f()");}

9 virtual void g() { printf("Base:g()");}

10 private:

11 int nBase;

12

13 };

14

15

16 class Derive : public Base {

17 public:

18 Derive(){ nDerive=2;printf("Derive"); }

19 ~Derive(){ printf("~Derive"); }

20 virtual void g(){ printf("Dervie:g()");}

21 virtual void h(){ printf("Dervie:h()");}

22 private:

23 int nDerive;

24 };

25

26

27

28 int main()

29 {

30 Derive d;

31 Base *b = &d;

32 b->g();

33 return 0;

34 }

匯編代碼(VS2010編譯)

1. 內存分布

1 類Derive對象

2 0019FD30 0139583C =>.rdata:const Derive::`vftable'

3 0019FD34 00000001 =>Base.nBase

4 0019FD38 00000002 =>Derive.nDerive

5

6 虛函數表

7 0139583C 01391163 Base::f(void)

8 01395840 0139110E Derive::g(void)

9 01395844 013911AE Derive::h(void)

2. 構造函數

1 pop ecx ;=>this指針出棧

2 mov [ebp+this], ecx ;=>保存this指針

3 mov ecx, [ebp+this]

4 call j_?0Base@@QAE@XZ ;=>調用基類構造函數Base::Base(void)

5 mov eax, [ebp+this] ;=>eax=this指針

6 mov dword ptr [eax], offset ?_7Derive@@6B@ ;=>初始化虛表指針為const Derive::`vftable'

3. 析構函數

1 pop ecx ;=>this指針出棧

2 mov [ebp+this], ecx ;=>保存this指針

3 mov eax, [ebp+this]

4 mov dword ptr [eax], offset ?_7Derive@@6B@ ;=>重置虛表指針為const Derive::`vftable'

5 mov esi, esp

6 push offset aDerive ; "~Derive"

7 call ds:__imp__printf

8 add esp, 4

9 cmp esi, esp

10 call j___RTC_CheckEsp

11 mov ecx, [ebp+this] ;=>ecx傳參this指針

12 call j_?1Base@@QAE@XZ ; =>調用基類析構函數 Base::~Base(void)

2. 多重繼承:

C++源碼

1 #include <iostream>

2 using namespace std;

3

4 class Base1 {

5 public:

6 virtual void f() { cout << "Base1::f" << endl; }

7 virtual void g() { cout << "Base1::g" << endl; }

8 Base1(){b1 = 1; printf("Base1"); }

9 ~Base1(){ printf("~Base1"); }

10 private:

11 int b1;

12

13 };

14

15 class Base2 {

16 public:

17 virtual void f() { cout << "Base2::f" << endl; }

18 virtual void g() { cout << "Base2::g" << endl; }

19 Base2(){b2 = 2; printf("Base2"); }

20 ~Base2(){ printf("~Base2"); }

21 private:

22 int b2;

23 };

24

25 class Derive : public Base1, public Base2{

26 public:

27 virtual void f() { cout << "Derive::f" << endl; }

28 virtual void g1() { cout << "Derive::g1" << endl; }

29 Derive(){ d1 = 3; printf("Derive"); }

30 ~Derive(){ printf("~Derive"); }

31 private:

32 int d1;

33

34 };

35

36 typedef void(*Fun)(void);

37

38 int main()

39 {

40

41 Derive d;

42 Base1 *b1 = &d;

43 b1->f();

44 b1->g();

45 Base2 *b2 = &d;

46 b2->f();

47 b2->g();

48 return 0;

49 }

匯編代碼(VS2010編譯)

1.內存分布

;內存布局

0019FA0C 008F584C ;=>.rdata:const Derive::`vftable'{for `Base1'} 第壹個虛表

0019FA10 00000001 ;=>Base1.b1

0019FA14 008F583C ;=>.rdata:const Derive::`vftable'{for `Base2'} 第二個虛表

0019FA18 00000002 ;=>Base2.b2

0019FA1C 00000003 ;=>Derive.d1

;虛函數表Derive::`vftable'{for `Base1'}

00FB584C 00FB1041 ;=>Derive::f(void)

00FB5850 00FB1118 ;=>Base1::g(void)

00FB5854 00FB111D ;=>Derive::g1(void)

;虛函數表Derive::`vftable'{for `Base2'}

00FB583C 00FB1113 ;=>Base2::g(void)

00FB5840 00FB1028 ;=>[thunk]:Derive::f`adjustor{8}' (void)

;追蹤地址:00FB1028

00FB1028 jmp ?f@Derive@@W7AEXXZ ;=>[thunk]:Derive::f`adjustor{8}' (void)

;追蹤函數:?f@Derive@@W7AEXXZ

00FB1F30 ?f@Derive@@W7AEXXZ proc near

00FB1F30 sub ecx, 8 ;=>調整this指針 this = this+8,則this=>Derive::`vftable'{for `Base2'}

00FB1F33 jmp j_?f@Derive@@UAEXXZ ;=>Derive::f(void)

2.構造函數

1 00FB14D9 mov [ebp-4], ecx ;=>this指針保存在esp-4處

2 00FB14DC mov ecx, [ebp-4] ;=>ecx獲得this指針

3 00FB14DF call j_?0Base1@@QAE@XZ ;=>調用構造函數 Base1::Base1(void)

4 00FB14E4 mov ecx, [ebp-4] ;=>ecx獲得this指針

5 00FB14E7 add ecx, 8 ;=>ecx獲得this+8

6 00FB14EA call j_?0Base2@@QAE@XZ ;=>調用構造函數 Base2::Base2(void)

7 00FB14EF mov eax, [ebp-4] ;=>eax獲得this指針

8 00FB14F2 mov dword ptr [eax], offset ?_7Derive@@6BBase1@@@ ;=>初始化第壹個虛表指針為const Derive::`vftable'{for `Base1'}

9 00FB14F8 mov eax, [ebp-4] ;=>eax獲得this指針

10 00FB14FB mov dword ptr [eax+8], offset ?_7Derive@@6BBase2@@@ ;=>初始化第二個虛表指針const Derive::`vftable'{for `Base2'}

11 00FB1502 mov eax, [ebp-4] ;=>eax獲得this指針

12 00FB1505 mov dword ptr [eax+10h], 3

13 00FB150C push offset Format ; "Derive"

14 00FB1511 call ds:__imp__printf

15 00FB1517 add esp, 4

3.析構函數

00FB17C9 mov [ebp-4], ecx ;=>this指針保存在esp-4處

00FB17CC mov eax, [ebp-4] ;=>ecx獲得this指針

00FB17CF mov dword ptr [eax], offset ?_7Derive@@6BBase1@@@ ;=>重置第壹個虛表指針為const Derive::`vftable'{for `Base1'}

00FB17D5 mov eax, [ebp-4]

00FB17D8 mov dword ptr [eax+8], offset ?_7Derive@@6BBase2@@@ ;=>重置第二個虛表指針為const Derive::`vftable'{for `Base2'}

00FB17DF push offset aDerive ; "~Derive"

00FB17E4 call ds:__imp__printf

00FB17EA add esp, 4

00FB17ED mov ecx, [ebp-4] ;=>ecx獲得this指針

00FB17F0 add ecx, 8 ;=>ec;得this+8

00FB17F3 call j_?1Base2@@QAE@XZ ;=>調用虛構函數Base2::~Base2(void)

00FB17F8 mov ecx, [ebp-4] ;=>ecx獲得this指針

00FB17FB call j_?1Base1@@QAE@XZ ;=>調用虛構函數Base1::~Base1(void)

4.虛函數調用

;Base1 *b1 = &d;

00FB1431 lea eax, [ebp-14h] ;=>eax獲得this指針

00FB1434 mov [ebp-18h], eax ;=>局部變量b1賦值為this指針

;b1->f();

00FB1437 mov eax, [ebp-18h] ;=>eax獲得b1值

00FB143A mov edx, [eax] ;=>edx指向第壹個虛表

00FB143C mov ecx, [ebp-18h] ;=>ecx傳遞this指針

00FB143F mov eax, [edx] ;=>eax獲得成員函數Derive::f(void)的地址

00FB1441 call eax ;=>調用成員函數Derive::f(void)

;b1->g();

00FB1443 mov eax, [ebp-18h] ;=>eax獲得b1值

00FB1446 mov edx, [eax] ;=>edx指向第壹個虛表

00FB1448 mov ecx, [ebp-18h] ;=>ecx傳遞this指針

00FB144B mov eax, [edx+4] ;=>eax獲得成員函數Derive::g(void)的地址

00FB144E call eax ;=>調用成員函數Derive::g(void)

;Base2 *b2 = &d;

00FB1457 lea ecx, [ebp-14h] ;=>ecx獲得this指針

00FB145A add ecx, 8 ;=>ecx=this+8指向第二個虛表

00FB145D mov [ebp-64h] ;=>保存ecx到臨時變量中

00FB1460 jmp short loc_FB1469 ;--|

; |

00FB1469 mov edx, [ebp-64h];<----|

00FB146C mov [ebp-1Ch], edx ;=>局部變量b2賦值為this+8

;b2->f();

00FB146F mov eax, [ebp-1Ch] ;=>eax獲得b2值

00FB1472 mov edx, [eax] ;=>edx獲得指向第二個虛表

00FB1474 mov ecx, [ebp-1Ch] ;=>ecx傳遞this+8

00FB1477 mov eax, [edx+4] ;=>eax為第二個虛表的第二個元素=>[thunk]:Derive::f`adjustor{8}' (void),間接調用Derive::f(void)

00FB147A call eax

;b2->g();

00FB147C mov eax, [ebp+b2] ; =>eax獲得b2值

00FB147F mov edx, [eax] ;=>edx獲得指向第二個虛表

00FB1481 mov ecx, [ebp+b2] ;=>ecx傳遞this+8

00FB1484 mov eax, [edx] ;=>eax為第二個虛表的第壹個元素=>Base2::g(void)

00FB1486 call eax ;=>調用Base2::g(void)

  • 上一篇:最精彩的十部武俠打鬥片(壹)
  • 下一篇:銷售年度工作總結怎麽寫
  • copyright 2024編程學習大全網