c++反汇编与逆向分析技术揭秘_C++反汇编与逆向分析技术揭秘
一、單類繼承
- 在父類中聲明為私有的成員,子類對象無法直接訪問,但是在子類對象的內存結構中,父類私有的成員數據依然存在。C++語法規定的訪問限制僅限于編譯層面,在編譯過程中進行語法檢查,因此訪問控制不會影響對象的內存結構。
- 子類未提供構造函數或析構函數,而父類卻需要構造函數與析構函數時,編譯器會為子類提供默認的構造函數與析構函數。但是子類有構造函數,而父類不存在構造函數,且沒有虛函數,則編譯器不會為父類提供默認的構造函數。1. 內存結構:
①先安排父類的數據
②后安排子類新定義的數據說明:基于上述的內存排列方法,即父類數據成員被安排前面。不管是父類的對象,還是子類的對象,父類的數據成員在內存中相對于對象的首地址的偏移值都是一樣的。而且成員數據訪問都是基于this指針間接尋址的。所以,對于子類對象而言,使用父類指針或者子類指針都可以正確訪問其父類數據。2. 虛表:
虛表的排列順序是按虛函數在類繼承層次中首次聲明的順序依次排列的。只要繼承了父類,其派生類的虛函數表中的父類部分的排列就與父類一樣。子類新定義的虛函數會按照聲明順序緊跟其后。 3. 構造函數:
①先調用父類構造函數
②然后按照聲明順序調用成員數據變量的構造函數和初始化列表中指定的成員
③最后再執行子類構造函數的函數體。說明:
①父類構造函數,虛表指針修改為指向父類的虛表,所以在父類構造函數內調用虛函數,調用的是父類的虛函數。
②子類構造函數,虛表指針修改為指向子類的虛表4. 析構函數:
①先調用子類析造函數
②然后成員對象的析構函數,按照聲明的順序以倒序方式依次調用成員對象的析構函數。
③再執行父類構造函數說明: - 析構函數執行會首先設置虛表指針為自身虛表,再調用自身的析構函數。防止父類析構函數內調用子類對象的虛函數。
- 類有派生與繼承關系,需要聲明析構函數為虛函數。若析構函數不是虛函數時,當使用父類指針指向堆中的子類對象,并使用delete釋放對象空間時,編譯器會按照指針類型調用父類的析構函數,從而引發錯誤。
識別類之間的關系:
先定位構造函數,根據構造先后順序得到與之相關的其他類。
再根據虛表,利用IDA中使用引用參考功能可得到所有的構造和析構函數。
二、多重繼承1. 內存排列:
- 數據成員的排列順序由繼承父類的先后順序所決定,從左向右依次排列。
- 子類虛表指針的個數取決于所繼承的父類的個數,有幾個父類便對應幾個虛表指針(虛基類除外)。
- 將一個子類對象賦值給某個父類指針時,該父類指針便指向該父類所對應的虛表指針。
三、單類繼承與多重繼承比較:
- 單繼承類
- 在類對象占用的內存空間中,只保存一份虛表指針
- 只有一個虛表指針,對應的也只有一個虛表
- 虛表中各項保存了類中各虛函數的首地址
- 構造時先構造父類,再構造自身,并且只調用一次父類構造函數
- 析構時限析構自身,再析構父類,并且只調用一次父類析構函數
- 多重繼承類
- 在類中所占用的內存空間中,根據繼承父類的個數保存對應的虛表指針
- 根據所保存的虛表指針的個數,對應產生相應個數的虛表。
- 轉換父類指針時,需要跳轉到對象的首地址。
- 構造時需要按照繼承順序調用多個父類構造函數。
- 析構時先析構自身,然后以與構造函數相反的順序調用所有父類的析構函數
- 當對象作為成員時,整個類對象的內存結構和多重繼承相似。當類中無虛函數時,整個類對象內存結構和多重繼承完全一樣,可酌情還原;當父類或成員對象存在虛函數時,通過過觀察虛表指針的位置和構造函數、析構函數中填寫虛表指針的數目及目標地址,來還原繼承或成員關系。
四、工程案例分析
1. 單類繼承:
#include <iostream>
using namespace std;
class Base {
public:
Base(){ nBase= 1;printf("CBase"); }
~Base(){ printf("~CBase"); }
virtual void f() { printf("Base:f()");}
virtual void g() { printf("Base:g()");}
private:
int nBase;
};
class Derive : public Base {
public:
Derive(){ nDerive=2;printf("Derive"); }
~Derive(){ printf("~Derive"); }
virtual void g(){ printf("Dervie:g()");}
virtual void h(){ printf("Dervie:h()");}
private:
int nDerive;
};
int main()
{
Derive d;
Base *b = &d;
b->g();
return 0;
}
1. 內存分布
類Derive對象
0019FD30 0139583C =>.rdata:const Derive::`vftable'
0019FD34 00000001 =>Base.nBase
0019FD38 00000002 =>Derive.nDerive
虛函數表
0139583C 01391163 Base::f(void)
01395840 0139110E Derive::g(void)
01395844 013911AE Derive::h(void)
2. 構造函數
pop ecx ;=>this指針出棧
mov [ebp+this], ecx ;=>保存this指針
mov ecx, [ebp+this]
call j_??0Base@@QAE@XZ ;=>調用基類構造函數Base::Base(void)
mov eax, [ebp+this] ;=>eax=this指針
mov dword ptr [eax], offset ??_7Derive@@6B@ ;=>初始化虛表指針為const Derive::`vftable'
3. 析構函數
pop ecx ;=>this指針出棧
mov [ebp+this], ecx ;=>保存this指針
mov eax, [ebp+this]
mov dword ptr [eax], offset ??_7Derive@@6B@ ;=>重置虛表指針為const Derive::`vftable'
mov esi, esp
push offset aDerive ; "~Derive"
call ds:__imp__printf
add esp, 4
cmp esi, esp
call j___RTC_CheckEsp
mov ecx, [ebp+this] ;=>ecx傳參this指針
call j_??1Base@@QAE@XZ ; =>調用基類析構函數 Base::~Base(void)
總結
以上是生活随笔為你收集整理的c++反汇编与逆向分析技术揭秘_C++反汇编与逆向分析技术揭秘的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2020各银行信用卡分期手续费
- 下一篇: python if name main