浅析VS2010反汇编 VS 反汇编方法及常用汇编指令介绍 VS2015使用技巧 调试-反汇编 查看C语言代码对应的汇编代码...
淺析VS2010反匯編
2015年07月25日 21:53:11 閱讀數:4374第一篇
1. 如何進行反匯編
在調試的環境下,我們可以很方便地通過反匯編窗口查看程序生成的反匯編信息。如下圖所示。
記得中斷程序的運行,不然看不到反匯編的指令
看一個簡單的程序及其生成的匯編指令
#include<stdio.h> #include<windows.h> const long Lenth=5060000/5; int main(){while(true){for(long i=0;i<Lenth;i++){;}Sleep(10);} }匯編窗口
?
?
2. ?預備知識:
?
函數調用大家都不陌生,調用者向被調用者傳遞一些參數,然后執行被調用者的代碼,最后被調用者向調用者返回結果,還有大家比較熟悉的一句話,就是函數調用是在棧上發生的,那么在計算機內部到底是如何實現的呢? 對于程序,編譯器會對其分配一段內存,在邏輯上可以分為代碼段,數據段,堆,棧 代碼段:保存程序文本,指令指針EIP就是指向代碼段,可讀可執行不可寫 數據段:保存初始化的全局變量和靜態變量,可讀可寫不可執行 BSS:未初始化的全局變量和靜態變量 堆(Heap):動態分配內存,向地址增大的方向增長,可讀可寫可執行 棧(Stack):存放局部變量,函數參數,當前狀態,函數調用信息等,向地址減小的方向增長,非常非常重要,可讀可寫可執行 如圖所示 寄存器 EAX:累加(Accumulator)寄存器,常用于函數返回值 EBX:基址(Base)寄存器,以它為基址訪問內存 ECX:計數器(Counter)寄存器,常用作字符串和循環操作中的計數器 EDX:數據(Data)寄存器,常用于乘除法和I/O指針 ESI:源變址寄存器 DSI:目的變址寄存器 ESP:堆棧(Stack)指針寄存器,指向堆棧頂部 EBP:基址指針寄存器,指向當前堆棧底部 EIP:指令寄存器,指向下一條指令的地址第二篇
?
一、VS反匯編方法
1、調出反匯編窗口。
2、調用寄存器窗口(只有在反匯編下才可見)
如果在調試狀態還是沒有此菜單項,可試著以下操作:
在VS中點擊“工具”->“導入和導出設置”,選擇“重置所有設置”,下一步,這時你可以保存當前設置或不保存,我覺得無所謂,下一步,選擇“Visual C**開發設置”,“完成”。這樣,“調試”->“窗口”->“寄存器”菜單項應該用顯示出來了,記得要確保你的程序是在調試的過程中。
3、查看內存
點擊“調試”->“窗口”->“內存”->“內存1”...“內存4”(選一個就可以了。)。在內存窗口中的“地址”欄輸入地址,按回車即可看到該地地址處的內存信息。
?二、常用匯編指令介紹
1、常用指令
為了照顧到沒學過匯編程序的同志們,這里簡單介紹一下常見的幾種匯編指令。
A、add:加法指令,第一個是目標操作數,第二個是源操作數,格式為:目標操作數 = 目標操作數 + 源操作數;
B、sub:減法指令,格式同 add;
C、call:調用函數,一般函數的參數放在寄存器中;
D、ret:跳轉會調用函數的地方。對應于call,返回到對應的call調用的下一條指令,若有返回值,則放入eax中;
E、push:把一個32位的操作數壓入堆棧中,這個操作在32位機中會使得esp被減4(字節),esp通常是指向棧頂的(這里要指出的是:學過單片機的同學請注意單片機種的堆棧與Windows下的堆棧是不同的,請參考相應資料),這里頂部是地址小的區域,那么,壓入堆棧的數據越多,esp也就越來越小;
F、pop:與push相反,esp每次加4(字節),一個數據出棧。pop的參數一般是一個寄存器,棧頂的數據被彈出到這個寄存器中;
一般不會把sub、add這樣的算術指令,以及call、ret這樣的跳轉指令歸入堆棧相關指令中。但是實際上在函數參數傳遞過程中,sub和add最常用來操作堆棧;call和ret對堆棧也有影響。
?
G、mov:數據傳送。第一個參數是目的操作數,第二個參數是源操作數,就是把源操作數拷貝到目的一份。
H、xor:異或指令,這本身是一個邏輯運算指令,但在匯編指令中通常會見到它被用來實現清零功能。
????????????? 用 xor eax,eax這種操作來實現 mov eax,0,可以使速度更快,占用字節數更少。
I、lea:取得第二個參數地址后放入到前面的寄存器(第一個參數)中。
???????????????然而lea也同樣可以實現mov的操作,例如:
????????????????????????????????? lea edi,[ebx-0ch]
方括號表示存儲單元,也就是提取方括號中的數據所指向的內容,然而lea提取內容的地址,這樣就實現了把(ebx-0ch)放入到了edi中,但是mov指令是不支持第二個操作數是一個寄存器減去一個數值的。
?
J、stos:串行存儲指令,它實現把eax中的數據放入到edi所指的地址中,同時edi后移4個字節,這里的stos實際上對應的是stosd,其他的還有stosb,stosw分別對應1,2個字節。
K、jmp:無條件跳轉指令,對應于大量的條件跳轉指令。
L、jg:條件跳轉,大于時成立,進行跳轉,通常條件跳轉之前會有一條比較指令(用于設置標志位)。
M、jl:小于時跳轉。
N、jge:大于等于時跳轉。
O、cmp:比較大小指令,結果用來設置標志位。
P ,rep 根據ECX寄存器的值進行重復循環操作
?
注:mov ax,[bx]
[ ]表示是間接尋址,bx和[bx]的區別是,前者操作數就是bx中存放的數,后者操作數是以bx中存放的數為地址的單元中的數。比如bx中存放的數是40F6H,40F6H、40F7H兩個單元中存放的數是22H、23H,則
mov ax,[bx];2223H傳送到ax中
mov ax,bx;40F6H傳送到ax中
?
ILT是INCREMENTAL?LINK?TABLE的縮寫,這個@ILT其實就是一個靜態函數跳轉的表,它記錄了一些函數的入口然后跳過去,每個跳轉jmp占一個字節,然后就是一個四字節的內存地址,加起為五個字節
比如代碼中有多處地方調用boxer函數,別處的調用也通過這個ILT表的入口來間接調用,而不是直接call?該函數的偏移,這樣在編譯程序時,如果boxer函數更新了,地址變了,只需要修改跳表中的地址就可以,有利于提高鏈接生成程序的效率。這個是用在程序的調試階段,當編譯release程序時,就不再用這種方法。
?
我試著將HEX數據改成00 00,對應的匯編指令變成了add byte ptr [eax],al ,反過來,如果將一個地方的匯編指令改成add byte ptr [eax],al ,對應的HEX數據就成了00 00,也就是說,他們是一一對應的,編譯器認為,00 00 這樣兩個字節寬度的二進制數對應的匯編指令就是add byte ptr [eax],al?;
?
dword?雙字 就是四個字節
ptr?pointer縮寫 即指針
2? 、函數參數傳遞方式
函數調用規則指的是調用者和被調用函數間傳遞參數及返回參數的方法,在Windows上,常用的有Pascal方式、WINAPI方式(_stdcall)、C方式(_cdecl)。
A、_cdecl C調用規則:
(a)參數從右到左進入堆棧;
(b)在函數返回后,調用者要負責清除堆棧,這種調用方式通常會生成較大的可執行程序。
B、_stdcall又稱為WINAPI,調用規則如下:
(a)參數從右到左進入堆棧;
(b)被調用的函數在返回前自行清理堆棧,這種方式生成的代碼比cdecl小。
C、Pascal調用規則(主要用于Win16函數庫中,現在基本不用):
(a)參數從左到右進入堆棧;
(b)被調用的函數在返回前自行清理堆棧。
(c)不支持可變參數的函數調用。
第三篇
?
了解反匯編的一些小知識對于我們在開發軟件時進行編程與調試大有好處,下面以簡單介紹一下反匯編的一些小東西!如果有些解釋有問題的地方,希望大家能夠指出。
1、新建簡單的VC控制臺應用程序(對此熟悉的同學可以略過)
A、打開Microsoft Visual Studio 2010,選擇主菜單“File”
B、選擇子菜單“New”下面的“Project”,打開“New Project”對話框。
C、左邊選擇Visual C++下的win32,右邊選擇Win32 Console Application,然后輸入一個工程名,點擊“OK”即可,在出現的向導中,一切默認,點擊Finish即可。
D、在出現的編輯區域內會出現以你設定的工程名命名的CPP文件。內容如下:
????? #include "stdafx.h"
????? int _tmain(int argc, _TCHAR* argv[])
????? {
??????????? return 0;
??????}
2、VS查看匯編代碼
A、VC處于調試狀態才能看到匯編指令窗口。因此,可以在 return 0 上設置一個斷點:把光標移到 return 0 那一行上,然后按下F9鍵設置一個斷點。
B、按下F5鍵進入調試狀態,當程序停在 return 0?這一行上時,打開菜單“Debug”下的“Windows”子菜單,選擇“Disassembly”。這樣,出現一個反匯編的窗口,顯示下面的信息:
--- d:/my documents/visual studio 2008/projects/casmtest/casmtest/casmtest_main.cpp?
// CAsmTest.cpp : 定義控制臺應用程序的入口點。
//
#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,[ebp-0C0h]?
00411382? mov???????? ecx,30h?
00411387? mov???????? eax,0CCCCCCCCh?
0041138C? rep stos??? dword ptr es:[edi]?
?return 0;
0041138E? xor???????? eax,eax?
}
00411390? pop???????? edi??
00411391? pop???????? esi??
00411392? pop???????? ebx??
00411393? mov???????? esp,ebp?
00411395? pop???????? ebp??
00411396? ret??
上面就是系統生成的main函數原型,確切的說是_tmain()的反匯編的相關信息,相信學過匯編語言的肯定就能夠了解它所做的操作了。
?
VC中訪問無效變量出錯原因
我們看上面主函數反匯編后的其中一段代碼如下:
0041137C? lea???????? edi,[ebp-0C0h]?
00411382? mov???????? ecx,30h?
00411387? mov???????? eax,0CCCCCCCCh?
0041138C? rep stos??? dword ptr es:[edi]
從代碼的表面上看,它是實現把從ebp-0C0h開始的30h個字的空間寫入0CCCCCCCCh。其中eax為四位的數據,這樣可以計算:
????????????????????? 0C0h = 30h * 4
也就是把從ebp-0C0h 到ebp之間的空間初始化為0CCCCCCCCh。大家在學習反匯編的過程中會發現,其實編譯器會根據情況把相應長度的這樣一段作為局部變量的空間,而這里把局部變量區域全都初始化成0CCCCCCCCh也是有其用意的,做VC編程的工作者,特別是初學者可能不會對0CCCCCCCCh這個常量陌生。0cch實際上是int 3指令的機器碼,這是一個斷點中斷指令(在反編譯出的信息中大家會看到int 3),因為局部變量不可被執行,或者如果在沒有初始化的時候進行了訪問,則就會出現訪問失敗錯誤。這個在VC編譯Debug版本中才能看到提示這個錯誤,在Release版本中,會以另外一種錯誤形式體現。下面,我們修改主程序看下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 );??? //調用庫函數printf輸出數據
??? delete []pTest;??? ??? ??? ??? ??? ??? ??? //刪除這個指針
??? return 0;
}
這里僅僅看下在new與delete進行空間管理時進行反匯編時可能出現的一些情況,我們把上面源代碼稱為源代碼(1),我們按照前面講解的查看VS下反匯編的方法可以看到對應于上面代碼的反匯編代碼如下:
--- 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)函數預處理部分
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,[ebp-0E8h]??? ;提出要置為0CCCCCCCCh 的空間起始地址
004113D2? mov???????? ecx,3Ah????? ;要置為0CCCCCCCCh 的個數,每個占4個字節
004113D7? mov???????? eax,0CCCCCCCCh? ;于是3Ah * 4 = 0E8h
004113DC? rep stos??? dword ptr es:[edi]? ;進行置為0CCCCCCCCh操作;
?
(2)定義一個int 型指針,分配空間后,并初始化為 3 ,
??? int *pTest = new int(3);??? ??? ??? ??? //定義一個整型指針,并初始化為 3
004113DE? push??????? 4??? ;要分配的空間長度,會根據定義的數據類型而不同
004113E0? call??????? operator new (411186h)?? ;分配空間,并把分配空間的起始地址放入eax中
004113E5? add???????? esp,4??? ;由于new與delete函數本身沒有對棧進行彈出操作,所以,要編寫者自己處理
004113E8? mov???????? dword ptr [ebp-0E0h],eax? ;比較分配的空間是否為0,如果為0?
004113EE? cmp???????? dword ptr [ebp-0E0h],0?
004113F5? je????????? wmain+51h (411411h)?
004113F7? mov???????? eax,dword ptr [ebp-0E0h]????? ;對于分配的地址分配空間進行賦值為:3
004113FD? mov???????? dword ptr [eax],3?
00411403? mov???????? ecx,dword ptr [ebp-0E0h]?
00411409? mov???????? dword ptr [ebp-0E8h],ecx?? ;似乎用[ebp - 0E0h]和[ebp - 0E8h]作為了中間存儲單元
0041140F? jmp???????? wmain+5Bh (41141Bh)?
00411411? mov???????? dword ptr [ebp-0E8h],0???? ;上面分配空間失敗是的操作
0041141B? mov???????? edx,dword ptr [ebp-0E8h]?
00411421? mov???????? dword ptr [pTest],edx?????????? ;數據最后送入pTest變量中
;調用printf函數進行數據輸出
??? printf( "*pTest = %d/r/n", *pTest );??? //調用庫函數printf輸出數據
00411424? mov???????? esi,esp?? ;用于調用printf后的Esp檢測,不明白編譯器為什么這樣做
00411426? mov???????? eax,dword ptr [pTest]?? ;提取要打印的數據,先是地址,下面一條是提取具體數據
00411429? mov???????? ecx,dword ptr [eax]?
0041142B? push??????? ecx???????? ;兩個參數入棧
0041142C? push??????? offset string "*pTest = %d/r/n" (41573Ch)?
00411431? call??????? dword ptr [__imp__printf (4182C4h)]????? ;調用函數
00411437? add???????? esp,8???????? ;由于庫函數無出棧管理操作,同new與delete,所以要加 8,進行堆棧處理
0041143A? cmp???????? esi,esp??????? ;對堆棧的棧頂進行測試
0041143C? call??????? @ILT+325(__RTC_CheckEsp) (41114Ah)?
;進行指針變量的清理工作
??? delete []pTest;??? ??? ??? ??? ??? ??? ??? //刪除這個指針
00411441? mov???????? eax,dword ptr [pTest]?? ;[pTest] 中放入的是分配的地址,下面幾條指令轉悠一圈
00411444? mov???????? dword ptr [ebp-0D4h],eax?? ;就是要把要清理的地址送入堆棧,然后調用delete函數
0041144A? mov???????? ecx,dword ptr [ebp-0D4h]?
00411450? push??????? ecx??
00411451? call??????? operator delete (411091h)?
00411456? add???????? esp,4???? ;對堆棧進行處理,同new與printf函數
;函數結束后,進行最終的清理工作
??? return 0;
00411459? xor???????? eax,eax?? ;做相應的清理工作,堆棧中保存的變量送回原寄存器
}
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 -------------------------------------------------------------;后面不再是源代碼
?
在列出反匯編程序時把反匯編代碼的上下的分解注釋也列了出來,親手去查看的朋友可能會發現在這段代碼的之外的其他部分會有大量的int 3匯編中的中斷指令,這個是與上面的所說的0CCCCCCCCh具有一致性,這些區域是無效區域,但代碼訪問這些區域時就會出現非法訪問提示。當然,你應該可以想到,那個提示是可以被屏蔽掉的,你可以把這部分區域填充上數據或者修改 iint 3 調用的中斷程序。
從以上反匯編程序,我們可以發現幾點:
A、一些內部的庫函數是不會對堆棧進行出棧管理的,所以若要對反匯編程序進行操作時,一點要注意這一點
B、編譯器會自動的加上一些對棧頂的檢查工作,這個是我們在做VC調試時經常遇到的一個問題,就是堆棧錯誤
當然以上只是對debug版本下的程序進行反匯編,如果為release 版本,代碼就會進行大量的優化,在理解時會有一定的難度,有興趣朋友可以試著反匯編一下,推薦大家有IDA返回工具,感覺挺好用的。
?
?
?
?
版權聲明:本文為原創文章,轉載請標明出處。 https://blog.csdn.net/u013467442/article/details/47060261VS 反匯編方法及常用匯編指令介紹
2012年09月27日 09:33:58 閱讀數:11296?
?
在調試沒有源碼的文件時,我們可能要用到反匯編設計。
一、VS反匯編方法
1、調出反匯編窗口。
2、調用寄存器窗口(只有在反匯編下才可見)
如果在調試狀態還是沒有此菜單項,可試著以下操作:
在VS中點擊“工具”->“導入和導出設置”,選擇“重置所有設置”,下一步,這時你可以保存當前設置或不保存,我覺得無所謂,下一步,選擇“Visual C#開發設置”,“完成”。這樣,“調試”->“窗口”->“寄存器”菜單項應該用顯示出來了,記得要確保你的程序是在調試的過程中。
3、查看內存
點擊“調試”->“窗口”->“內存”->“內存1”...“內存4”(選一個就可以了。)。在內存窗口中的“地址”欄輸入地址,按回車即可看到該地地址處的內存信息。
?二、常用匯編指令介紹
1、常用指令
為了照顧到沒學過匯編程序的同志們,這里簡單介紹一下常見的幾種匯編指令。
A、add:加法指令,第一個是目標操作數,第二個是源操作數,格式為:目標操作數 = 目標操作數 + 源操作數;
B、sub:減法指令,格式同 add;
C、call:調用函數,一般函數的參數放在寄存器中;
D、ret:跳轉會調用函數的地方。對應于call,返回到對應的call調用的下一條指令,若有返回值,則放入eax中;
E、push:把一個32位的操作數壓入堆棧中,這個操作在32位機中會使得esp被減4(字節),esp通常是指向棧頂的(這里要指出的是:學過單片機的同學請注意單片機種的堆棧與Windows下的堆棧是不同的,請參考相應資料),這里頂部是地址小的區域,那么,壓入堆棧的數據越多,esp也就越來越小;
F、pop:與push相反,esp每次加4(字節),一個數據出棧。pop的參數一般是一個寄存器,棧頂的數據被彈出到這個寄存器中;
一般不會把sub、add這樣的算術指令,以及call、ret這樣的跳轉指令歸入堆棧相關指令中。但是實際上在函數參數傳遞過程中,sub和add最常用來操作堆棧;call和ret對堆棧也有影響。
?
G、mov:數據傳送。第一個參數是目的操作數,第二個參數是源操作數,就是把源操作數拷貝到目的一份。
H、xor:異或指令,這本身是一個邏輯運算指令,但在匯編指令中通常會見到它被用來實現清零功能。
????????????? 用 xor eax,eax這種操作來實現 mov eax,0,可以使速度更快,占用字節數更少。
I、lea:取得第二個參數地址后放入到前面的寄存器(第一個參數)中。
???????????????然而lea也同樣可以實現mov的操作,例如:
????????????????????????????????? lea edi,[ebx-0ch]
方括號表示存儲單元,也就是提取方括號中的數據所指向的內容,然而lea提取內容的地址,這樣就實現了把(ebx-0ch)放入到了edi中,但是mov指令是不支持第二個操作數是一個寄存器減去一個數值的。
?
J、stos:串行存儲指令,它實現把eax中的數據放入到edi所指的地址中,同時edi后移4個字節,這里的stos實際上對應的是stosd,其他的還有stosb,stosw分別對應1,2個字節。
K、jmp:無條件跳轉指令,對應于大量的條件跳轉指令。
L、jg:條件跳轉,大于時成立,進行跳轉,通常條件跳轉之前會有一條比較指令(用于設置標志位)。
M、jl:小于時跳轉。
N、jge:大于等于時跳轉。
O、cmp:比較大小指令,結果用來設置標志位。
2? 、函數參數傳遞方式
函數調用規則指的是調用者和被調用函數間傳遞參數及返回參數的方法,在Windows上,常用的有Pascal方式、WINAPI方式(_stdcall)、C方式(_cdecl)。
A、_cdecl C調用規則:
(a)參數從右到左進入堆棧;
(b)在函數返回后,調用者要負責清除堆棧,這種調用方式通常會生成較大的可執行程序。
B、_stdcall又稱為WINAPI,調用規則如下:
(a)參數從右到左進入堆棧;
(b)被調用的函數在返回前自行清理堆棧,這種方式生成的代碼比cdecl小。
C、Pascal調用規則(主要用于Win16函數庫中,現在基本不用):
(a)參數從左到右進入堆棧;
(b)被調用的函數在返回前自行清理堆棧。
(c)不支持可變參數的函數調用。
?
?
VS2015使用技巧 調試-反匯編 查看C語言代碼對應的匯編代碼
時間:2017-02-26 21:27:26????? 閱讀:9414????? 評論:0????? 收藏:0??????[點我收藏+]標簽:vs2015使用技巧???調試-反匯編???查看c語言代碼對應的匯編代碼???
鎮場文:
?????? 學儒家經世致用,行佛家普度眾生,修道家全生保真,悟易理象數通變。以科技光耀善法,成就一良心博客。
______________________________________________________________________________________________________
code:
?
查看反匯編步驟:
????step0?
????????在一行上設置斷點,鼠標挪到最左邊 豎著的條條 處,點擊一下即可設置斷點
?
?
????step1
????????按F5或者 調試->開始調試
?
????step1 show:
?
????step2:
????????點擊 調試->窗口->反匯編
?
????step2 show:
????????這個就是C語言對應的匯編語言啦。我的電腦是64位的。
總結
以上是生活随笔為你收集整理的浅析VS2010反汇编 VS 反汇编方法及常用汇编指令介绍 VS2015使用技巧 调试-反汇编 查看C语言代码对应的汇编代码...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JDBC事务和JTA事务的区别 --包含
- 下一篇: 前端html笔记