日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

VS2008查看匯編代碼

 云將東游 2014-08-13
了解反匯編的一些小知識對于我們在開發(fā)軟件時進行編程與調(diào)試大有好處,下面以VS2008環(huán)境下的VC++簡單介紹一下反匯編的一些小東西!如果有些解釋有問題的地方,希望大家能夠指出。
1、新建簡單的VC控制臺應(yīng)用程序(對此熟悉的同學(xué)可以略過)
A、打開Microsoft Visual Studio 2008,選擇主菜單“File”
B、選擇子菜單“New”下面的“Project”,打開“New Project”對話框。
C、左邊選擇Visual C++下的win32,右邊選擇Win32 Console Application,然后輸入一個工程名,點擊“OK”即可,在出現(xiàn)的向?qū)е?,一切默認,點擊Finish即可。
D、在出現(xiàn)的編輯區(qū)域內(nèi)會出現(xiàn)以你設(shè)定的工程名命名的CPP文件。內(nèi)容如下:
      #include "stdafx.h"
      int _tmain(int argc, _TCHAR* argv)
      {
            return 0;
      }
2、VS查看匯編代碼
A、VC處于調(diào)試狀態(tài)才能看到匯編指令窗口。因此,可以在 return 0 上設(shè)置一個斷點:把光標(biāo)移到 return 0 那一行上,然后按下F9鍵設(shè)置一個斷點。
B、按下F5鍵進入調(diào)試狀態(tài),當(dāng)程序停在 return 0 這一行上時,打開菜單“Debug”下的“Windows”子菜單,選擇“Disassembly”。這樣,出現(xiàn)一個反匯編的窗口,顯示下面的信息:
--- d:/my documents/visual studio 2008/projects/casmtest/casmtest/casmtest_main.cpp
// CAsmTest.cpp : 定義控制臺應(yīng)用程序的入口點。
//
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv)
{
00411370  push        ebp  
00411371  mov         ebp,esp
00411373  sub         esp,0C0h
00411379  push        ebx  
0041137A  push        esi  
0041137B  push        edi  
0041137C  lea         edi,
00411382  mov         ecx,30h
00411387  mov         eax,0CCCCCCCCh
0041138C  rep stos    dword ptr es:
return 0;
0041138E  xor         eax,eax
}
00411390  pop         edi  
00411391  pop         esi  
00411392  pop         ebx  
00411393  mov         esp,ebp
00411395  pop         ebp  
00411396  ret   
上面就是系統(tǒng)生成的main函數(shù)原型,確切的說是_tmain()的反匯編的相關(guān)信息,相信學(xué)過匯編語言的肯定就能夠了解它所做的操作了。
3、簡單了解一下常見的匯編指令
為了照顧到?jīng)]學(xué)過匯編程序的同志們,這里簡單介紹一下常見的幾種匯編指令。
A、add:加法指令,第一個是目標(biāo)操作數(shù),第二個是源操作數(shù),格式為:目標(biāo)操作數(shù) = 目標(biāo)操作數(shù) + 源操作數(shù)。
B、sub:減法指令,格式同 add。
C、call:調(diào)用函數(shù),一般函數(shù)的參數(shù)放在寄存器中。
D、ret:跳轉(zhuǎn)會調(diào)用函數(shù)的地方。對應(yīng)于call,返回到對應(yīng)的call調(diào)用的下一條指令,若有返回值,則放入eax中。
E、push:把一個32位的操作數(shù)壓入堆棧中,這個操作在32位機中會使得esp被減4(字節(jié)),esp通常是指向棧頂?shù)模ㄟ@里要指出的是:學(xué)過單片機的同學(xué)請注意單片機種的堆棧與Windows下的堆棧是不同的,請參考相應(yīng)資料),這里頂部是地址小的區(qū)域,那么,壓入堆棧的數(shù)據(jù)越多,esp也就越來越小。
F、pop:與push相反,esp每次加4(字節(jié)),一個數(shù)據(jù)出棧。pop的參數(shù)一般是一個寄存器,棧頂?shù)臄?shù)據(jù)被彈出到這個寄存器中。
一般不會把sub、add這樣的算術(shù)指令,以及call、ret這樣的跳轉(zhuǎn)指令歸入堆棧相關(guān)指令中。但是實際上在函數(shù)參數(shù)傳遞過程中,sub和add最常用來操作堆棧;call和ret對堆棧也有影響。
G、mov:數(shù)據(jù)傳送。第一個參數(shù)是目的操作數(shù),第二個參數(shù)是源操作數(shù),就是把源操作數(shù)拷貝到目的一份。
H、xor:異或指令,這本身是一個邏輯運算指令,但在匯編指令中通常會見到它被用來實現(xiàn)清零功能。用 xor eax,eax這種操作來實現(xiàn) mov eax,0,可以使速度更快,占用字節(jié)數(shù)更少。
I、lea:取得第二個參數(shù)地址后放入到前面的寄存器(第一個參數(shù))中。
然而lea也同樣可以實現(xiàn)mov的操作,例如:
                                  lea edi,
方括號表示存儲單元,也就是提取方括號中的數(shù)據(jù)所指向的內(nèi)容,然而lea提取內(nèi)容的地址,這樣就實現(xiàn)了把(ebx-0ch)放入到了edi中,但是mov指令是不支持第二個操作數(shù)是一個寄存器減去一個數(shù)值的。
J、stos:串行存儲指令,它實現(xiàn)把eax中的數(shù)據(jù)放入到edi所指的地址中,同時edi后移4個字節(jié),這里的stos實際上對應(yīng)的是stosd,其他的還有stosb,stosw分別對應(yīng)1,2個字節(jié)。
K、jmp:無條件跳轉(zhuǎn)指令,對應(yīng)于大量的條件跳轉(zhuǎn)指令。
L、jg:條件跳轉(zhuǎn),大于時成立,進行跳轉(zhuǎn),通常條件跳轉(zhuǎn)之前會有一條比較指令(用于設(shè)置標(biāo)志位)。
M、jl:小于時跳轉(zhuǎn)。
N、jge:大于等于時跳轉(zhuǎn)。
O、cmp:比較大小指令,結(jié)果用來設(shè)置標(biāo)志位。
4、函數(shù)參數(shù)傳遞方式
函數(shù)調(diào)用規(guī)則指的是調(diào)用者和被調(diào)用函數(shù)間傳遞參數(shù)及返回參數(shù)的方法,在Windows上,常用的有Pascal方式、WINAPI方式(_stdcall)、C方式(_cdecl)。
A、_cdecl C調(diào)用規(guī)則:
(a)參數(shù)從右到左進入堆棧;
(b)在函數(shù)返回后,調(diào)用者要負責(zé)清除堆棧,這種調(diào)用方式通常會生成較大的可執(zhí)行程序。
B、_stdcall又稱為WINAPI,調(diào)用規(guī)則如下:
(a)參數(shù)從右到左進入堆棧;
(b)被調(diào)用的函數(shù)在返回前自行清理堆棧,這種方式生成的代碼比cdecl小。
C、Pascal調(diào)用規(guī)則(主要用于Win16函數(shù)庫中,現(xiàn)在基本不用):
(a)參數(shù)從左到右進入堆棧;
(b)被調(diào)用的函數(shù)在返回前自行清理堆棧。
(c)不支持可變參數(shù)的函數(shù)調(diào)用。
5、VC中訪問無效變量出錯原因
我們看上面主函數(shù)反匯編后的其中一段代碼如下:
0041137C  lea         edi,
00411382  mov         ecx,30h
00411387  mov         eax,0CCCCCCCCh
0041138C  rep stos    dword ptr es:
從代碼的表面上看,它是實現(xiàn)把從ebp-0C0h開始的30h個字的空間寫入0CCCCCCCCh。其中eax為四位的數(shù)據(jù),這樣可以計算:
                      0C0h = 30h * 4
也就是把從ebp-0C0h 到ebp之間的空間初始化為0CCCCCCCCh。大家在學(xué)習(xí)反匯編的過程中會發(fā)現(xiàn),其實編譯器會根據(jù)情況把相應(yīng)長度的這樣一段作為局部變量的空間,而這里把局部變量區(qū)域全都初始化成0CCCCCCCCh也是有其用意的,做VC編程的工作者,特別是初學(xué)者可能不會對0CCCCCCCCh這個常量陌生。0cch實際上是int 3指令的機器碼,這是一個斷點中斷指令(在反編譯出的信息中大家會看到int 3),因為局部變量不可被執(zhí)行,或者如果在沒有初始化的時候進行了訪問,則就會出現(xiàn)訪問失敗錯誤。這個在VC編譯Debug版本中才能看到提示這個錯誤,在Release版本中,會以另外一種錯誤形式體現(xiàn)。下面,我們修改主程序看下new與delete的反匯編的效果(注釋直接加到反匯編的代碼中了)。
VC生成工程,寫入源代碼如下:
(1)情況1
// ASM_Test.cpp : Defines the entry point for the console application.                    (  源代碼1 )
//
#include "stdafx.h"
#include "stdlib.h"
int _tmain(int argc, _TCHAR* argv)
{
    int *pTest = new int(3);                //定義一個整型指針,并初始化為 3
    printf( "*pTest = %d/r/n", *pTest );    //調(diào)用庫函數(shù)printf輸出數(shù)據(jù)
    delete pTest;                            //刪除這個指針
    return 0;
}
這里僅僅看下在new與delete進行空間管理時進行反匯編時可能出現(xiàn)的一些情況,我們把上面源代碼稱為源代碼(1),我們按照前面講解的查看VS下反匯編的方法可以看到對應(yīng)于上面代碼的反匯編代碼如下:
--- f:/mysource/asm_test/asm_test/asm_test.cpp ---------------------------------                      ( 反匯編代碼 1)
// ASM_Test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "stdlib.h"
int _tmain(int argc, _TCHAR* argv)
{
;(1)函數(shù)預(yù)處理部分
004113C0  push        ebp  
004113C1  mov         ebp,esp ;保存堆棧的棧頂位置
004113C3  sub         esp,0E8h ;要置為0CCCCCCCCh 保留變量空間長度
004113C9  push        ebx       ;保存寄存器ebx、esi、edi
004113CA  push        esi  
004113CB  push        edi  
004113CC  lea         edi,    ;提出要置為0CCCCCCCCh 的空間起始地址
004113D2  mov         ecx,3Ah      ;要置為0CCCCCCCCh 的個數(shù),每個占4個字節(jié)
004113D7  mov         eax,0CCCCCCCCh  ;于是3Ah * 4 = 0E8h
004113DC  rep stos    dword ptr es:  ;進行置為0CCCCCCCCh操作
;(2)定義一個int 型指針,分配空間后,并初始化為 3 ,
    int *pTest = new int(3);                //定義一個整型指針,并初始化為 3
004113DE  push        4    ;要分配的空間長度,會根據(jù)定義的數(shù)據(jù)類型而不同
004113E0  call        operator new (411186h)   ;分配空間,并把分配空間的起始地址放入eax中
004113E5  add         esp,4    ;由于new與delete函數(shù)本身沒有對棧進行彈出操作,所以,要編寫者自己處理
004113E8  mov         dword ptr ,eax  ;比較分配的空間是否為0,如果為0
004113EE  cmp         dword ptr ,0
004113F5  je          wmain+51h (411411h)
004113F7  mov         eax,dword ptr       ;對于分配的地址分配空間進行賦值為:3
004113FD  mov         dword ptr ,3
00411403  mov         ecx,dword ptr  
00411409  mov         dword ptr ,ecx   ;似乎用和作為了中間存儲單元
0041140F  jmp         wmain+5Bh (41141Bh)
00411411  mov         dword ptr ,0     ;上面分配空間失敗是的操作
0041141B  mov         edx,dword ptr  
00411421  mov         dword ptr ,edx           ;數(shù)據(jù)最后送入pTest變量中
;調(diào)用printf函數(shù)進行數(shù)據(jù)輸出
    printf( "*pTest = %d/r/n", *pTest );    //調(diào)用庫函數(shù)printf輸出數(shù)據(jù)
00411424  mov         esi,esp   ;用于調(diào)用printf后的Esp檢測,不明白編譯器為什么這樣做
00411426  mov         eax,dword ptr    ;提取要打印的數(shù)據(jù),先是地址,下面一條是提取具體數(shù)據(jù)
00411429  mov         ecx,dword ptr  
0041142B  push        ecx         ;兩個參數(shù)入棧
0041142C  push        offset string "*pTest = %d/r/n" (41573Ch)
00411431  call        dword ptr       ;調(diào)用函數(shù)
00411437  add         esp,8         ;由于庫函數(shù)無出棧管理操作,同new與delete,所以要加 8,進行堆棧處理
0041143A  cmp         esi,esp        ;對堆棧的棧頂進行測試
0041143C  call        @ILT+325(__RTC_CheckEsp) (41114Ah)
;進行指針變量的清理工作
    delete pTest;                            //刪除這個指針
00411441  mov         eax,dword ptr    ; 中放入的是分配的地址,下面幾條指令轉(zhuǎn)悠一圈
00411444  mov         dword ptr ,eax   ;就是要把要清理的地址送入堆棧,然后調(diào)用delete函數(shù)
0041144A  mov         ecx,dword ptr  
00411450  push        ecx  
00411451  call        operator delete (411091h)
00411456  add         esp,4     ;對堆棧進行處理,同new與printf函數(shù)
;函數(shù)結(jié)束后,進行最終的清理工作
    return 0;
00411459  xor         eax,eax   ;做相應(yīng)的清理工作,堆棧中保存的變量送回原寄存器
}
0041145B  pop         edi  
0041145C  pop         esi  
0041145D  pop         ebx  
0041145E  add         esp,0E8h       ;進行堆棧的棧頂判斷
00411464  cmp         ebp,esp
00411466  call        @ILT+325(__RTC_CheckEsp) (41114Ah)
0041146B  mov         esp,ebp
0041146D  pop         ebp  
0041146E  ret  
--- No source file -------------------------------------------------------------;后面不再是源代碼
在列出反匯編程序時把反匯編代碼的上下的分解注釋也列了出來,親手去查看的朋友可能會發(fā)現(xiàn)在這段代碼的之外的其他部分會有大量的int 3匯編中的中斷指令,這個是與上面的所說的0CCCCCCCCh具有一致性,這些區(qū)域是無效區(qū)域,但代碼訪問這些區(qū)域時就會出現(xiàn)非法訪問提示。當(dāng)然,你應(yīng)該可以想到,那個提示是可以被屏蔽掉的,你可以把這部分區(qū)域填充上數(shù)據(jù)或者修改 iint 3 調(diào)用的中斷程序。
從以上反匯編程序,我們可以發(fā)現(xiàn)幾點:
A、一些內(nèi)部的庫函數(shù)是不會對堆棧進行出棧管理的,所以若要對反匯編程序進行操作時,一點要注意這一點
B、編譯器會自動的加上一些對棧頂?shù)臋z查工作,這個是我們在做VC調(diào)試時經(jīng)常遇到的一個問題,就是堆棧錯誤
當(dāng)然以上只是對debug版本下的程序進行反匯編,如果為release 版本,代碼就會進行大量的優(yōu)化,在理解時會有一定的難度,有興趣朋友可以試著反匯編一下,推薦大家有IDA返回工具,感覺挺好用的。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多