軟件逆向工程原理與實(shí)踐課件:x86與x64體系結(jié)構(gòu)_第1頁
軟件逆向工程原理與實(shí)踐課件:x86與x64體系結(jié)構(gòu)_第2頁
軟件逆向工程原理與實(shí)踐課件:x86與x64體系結(jié)構(gòu)_第3頁
軟件逆向工程原理與實(shí)踐課件:x86與x64體系結(jié)構(gòu)_第4頁
軟件逆向工程原理與實(shí)踐課件:x86與x64體系結(jié)構(gòu)_第5頁
已閱讀5頁,還剩125頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

x86與x64體系結(jié)構(gòu)2.1x86基本概念2.2IA-32內(nèi)存模型與內(nèi)存管理2.3IA-32寄存器2.4IA-32數(shù)據(jù)類型2.5函數(shù)調(diào)用、中斷與異常2.6IA-32指令集2.7x64體系結(jié)構(gòu)簡介2.8思考與練習(xí)

2.1x86基本概念

x86是基于Intel8086/8088處理器的一系列向后兼容的指令集體系結(jié)構(gòu)(InstructionSetArchitectures,ISA)的總稱。IA-32(全稱IntelArchitecture,32-bit)體系結(jié)構(gòu)指的是32位版本的x86指令集體系結(jié)構(gòu)。IA-32體系結(jié)構(gòu)的處理器指與Intel奔騰II處理器兼容的32位處理器,或由其他處理器廠商生產(chǎn)的支持同一指令集的、能夠運(yùn)行32位操作系統(tǒng)的處理器。

一般而言,x86處理器具有三種操作模式:實(shí)模式、保護(hù)模式和系統(tǒng)管理模式。實(shí)模式(RealMode)只支持16位指令集和寄存器,是MS-DOS的運(yùn)行環(huán)境。保護(hù)模式(ProtectedMode)支持虛擬內(nèi)存、分頁等機(jī)制,當(dāng)前的Windows和Linux等主流操作系統(tǒng)均運(yùn)行于保護(hù)模式下。實(shí)模式和保護(hù)模式在內(nèi)存管理、寄存器和指令特征方面均有所差別,在本章的后續(xù)內(nèi)容將詳細(xì)講解。系統(tǒng)管理模式(SystemManagementMode)主要用于執(zhí)行嵌入在固件中的特殊代碼,在該模式下,包括操作系統(tǒng)本身在內(nèi)的所有正常執(zhí)行均被掛起,而特殊的分隔軟件則運(yùn)行于高特權(quán)狀態(tài),該軟件可以是固件或硬件輔助調(diào)試器的一部分。系統(tǒng)管理模式的典型應(yīng)用包括系統(tǒng)安全、電源管理、處理器溫度控制、硬件錯(cuò)誤管理等。

x86體系結(jié)構(gòu)是一種CISC(Complex

InstructionSetComputer)體系結(jié)構(gòu),支持字節(jié)尋址,數(shù)據(jù)字節(jié)在內(nèi)存中以小端序存放。根據(jù)具體處理器的差異,x86整數(shù)運(yùn)算和內(nèi)存訪問的最大本地長度可以為16位、32位或64位,典型指令長度為2~3字節(jié)。較少的通用寄存器數(shù)量使得寄存器相關(guān)的尋址方式成為訪問操作數(shù)的主要方式。

2.1.1字節(jié)序

多字節(jié)數(shù)據(jù)在內(nèi)存中按照怎樣的順序存放,或者在網(wǎng)絡(luò)上按照怎樣的順序傳輸,是與CPU有關(guān)的。不同硬件體系結(jié)構(gòu)對應(yīng)的字節(jié)序分以下兩種。

(1)大端序(BigEndian):高位字節(jié)存入低地址,低位字節(jié)存入高地址。

(2)小端序(LittleEndian):低位字節(jié)存入低地址,高位字節(jié)存入高地址。

下面看一個(gè)例子。

對于以下程序變量聲明:

BYTEb=0x12;

WORDw=0x1234;

DWORDdw=0x12345678;

charstr[]="abcde";

表2-1中給出了上例各類型變量的大端序和小端序存儲(chǔ)方式。易見,大端序更符合一般思維習(xí)慣。字符數(shù)組str并沒有被當(dāng)作多字節(jié)數(shù)據(jù)看待,而是將數(shù)組中的每個(gè)字符看作單字節(jié)數(shù)據(jù)。小端序主要被Intel處理器所使用,而大端序則主要被RISC(Reduced

InstructionSetComputer)架構(gòu)的處理器(包括PowerPC、MIPS等)使用。因此可以說,x86體系結(jié)構(gòu)是一種小端序體系結(jié)構(gòu)。

2.1.2權(quán)限級(jí)別

在保護(hù)模式下,存在4個(gè)權(quán)限級(jí)別(PrivilegeLevel,PL),編號(hào)從0到3,0權(quán)限級(jí)別最高,3權(quán)限級(jí)別最低。有人形象地將不同的權(quán)限級(jí)別看作多個(gè)同心的保護(hù)環(huán)(如圖2-1所示)。最內(nèi)側(cè)的Ring0運(yùn)行操作系統(tǒng)內(nèi)核;Ring1和Ring2較少使用,一般可根據(jù)需要運(yùn)行操作系統(tǒng)服務(wù)或驅(qū)動(dòng)程序;最外側(cè)的Ring3則運(yùn)行一般應(yīng)用程序。在Ring0上,程序能夠更改所有系統(tǒng)設(shè)置,執(zhí)行所有指令并訪問所有數(shù)據(jù)。在Ring3上,程序僅可讀寫系統(tǒng)設(shè)置的一個(gè)子集。對于Windows等現(xiàn)代操作系統(tǒng),內(nèi)核運(yùn)行于Ring0,用戶態(tài)應(yīng)用程序運(yùn)行于Ring3。

圖2-1保護(hù)環(huán)示意圖

當(dāng)前程序的Ring等級(jí)通常在CS寄存器中保存,稱為當(dāng)前特權(quán)級(jí)(CurrentPrivilegeLevel,CPL),等于當(dāng)前指令所在代碼段的特權(quán)級(jí)。權(quán)限級(jí)別檢查的目的是,阻止在較外側(cè)保護(hù)環(huán)上運(yùn)行的進(jìn)程隨意訪問存在于較內(nèi)側(cè)保護(hù)環(huán)上的段,為此,需要基于特定的規(guī)則,對CS寄存器中的CPL、段選擇器中的請求特權(quán)級(jí)(RequestPrivilegeLevel,RPL)、段描述符中的描述符特權(quán)級(jí)(DescriptorPrivilegeLevel,DPL)進(jìn)行相應(yīng)的比較檢查。

2.2IA-32內(nèi)存模型與內(nèi)存管理

IA-32體系結(jié)構(gòu)既支持直接物理內(nèi)存尋址也支持虛擬內(nèi)存(通過分頁)。采用直接物理內(nèi)存尋址時(shí),線性地址即看作物理地址;使用分頁模式時(shí),所有的代碼、數(shù)據(jù)、堆棧和系統(tǒng)段均可能被分頁,且只有最近被訪問過的頁保留在物理內(nèi)存中。IA-32體系結(jié)構(gòu)能夠支持多種內(nèi)存模型和不同操作模式下的內(nèi)存管理。

2.2.1內(nèi)存模型

處理器從邏輯上組織內(nèi)存的方式可分為兩種:平面內(nèi)存模型和分段內(nèi)存模型。

1)平面內(nèi)存模型

在平面內(nèi)存模型中,內(nèi)存顯示為連續(xù)的字節(jié)序列,該字節(jié)序列的編址從0起始,對于IA-32體系結(jié)構(gòu),結(jié)束于232-1。特定的地址稱為線性地址,對應(yīng)的地址空間稱為線性地址空間,具體模型如圖2-2所示。

圖2-2平面內(nèi)存模型示意圖

2)分段內(nèi)存模型

在分段模型中,程序內(nèi)存由一系列獨(dú)立的地址空間(稱為“段”)組成,每個(gè)段最大232,IA-32程序最多使用16?383個(gè)段。代碼、數(shù)據(jù)和棧在不同的段中。

在分段模型中,程序的地址稱為邏輯地址。邏輯地址由兩部分組成:段選擇器和偏移地址。邏輯地址通過CPU轉(zhuǎn)化為線性地址,這一過程是對應(yīng)用程序透明的,具體過程如圖2-3所示。

圖2-3分段內(nèi)存模型示意圖

2.2.2不同操作模式下的內(nèi)存管理

對于分段內(nèi)存模型,在不同的操作模式(實(shí)模式和保護(hù)模式)下,其內(nèi)存管理方式和尋址模式存在差異。實(shí)模式一般提供不受保護(hù)的段,保護(hù)模式則能夠提供嚴(yán)密的內(nèi)存保護(hù)機(jī)制。以下具體介紹。

1)實(shí)模式

實(shí)模式用于實(shí)現(xiàn)早期處理器的16位執(zhí)行環(huán)境。實(shí)模式使用20位的地址空間,因?yàn)樵缙谔幚砥?Intel8086/8088)只有20條地址線。實(shí)模式的示意圖如圖2-4所示。

圖2-4實(shí)模式示意圖

在實(shí)模式下,邏輯地址由16位的段選擇器和16位的段內(nèi)偏移地址組成。16位的段選擇器用于確定一個(gè)20位的段基址,確定方法是在16位段選擇器內(nèi)容之后補(bǔ)4位0。20位的段基址與16位的段內(nèi)偏移地址相加,得到此邏輯地址所對應(yīng)的線性地址。從邏輯地址到線性地址的映射過程如圖2-5所示。由此可見,線性地址范圍為0~220-1,且線性地址空間由一系列64

KB大的段組成。

圖2-5實(shí)模式下邏輯地址到線性地址的映射過程

2)保護(hù)模式

保護(hù)模式也是分段內(nèi)存模型的實(shí)例。Windows操作系統(tǒng)運(yùn)行在保護(hù)模式下。與實(shí)模式不同的是,保護(hù)模式對物理地址的解析過程不僅由CPU獨(dú)自完成,操作系統(tǒng)也通過維護(hù)特殊的表結(jié)構(gòu)為內(nèi)存保護(hù)等功能提供支持。保護(hù)模式與實(shí)模式的一個(gè)重要區(qū)別在于,段寄存器中到底存放的是段基址(實(shí)模式)還是描述符表的索引(保護(hù)模式)。

在保護(hù)模式下,對內(nèi)存的保護(hù)機(jī)制包括兩種:分段和分頁。分段是強(qiáng)制的,分頁是可選的,分頁建立在分段的基礎(chǔ)上。保護(hù)模式的內(nèi)存管理過程如圖2-6所示。

圖2-6保護(hù)模式的內(nèi)存管理示意圖

在保護(hù)模式下,段選擇器長度為16位,段內(nèi)偏移地址長度為32位。段選擇器所存儲(chǔ)的,不是某個(gè)段在物理內(nèi)存中的物理地址,而是一個(gè)線性結(jié)構(gòu)的索引。該線性結(jié)構(gòu)即描述符表(DescriptorTable),描述符表中的每一項(xiàng)稱為段描述符。段選擇器除了包含描述符表的索引外,還包含一些其他信息,如圖2-7所示,段選擇器的高13位是描述符表的索引(因此描述符表的項(xiàng)數(shù)不能超過213-1個(gè)),第14位表示描述符表類型,第15、16位定義段選擇器的請求特權(quán)級(jí)(RPL)。

圖2-7段選擇器結(jié)構(gòu)

段描述符存儲(chǔ)線性地址空間中段的元數(shù)據(jù),其長度一般為64位。段描述符的內(nèi)容包括段的32位線性基址、長度上限、描述符特權(quán)級(jí)(DPL)等。將段描述符中的32位線性基址與32位的段內(nèi)偏移地址相加,得到線性地址。因此,保護(hù)模式下的線性地址空間大小為4

GB。

描述符表可分為全局描述符表(GlobalDescriptorTable,GDT)和局部描述符表(LocalDescriptorTable,LDT)兩類。GDT必須存在,由操作系統(tǒng)在啟動(dòng)時(shí)創(chuàng)建,并被所有任務(wù)共享;LDT不是必須的,如果存在,則被單一任務(wù)或一組任務(wù)使用。寄存器GDTR用來保存GDT的基線性地址以及GDT的長度信息,其長度為48位,其中,高32位保存GDT的基線性地址,低16位存儲(chǔ)GDT的實(shí)際長度。操作系統(tǒng)通過特權(quán)指令LGDT向GDTR加載數(shù)據(jù),通過特權(quán)指令SGDT存儲(chǔ)GDTR中的數(shù)據(jù)。

2.3IA-32寄存器

IA-32的執(zhí)行環(huán)境中,寄存器主要分為通用寄存器、EFLAGS寄存器、指令指針寄存器、段寄存器、控制寄存器、調(diào)試寄存器、內(nèi)存管理寄存器等類型,本節(jié)主要介紹前四種。

2.3.1通用寄存器

IA-32體系結(jié)構(gòu)擁有8個(gè)32位通用寄存器(GeneralPurposeRegister,GPR),其名稱與基本功能如表2-2所示。

在這8個(gè)通用寄存器中,一些通用寄存器可進(jìn)一步切分為16位或8位寄存器,以保證向后兼容性,具體如圖2-8所示。例如,寄存器AX引用寄存器EAX的低位字,而AH和AL標(biāo)識(shí)符則分別引用寄存器AX的高字節(jié)和低字節(jié)。對于棧指針寄存器和變址寄存器,也可使用對應(yīng)的16位版本(BP、SP、SI、DI)來引用32位寄存器的低16位。

圖2-8通用寄存器切分及命名示意圖

值得注意的是,一般Win32API都會(huì)先將返回值保存在EAX中再返回。此外,Win32API函數(shù)內(nèi)部會(huì)使用ECX和EDX,因而在編寫匯編程序調(diào)用Win32API之前,如果ECX和EDX寄存器正在使用,應(yīng)先將ECX和EDX中的內(nèi)容備份到其他寄存器或棧中。

2.3.2EFLAGS寄存器

32位的EFLAGS寄存器用于存儲(chǔ)算數(shù)操作符狀態(tài)或其他執(zhí)行狀態(tài)。該寄存器中的各個(gè)位表示不同的標(biāo)識(shí),包括一組狀態(tài)標(biāo)識(shí)、一個(gè)控制標(biāo)識(shí)和一組系統(tǒng)標(biāo)識(shí)。EFLAGS寄存器中的標(biāo)識(shí)主要用于實(shí)現(xiàn)條件分支。

EFLAGS寄存器的各標(biāo)識(shí)位的名稱、類型和縮寫見圖2-9。其中,與程序調(diào)試相關(guān)的狀態(tài)標(biāo)識(shí)包括:零標(biāo)識(shí)(ZF),溢出標(biāo)識(shí)(OF),進(jìn)位標(biāo)識(shí)(CF)和符號(hào)標(biāo)識(shí)(SF)。各標(biāo)識(shí)的具體含義如下:

(1)零標(biāo)識(shí)(ZF)。若算數(shù)或邏輯運(yùn)算結(jié)果為0,則ZF值為1,否則ZF值為0。

(2)溢出標(biāo)識(shí)(OF)。有符號(hào)整數(shù)溢出時(shí),OF置為1;最高有效位(MSB)改變時(shí),OF置為1。

(3)進(jìn)位標(biāo)識(shí)(CF)。無符號(hào)整數(shù)溢出時(shí),CF置為1。

(4)符號(hào)標(biāo)識(shí)(SF)。等于運(yùn)算結(jié)果的最高位(即有符號(hào)整數(shù)的符號(hào)位);0表示正數(shù),1表示負(fù)數(shù)。

(5)方向標(biāo)識(shí)(

DF)。另一個(gè)需要注意的標(biāo)識(shí)是控制標(biāo)識(shí)(DF),該標(biāo)識(shí)位為方向標(biāo)識(shí),用于控制串處理指令處理信息的方向。當(dāng)DF為1時(shí),每次操作后使變址寄存器ESI和EDI減小,這樣就使串處理從高地址向低地址方向處理;當(dāng)DF為0時(shí),處理方向相反。DF標(biāo)識(shí)由STD指令置位,由CLD指令清除。

(6)陷阱標(biāo)識(shí)(TF)和中斷允許標(biāo)識(shí)(IF)。它們是與中斷和異常相關(guān)的標(biāo)識(shí)位。如果TF標(biāo)識(shí)位置為1,CPU將在執(zhí)行完每條指令后產(chǎn)生單步中斷,調(diào)試器使用該特性在調(diào)試程序時(shí)進(jìn)行單步執(zhí)行,該標(biāo)識(shí)位還可用于檢查調(diào)試器是否正常運(yùn)行。如果IF位置位,則CPU在收到中斷請求后,應(yīng)該對中斷請求進(jìn)行響應(yīng)處理。

圖2-9

EFLAGS寄存器中的標(biāo)識(shí)位及其功能

2.3.3指令指針寄存器

32位指令指針寄存器(EIP)存放指令指針,即當(dāng)前代碼段中將被執(zhí)行的下一條指令的線性地址偏移。程序運(yùn)行時(shí),CPU根據(jù)CS段寄存器和EIP寄存器中的地址偏移讀取下一條指令,將指令傳送到指令緩沖區(qū),并將EIP寄存器的值自增,增大的大小即被讀取指令的字節(jié)數(shù)。EIP寄存器的值一般不能直接修改,EIP寄存器的更改有兩種途徑:一是通過特殊的跳轉(zhuǎn)和調(diào)用/返回指令JMP、Jcc、CALL、RET等;二是通過中斷或異常進(jìn)行修改。

2.3.4段寄存器

在IA-32體系結(jié)構(gòu)中,存在6個(gè)16位的段寄存器:CS、SS、DS、ES、FS和GS,分別用于存儲(chǔ)保護(hù)模式下邏輯地址中的段選擇器。

(1)代碼段寄存器(CS,CodeSegment):存放應(yīng)用程序代碼所在的段的段描述符索引(該段描述符中包含代碼段的線性基址)。易知,CPU在獲取將要執(zhí)行的下一條指令時(shí),使用CS寄存器找到代碼段的線性基址,再與EIP中的線性地址偏移量相加,從而得到下一條指令的線性地址。

(2)棧段寄存器(SS,StackSegment):存放棧段的段描述符索引(該段描述符中包含棧段的線性基址)。

(3)數(shù)據(jù)段寄存器(DS(DataSegment)、ES、FS、GS):存放數(shù)據(jù)段的段描述符索引(這些描述符中均包含數(shù)據(jù)段的線性基址)。其中,DS數(shù)據(jù)段含有程序使用的大部分?jǐn)?shù)據(jù),ES、FS和GS分別對應(yīng)IA-32中引入的附加數(shù)據(jù)段。ES數(shù)據(jù)段可以為某些串指令存放目的數(shù)據(jù),F(xiàn)S數(shù)據(jù)段寄存器可用于計(jì)算結(jié)構(gòu)化異常處理(StructuredExceptionHandler,SEH)、線程環(huán)境塊(ThreadEnvironmentBlock,TEB)、進(jìn)程環(huán)境塊(ProcessEnvironmentBlock,PEB)等地址。

2.4IA-32數(shù)據(jù)類型

2.4.1基本數(shù)據(jù)類型基本數(shù)據(jù)類型包括字節(jié)(bytes)、字(words)、雙字(doublewords)、四倍長字(quadwords)和雙四倍長字(doublequadwords),對應(yīng)的數(shù)據(jù)長度如圖2-10所示。字節(jié)為8位,字為16位,雙字為32位,四倍長字為64位,雙四倍長字為128位。IA-32指令集中的特定指令能夠直接操作這些基本數(shù)據(jù)類型的數(shù)據(jù)。四倍長字?jǐn)?shù)據(jù)類型首次出現(xiàn)于Intel486處理器中,而雙四倍長字?jǐn)?shù)據(jù)類型則首次出現(xiàn)于具有流式SIMD擴(kuò)展(StreamingSIMDExtensions,SSE)的奔騰III處理器中。

圖2-10IA-32與x64的基本數(shù)據(jù)類型及其長度

寄存器的數(shù)據(jù)類型依據(jù)其長度與基本數(shù)據(jù)類型的長度之間的對應(yīng)關(guān)系而定。例如,通用寄存器AL、BL、CL等保存字節(jié)類型值,AX、BX、CX等保存字類型值,EAX、EBX、ECX等保存雙字類型的值。x86體系結(jié)構(gòu)不存在64位的通用寄存器,在某些場景下將EDX:EAX合看作64位,通過RDTSC指令能將64位值寫入EDX:EAX。

2.4.2數(shù)值數(shù)據(jù)類型

對于一些算數(shù)運(yùn)算指令,基本數(shù)據(jù)類型可以被進(jìn)一步地解釋為數(shù)值數(shù)據(jù)類型(有符號(hào)或無符號(hào)整型、浮點(diǎn)型等),以支持對數(shù)值類型的操作。

IA-32和x64體系結(jié)構(gòu)定義了兩種整數(shù)類型:有符號(hào)整型和無符號(hào)整型。有符號(hào)整型采用補(bǔ)碼表示。一部分整型指令(如ADD、SUB、PADDB、PSUBB等)既可以操作有符號(hào)整型,也可以操作無符號(hào)整型;另一部分整型指令僅能操作一種類型(如IMUL、MUL、IDIV、DIV、FIADD、FISUB等)。無符號(hào)整型有時(shí)又稱序數(shù)。

IA-32體系結(jié)構(gòu)定義了三種主要的浮點(diǎn)類型:單精度浮點(diǎn)型、雙精度浮點(diǎn)型和雙擴(kuò)展精度浮點(diǎn)型。圖2-11給出了不同浮點(diǎn)型的數(shù)據(jù)格式,這些浮點(diǎn)型的數(shù)據(jù)格式與IEEE754標(biāo)準(zhǔn)所定義的二進(jìn)制算數(shù)浮點(diǎn)數(shù)一致。單精度浮點(diǎn)型(32位)和雙精度浮點(diǎn)型(64位),被包含SSE擴(kuò)展或包含高級(jí)向量擴(kuò)展(AdvancedVectorExtensions,AVX)的Intel處理器所支持。雙擴(kuò)展精度浮點(diǎn)型需要浮點(diǎn)運(yùn)算單元(FPU)支持。半精度浮點(diǎn)型(16位)僅被包含F(xiàn)16C擴(kuò)展的處理器體系結(jié)構(gòu)的一些傳統(tǒng)單精度指令所支持。不同浮點(diǎn)型的長度、精度和取值范圍如表2-3所示。

圖2-11浮點(diǎn)型的數(shù)據(jù)格式

2.4.3指針類型

IA-32定義了兩種類型的指針:近指針和遠(yuǎn)指針。近指針是一個(gè)段內(nèi)偏移,長度為32位或16位。在分段內(nèi)存模型下使用近指針時(shí),必須已知要訪問的段。遠(yuǎn)指針則是一個(gè)邏輯地址,由16位的段選擇器和32位(或16位)的段內(nèi)偏移組成。遠(yuǎn)指針用于在分段內(nèi)存模型中邏輯地址向線性地址的轉(zhuǎn)換。包含32位段內(nèi)偏移的近指針和遠(yuǎn)指針數(shù)據(jù)格式如圖2-12所示。

圖2-12近指針和遠(yuǎn)指針數(shù)據(jù)格式示意圖

在64位模式下,近指針為64位,遠(yuǎn)指針則有3種形式,分別為:

①16位段選擇器+16位段內(nèi)偏移;

②16位段選擇器+32位段內(nèi)偏移;

③16位段選擇器+64位段內(nèi)偏移。

2.5函數(shù)調(diào)用、中斷與異常

2.5.1棧棧是一塊連續(xù)的內(nèi)存區(qū)域,存在于一個(gè)棧段內(nèi),該棧段由段寄存器SS標(biāo)識(shí)(在平面內(nèi)存模型下,??梢晕挥诔绦蚓€性地址空間的任意位置)。棧的大小最大可與段的大小相同,在IA-32體系結(jié)構(gòu)下可達(dá)4

GB。棧的一般結(jié)構(gòu)如圖2-13所示。圖2-13棧結(jié)構(gòu)示意圖

在任意時(shí)刻,寄存器ESP所包含的棧指針都指向棧頂位置,該指針保存的是棧頂位置相對SS段基址的偏移量。將數(shù)據(jù)壓棧一般使用PUSH指令,從棧頂移除數(shù)據(jù)通常使用POP指令,具體指令的功能見第2.6.2小節(jié)。通常情況下,棧由高地址向低地址擴(kuò)展,即壓棧操作導(dǎo)致棧頂指針值減小,出棧操作導(dǎo)致棧頂指針值增大。

程序或操作系統(tǒng)可以設(shè)置多個(gè)棧,例如,多任務(wù)系統(tǒng)中的每個(gè)任務(wù)都可以有自己的棧。系統(tǒng)中棧的數(shù)量受到最大段數(shù)量和可用物理內(nèi)存的限制。當(dāng)系統(tǒng)設(shè)置多個(gè)棧時(shí),僅有SS寄存器所引用的當(dāng)前棧處于可用狀態(tài)。所有針對棧的指令操作(包括第2.6.2小節(jié)介紹的PUSH、POP、CALL、RET等)都必須基于SS寄存器對當(dāng)前棧的引用。

根據(jù)棧段寬度的不同,棧指針可以按照字(16位)或雙字(32位)進(jìn)行對齊。當(dāng)前代碼段的段描述符中的D標(biāo)識(shí)可用于設(shè)置棧段寬度。PUSH和POP指令,就是依據(jù)D標(biāo)識(shí)來確定到底對棧頂指針自增或自減多少字節(jié)的。

與壓棧和出棧操作相關(guān)的另一個(gè)棧屬性是地址長度,該屬性將決定到底是使用SP還是ESP保存棧頂指針來訪問棧。默認(rèn)的地址長度屬性由棧描述符中的B標(biāo)識(shí)決定。

2.5.2棧幀與函數(shù)調(diào)用連接信息

棧通常被切分為棧幀。棧幀可以看作是將調(diào)用函數(shù)和被調(diào)用函數(shù)聯(lián)系起來的機(jī)制。每一個(gè)棧幀可以包含:局部變量、向被調(diào)用函數(shù)傳遞的參數(shù)、函數(shù)調(diào)用的連接信息(棧幀相關(guān)的指針)等內(nèi)容。處理器提供兩個(gè)指針用于連接調(diào)用函數(shù)與被調(diào)用函數(shù):棧幀基指針和返回指令指針(又稱返回地址)。

1.棧幀基指針

棧幀基指針包含在EBP寄存器中,用以作為被調(diào)用函數(shù)棧幀的固定參考點(diǎn)。使用棧幀基指針時(shí),被調(diào)用方法首先將棧頂指針內(nèi)容復(fù)制到EBP寄存器中,然后再壓入局部變量。與ESP寄存器類似地,EBP寄存器自動(dòng)指向當(dāng)前棧(由SS段寄存器所指定)中的地址。

2.返回指令指針

在執(zhí)行被調(diào)用函數(shù)的第一條指令之前,CALL指令將EIP寄存器中的地址壓棧,這一被壓入棧中的指令地址稱為返回指令指針。該指針指向從被調(diào)用函數(shù)返回并恢復(fù)調(diào)用函數(shù)執(zhí)行時(shí),所應(yīng)該執(zhí)行的那條調(diào)用函數(shù)指令的地址。為了從被調(diào)用函數(shù)中返回,被調(diào)用函數(shù)的RET指令將返回指令指針從棧頂彈出到EIP寄存器中,從而恢復(fù)調(diào)用函數(shù)的執(zhí)行。正常情況下,返回指令指針等于調(diào)用者方法中緊跟CALL指令的語句的地址。

處理器不負(fù)責(zé)跟蹤返回指令指針?biāo)赶虻奈恢茫绦騿T應(yīng)該自己保證在執(zhí)行RET指令之前,棧頂指針恰好指向返回指令指針?biāo)幍臈卧R环N簡單的將棧頂指針指向返回指令指針?biāo)帡卧姆椒ㄊ菍SP指向EBP的位置。處理器并不要求返回指令指針必須指回到調(diào)用函數(shù),在執(zhí)行RET指令之前,返回指令指針可以被修改為指向當(dāng)前代碼段中的其他任一指令,使用這一機(jī)制必須非常小心。

2.5.3函數(shù)調(diào)用過程

本小節(jié)簡要介紹使用CALL和RET指令進(jìn)行的函數(shù)調(diào)用和返回過程。CALL指令使得控制流轉(zhuǎn)移到當(dāng)前代碼段或其他代碼段中的被調(diào)用函數(shù)中,兩種控制流轉(zhuǎn)移分別稱為近調(diào)用和遠(yuǎn)調(diào)用。近調(diào)用通常提供對本地函數(shù)的訪問,遠(yuǎn)調(diào)用通常提供對操作系統(tǒng)函數(shù)或其他進(jìn)程函數(shù)的訪問。

RET指令同樣提供兩種返回,近返回和遠(yuǎn)返回,分別對應(yīng)于CALL指令的近調(diào)用和遠(yuǎn)調(diào)用。RET指令還允許程序通過增加棧頂指針的值來從棧上釋放參數(shù),增加的字節(jié)數(shù)可由RET指令的參數(shù)指定。

以下以近調(diào)用為例,說明CALL和RET指令操作的具體步驟。

近調(diào)用步驟如下:

(1)將當(dāng)前EIP寄存器的值(返回指令指針)壓棧。

(2)將被調(diào)用函數(shù)首條指令的地址偏移載入EIP寄存器。

(3)開始執(zhí)行被調(diào)用函數(shù)。

近返回的步驟如下:

(1)將棧頂值彈出到EIP寄存器,棧頂值即為返回指令指針(調(diào)用者方法中緊跟CALL指令的語句的地址)。

(2)如果RET指令有參數(shù)n(例如“RETN8”),則將棧頂指針ESP增加n字節(jié),以釋放棧上的參數(shù)。

(3)恢復(fù)對調(diào)用者函數(shù)的執(zhí)行。

圖2-14給出了棧在近調(diào)用和近返回過程中的變化情況。對于遠(yuǎn)調(diào)用,除需要保存EIP之外,還要保存CS段寄存器的值。

圖2-14近調(diào)用和近返回過程中的棧

函數(shù)狀態(tài)保存也是函數(shù)調(diào)用和返回過程中的重要問題。我們知道,在函數(shù)調(diào)用發(fā)生時(shí),CPU不會(huì)自動(dòng)保存通用寄存器、段寄存器或EFLAGS寄存器的內(nèi)容。調(diào)用者函數(shù)應(yīng)該顯式地保存那些當(dāng)函數(shù)調(diào)用返回后還需要繼續(xù)使用的寄存器的內(nèi)容。保存方法可以是壓棧或保存在數(shù)據(jù)段中。

PUSHA和POPA指令提供了利用棧來保存和加載通用寄存器的方法,這兩種指令在第2.6.2節(jié)均會(huì)具體講解。如果被調(diào)用函數(shù)更改了某個(gè)段寄存器的狀態(tài),那么在執(zhí)行被調(diào)用函數(shù)的RET指令之前,應(yīng)該將段寄存器恢復(fù)到以前的狀態(tài)。如果調(diào)用者方法希望保存EFLAGS寄存器的內(nèi)容,那么可以通過PUSHF/PUSHFD指令將該寄存器內(nèi)容壓棧,并通過POPF/POPFD指令將保存的寄存器值彈出到該寄存器中。

2.5.4調(diào)用慣例

調(diào)用慣例是對函數(shù)調(diào)用時(shí)如何傳遞參數(shù)和返回值的約定。調(diào)用管理幫助我們回答以下問題:

(1)參數(shù)傳遞用寄存器?用棧?還是兩者都用?

(2)參數(shù)是從左到右壓棧還是從右到左壓棧?

(3)返回值存儲(chǔ)在棧?寄存器?還是兩者都存?

從理論上講,函數(shù)調(diào)用的參數(shù)傳遞可以通過以下三種方式進(jìn)行:

(1)通用寄存器傳參。調(diào)用者函數(shù)可以利用除ESP和EBP之外的其他6個(gè)通用寄存器,向被調(diào)用函數(shù)傳遞6個(gè)參數(shù)。這些參數(shù)應(yīng)在CALL指令之前被加載到這些通用寄存器中。

(2)數(shù)據(jù)段參數(shù)列表傳參。通過數(shù)據(jù)段上的參數(shù)列表,可以將大量參數(shù)或復(fù)雜數(shù)據(jù)結(jié)構(gòu)傳入被調(diào)用函數(shù)。一個(gè)指向數(shù)據(jù)段參數(shù)列表的指針可以通過通用寄存器或壓棧傳給被調(diào)用函數(shù)。

(3)棧傳參。通過壓棧操作也可以將大量參數(shù)傳入被調(diào)用函數(shù)。參數(shù)被放入調(diào)用者函數(shù)的棧幀,然后使用棧幀基址針(EBP)來訪問這些參數(shù)。

當(dāng)前主要的調(diào)用慣例包括以下幾種:

(1)cdecl:在C語言中使用,參數(shù)從右到左壓棧,調(diào)用者函數(shù)負(fù)責(zé)清理?xiàng)I系暮瘮?shù)參數(shù)(常通過函數(shù)調(diào)用返回后對ESP值進(jìn)行增大操作來實(shí)現(xiàn))。

(2)

stdcall:常用于Win32API,被調(diào)用函數(shù)負(fù)責(zé)清理?xiàng)I系暮瘮?shù)參數(shù)(常使用RETNn)。

(3)

fastcall:類似于stdcall,但使用寄存器ECX、EDX傳遞函數(shù)的前2個(gè)參數(shù)。

2.5.5中斷與異常

CPU提供兩種中止程序執(zhí)行的機(jī)制:中斷(interrupt)和異常(exception),中斷通常指由I/O設(shè)備觸發(fā)的異步事件;異常指CPU在執(zhí)行指令時(shí),檢測到一個(gè)或多個(gè)預(yù)定義條件時(shí)產(chǎn)生的同步事件。IA-32體系結(jié)構(gòu)規(guī)定了兩類主要異常:故障(fault)和陷入(trap),故障是可修正的異常,陷入是調(diào)用特定指令(如SYSENTER)時(shí)產(chǎn)生的異常。故障處理后執(zhí)行產(chǎn)生故障的指令,陷入處理后執(zhí)行產(chǎn)生陷入的指令的下一條指令。

IA-32體系結(jié)構(gòu)共支持256個(gè)不同的中斷向量/中斷描述符。其中,Intel保留了前32個(gè)作為現(xiàn)在和未來的CPU的預(yù)定義中斷使用,Intel目前定義了19種預(yù)定義的中斷和異常(中斷編號(hào)0~14、16~19)。另外224個(gè)中斷向量/中斷描述符(編號(hào)32~255)用于用戶定義的中斷,用戶定義的中斷又稱可屏蔽中斷(maskableinterrupts)。每個(gè)中斷編號(hào)與中斷描述和中斷源的關(guān)系如表2-4所示。

2.6IA-32指令集

2.6.1指令一般格式

IA-32體系結(jié)構(gòu)下指令的一般格式如表2-5所示,其中,指令操作碼Opcode是必需的,其他組成元素根據(jù)不同指令類型的需要是可選的。

指令各組成部分的具體含義如下:

(1)

Instructionprefix:指令前綴,可選的指令前綴作為指令的補(bǔ)充說明信息,主要用于REP指令、跨段指令、將操作數(shù)從32位轉(zhuǎn)換為16位、將地址從16位轉(zhuǎn)換為32位等情況。

(2)??Opcode:指令操作碼,定義指令行為,是匯編語句的主要組成部分。匯編指令助記符與指令操作碼一一對應(yīng)。

(3)??ModeR/M:操作數(shù)類型,用于輔助Opcode解釋匯編指令后的操作數(shù)類型。R表示寄存器,M表示內(nèi)存單元。在1字節(jié)的ModeR/M中,第6、7位描述第0~2位是寄存器還是內(nèi)存單元,第3~5位用于輔助Opcode。

(4)??SIB:全稱為Scale-Index-Base,輔助ModeR/M的尋址,用于計(jì)算地址偏移,說明內(nèi)存地址如何計(jì)算。其中Scale為2位,Index為3位,Base為3位。計(jì)算公式為

Address=Reg[base]+Reg[Index]?×?2scale

(5)??Displacement:用于輔助SIB,例如指令MOVEAX,DWORDPTRDS:[EDX?+?ECX*4?+?2]中的“+2”即由此字段指定。

(6)

Immediate:立即數(shù),用于表示指令操作數(shù)為一個(gè)常量值的情況。

反匯編工具通常通過查表的方式將由以上6部分組成的機(jī)器指令編碼解釋為相應(yīng)的匯編指令。這一過程深入反匯編工具內(nèi)部,從逆向分析的角度,我們更關(guān)心匯編指令的語義和用法,以下將對其進(jìn)行介紹。

2.6.2指令分類及常用指令功能

IA-32體系結(jié)構(gòu)中的通用指令集提供基本的數(shù)據(jù)移動(dòng)、算數(shù)和邏輯運(yùn)算、程序流控制、函數(shù)調(diào)用與返回、字符串操作等指令,用于實(shí)現(xiàn)運(yùn)行于IA-32處理器上的應(yīng)用程序和系統(tǒng)軟件。通用指令相關(guān)的操作數(shù)一般包括內(nèi)存數(shù)據(jù)、通用寄存器中的數(shù)據(jù)、EFLAGS寄存器中的數(shù)據(jù)、內(nèi)存中的地址信息、段寄存器等。通用指令集合中的指令可進(jìn)一步分為數(shù)據(jù)轉(zhuǎn)移指令、算術(shù)運(yùn)算指令、邏輯指令、移位和循環(huán)指令、控制轉(zhuǎn)移指令、字符串指令、標(biāo)識(shí)控制指令、段寄存器操作指令等類型。

匯編語言的指令繁多,在進(jìn)行軟件逆向分析時(shí),只需要掌握其中的一部分,其余指令可以通過查閱相關(guān)的手冊獲得其用法。本小節(jié)歸納和解釋其中一些常見的、與一般應(yīng)用程序相關(guān)的指令及其功能。

1.?dāng)?shù)據(jù)轉(zhuǎn)移

數(shù)據(jù)轉(zhuǎn)移指令在內(nèi)存與通用寄存器(或段寄存器)之間移動(dòng)數(shù)據(jù)。此類指令中最常見的是MOV指令。

1)

MOV

MOV指令能夠在通用寄存器之間移動(dòng)數(shù)據(jù),或在內(nèi)存與通用寄存器(或段寄存器)之間移動(dòng)數(shù)據(jù),或?qū)⒘⒓磾?shù)移動(dòng)到通用寄存器或內(nèi)存中。典型用法如表2-6所示。此外,MOV指令還支持控制寄存器與通用寄存器之間的數(shù)據(jù)移動(dòng),以及調(diào)試寄存器與通用寄存器之間的數(shù)據(jù)移動(dòng)。

2)字符串操作指令

直接在內(nèi)存之間移動(dòng)數(shù)據(jù)或直接修改內(nèi)存數(shù)據(jù),是字符串操作指令的一個(gè)特征。典型的操作包括字符串移動(dòng)MOVS、字符串掃描SCAS和字符串存儲(chǔ)STOS,還包括與操作次數(shù)相關(guān)的重復(fù)指令,具體功能如表2-7所示。

數(shù)據(jù)(字符串字符)的源地址應(yīng)預(yù)先使用ESI寄存器保存,目標(biāo)地址應(yīng)預(yù)先使用EDI寄存器保存。ESI和EDI保存的都是絕對地址,即相對于數(shù)據(jù)段起始位置的偏移量。ESI寄存器默認(rèn)識(shí)別DS數(shù)據(jù)段中的數(shù)據(jù)地址,但也可改為與CS、SS、ES、FS或GS段相關(guān)聯(lián);EDI寄存器默認(rèn)識(shí)別ES數(shù)據(jù)段中的數(shù)據(jù)地址,且不允許與其他段相關(guān)聯(lián)。

MOVS指令用于實(shí)現(xiàn)字符串或內(nèi)存的復(fù)制,存在三種數(shù)據(jù)長度相關(guān)的指令形式:MOVSB、MOVSW和MOVSD,它們分別在兩個(gè)內(nèi)存地址之間移動(dòng)1字節(jié)、2字節(jié)和4字節(jié)數(shù)據(jù)。每次數(shù)據(jù)移動(dòng)操作后,ESI和EDI中的源地址和目標(biāo)地址會(huì)自動(dòng)更新,即自增或自減1字節(jié)、2字節(jié)或4字節(jié)。到底是自增還是自減,這是由EFLAGS寄存器中的DF標(biāo)識(shí)位決定的。當(dāng)DF=1時(shí),ESI和EDI的值自減;當(dāng)DF=0時(shí),ESI和EDI的值自增。

SCAS指令用于實(shí)現(xiàn)字符串內(nèi)容與寄存器內(nèi)容的掃描比較,存在三種數(shù)據(jù)長度相關(guān)的指令形式:SCASB、SCASW和SCASD,它們分別用EDI所指向的內(nèi)存內(nèi)容(1字節(jié)、2字節(jié)或4字節(jié))減去寄存器AL(或AX、EAX)的內(nèi)容,并更新EFLAGS寄存器中的狀態(tài)標(biāo)識(shí)。內(nèi)存內(nèi)容和寄存器AL(或AX、EAX)的內(nèi)容均不改變。與MOVS指令類似地,EDI寄存

器中的內(nèi)存地址也會(huì)自動(dòng)自增或自減,自增和自減方向由EFLAGS寄存器中的DF標(biāo)識(shí)位決定。

3)棧操作指令

棧操作指令(PUSH、POP、PUSHA、POPA)負(fù)責(zé)從棧上移出數(shù)據(jù)或向棧頂壓入數(shù)據(jù),基本功能如表2-8所示。PUSH指令先將ESP寄存器中保存的棧頂指針值減小,然后將指令操作數(shù)的內(nèi)容壓入到棧頂位置。根據(jù)這一順序可見,ESP寄存器所指的棧單元內(nèi)容是棧頂元素,屬于棧的一部分。PUSH指令的操作數(shù)可以是內(nèi)存位置、立即數(shù)、寄存器(包括段寄存器)。PUSH指令常用于在函數(shù)調(diào)用前將參數(shù)壓棧,或用于在棧上保留臨時(shí)變量的存儲(chǔ)空間。

POP指令將當(dāng)前棧頂?shù)淖?或雙字)內(nèi)容復(fù)制到目標(biāo)操作數(shù)中,然后ESP中的棧頂指針值增加,指向新的棧頂位置。POP指令的目標(biāo)操作數(shù)可以是通用寄存器、段寄存器或內(nèi)存位置。

PUSHA/PUSHAD指令將8個(gè)通用寄存器的內(nèi)容依次壓棧。這些指令簡化了方法調(diào)用時(shí)的通用寄存器內(nèi)容保存方法。PUSHA壓入16位寄存器內(nèi)容(AX、CX、DX、BX、SP、BP、SI、DI),PUSHAD壓入32位寄存器內(nèi)容(EAX、ECX、EDX、EBX、ESP、EBP、ESI、EDI)。此處壓入的SP和ESP的值是PUSHA/PUSHAD指令調(diào)用前的值。

4)數(shù)據(jù)交換指令

XCHG指令置換兩個(gè)操作數(shù)的內(nèi)容,指令等價(jià)于3條MOV指令。如果XCHG指令的操作數(shù)中存在內(nèi)存位置,則CPU會(huì)保證在數(shù)據(jù)交換過程中數(shù)據(jù)的一致性和操作的原子性。

BSWAP指令將32位寄存器中的各個(gè)字節(jié)進(jìn)行逆序排列。第0~7位與第24~31位內(nèi)容置換,第8~15位與第16~23位內(nèi)容置換。BSWAP指令能夠幫助我們將數(shù)據(jù)格式在大端序和小端序之間進(jìn)行轉(zhuǎn)換。

XADD指令交換兩個(gè)操作數(shù)的內(nèi)容,然后對兩個(gè)操作數(shù)進(jìn)行加法操作,并將計(jì)算結(jié)果存入目標(biāo)操作數(shù)。EFLAGS寄存器的狀態(tài)位反映加法運(yùn)算的結(jié)果。數(shù)據(jù)交換指令的功能如表2-9所示。

5)?LEA?指令

LEA指令的全稱為LoadEffectiveAddress,該指令能夠計(jì)算出一個(gè)內(nèi)存的有效地址(在一個(gè)段中的偏移量),該內(nèi)存由源操作數(shù)指定,計(jì)算出的有效地址放入目標(biāo)寄存器。此指令常用于在字符串操作之前對ESI或EDI寄存器進(jìn)行初始化。

2.算數(shù)、邏輯與移位運(yùn)算

二元算數(shù)運(yùn)算指令提供基本的整數(shù)二元運(yùn)算,其操作數(shù)可以是字節(jié)、字或雙字長整數(shù),位置可位于通用寄存器或內(nèi)存。一元算數(shù)運(yùn)算則包括INC、DEC、NEG等基本操作。常見算數(shù)運(yùn)算指令的功能如表2-10所列。

整數(shù)相加(ADD)、整數(shù)帶進(jìn)位加法(ADC)、整數(shù)相減(SUB)、整數(shù)帶借位相減(SBB),分別對有符號(hào)或無符號(hào)的整數(shù)操作數(shù)進(jìn)行加法和減法運(yùn)算。ADC指令計(jì)算兩個(gè)操作數(shù)之和,如果計(jì)算使得CF置位,則再加1。SBB指令計(jì)算兩個(gè)操作數(shù)之差,如果計(jì)算使得CF置位,則再減1。加減法運(yùn)算接受兩個(gè)操作數(shù),運(yùn)算結(jié)果一般放入第一個(gè)操作數(shù)中。

處理器提供兩種乘法指令,無符號(hào)乘法(MUL)和有符號(hào)乘法(IMUL);兩種除法指令,無符號(hào)除法(DIV)和有符號(hào)除法(IDIV)。乘法的運(yùn)算結(jié)果的長度可能達(dá)到源操作數(shù)的兩倍。乘法和除法運(yùn)算一般支持單個(gè)操作數(shù),將寄存器AL、AX或EAX的值與操作數(shù)相乘,結(jié)果存入AX或DX:AX或EDX:EAX。除法運(yùn)算根據(jù)操作數(shù)(除數(shù))的長度不同,選擇AX或DX:AX或EDX:EAX作為被除數(shù),進(jìn)行除法運(yùn)算,運(yùn)算結(jié)果包括商和余數(shù)兩部分,將商保存在AL或AX或EAX中,并將余數(shù)保存在AH或DX或EDX中。

取反指令(NEG)用0減去一個(gè)有符號(hào)的整數(shù),達(dá)到對操作數(shù)二進(jìn)制補(bǔ)碼取反的效果。

比較指令(CMP)計(jì)算兩個(gè)操作數(shù)的差異(通過相減運(yùn)算),并根據(jù)計(jì)算結(jié)果對EFLAGS寄存器的OF、SF、ZF、AF、PF和CF標(biāo)識(shí)位進(jìn)行更新。操作數(shù)在CMP過程中的值不變,相減運(yùn)算結(jié)果也不會(huì)保存下來。CMP指令常與條件跳轉(zhuǎn)(Jcc)指令結(jié)合使用,跳轉(zhuǎn)的依據(jù)即CMP的運(yùn)算結(jié)果。

邏輯指令為不同長度的值(字節(jié)、字、雙字)提供基本的與、或、非、異或邏輯操作,常見指令的功能如表2-11所示。AND、OR、XOR、TEST指令要求兩個(gè)操作數(shù),NOT指令接受一個(gè)操作數(shù)。TEST指令比較兩個(gè)操作數(shù)(通過邏輯AND運(yùn)算),并設(shè)置EFLAGS寄存器中的適當(dāng)標(biāo)識(shí)位(PF、SF和ZF)。TEST與AND的區(qū)別在于,TEST運(yùn)算并不保存運(yùn)算結(jié)果,僅修改EFLAGS寄存器中的標(biāo)識(shí)位。TEST指令常與Jcc指令配合使用。

移位和循環(huán)移動(dòng)指令為字和雙字操作數(shù)提供移位和按位循環(huán)移動(dòng)操作,常見指令功能如表2-12所示。算數(shù)/邏輯移位的操作和移動(dòng)方式如圖2-15所示,算數(shù)移位用于有符號(hào)數(shù),邏輯移位用于無符號(hào)數(shù)。在兩個(gè)操作數(shù)中,第二個(gè)操作數(shù)代表第一個(gè)操作數(shù)應(yīng)被移動(dòng)的位數(shù),運(yùn)算結(jié)果仍保存在第一個(gè)操作數(shù)中。

圖2-15移位指令的操作和移動(dòng)方向

雙精度移位指令SHRD和SHLD接受三個(gè)操作數(shù),將一個(gè)操作數(shù)中指定位數(shù)的內(nèi)容移入另一個(gè)操作數(shù)中。例如,指令“SHRDAX,BX,10”的含義是,將AX寄存器邏輯右移10位,BX的右邊10位移入AX的左邊10位中,同時(shí)BX的內(nèi)容保持不變。同理,指令“SHLDEBX,ECX,13”的含義是,將EBX寄存器內(nèi)容左移13位,將ECX左側(cè)的13位移入EBX的右側(cè)13位,同時(shí)ECX內(nèi)容保持不變。CF標(biāo)識(shí)位用于保存最后一次移出目標(biāo)操作數(shù)的那個(gè)位。

循環(huán)移位指令的操作和移位方向如圖2-16所示。ROL和ROR指令將操作數(shù)寄存器中的內(nèi)容做循環(huán)移動(dòng),并根據(jù)最高位或最低位的值更新CF標(biāo)識(shí)位。RCL和RCR指令的循環(huán)移動(dòng)操作會(huì)經(jīng)由CF標(biāo)識(shí)位,RCL指令將CF標(biāo)識(shí)位看作對其操作數(shù)高位進(jìn)行的1位擴(kuò)展,RCR指令則將CF標(biāo)識(shí)位看作對其操作數(shù)低位進(jìn)行的1位擴(kuò)展,在此基礎(chǔ)上進(jìn)行循環(huán)移位操作。CF標(biāo)識(shí)位的值可在后續(xù)由條件跳轉(zhuǎn)指令(JC或JNC)進(jìn)行測試。

圖2-16循環(huán)移位指令的操作和移動(dòng)方向

3.控制轉(zhuǎn)移

控制轉(zhuǎn)移指令提供跳轉(zhuǎn)、條件跳轉(zhuǎn)、循環(huán)、方法調(diào)用與返回等典型的控制流操作功能。條件跳轉(zhuǎn)僅當(dāng)EFLAGS寄存器的特定狀態(tài)位被置位時(shí)進(jìn)行跳轉(zhuǎn)。而JMP指令、方法調(diào)用與返回指令等均屬于無條件跳轉(zhuǎn)。

1)跳轉(zhuǎn)指令

跳轉(zhuǎn)指令中最常用的是JMP指令,提供向目標(biāo)指令地址的無條件跳轉(zhuǎn)。跳轉(zhuǎn)的目標(biāo)地址可以在當(dāng)前代碼段內(nèi),也可以指向另一代碼段,前者稱為近跳轉(zhuǎn)(neartransfer),后者稱為遠(yuǎn)跳轉(zhuǎn)(fartransfer)。JMP指令的操作數(shù)保存的是目標(biāo)指令的地址(可以是相對地址或絕對地址)。相對地址是指相對于當(dāng)前EIP寄存器中地址的偏移量(有符號(hào)整數(shù))。

絕對地址是指相對于代碼段基址的偏移量,有兩種形式:

①由通用寄存器保存的地址,此地址作為近指針被復(fù)制到EIP寄存器中,用于近跳轉(zhuǎn);

②由處理器的標(biāo)準(zhǔn)尋址模式所指定的地址,此地址可以是近指針或者遠(yuǎn)指針,如果是近指針,則該地址轉(zhuǎn)換后復(fù)制到EIP寄存器,如果是遠(yuǎn)指針,則地址轉(zhuǎn)換為一個(gè)段選擇器和一個(gè)偏移量,段選擇器復(fù)制到CS寄存器中,偏移量復(fù)制到EIP寄存器中。

條件跳轉(zhuǎn)指令系列Jcc用于根據(jù) EFLAGS寄存器中的特定標(biāo)識(shí)位決定是否進(jìn)行跳轉(zhuǎn),具體指令及其功能見表2-13,表中成對出現(xiàn)的指令(如JA/JNBE)實(shí)際上是同一條指令的不同名稱。條件跳轉(zhuǎn)指令分為有符號(hào)條件跳轉(zhuǎn)和無符號(hào)條件跳轉(zhuǎn)兩大類。無符號(hào)條件跳轉(zhuǎn)測試無符號(hào)整數(shù)運(yùn)算的結(jié)果及其標(biāo)識(shí)位,有符號(hào)條件跳轉(zhuǎn)測試有符號(hào)整數(shù)運(yùn)算的結(jié)果及其標(biāo)識(shí)位。Jcc指令的目標(biāo)操作數(shù)是一個(gè)相對地址(與EIP寄存器中地址相關(guān)的有符號(hào)偏移量),指向當(dāng)前代碼段中的一條指令。Jcc指令不支持遠(yuǎn)跳轉(zhuǎn),但可以通過Jcc和JMP相結(jié)合的方式實(shí)現(xiàn)遠(yuǎn)跳轉(zhuǎn)。

2)循環(huán)控制指令

循環(huán)控制指令實(shí)際上是一種條件跳轉(zhuǎn)指令。其中最常用的LOOP指令使用ECX寄存器的值作為循環(huán)次數(shù)的計(jì)數(shù)器。循環(huán)指令對ECX寄存器的值進(jìn)行自減操作,然后進(jìn)行測試,當(dāng)ECX的值不為0時(shí),程序控制流跳轉(zhuǎn)到由目標(biāo)操作數(shù)指定的指令地址,該地址是相對于當(dāng)前EIP寄存器內(nèi)容的相對偏移地址,指向循環(huán)指令塊的第一條指令;當(dāng)ECX的值為0時(shí)終止循環(huán),執(zhí)行緊跟LOOP指令的那條指令。如果ECX寄存器的內(nèi)容初始為0,則自減后變?yōu)镕FFFFFFFH,從而會(huì)導(dǎo)致LOOP循環(huán)執(zhí)行232次。

LOOPE/LOOPZ指令與LOOP指令的功能類似,區(qū)別之處在于每次循環(huán)除了測試ECX是否為0以外,還要測試ZF標(biāo)識(shí)位是否置位。如果ECX不為0,且ZF標(biāo)識(shí)位置位,則程序控制流跳轉(zhuǎn)到由目標(biāo)操作數(shù)指定的指令地址。如果ECX的值為0,或ZF標(biāo)識(shí)位清零,則循環(huán)終止且執(zhí)行緊跟LOOPE/LOOPZ指令的那條指令。LOOPNE/LOOPNZ指令與LOOPE/LOOPZ指令的區(qū)別在于,當(dāng)ZF標(biāo)識(shí)位置位時(shí)終止循環(huán)。上述循環(huán)控制指令的功能見表2-14。

表2-13中已介紹的JCXZ和JECXZ指令分別測試CX和ECX寄存器是否為0,這兩條指令常與循環(huán)控制指令配合使用,用于開始循環(huán)。循環(huán)控制指令對ECX寄存器的自減操作先于對ECX值的測試,如果ECX的值初始為0,則會(huì)導(dǎo)致循環(huán)232次,為避免此問題,可以向循環(huán)代碼塊之前插入JECXZ指令,當(dāng)ECX初始為0時(shí)跳過循環(huán)。JCXZ和JECXZ指令也常與字符串掃描指令配合使用,用于決定循環(huán)終止條件。

4.函數(shù)調(diào)用與返回指令

CALL指令將程序控制從當(dāng)前函數(shù)轉(zhuǎn)移到另一個(gè)被調(diào)用函數(shù)。為了保證后續(xù)從被調(diào)用函數(shù)返回到調(diào)用函數(shù),CALL指令在跳轉(zhuǎn)到被調(diào)用函數(shù)之前會(huì)在棧上保存當(dāng)前的EIP寄存器。EIP寄存器在程序流控制轉(zhuǎn)移的前一時(shí)刻包含了緊隨CALL指令的下一條指令的地址。該地址即第2.5.2節(jié)所述的返回指令指針。

CALL指令的操作數(shù)是一個(gè)目標(biāo)地址,即被調(diào)用函數(shù)中首條指令的地址。該地址可以是一個(gè)相對地址或一個(gè)絕對地址。如果是絕對地址,則該地址可以是一個(gè)近指針或遠(yuǎn)指針??梢姡珻ALL指令操作數(shù)的描述方式類似于JMP指令的操作數(shù),與JMP的區(qū)別在于,JMP指令的跳轉(zhuǎn)是單向的,不在棧上保存返回地址。

RET指令將程序控制流從當(dāng)前被調(diào)用函數(shù)轉(zhuǎn)換到調(diào)用者函數(shù)??刂屏鞯霓D(zhuǎn)換通過將棧上的返回指令指針復(fù)制到EIP寄存器來完成。此后的程序執(zhí)行從EIP指向的指令繼續(xù)向下進(jìn)行。RET指令可使用一個(gè)可選的操作數(shù),在RET操作時(shí),該操作數(shù)的值將會(huì)被加到ESP寄存器之上,使得棧頂指針的增長能夠直接從棧上移除調(diào)用者方法所壓入的實(shí)參。

5.中斷指令

中斷相關(guān)的指令包括INT(軟件中斷)、INTO(溢出時(shí)中斷)、BOUND(檢測到值超出范圍)、IRET(從中斷返回)等。其中,INT、INTO、BOUND指令允許程序顯式地發(fā)起一個(gè)特定的中斷或異常。這些中斷或異常進(jìn)而能夠引起對中斷和異常處理程序的調(diào)用。

INTn指令能夠通過中斷向量編號(hào)發(fā)起任意的處理器中斷或異常。這一指令能夠用來支持軟件生成的中斷,或測試中斷和異常處理程序的操作。INT3指令作為INTn的特例,顯式地調(diào)用斷點(diǎn)異常處理程序。

如果EFLAGS寄存器中的OF標(biāo)識(shí)位被置位,則INTO指令產(chǎn)生一個(gè)溢出異常。如果OF標(biāo)識(shí)位沒有置位,該指令就不產(chǎn)生任何異常并繼續(xù)向下執(zhí)行。OF標(biāo)識(shí)位表示的是算數(shù)運(yùn)算是否溢出,但OF標(biāo)識(shí)位的置位并不會(huì)自動(dòng)發(fā)起溢出異常,溢出異常的發(fā)起只能以兩種方式:

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論