C++反彙編-結構體和類

學無止盡,積土成山,積水成淵-《C++反彙編與逆向分析技術揭祕》 讀書筆記函數

對象的內存佈局佈局

 通常計算公式:優化

對象內存大小 = sizeof(數據成員1)+ sizeof(數據成員2) +. .. + sizeof(數據成員n)
 
若類中沒有繼承和虛函數的定義,還有三種特殊狀況考慮:空類、內存對齊、靜態成員函數。
  • 空類:空類的長度爲1字節,若是不佔用字節的話,this指針會懸空。考慮到類能夠僅有成員函數,沒有數據成員。
  • 內存對齊:在VC中,類和結構體中的數據成員是根據在類或結構中出現的順序來依次申請空間的,因爲內存對齊緣由,可能不會連續的排列,數據成員之間可能有間隙。
  • 靜態變量:與靜態全局變量相似,存在的位置和全局變量一致,只是編譯器增長了做用域的檢查,做用域以外不可見。
訪問對象中的數據成員時,通常採用寄存器間接相對尋址,表達式esp±n±offset,或ebp±n±offset(其中esp±n或ebp±n是對象的首地址,offset 爲數據成員相對對象首地址的偏移)。因爲n和offset在編譯階段時屬於常量,採用編譯器優化時,表達式可能簡化爲 ebp±n 或 esp±n。

 

this指針this

this指針是保存所屬對象的首地址。在調用成員函數時,遵循默認的thiscall約定,利用寄存器ECX保存對象的首地址(即this指針),以寄存器傳參的方式傳遞到成員函數中,便是this指針的約定。成員函數中訪問成員數據既是經過this指針間接訪問的。
  • thiscall並不屬於關鍵字,是C++成員函數特有的調用方式。
  • thiscall的參數壓棧順序也是從右至左
  • thiscall的棧平衡方式與_stdcall相同,由被調用方平衡。
  • 並非全部的this指針的傳遞都是經過寄存器ECX,能夠強制改用其餘調用方式(_如stdcall)
    1  class CTest
    2 {
    3  public:
    4       void __stdcall SetNumber( int nNumber){
    5                    m_nInt = nNumber;
    6            }
    7       int nNumber;
    8 }
使用thiscall調用方式的成員函數的要點分析
函數調用:
1  lea ecx, [mem]             ; 取對象地址
2  call FUN_ADDRESS           ; 調用成員函數
3      
函數內部:
1  mov XXX, ecx                    ; 發現函數內使用ecx中的數據,證實確是經過ecx來傳遞參數
2  mov [reg +  1], XXX

 

 
靜態數據成員
靜態數據成員和靜態變量的原理相同,所以靜態數據成員的初值會被寫入編譯連接後的執行文件中。當程序被加載時,操做系統將執行文件中的數據讀到對應的內存單元中,靜態數據成員便已經存在,而這時類並無實例對象。靜態數據成員不屬於某一個對象,與對象之間是多對一的關係。靜態數據成員僅僅和類相關,和對象無關。
因此在計算某個類的對象所佔用內存的大小,靜態數據成員是不計算在內的。
 
區別:

 

  • 靜態數據成員是常量地址,而普通數據成員通常存儲在棧空間。
  • 靜態成員經過當即數間接尋址訪問,而普通數據成員通常經過寄存器相對間接尋址訪問。
  • 靜態成員訪問時不須要this指針,而普通數據成員訪問時須要使用this指針。

 

對象做爲函數參數spa

 
對象做爲函數參數,編譯器會把對象視爲由幾個基本類型的數據的組合,和多個參數函數傳參相似。類對象中的數據成員的傳參順序爲:最早定義的數據成員最後壓棧,最後定義的數據成員最早壓棧。當類有構造函數和析構函數,過程會更復雜一些。因爲對象在向函數傳遞過程當中,因爲複製了對象,等同於又定義了一個對象,會調用複製構造函數。在函數退出時,複製的對象做爲函數內部的局部變量被銷燬,會調用析構函數。

 

 
對象做爲返回值
 
對象做爲函數返回值,與基本類型不一樣。基本數據類型(雙精度浮點數以及非標準的_int64類型除外)做爲返回值時,經過寄存器EAX傳遞。對象做爲返回值時,首先在 調用函數中申請返回對象使用的棧空間,而後將返回對象的首地址做爲參數,經過 寄存器EAX傳遞給 被調用函數。在退出被調用函數時,將返回對象中的數據複製到 調用函數開闢的返回對象的棧空間,把返回對象的首地址經過寄存器EAX返回。返回的對象是臨時存在的,也就是C++臨時對象,做用域僅僅限於單條語句。
 
 1  class CReturn{
 2  public:
 3  int m_nNumber;
 4  in m_nArry[ 10];
 5 };
 6 CReturn GetCReturn()
 7 {
 8 CReturn RetObj;
 9 RetObj.m_nNumber =  0;
10  for( int i= 0; i<  10; i++)
11 {
12 RetObj.m_nArry[i] = i +  1;
13 }
14  return RetObj;
15 }
16  void main( int argc,  char *argv[])
17 {
18 CReturn objA;
19 objA = GetCReturn();
20 printf( " %d %d %d ", objA.m_nNumber, objA.m_nArry[ 0],  objA.m_nArry[ 9]);
21 }
相關文章
相關標籤/搜索