好久沒有寫過文章了,本身一直是作C/C++開發的,我一直認爲,做爲一個C/C++程序員,若是可以好好學一下彙編和逆向分析,那麼對於咱們去理解C/C++將會有很大的幫助,由於程序中全部的奧祕都藏在彙編中,不少問題咱們從表面上沒法看出究竟是爲何,只要用逆向工具一分析,很快就能知道其中的因此然來。咱們都知道虛函數表是放在類對象的最前面,可是不少人並不知道虛函數表的第一項存放的是什麼,下面我用IDA分析一下C++的虛函數調用,從彙編的角度去理解虛函數。此文只適合具備逆向分析基礎的讀者,若是沒有逆向分析基礎,請直接繞過。有以下C++代碼:程序員
class Test { public: Test() { printf("Test::Test\n"); } virtual ~Test() { printf("Virtual ~Test()\n"); } virtual void prointer()=0; virtual void pointf()=0; }; class TestA:public Test { public: TestA() { printf("TestA::TestA\n"); } virtual ~TestA() { printf("TestA::TestA\n"); } virtual void prointer() { printf("Derive Class TestA::Pointer\n"); } virtual void pointf() { printf("Derive Class TestA::Pointf\n"); } }; int _tmain(int argc, _TCHAR* argv[]) { TestA *pTest=new TestA; pTest->pointf(); pTest->prointer(); delete pTest; return 0; }
用IDA打開EXE文件,用流程圖模式顯示:函數
運行到調用new的地方:工具
一層一層跟進去,咱們會發現,new的內部其實就是調用的malloc進行內存分配,msvcr100d_malloc即是malloc函數:學習
往下走,發現以下指令:ui
text:00C3149F call j_??2@YAPAXI@Z_0 ; operator new(uint) .text:00C314A4 add esp, 4 .text:00C314A7 mov [ebp+this], eax .text:00C314AD mov [ebp+var_4], 0 .text:00C314B4 cmp [ebp+this], 0 .text:00C314BB jz short loc_C314D0 .text:00C314BD mov ecx, [ebp+this] ; this .text:00C314C3 call j_??0TestA@@QAE@XZ ; TestA::TestA(void) .text:00C314C8 mov [ebp+var_10C], eax ; new的返回值賦給[epb+var_10C] .text:00C314CE jmp short loc_C314DA ; eax=this .text:00C314D0 ; --------------------------------------------------------------------------- .text:00C314D0 .text:00C314D0 loc_C314D0: ; CODE XREF: _wmain+5Bj .text:00C314D0 mov [ebp+var_10C], 0 .text:00C314DA .text:00C314DA loc_C314DA: ; CODE XREF: _wmain+6Ej .text:00C314DA mov eax, [ebp+var_10C] ; eax=this .text:00C314E0 mov [ebp+var_104], eax ; [ebp+var_104]=this .text:00C314E6 mov [ebp+var_4], 0FFFFFFFFh .text:00C314ED mov ecx, [ebp+var_104] ; ecx=this .text:00C314F3 mov [ebp+pTest], ecx ; [ebp+pTest]=this .text:00C314F6 mov eax, [ebp+pTest] ; eax=this .text:00C314F9 mov edx, [eax] .text:00C314FB mov esi, esp .text:00C314FD mov ecx, [ebp+pTest] .text:00C31500 mov eax, [edx+8] ; eax=this+8 .text:00C31503 call eax ; 調用this+8處的代碼
跟進去,發現以下指令:this
text:00C31820 push ebp .text:00C31821 mov ebp, esp .text:00C31823 sub esp, 0CCh .text:00C31829 push ebx .text:00C3182A push esi .text:00C3182B push edi .text:00C3182C push ecx .text:00C3182D lea edi, [ebp+var_CC] .text:00C31833 mov ecx, 33h .text:00C31838 mov eax, 0CCCCCCCCh .text:00C3183D rep stosd .text:00C3183F pop ecx .text:00C31840 mov [ebp+this], ecx .text:00C31843 mov esi, esp .text:00C31845 push offset aDeriveClassT_0 ; "Derive Class TestA::Pointf\n" .text:00C3184A call ds:__imp__printf .text:00C31850 add esp, 4 .text:00C31853 cmp esi, esp .text:00C31855 call j___RTC_CheckEsp .text:00C3185A pop edi .text:00C3185B pop esi .text:00C3185C pop ebx .text:00C3185D add esp, 0CCh .text:00C31863 cmp ebp, esp .text:00C31865 call j___RTC_CheckEsp .text:00C3186A mov esp, ebp .text:00C3186C pop ebp .text:00C3186D retn .text:00C3186D ?pointf@TestA@@UAEXXZ endp
這就是pTest->pointf();調用的彙編代碼,繼續往下走:spa
調用了[edx+4]其實也就是this+4處的代碼,跟進去,代碼以下:code
.text:00C517B0 push ebp .text:00C517B1 mov ebp, esp .text:00C517B3 sub esp, 0CCh .text:00C517B9 push ebx .text:00C517BA push esi .text:00C517BB push edi .text:00C517BC push ecx .text:00C517BD lea edi, [ebp+var_CC] .text:00C517C3 mov ecx, 33h .text:00C517C8 mov eax, 0CCCCCCCCh .text:00C517CD rep stosd .text:00C517CF pop ecx .text:00C517D0 mov [ebp+this], ecx .text:00C517D3 mov esi, esp .text:00C517D5 push offset aDeriveClassTes ; "Derive Class TestA::Pointer\n" .text:00C517DA call ds:__imp__printf .text:00C517E0 add esp, 4 .text:00C517E3 cmp esi, esp .text:00C517E5 call j___RTC_CheckEsp .text:00C517EA pop edi .text:00C517EB pop esi .text:00C517EC pop ebx .text:00C517ED add esp, 0CCh .text:00C517F3 cmp ebp, esp .text:00C517F5 call j___RTC_CheckEsp .text:00C517FA mov esp, ebp .text:00C517FC pop ebp .text:00C517FD retn .text:00C517FD ?prointer@TestA@@UAEXXZ endp
以上其實就是調用pTest->prointer();的彙編代碼,繼續往下執行:對象
這裏調用的就是this處的指令,跟進去,有以下指令:blog
text:011D1890 push ebp .text:011D1891 mov ebp, esp .text:011D1893 sub esp, 0CCh .text:011D1899 push ebx .text:011D189A push esi .text:011D189B push edi .text:011D189C push ecx .text:011D189D lea edi, [ebp+var_CC] .text:011D18A3 mov ecx, 33h .text:011D18A8 mov eax, 0CCCCCCCCh .text:011D18AD rep stosd .text:011D18AF pop ecx .text:011D18B0 mov [ebp+this], ecx .text:011D18B3 mov ecx, [ebp+this] ; this .text:011D18B6 call j_??1TestA@@UAE@XZ ; TestA::~TestA(void) .text:011D18BB mov eax, [ebp+arg_0] .text:011D18BE and eax, 1 .text:011D18C1 jz short loc_11D18CF .text:011D18C3 mov eax, [ebp+this] .text:011D18C6 push eax ; void * .text:011D18C7 call j_??3@YAXPAX@Z_0 ; operator delete(void *) .text:011D18CC add esp, 4
此處就是析構函數調用的地方,綜上所述,咱們能夠得出結論,new最終是調用的malloc進行內存分配,在虛函數表中,虛析構函數是放在最前面的。固然這只是最簡單的類繼承體系,並且有些地方寫得不夠詳細,你們能夠本身動手寫代碼用IDA去進行逆向分析,探索其中的奧祕,以上只是本人的一些理解,若是有不合理的地方歡迎你們指正,但願能和你們共同窗習,共同進步。