《軟件調試分析技術》學習筆記(一)
今天開始寫寫一些心得體驗。
《軟件調試分析技術》是好友Monster的處女作品。作為一直以的好伙伴,他是我看著長大的,(*^__^*) 嘻嘻……之所以有今天這樣的成績,是與他的努力和天賦脫不了關系的。他大方地給了我PDF版的,我也大方的給了我們全班。但我們班有同學說,“這是撒子呦,看不看不懂”。我決心寫一些學習筆記,和我班的同學一起來多交流,讓更多熱愛次行業的人都進入軟件調試這個神殿。
?
好了,閑話不說。
1)逆向工程(reverse engineering)簡介
逆向工程是通過對現有的二進制可執行文件進過反匯編、反編譯、調試模擬程序運行等手段,分析出程序的執行流程、數據結構等。
逆向工程不是簡單的復制和模仿,而是運用相關手段對產品進行分析再設計等創新處理,從而使程序表現出更加優良的性能、縮短新程序的開發周期、提高設計開發效率。
2)學習前提
建議在學習逆向分析技術之前有一定的編程功底。具有一定的代碼邏輯能力,最好還做過一些Windows應用程序,了解一些常用的API,PE文件格式、COM原理、Windows的消息處理機制、異常處理機制等。需要和二進程代碼打交道,所以一定要掌握好匯編語言(更重要的是反匯編)
由此可以看出來,同學們不是智商不夠,而是基礎知識比較薄弱
3)常用工具
調試工具OllyDBG、SoftICE、WinDBG、Syser Debugger,分析工具PEID、ExeInfo、FI、FFI…… 等,常見的PE工具有:LoadPE、PETools、Stud_PE、PEditor等,常見的反匯編工具有:IDA pro、W32dASM、C32ASM等。
M運用了很多篇幅簡單介紹了各個工具的使用,我認為,這些工具的使用要在平常實戰中漸漸熟悉,所以沒必要將此章目看的過細。本人也只用過OllyDBG。
4)windows消息處理機制
5)PE文件格式
PE 的意思就是Portable Executable(可移植的執行體)。它是Win32環境自身所帶的執行體文件格式。它的一些特性繼承自Unix的Coff (common object file format)文件格式。PE文件格式給了我們洞悉Windows結構的良機。
DOS MZ header |
DOS stub |
PE header |
Section table |
Section 1 |
Section 2 |
Section ... |
Section n |
?
?
上圖是 PE文件結構的總體層次分布。所有PE文件(甚至32位的DLLs)必須以一個簡單的DOS MZ header開始。我們通常對此結構沒有太大興趣。有了它,一旦程序在DOS下執行,DOS就能識別出這是有效的執行體,然后運行緊隨MZ header 之后的DOS stub。DOS stub實際上是個有效的EXE,在不支持PE文件格式的操作系統中,它將簡單顯示一個錯誤提示,類似于字符串"This program requires Windows"或者程序員可根據自己的意圖實現完整的 DOS代碼。通常我們也不對DOS stub太感興趣:因為大多數情況下它是由匯編器/編譯器自動生成。通常,它簡單調用中斷21h服務9來顯示字符串"This program cannot run in DOS mode"。
緊接著 DOS stub的是PE header。PE header是PE相關結構IMAGE_NT_HEADERS的簡稱,其中包含了許多PE裝載器用到的重要域。當我們更加深入研究PE文件格式后,將對這些重要域耳目能詳。執行體在支持PE文件結構的操作系統中執行時,PE裝載器將從DOS MZ header 中找到PE header 的起始偏移量。因而跳過了DOS stub直接定位到真正的文件頭PE header。PE文件的真正內容劃分成塊,稱之為sections(節)。每節是一塊擁有共同屬性的數據,比如代碼/數據、讀/寫等。我們可以把PE文件想象成一邏輯磁盤,PE header 是磁盤的boot扇區,而sections就是各種文件,每種文件自然就有不同屬性如只讀、系統、隱藏、文檔等等。
上述為摘抄,翻譯的不是很好
PE文件最前面緊隨DOS MZ文件頭的是一個DOS可執行文件(Stub)。這使得PE文件成為一個合法的MS-DOS可執行文件。DOS? MZ文件頭后面是一個32位的PE文件標志0x50450000(IMAGE_NT_SIGNATURE),即PE00。接下來的是PE的映像文件頭,包含的信息有該程序的運行平臺,有多少個節,文件鏈接的時間,文件命名格式,后面還緊跟一個可選映像頭,包含PE文件的邏輯分布信息,程序加載信息,開始地址,保留的堆棧數量,數據段大小等。可選頭還有一個重要的域,稱為“數據目錄表”的數組,表的每一項都是指向某一節的指針。可選映像頭后面緊跟的是節表和節。節通過節表來實現索引。實際上,節的內容才是真正執行的數據和程序。每一個節都有相關的標志。每一個節會被一個或多個目錄表指向,目錄表可通過可選頭的“數據目錄表”的入口找到。就像輸出函數表或基址重定位表。也存在沒有目錄表指向的節。
具體PE還有很多,這里只是一個初步的探究,如果想深入,網上找資料。
《軟件調試分析技術》學習筆記(二)
1.寄存器
寄存器M講的比較透徹。寄存器是中央處理器CPU的組成部分,是有限存貯容量的高速存貯部件,它們可用來暫存指
令、數據和位址,是內存階層中的最頂端,也是系統獲得操作資料的最快速途徑。
1.1數據寄存器??
??? 數據寄存器主要用來保存操作數和運算結果等信息,從而節省讀取操作數所需占用總線和訪問
存儲器的時間。這些 低16位寄存器分別命名為:AX、BX、CX和DX,它和先前的CPU中的寄存器
相一致。在16位CPU中,AX、BX、CX和DX不能作為基址和變址寄存器來存放存儲單元的地址,
但在32位CPU中,其32位寄存器EAX、EBX、ECX和EDX不僅可傳送數據、暫存數據保存算術邏輯
運算結果,而且也可作為指針寄存器, 所以,這些32位寄存器更具有通用性。
1.2變址寄存器
??? 寄存器ESI、EDI、SI和DI稱為變址寄存器(Index Register),它們主要用于存放存儲單元在段內
的偏移量, 用它們可實現多種存儲器操作數的尋址方式,為以不同的地址形式訪問存儲單元提供
方便。它們可作一般的存儲器指針使用。在字符串操作指令的執行過程中,對它們有特定的要求,
而且還具有特 殊的功能。
1.3指針寄存器
??? 寄存器EBP、ESP、BP和SP稱為指針寄存器(Pointer Register),主要用于存放堆棧內存儲單元
的偏移量, 用它們可實現多種存儲器操作數的尋址方式,為以不同的地址形式訪問存儲單元提供
方便。指針寄存器不可分割成8位寄存器。作為通用寄存器,也可存儲算術邏輯運算的操作數和運
算結果
它們主要用于訪問堆棧內的存儲單元,并且規定:
BP為基指針(Base Pointer)寄存器,用它可直接存取堆棧中的數據;?
SP為堆棧指針(Stack Pointer)寄存器,用它只可訪問棧頂。
1.4段寄存器
??? 段寄存器是根據內存分段的管理模式而設置的。內存單元的物理地址由段寄存器的值和一個偏
移量組合而成 的,這樣可用兩個較少位數的值組合成一個可訪問較大物理空間的內存地址
1.5指令指針寄存器
??? 指令指針EIP、IP(Instruction Pointer)是存放下次將要執行的指令在代碼段的偏移量。在具有預
取指令功能的系統中,下次要執行的指令通常已被預取到指令隊列中,除非發生轉移情況。
1.6標志寄存器
??? 標志寄存器(Flags Register,FR)又稱程序狀態字(Program Status Word,PSW)。這是一個存
放條件標志、控制標志寄存器,主要用于反映處理器的狀態和運算結果的某些特征及控制指令的執
行。
其中有些要有一定的匯編基礎,希望大家好好在實踐里體驗。
2? 匯編指令
??? 匯編指令是匯編語言中使用的一些操作符和助記符,還包括一些偽指令(如assume,end)。用
于告訴匯編程序如何進行匯編的指令,它既不控制機器的操作也不被匯編成機器代碼,只能為匯編
程序所識別并指導匯編如何進行。匯編指令多到數不過來。這里來介紹些常用到的:
一、數據傳輸指令
MOV??????? 傳送字或字節
PUSH?????? 把字壓入堆棧
POP??????? 把字彈出堆棧
PUSHA????? 把AX,CX,DX,BX,SP,BP,SI,DI依次壓入堆棧
POPA?????? 把DI,SI,BP,SP,BX,DX,CX,AX依次彈出堆棧
PUSHAD???? 把EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI依次壓入堆棧
POPAD????? 把EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX依次彈出堆棧
PUSHF????? 標志入棧
POPF?????? 標志出棧
LEA??????? 裝入有效地址
LDS??????? 傳送目標指針,把指針內容裝入DS
LES??????? 傳送目標指針,把指針內容裝入ES
LAHF?????? 標志寄存器傳送,把標志裝入AH
SAHF?????? 標志寄存器傳送,把AH內容裝入標志寄存器
二、算術運算指令
ADD??????? 加法
ADC??????? 帶進位加法
INC??????? 加 1?
SUB??????? 減法
SBB??????? 帶借位減法
DEC??????? 減 1
NEC??????? 求相反數(以 0 減之)
CMP??????? 比較(兩操作數作減法,僅修改標志位,不回送結果)
MUL??????? 無符號乘法
IMUL?????? 整數乘法
DIV??????? 無符號除法
IDIV?????? 整數除法
三、邏輯運算指令
OR???????? 或運算
AND??????? 與運算
XOR??????? 異或運算
NOT??????? 取反
TEST?????? 測試,兩操作數作與運算,僅修改標志位,不回送結果
SHL??????? 邏輯左移
SHR??????? 邏輯右移
ROL??????? 循環左移
ROR??????? 循環右移
RCL??????? 通過進位的循環左移
RCR??????? 通過進位的循環右移
四、串指令
MOVSX????? 先符號擴展,再傳送
MOVZX????? 先零擴展,再傳送
MOVS?????? 串傳送
CMPS?????? 串比較
五、程序轉移指令
JMP??????? 無條件轉移指令
CALL?????? 過程調用
RET??????? 過程返回
JG/JNLE??? 大于轉移
JGE/JNL??? 大于或等于轉移
JL/JNGE??? 小于轉移
JLE/JNG??? 小于或等于轉移
JE/JZ????? 等于轉移
JNE/JNZ??? 不等于時轉移
LOOP?????? CX不為零時循環
《軟件調試分析技術》學習筆記(三)
M給出一個C程序
- #include?<stdio.h>???
- #include?<stdlib.h>???
- int?a;???
- int?main()???
- {???
- ??int?b;???
- ??int?*c;???
- ??c?=?(int*)malloc(sizeof(int));???
- ??a?=?1;???
- ??b?=?2;???
- ??*c?=?3;???
- ??free(c);???
- ??return?0;???
- }???
?
這段代碼定義了一個整型全局變量a,在主函數main()中定義了一個整型局部變量b和一個整形指針變量c,然后調用malloc()函數申請大小為1個整形變量的內存并把申請到的內存地址賦值給指針變量c,再依次給變量a、b和c指向的內存賦值1、2、3,接下來釋放剛才申請到的堆內存,釋放后退出主函數main()。
?
用OD加載
- .text:00401000????push????ebp???
- .text:00401001????mov?????ebp,?esp???
- .text:00401003????sub?????esp,?8???
這里把棧頂向下壓8個字節,為整型變量b和指針變量c開辟空間。它們都是局部變量。
- .text:00401006????push????4???????????????;?Size???
- .text:00401008????call????ds:__imp__malloc???
- .text:0040100E????add?????esp,?4???
- .text:00401011????mov?????[ebp+c],?eax??
一個整型變量占用的空間為4個字節,這里調用函數malloc()申請大小為4個字節的堆空間,然
后把申請到的內存空間地址賦值給變量c。
- .text:00401014????mov??????a@@3HA,?1??????;?int?a???
- .text:0040101E????mov?????[ebp+b],?2???
- .text:00401025????mov?????eax,?[ebp+c]???
- .text:00401028????mov?????dword?ptr?[eax],?3???
這里分別給三個變量賦值1、2、3。可以看到變量a所使用的內存空間地址是一個常量,它存在于程序的數據段中;變量b所使用到的內存空間地址是ebp+b,它位于棧區;指針變量c儲存的數據是剛才由函數malloc()申請到的堆空間地址。
- .text:0040102E????mov?????ecx,?[ebp+c]???
- .text:00401031????push????ecx?????????????;?Memory???
- .text:00401032????call????ds:__imp__free???
- .text:00401038????add?????esp,?4???
- .text:0040103B????xor?????eax,?eax???
- .text:0040103D????mov?????esp,?ebp???
- .text:0040103F????pop?????ebp???
《軟件調試分析技術》學習筆記(四)
- 一起看看數組在程序中的使用。C語言代碼:??
- #include?<stdio.h>???
- int?main()???
- {???
- ??int?a[2];???
- ??a[1]?=?0;???
- ??a[a[1]]?=?1;???
- ??return?0;???
- }???
?
這段代碼定義了一個大小為2的整型數組,給數組下標為1的變量賦值0,然后取數組下標為1的變量的值作為新的下標,給該變量賦值1,這里數組下標為1的變量的值為0,就是說給數組下標為0的變量賦值1,最后退出主函數main()。
載入OD看看反匯編代碼:
?
- .text:00401000????push????ebp???
- .text:00401001????mov?????ebp,?esp???
- .text:00401003????sub?????esp,?8???
這里為數組a開辟內存空間。一個整型變量所占的內存空間為4字節,數組a的大小為2,因此把棧頂向下壓8個字節。
?
- text:00401006????mov?????[ebp+a+1*4],?0??
? 只要知道數組就是一個指針,這句代碼就很容易理解了。OD是一個好工具,這里它已經分析出a是一個數組了,而ebp+a是數組的基地址。a是一個整型數組,其中的每一個變量占用的大小都是4字節,因此在數組中下標為i的變量的地址就可以表示為數組的基地址加上數組下標為i的變量與數組下標為0的變量的偏移量,即ebp+a+i*4,這一句代碼中的地址ebp+a+1*4就是數組下標為1的變量的地址。
- text:0040100D????mov?????eax,?[ebp+a+1*4]???
- .text:00401010????mov?????[ebp+eax*4+a],?1???
這兩句代碼,獲取數組中下標為1的變量的值,把這個值作為新的數組下標eax,并給數組下標為eax的變量賦值1。
- .text:00401018????xor?????eax,?eax???
- .text:0040101A????mov?????esp,?ebp???
- .text:0040101C????pop?????ebp???
- .text:0040101D????retn??
《軟件調試分析技術》學習筆記(五)
在了解了變量的使用方式之后,本節來講講數在反匯編代碼中的簡單運算。??計算機在使用數的時候一般會用到二進制,十進制和十六進制。
二進制是計算機技術中廣泛采用的一種數制。二進制數據是用0和1兩個數碼來表示的數。它的基數為2,進位規則是“逢二進一”,借位規則是“借一當二”。現在的CPU使用的基本都是二進程數,用高電平表示1,低電平表示0。為了方便區別,一般在二進程數后追加一個字母B,例如二進制數10則表示為10B。B是二進制的英文binary的首字母。
??? 在人們的日常生活使用的數一般都是十進制數。人類的各種古文明都很有默契地使用十進制數,這與人類有十根手指有密切的關系。十進制數使用十個數碼0123456789來表示,逢十進一。為了方便區別,一般在十進程數后追加一個字母D,例如十進制數10則表示為10D。D是十進制的英文decimal的首字母。
??? 在計算機中使用的十進制數一般是BCD碼,BCD碼用4位二進制數來表示1位十進制數。例如在二進制數10000B表示十進制數的16D,而BCD碼10000卻表示十進制數10D,它們在內存中的儲存形式完全相同,但表示的數卻不同。
??? 雖然計算機在處理數據時使用的是二進制數,但是二進制數表示起來是很不方便的,例如一個十進制數1000000D要表示成二進制數則為11110100001001000000B。4位二進制數能表示的最小的數為0000B,能表示的最大的數是1111B,即十進制數15D,這樣就可以用4位二進制數來表示16個數字,把這十六個數字分別用一個數碼來表示,于是就行成了十六進制數。十六進制數用01234567890ABCDEF這十六個數碼來表示0-15D這十六個數,每逢十六進一。為了方便表示,為了方便區別,一般在十六進程數后追加一個字母H,例如十六進制數10則表示為10H。H是十六進制的英文hexadecimal的首字母。一般在程序中用到的十六進制數的表示方法是在數之前加前綴0x,例如十六進制數10則表示為0x10。
??? 通常這三種進制的數之間還需要互相轉化。一位十六進程數和四位二進制數是互相對應的,對應關系如下:
十六進制數??? 二進制數??????? 十六進制數??? 二進制數
0???????????? 0?????????????? 8???????????? 1000
1???????????? 1?????????????? 9???????????? 1001
2???????????? 10????????????? A???????????? 1010
3???????????? 11????????????? B???????????? 1011
4???????????? 100???????????? C???????????? 1100
5???????????? 101???????????? D???????????? 1101
6???????????? 110???????????? E???????????? 1110
7???????????? 111???????????? F???????????? 1111
??? 了解了這個轉化方式那要轉化就很容易了,例如十六進制數0xBC就可以用二進制數10111100B;相反地,二進制數1001010B也可以表示為十六進制數0x4A。二進程數與十進制數之間的轉換也是比較容易的。我記得當年學《微型計算機原理》的時候課本上有一句口訣:十進制數轉化二進制數,除二取余;二進制數轉化十進制數,乘二取整。聽起來可能不太好理解,但這是轉化方法的精髓。把十進制數轉換成二進制數,是一個連續除2的過程,把要轉換的十進制數數,除以2,得到一組商和余數;再用計算得到的商來計算,除以2,又得到另一組商和余數。這樣一直計算到得到的商為零為止,計算結束后把每一次計算的余數從后往前連起來就得到了這個十進制數對應的二進程數。例如現在有一個十進制11D,轉化過程如下:
11 / 2??????? 商:5?? 余:1
5? / 2??????? 商:2?? 余:1
2? / 2??????? 商:1?? 余:0
1? / 2??????? 商:0?? 余:1
??? 把每一次計算的余數從后往前連起來則為1011,這樣就計算出了十進制數11D對應的二進制數1011B。二進制數轉化十進制數相對簡單一點。二進制數從右往左數第n位表示2的n-1次冪,當第n位為0時忽略,把第n位為1的所有冪加起來就得到了這個十進制數。例如一個二進制數1011B,轉化過程如下:
2^3 + 2^1 + 2^0 = 8 + 2 + 1 = 11
??? 那么11D就是二進制數1011B所對應的十進制數。
??? 再來看看另一個問題,機器碼的表示方法。機器碼的出現主要是為了解決計算機中數的符號和計算問題。這里介紹計算機的原碼、反碼、補碼。
??? 原碼是一種計算機中對數字的二進制定點表示方法。原碼表示法在數值前面增加了一位符號位(即最高位為符號位),符號位為0表示正數或者0,符號位為1表示負數,其余位表示數值的大小。例如一個int型的十進制數100,用原碼表示則為:0000000001100100;同樣一個int型的十進制數-100,用原碼表示則為:1000000001100100。用原碼這樣來表示一個數是很直觀的,但是它有一個很嚴重的缺點,就是源碼不可以直接進行運算。1 + (-1) = 0,這是很顯然的,用源碼計算:
0000000000000001 + 1000000000000001 = 1000000000000010
??? 用原碼計算的結果為1000000000000010,轉換為十進制數則是-2,這顯然是錯誤的。 反碼是原碼到補碼之間的一個過渡產品,它的求法很簡單,正數的反碼與其原碼相同;負數的反碼對其絕對值的原碼逐位求反例如一個int 型的十進制數100,用原碼表示則為:
0000000001100100
同樣一個int型的十進制數-100,其絕對值為100,其絕對值的原碼為
0000000001100100,逐位求反則得-100的反碼為:1111111110011011。
??? 補碼是計算機程序在進行運算時統一使用的機器碼,他可以將符號位和其它位統一處理。另外,兩個用補碼表示的數相加時,如果最高位(符號位)有進位,則進位被舍棄。補碼的計算方法也很簡單,正數的補碼和它的原碼相等,負數的補碼等于它的反碼加一。例如一個int型的十進制數-1,其補碼計算過程為:首先求出它的絕對值為1,計算它的絕對值1的原碼
0000000000000001,
逐位求反則得-1的反碼為:1111111111111110,給它加1就變成了:
1111111111111111。
反碼可以直接帶入運算,例如用反碼來計算1 + (-1):
0000000000000001 + 1111111111111111 = 0
??? 然后來看看幾種基本的邏輯運算。邏輯常量只有兩個,即0和1,用來表示兩個對立的邏輯狀態。邏輯運算通常用來測試邏輯常量的真假值。大于、大于等于、小于、小于等于、等于、不等于……這些用來比較的邏輯運算就不講了,上過小學的讀者肯定都會的。這里來看看與運算、或運算、異或運算以及非運算。
??? 與運算,把兩個操作數的逐位進行比較,若兩個操作數的第n位全為1,則運算結果的第n位為
1,否則為0。匯編語言中用and指令來表示與運算,例如:
0010110011010100
1010110010011010
and
-------------------
0010110010010000
??? 或運算,把兩個操作數的逐位進行比較,若兩個操作數的第n位若有一個為1,則運算結果的第n位為1,否則為0。匯編語言中用or指令來表示或運算,例如:
0010110011010100
1010110010011010
or
-------------------
1010110011011110
??? 異或運算,把兩個操作數的逐位進行比較,若兩個操作數的第n位有一個為0,而另一個為1,則運算結果的第n位為1,否則為0。匯編語言中用xor指令來表示異或運算,例如:
0010110011010100
1010110010011010
xor
-------------------
1000000001001110
??? 非運算,只有一個操作數,它的計算方法是把該操作數逐位求反。匯編語言中用not指令來表示非運算,例如:
1010110010011010
not
-------------------
0101001101100101
??? 筆者猶記得,當年教的模擬電路老師的幾句簡單的口令:與運算,有零則零;或運算,有一則
《軟件調試分析技術》學習筆記(六)
異或運算,相同則1,不同則0;非運算,零則一,一則零。
??? 明白了機器數和運算的原理以后來看看它們在程序運算中的具體使用方法。C語言代碼如下:
- #include?<stdio.h>???
- int?main()???
- {???
- ??int?a,?b,?c;???
- ??a?=?1;???
- ??b?=?2;???
- ??c?=?a?+?b;???
- ??c?=?a?-?b;???
- ??c?=?a?*?b;???
- ??c?=?a?/?b;???
- ??c?=?a?%?b;???
- ??c?=?a?&?b;???
- ??c?=?a?|?b;???
- ??c?=?a?^?b;???
- ??c?=?~a;???
- ??return?0;???
- }???
這段代碼定義了三個整型變量a、b、c,給變量a賦值1,給變量b賦值2,然后分別進行加、減、乘、除、模、與、或、異或還有非運算。看反匯編代碼:
- .text:00401000????push????ebp???
- .text:00401001????mov?????ebp,?esp???
- .text:00401003????sub?????esp,?0Ch???
這里抬高棧頂,用來為定義的局部變量開辟儲存空間。一個整型變量占4字節,那么0x0C字節則可開辟出三個整型變量的空間,用來儲存整型變量a、b、c。
- .text:00401006????mov?????[ebp+a],?1???
- .text:0040100D????mov?????[ebp+b],?2???
?
這兩句分別對應C語言代碼a = 1;和b = 2;。用來給整型變量a、b賦值。
- .text:00401014????mov?????eax,?[ebp+a]???
- .text:00401017????add?????eax,?[ebp+b]???
- .text:0040101A????mov?????[ebp+c],?eax??
? 這里取變量a到eax中,然后把eax和變量b相加,最后把結果放到變量c里。
- .text:0040101D????mov?????ecx,?[ebp+a]???
- .text:00401020????sub?????ecx,?[ebp+b]???
- .text:00401023????mov?????[ebp+c],?ecx??
這里取變量a到ecx中,然后把ecx和變量b相減,最后把結果放到變量c里。
- .text:00401026????mov?????edx,?[ebp+a]??
- .text:00401029????imul????edx,?[ebp+b]???
- .text:0040102D????mov?????[ebp+c],?edx??
這里取變量a到edx中,然后把edx和變量b相乘,最后把結果放到變量c里。
- .text:00401030????mov?????eax,?[ebp+a]??
- .text:00401033????cdq???
- .text:00401034????idiv????[ebp+b]???
- .text:00401037????mov?????[ebp+c],?eax??
這里取變量a到eax中,接著擴展符號位,然后除以變量b,最后把商放到變量c里。
- .text:0040103A????mov?????eax,?[ebp+a]???
- .text:0040103D????cdq???
- .text:0040103E????idiv????[ebp+b]???
- .text:00401041????mov?????[ebp+c],?edx???
這里取變量a到eax中,接著擴展符號位,然后除以變量b,最后把余數放到變量c里,這里和上面的除法運算的代碼是很像的,只有最后儲存運算結果的時候不同,除法運算儲存的是eax的值,而模運算儲存的是edx的值。細心的讀者可能注意到了,做除法運算的時候多了一條cdq指令,它是干什么的?還有,idiv指令只有一個操作數它是怎么完成除法運算的。
??? 其實dcq指令的作用是將雙字符號擴展至8字節,指的是擴展eax的符號位至edx寄存器中,也就是說,當eax小于80000000時使edx為00000000,當eax大于等于80000000時使edx為FFFFFFFF。
??? idiv指令是有符號除法,被除數沒有的指令中標出來,它是edx:eax。當idiv指令有操作數的時候,除數是操作數;如果沒有操作數,那么除數就是ecx。指令的運算結果:eax為商,edx是余數。
- .text:00401044????mov?????eax,?[ebp+a]???
- .text:00401047????and?????eax,?[ebp+b]???
- .text:0040104A????mov?????[ebp+c],?eax???
這里取變量a到ecx中,然后把ecx和變量b做與運算,最后把結果放到變量c里。
- .text:0040104D????mov?????ecx,?[ebp+a]???
- .text:00401050????or??????ecx,?[ebp+b]???
- .text:00401053????mov?????[ebp+c],?ecx???
這里取變量a到ecx中,然后把ecx和變量b做或運算,最后把結果放到變量c里。
?
- .text:00401056????mov?????edx,?[ebp+a]???
- .text:00401059????xor?????edx,?[ebp+b]???
- .text:0040105C????mov?????[ebp+c],?edx???
?這里取變量a到edx中,然后把edx和變量b做異或運算,最后把結果放到變量c里。
- .text:0040105F????mov?????eax,?[ebp+a]???
- .text:00401062????not?????eax???
- .text:00401064????mov?????[ebp+c],?eax???
這里取變量a到eax中,然后把eax和變量b做非運算,最后把結果放到變量c里。
- text:00401067????xor?????eax,?eax???
- .text:00401069????mov?????esp,?ebp???
- .text:0040106B????pop?????ebp???
- .text:0040106C????retn?