自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

全面剖析逆向還原代碼的內(nèi)存結(jié)構(gòu)

開發(fā) 開發(fā)工具
在逆向還原代碼的時候,必須得掌握了菱形繼承,多繼承,虛繼承虛函數(shù)的內(nèi)存虛表結(jié)構(gòu)。所以,這篇文章獻給正在學習C++的朋友們。

 // 聲明:以下代碼均在Win32_Sp3   VC6.0_DEBUG版中調(diào)試通過..

當然,由于水平有限,必定錯漏百出!所以,希望耽誤您的時間,懇求您的指點。在這里萬分感謝!

首先,我們定義如下類:

  1. class A 
  2. public
  3.   A() 
  4.   { 
  5.     m_nData = 1; 
  6.   } 
  7.   virtual void fun() 
  8.   { 
  9.  
  10.   } 
  11.   int m_nData; 
  12. }; 
  13.  
  14. class B 
  15. public
  16.   B() 
  17.   { 
  18.     m_nData = 2; 
  19.   } 
  20.   virtual void fun() 
  21.   { 
  22.      
  23.   } 
  24.   int m_nData; 
  25. }; 
  26.  
  27. class AB :public A, public B 
  28. public
  29.   AB() 
  30.   { 
  31.     m_nData = 3; 
  32.   } 
  33.   virtual void fun() 
  34.   { 
  35.      
  36.   } 
  37.   int m_nData; 
  38. }; 
  39.  
  40.    
  41. int main(int argc, char* argv[]) 
  42.   AB the; 
  43.  
  44.   return 0; 

類的構(gòu)造順序:先基類-->在成員對象-->在派生類(自己)

所以the對象的構(gòu)造過程如下:

按照繼承定義時寫的順序:

1、基類A構(gòu)造:虛表賦值,成員數(shù)據(jù)

2、基類B構(gòu)造:虛表賦值,成員數(shù)據(jù)

3、派生類AB構(gòu)造:虛表覆蓋,成員數(shù)據(jù)

內(nèi)存中結(jié)構(gòu)如下圖:

在做如下修改:

  1. class A 
  2. public
  3.   A() 
  4.   { 
  5.     m_nData = 1; 
  6.   } 
  7.   virtual void fun() 
  8.   { 
  9.  
  10.   } 
  11.   virtual void fun1()  // 新增加 
  12.   { 
  13.      
  14.   } 
  15.   int m_nData; 
  16. }; 
  17.  
  18. class B 
  19. public
  20.   B() 
  21.   { 
  22.     m_nData = 2; 
  23.   } 
  24.   virtual void fun() 
  25.   { 
  26.      
  27.   } 
  28.   virtual void fun2()  // 新增加 
  29.   { 
  30.      
  31.   } 
  32.   int m_nData; 
  33. }; 
  34.  
  35. class AB :public A, public B 
  36. public
  37.   AB() 
  38.   { 
  39.     m_nData = 3; 
  40.   } 
  41.   virtual void fun() 
  42.   { 
  43.      
  44.   } 
  45.   virtual void fun3()  // 新增加 
  46.   { 
  47.      
  48.   } 
  49.   int m_nData; 
  50. }; 
  51.  
  52.    
  53. int main(int argc, char* argv[]) 
  54.   AB the; 
  55.  
  56.   return 0; 

對于the對象來說,它的內(nèi)存結(jié)構(gòu)的內(nèi)存結(jié)構(gòu)還是不會改變,但是虛表的內(nèi)容會改變,改變后的虛表如下:

1、A::Vtable如下:

&AB::fun  &A::fun1  AB::fun3

2、B::Vtable如下:

&AB::fun  &B::fun2

總結(jié)一下:先按繼承聲明順序依次構(gòu)造虛表,如果子類有虛函數(shù),并且不同名,則填寫到聲明順序首位的基類虛表中的末尾項。

我們從淺到深慢慢的剖析虛繼承的內(nèi)存結(jié)構(gòu),首先看源碼如下:

 
  1. class A 
  2. public
  3.   A() 
  4.   { 
  5.     m_nDataA = 1; 
  6.   } 
  7.   int m_nDataA; 
  8. }; 
  9.  
  10. class B :virtual public A 
  11. public
  12.   B() 
  13.   { 
  14.     m_nDataB = 2; 
  15.   } 
  16.   int m_nDataB; 
  17. }; 
  18.  
  19. int main(int argc, char* argv[]) 
  20.   B the; 
  21.   return 0; 
 

假設(shè)沒有virtual虛繼承關(guān)鍵字,the對象在內(nèi)存中的結(jié)構(gòu)如下:

A::m_nDataA

B::m_nDataB

現(xiàn)在我們的源碼中有virtual繼承關(guān)鍵字,那么內(nèi)存結(jié)構(gòu)必然會有區(qū)別,那么內(nèi)存結(jié)構(gòu)是怎么樣的呢?如下:

B::base Offset m_nDataA     // A::m_nDataA數(shù)據(jù)的偏移

B::m_nDataB

A::m_nDataA

編譯器為什么要這么做呢?這個偏移值是什么?這么做的意義又何在?

首先,這么做是為了只存在于一份虛基類數(shù)據(jù)。后面會講解。

B::base Offset 偏移的值,一般為全局數(shù)據(jù)區(qū)中。編譯器為了和虛表區(qū)別。這個指針指向的地址的值一般為:0x00000000, 或者某些特殊值

而在他后面的4個字節(jié)中。才是真正數(shù)據(jù)的偏移地址

為什么取這兩個值?為什么不直接寫偏移呢?

編譯器為了在內(nèi)存中只產(chǎn)生一份基類數(shù)據(jù),當然就必須得寫偏移值,可是又為了和虛表區(qū)分。所以只能取特殊值作為區(qū)分。(當然這里僅個人猜想,不作參考)

繼續(xù)看源碼:

 
  1. class A 
  2. public
  3.   A() 
  4.   { 
  5.     m_nDataA = 1; 
  6.   } 
  7.   virtual void fun() 
  8.   { 
  9.   } 
  10.   int m_nDataA; 
  11. }; 
  12.  
  13. class B :virtual public A 
  14. public
  15.   B() 
  16.   { 
  17.     m_nDataB = 2; 
  18.   } 
  19.   virtual void fun() 
  20.   { 
  21.   } 
  22.   int m_nDataB; 
  23. }; 
  24.  
  25. int main(int argc, char* argv[]) 
  26.   B the; 
  27.   return 0; 
 

現(xiàn)在多了虛表的加入。內(nèi)存結(jié)構(gòu)有了大的變化

the對象的內(nèi)存結(jié)構(gòu)如下:

B::base Offset A   // B的父類A的偏移

B::m_nDataB

0x00000000  // 虛基類的非虛基類的分隔符

B::Virtual

A::m_nDataA

劃紅線的地方,產(chǎn)生覆蓋,我們慢慢剖析編譯器構(gòu)造的過程。

the對象初始化空間如下:

0xCCCCCCCC

0xCCCCCCCC

0xCCCCCCCC

0xCCCCCCCC

0xCCCCCCCC

先是虛基類的構(gòu)造。內(nèi)存結(jié)構(gòu)如下:

1、***步

B::base Offset A      // 填入A類的偏移 

0xCCCCCCCC

0xCCCCCCCC

0xCCCCCCCC

0xCCCCCCCC

2、第二步

B::base Offset A      // 由此處指向內(nèi)容向下4個字節(jié)為B的父類A的偏移。取出內(nèi)容偏移地址后。當前地址 + 偏移地址  ==  填寫A類虛表的地址

0xCCCCCCCC

0xCCCCCCCC

A::virtual        // A類的虛表

0xCCCCCCCC

3、第三步

B::base Offset A      

0xCCCCCCCC

0xCCCCCCCC

A::virtual        

A::m_nDataA

4、第四步(程序流程返回到派生類B構(gòu)造函數(shù))

B::base Offset A      

0xCCCCCCCC

0x00000000      // 填充全0,作為虛基類和非基類的分隔符

A::virtual        

A::m_nDataA

5、第五步(虛表賦值)

A::Offset A      

0xCCCCCCCC

0x00000000

B::virtual           // 由于派生類的有寫fun虛函數(shù)。構(gòu)成覆蓋關(guān)系。所以覆蓋A的虛表

A::m_nDataA

6、第六步

B::base Offset A     

B::m_nDataB    // B::m_nDataB

0x00000000

B::virtual

A::m_nDataA

我們繼續(xù)看源碼:

 
  1. class A 
  2. public
  3.   A() 
  4.   { 
  5.     m_nDataA = 1; 
  6.   } 
  7.   virtual void fun() 
  8.   { 
  9.   } 
  10.   virtual void fun1() 
  11.   { 
  12.   } 
  13.   int m_nDataA; 
  14. }; 
  15.  
  16. class B :virtual public A 
  17. public
  18.   B() 
  19.   { 
  20.     m_nDataB = 2; 
  21.   } 
  22.   virtual void fun() 
  23.   { 
  24.   } 
  25.   virtual void fun2() 
  26.   { 
  27.   } 
  28.   int m_nDataB; 
  29. }; 
  30.  
  31. int main(int argc, char* argv[]) 
  32.   B the; 
  33.   return 0; 
 

加入了虛函數(shù),構(gòu)成多個虛表的the對象內(nèi)存結(jié)構(gòu)如下:

B::Vtable

B::base Offset A

B::m_nDataB

0x00000000

A::Vtable

A::m_nDataA

我們繼續(xù)慢慢剖析內(nèi)存結(jié)構(gòu)。the對象初始化內(nèi)存空間如下:

0xCCCCCCCC

0xCCCCCCCC

0xCCCCCCCC

0xCCCCCCCC

0xCCCCCCCC

0xCCCCCCCC

1、***步

0xCCCCCCCC

B::base offset A   // B的虛基類A的偏移地址. 規(guī)律找出來了。不用看偏移基本也可以推測出以下上個區(qū)域

0xCCCCCCCC

0xCCCCCCCC     // 這里應該是虛基類和非虛基類的分隔符。

0xCCCCCCCC     // 后面的步驟會填充為A的虛表

0xCCCCCCCC     // 后面的步驟會填充為A的數(shù)據(jù)成員m_nDataA

結(jié)果會是我們推測的這樣嗎?這是構(gòu)造完虛基類A的情況。

果然和我們猜想的一樣.到這里了。你肯定會問。為什么不在是對象的首地址開始填充偏移地址了。

這里要搞清楚的是?,F(xiàn)在派生類有了自己虛函數(shù)Fun2(). 并且和父類不同名。所以必須單獨建立一張?zhí)摫砹?。于是編譯器就這樣安排內(nèi)存結(jié)構(gòu)了.

繼續(xù)往下剖析:

由于重復的操作,省略......  我們直接來看第五步

5、第五步(派生類B的構(gòu)造函數(shù))

B::virtual

B::base offset A

0xCCCCCCCC

0x00000000      //  虛基類和非虛基類的分隔符

A::Virtual          

A::m_nDataA

這里紅色標記的地方產(chǎn)生了派生類虛函數(shù)的覆蓋,虛表中的結(jié)構(gòu)如下:

B::fun    // 覆蓋掉了 A::fun

A::fun1

并且,產(chǎn)生一個B虛表,虛表中的結(jié)構(gòu)如下:

B::fun2

6、第六步

B::virtual

B::base offset A

B::m_nDataB     // B的成員數(shù)據(jù)

0x00000000      

A::Virtual          

A::m_nDataA

好了,現(xiàn)在有了前面的講解,我們來剖析下較為復雜菱形繼承的內(nèi)存結(jié)構(gòu),源碼如下:

 
  1. class A 
  2. public
  3.   A() 
  4.   { 
  5.     m_nDataA = 1; 
  6.   } 
  7.   int m_nDataA; 
  8. }; 
  9.  
  10. class B :virtual public A 
  11. public
  12.   B() 
  13.   { 
  14.     m_nDataB = 2; 
  15.   } 
  16.   int m_nDataB; 
  17. }; 
  18.  
  19. class C :virtual public A 
  20. public
  21.   C() 
  22.   { 
  23.     m_nDatac = 3; 
  24.   } 
  25.   int m_nDatac; 
  26. }; 
  27.  
  28. class BC :public B, public C 
  29. public
  30.   BC() 
  31.   { 
  32.     m_nDataBC = 4; 
  33.   } 
  34.   int m_nDataBC; 
  35. }; 
  36.  
  37. int main(int argc, char* argv[]) 
  38.   BC the; 
  39.   return 0; 
 

由于是虛繼承,所以虛基類只會產(chǎn)生一份拷貝.內(nèi)存結(jié)構(gòu)必然如下:

B::base offset A

B::m_nDataB

C::base offset A

C::m_nDataC

BC::m_nDataBC

A::m_nDataA 

在變形下.源碼如下:

 
  1. class A 
  2. public
  3.   A() 
  4.   { 
  5.     m_nDataA = 1; 
  6.   } 
  7.   int m_nDataA; 
  8.   virtual void fun(){}  // 新增加 
  9. }; 
  10.  
  11. class B :virtual public A 
  12. public
  13.   B() 
  14.   { 
  15.     m_nDataB = 2; 
  16.   } 
  17.   int m_nDataB; 
  18.   virtual void fun(){}  // 新增加 
  19. }; 
  20.  
  21. class C :virtual public A 
  22. public
  23.   C() 
  24.   { 
  25.     m_nDatac = 3; 
  26.   } 
  27.   int m_nDatac; 
  28.   virtual void fun(){}  // 新增加 
  29. }; 
  30.  
  31. class BC :public B, public C 
  32. public
  33.   BC() 
  34.   { 
  35.     m_nDataBC = 4; 
  36.   } 
  37.   int m_nDataBC; 
  38.   virtual void fun(){}  // 新增加 
  39. }; 
  40.  
  41. int main(int argc, char* argv[]) 
  42.   BC the; 
  43.   return 0; 
 

加了虛函數(shù)后,the對象的內(nèi)存結(jié)構(gòu)如下:

B::base offset A

B::m_nDataB

C::base offset A

C::m_nDataC

0x00000000

BC::vtable

A::m_nDataA 

紅色地方同理,派生類的fun多次覆蓋父類的。***為BC::vtable。

我們繼續(xù)變形如下:

 
  1. class A 
  2. public
  3.   A() 
  4.   { 
  5.     m_nDataA = 1; 
  6.   } 
  7.   virtual void fun(){} 
  8.   virtual void funA(){} 
  9.   int m_nDataA; 
  10. }; 
  11.  
  12. class B :virtual public A 
  13. public
  14.   B() 
  15.   { 
  16.     m_nDataB = 2; 
  17.   } 
  18.   virtual void fun(){} 
  19.   virtual void funB(){} 
  20.   int m_nDataB; 
  21. }; 
  22.  
  23. class C :virtual public A 
  24. public
  25.   C() 
  26.   { 
  27.     m_nDatac = 3; 
  28.   } 
  29.   virtual void fun(){} 
  30.   virtual void funC(){} 
  31.   int m_nDatac; 
  32. }; 
  33.  
  34. class BC :public B, public C 
  35. public
  36.   BC() 
  37.   { 
  38.     m_nDataBC = 4; 
  39.   } 
  40.   virtual void fun(){} 
  41.   virtual void funBC(){} 
  42.   int m_nDataBC; 
  43. }; 
  44.  
  45. int main(int argc, char* argv[]) 
  46.   BC the; 
  47.   return 0; 
 

the對象的內(nèi)存結(jié)構(gòu)如下:

B::vtable

B::base offset A 

B::m_nDataB

C::vtable

C::base offset A

C::m_nDataC

BC::m_nDataBC

0x00000000

A::vtable

A::m_nDataA

B::vtable中表末尾存放著BC::funBC, 而BC::fun則覆蓋到A::vtable中.

BC::funBC我們知道。即使不是虛繼承。也會自動填充到按定義順序首基類的虛表的末尾。

而B是定義的首繼承基類,而B::fun中又覆蓋掉了虛基類A的虛表的A::fun,由于B和C虛繼承于A。

所以B和C不能同時都在虛基類A中虛表末尾加上各自的虛函數(shù),所以只能自己建張表.

然而BC又是以B定義順序的基類.也不是虛繼承。就把BC::funBC直接填充到B::vtable末尾.

到此為止,我們分析了幾乎大部分虛繼承的內(nèi)存結(jié)構(gòu)。在看到內(nèi)存的時候。大家是否能還原出代碼呢?

當然了。還有很多更復雜的結(jié)構(gòu)。只要掌握了最基本的原理。無非就是組合使用了!

原文鏈接:http://www.cnblogs.com/ziolo/archive/2013/05/07/3066022.html

責任編輯:彭凡 來源: 博客園
相關(guān)推薦

2010-01-26 17:53:30

Android代碼結(jié)構(gòu)

2009-09-17 13:15:20

LINQ查詢

2009-09-09 14:40:43

Linq to sql

2009-09-22 15:22:08

Hibernate性能

2010-06-11 14:51:34

IS-IS路由協(xié)議

2010-03-10 15:44:04

2009-09-18 17:17:58

LINQ模型

2009-10-28 13:44:40

linux庫文件路徑

2010-06-09 13:06:22

UML業(yè)務(wù)建模實例

2009-10-19 10:52:48

綜合布線市場

2010-06-09 10:17:19

UML類圖元素

2011-11-16 09:55:27

云計算

2023-02-08 08:12:15

2010-09-06 10:45:36

無線組網(wǎng)

2024-12-12 09:24:28

RocksDB服務(wù)器

2010-06-11 17:18:26

UML精粹

2011-05-23 14:20:59

WordPress

2010-05-14 15:14:10

安裝Subversio

2009-09-02 17:14:28

C#修飾符

2010-06-01 11:22:30

SVN合并跟蹤
點贊
收藏

51CTO技術(shù)棧公眾號