跳轉到內容

x86 反彙編/物件和類

來自華夏公益教科書,開放的書籍,為開放的世界

X86 反彙編華夏公益教科書的“物件和類”頁面是一個存根。您可以透過擴充套件此部分來提供幫助。

面向物件程式設計

[編輯 | 編輯原始碼]

面向物件(OO)程式設計為我們提供了一個新的程式結構單元:物件。本章將研究從 C++ 中反彙編的類。本章不會直接處理 COM,但它將為將來討論反向 COM 元件(僅限 Windows 使用者)奠定許多基礎。

一個沒有繼承任何東西的基本類可以分為兩個部分,變數和方法。非靜態變數被塞進一個簡單的​​資料結構中,而方法則像其他任何函式一樣進行編譯和呼叫。

當你開始新增繼承和多型時,事情變得更加複雜。為了簡單起見,物件的結構將在沒有繼承的情況下進行描述。然而,在最後,將涵蓋繼承和多型。

在類中定義的所有靜態變數都駐留在應用程式整個生命週期內的靜態記憶體區域。在類中定義的每個其他變數都放置在一個稱為物件的​​資料結構中。通常,當建構函式被呼叫時,變數會按照順序放置到物件中,請參見圖 1


A

class ABC123 {
public:
	int a, b, c;
	ABC123():a(1), b(2), c(3) {};
};

B

0x00200000	dd 	1	;int a
0x00200004	dd	2	;int b
0x00200008	dd	3	;int c


圖 1:記憶體中物件的示例
圖 1.A:類“ABC123”的定義。此類具有三個整數,a、b 和 c。建構函式將 'a' 設定為等於 1,'b' 設定為等於 2,'c' 設定為等於 3。
圖 1.B:物件 ABC123 可能會如何放置在記憶體中,按順序排列來自類的變數。在記憶體地址 0x00200000 處有一個雙字整數(32 位),值為 1,代表變數 'a'。記憶體地址 0x00200004 處有一個雙字整數,值為 2,代表變數 'b'。在記憶體地址 0x00200008 處有一個雙字整數,值為 3,代表變數 'c'。


但是,編譯器通常需要將變數分離成大小為字(2 位元組)的倍數,以便定位它們。並非所有變數都滿足此要求,特別是 char 陣列;一些未使用的位可能會被用來填充變數,以滿足此大小要求。這在圖 2中有所說明。

A

class ABC123{
public:
	int a;
	char b[3];
	double c;

	ABC123():a(1),c(3) { strcpy(b,"02"); };
};

B

0x00200000	dd	1		;int a				; offset = abc123 + 0*word_size
0x00200004	db	'0'		;b[0] = '0'			; offset = abc123 + 2*word_size
0x00200005	db	'2'		;b[1] = '2'
0x00200006	db	0		;b[2] = null
0x00200007	db	0		;<= UNUSED BYTE
0x00200008	dd	0x00000000	;double c,  lower 32 bits	; offset = abc123 + 4*word_size
0x0020000C	dd	0x40080000	;double c,  upper 32 bits


圖 2:具有填充變數的物件的示例
圖 2.A:類“ABC123”的新定義。此類具有一個 32 位整數 a。一個 3 位元組字元陣列 b。還有一個 64 位雙精度數 c。constrictor 將 'a' 設定為 1,'b' 設定為“02”,'c' 設定為 3。
圖 2.B 顯示了 ABC123 可能會如何儲存在記憶體中。物件中的第一個雙字是變數 'a',位於 0x00200000 處,值為 1。變數 'b' 從記憶體位置 0x00200004 開始。它包含三個字元的三個位元組,'0'、'2' 和空值。下一個可用地址 0x00200007 未使用,因為它不是字的倍數。最後一個變數 'c' 從 0x00200008 開始,它是兩個雙字(64 位)。它包含的值為 3。


為了使應用程式訪問這些物件變數之一,需要對物件指標進行偏移以找到所需的變數。每個變數的偏移量由編譯器知道,並在需要時寫入物件程式碼中。圖 3顯示瞭如何偏移指標以檢索變數。

;abc123 = pointer to object
mov	eax, [abc123]	;eax = &a	;offset = abc123+0*word_size 	= abc123
mov	ebx, [abc123+4]	;ebx = &b	;offset = abc123+2*word_size 	= abc123+4
mov	ecx, [abc123+8]	;ecx = &c	;offset = abc123+4*word_size 	= abc123+8

圖 3:這顯示瞭如何偏移指標以檢索變數。第一行將變數 'a' 的地址放入 eax。第二行將變數 'b' 的地址放入 ebx。最後一行將變數 'c' 放入 ecx。

在低階,函式和方法幾乎沒有區別。反編譯時,有時很難區分兩者。它們都駐留在文字記憶體空間中,並且都以相同的方式被呼叫。方法呼叫示例可以在圖 4中看到。


A

//method call
abc123->foo(1, 2, 3);


B

push 3			; int c
push 2			; int b
push 1			; int a
push [ebp-4]		; the address of the object
call 0x00434125	; call to method


圖 4:方法呼叫。
圖 4.A:C++ 語法中的方法呼叫。abc123 是一個指向物件的指標,該物件具有一個方法 foo()。foo() 接受三個整數引數,1、2 和 3。
圖 4.B:x86 彙編中的相同方法呼叫。它接受四個引數,物件的地址和三個整數。指向物件的指標位於 ebp-4,方法位於地址 0x00434125。


方法呼叫中值得注意的特點是作為引數傳入的物件的地址。但是,這不是一個始終可靠的指標。圖 5顯示了第一個引數是按引用傳入的物件的函式。結果是看起來與方法呼叫相同的函式。

A

//function call
foo(abc123, 1, 2, 3);

B

push 3			; int c
push 2			; int b
push 1			; int a
push [ebp+4]		; the address of the object
call 0x00498372	; call to function

圖 5:函式呼叫。
圖 5.A: C++ 語法中的函式呼叫。foo() 接受四個引數,一個指標和三個整數引數。
圖 5.B: x86 彙編中的相同函式呼叫。它接受四個引數,物件的地址和三個整數。指向物件的指標位於 ebp-4,方法位於地址 0x00498372。

繼承和多型

[編輯 | 編輯原始碼]

繼承和多型完全改變了類的結構,物件不再只包含變數,它們還包含指向繼承方法的指標。這是因為多型要求在執行時確定方法或內部物件的地址。

考慮圖 6。應用程式如何知道要呼叫 D::one 還是 C::one?答案是編譯器弄清楚了一個約定,用於在物件內部對變數和方法指標進行排序,這樣,當引用它們時,對於任何繼承了其方法和變數的物件,偏移量都相同。

A *obj[2];
obj[0] = new C();
obj[1] = new D();

for(int i=0; i<2; i++)
	obj[i]->one();
圖 6:一個小的 C++ 多型迴圈,它呼叫一個函式 one。類 C 和 D 都繼承了一個抽象類 A。為了使此程式碼工作,類 A 必須具有一個名為“one”的虛方法。

抽象類 A 充當編譯器的藍圖,定義了任何繼承它的類的預期結構。在類 A 中定義的每個變數以及在 A 中定義的每個虛方法都將在其任何子類中具有完全相同的偏移量。圖 7聲明瞭一個可能的繼承方案及其在記憶體中的結構。注意 C::one 的偏移量與 D::one 相同,而 C 中 A::a 的副本的偏移量與 D 中的副本相同。在此,我們的多型迴圈只需遍歷指標陣列並確切地知道在何處找到每個方法。


A

class A{
public:
	int a;
	virtual void one() = 0;
};
class B{
public:
	int b;
	int c;
	virtual void two() = 0;
};
class C: public A{
public:
	int d;
	void one();
};
class D: public A, public B{
public:
	int e;
	void one();
	void two();
};

B

;Object C
0x00200000	dd	0x00423848	; address of C::one	;offset = 0*word_size
0x00200004	dd	1		; C's copy of A::a	;offset = 2*word_size
0x00200008	dd	4		; C::d			;offset = 4*word_size

;Object D
0x00200100	dd	0x00412348	; address of D::one	;offset = 0*word_size
0x00200104	dd	1		; D's copy of A::a	;offset = 2*word_size
0x00200108	dd	0x00431255	; address of D::two	;offset = 4*word_size
0x0020010C	dd	2		; D's copy of B::b	;offset = 6*word_size
0x00200110	dd	3		; D's copy of B::c	;offset = 8*word_size
0x00200114	dd	5		; D::e			;offset = 10*word_size


圖 7:一個多型繼承方案。

圖 7.A 定義了繼承方案。它顯示類 C 繼承了類 A,而類 D 繼承了類 A 和類 B。

圖 7.B 顯示了繼承方案在記憶體中的結構。類 C 的物件在前兩個雙字中擁有在類 A 中宣告的所有內容。物件的其餘部分由類 C 定義。類 D 的物件也在前兩個雙字中擁有在類 A 中宣告的所有內容。然後接下來的三個雙字是類 B 中宣告的所有內容。最後一個雙字是類 D 定義的變數。

類與結構體

[編輯 | 編輯原始碼]
華夏公益教科書