亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

WIN32匯編基礎(chǔ)

系統(tǒng) 1667 0

一、關(guān)于寄存器

寄存器有EAX,EBX,ECX,EDX,EDI,ESI,ESP,EBP等,似乎IP也是寄存器,但只有在CALL/RET在中會(huì)默認(rèn)使用它,其它情況很少使用到,暫時(shí)可以不用理會(huì)。
EAX是WIN32 API 默認(rèn)的返回值存放處。
ECX是LOOP指令自動(dòng)減一的寄存器。
ESP是堆棧指針。
EBP經(jīng)常用來在堆棧中尋址。
ESI好像常常用在指針尋址中,EDI不大清楚。

二、關(guān)于內(nèi)存尋址

WIN32中內(nèi)存是平坦的,對(duì)于每個(gè)程序來說都可以使用2G范圍的地址,但各個(gè)程序之間并不會(huì)干擾,這是因?yàn)楦鱾€(gè)程序所使用到的物理內(nèi)存被Windows自行安排,不會(huì)互相覆蓋,而且一個(gè)程序不會(huì)隨意地訪問到另一個(gè)程序的地址空間。

三、關(guān)于堆棧

Windows為每個(gè)程序安排了堆棧段,它是從高地址向低地址延伸的,之所以采用這種方式,是因?yàn)檫@樣可以使堆棧指針始終指向最近入棧的元素的起始地址,這樣的話,為訪問這個(gè)元素提供了非常便利的方式。

ESP作為堆棧指針始終指向棧頂,如果看一下PUSH和POP的操作就可以明白這句話:
PUSH: ESP <-- ESP-4 (ESP+3,ESP) <-- 入棧元素
POP: 出棧元素 <-- (ESP+3,ESP) ESP <-- ESP+4

因?yàn)镻USH和POP自動(dòng)修改了ESP的值,使它始終指向棧頂了。當(dāng)然也可以自己來修改ESP的值,例如我們可以:
sub esp,4 ;這樣就把棧頂指針向下移動(dòng)了。
這種操作常常用在局部變量的分配中,在子程序中使用到局部變量時(shí),就在堆棧中為它們提供空間,這樣可以使子程序退出時(shí)收回局部變量占用的空間,有利于子程序的模塊化。

我們可以用ESP來尋址堆棧中的元素,比如ESP指向當(dāng)前棧頂元素的起始地址,ESP-4指向前一個(gè)元素的起始地址,不過因?yàn)镋SP常常在變化,這樣用ESP在堆棧中尋址的話不方便,所以我們就用EBP來代替ESP尋址,首先把EBP入棧保存,然后把ESP賦值給EBP,這樣就可以用EBP來尋址堆棧中的數(shù)據(jù)了。我用一個(gè)例子來說明堆棧的變化。

push 0x00000001;1
push ebp ;2
mov ebp,esp ;3
push 0x12345678 ;4
mov eax,dword ptr[ebp+4] ;5
mov ebx,dword ptr[ebp-4] ;6
mov ax,word ptr[ebp-2] ;7
mov al,byte ptr[ebp-1] ;8
mov al,byte ptr[ebp-3] ;9
mov ax,word ptr[ebp-3] ;10

5 eax=0x00000001
6 ebx=0x12345678
7 ax=0x1234
8 al=0x12
9 al=0x56
10 ax=0x3456


堆棧使用在子程序的實(shí)現(xiàn)中,當(dāng)調(diào)用子程序時(shí),首先把參數(shù)入棧,然后把返回IP入棧,然后轉(zhuǎn)移到子程序處,如果有局部變量,則下移ESP,然后初始化該局部變量,這樣用到EBP來尋址局部變量,參數(shù)的尋址同樣要用到EBP。


四、簡(jiǎn)單的幾個(gè)關(guān)鍵字

ptr 顯式指定后面的數(shù)據(jù)的類型
offset 全局變量的地址
addr 局部變量的地址,也可以用在全局變量上
local 定義局部變量
proc 定義子程序
proto 聲明子程序

五、例子

Hello.asm文件的內(nèi)容如下:
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 第一部分:模式和源程序格式的定義語句
.386; 指令集
.model flat,stdcall; 工作模式
option casemap:none ; 格式
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定義
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 數(shù)據(jù)段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
szCaption db 'A MessageBox !',0
szText db 'Hello, World !',0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代碼段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
start:
invoke MessageBox,NULL,offset szText,offset szCaption,MB_OK
invoke ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
endstart ; 指定程序的入口
1. 第一部分 模式和源程序格式的定義語句
第一行 指定使用的指令集(編譯器使用)
Win32環(huán)境工作在80386及以上的處理器中,所以必須定義.386。如果程序(VxD等驅(qū)動(dòng)程序)中要用到特權(quán)指令,那么必須定義.386p。
第二行 定義程序工作的模式(包括內(nèi)存模式、語言模式、其它模式)
對(duì)Win32程序來說,只有一種內(nèi)存模式,即flat(平坦)模式。
Win32 API調(diào)用使用的是stdcall格式,所以Win32匯編中必須在.model中加上stdcall參數(shù)。
第三行 option語句
由于Win32 API中的API名稱區(qū)分大小寫,所以必須定義option casemap:none,來表明程序中的變量和子程序名對(duì)大小寫敏感。
2. 包含全部段的源程序結(jié)構(gòu):
.386
.model flat,stdcall
option casemap:none
<一些include語句>
.stack [堆棧段的大小]
.data
<一些初始化過的變量定義>
.data?
<一些沒有初始化過的變量定義>
.const
<一些常量定義>
.code
<代碼>
<開始標(biāo)記>
<其他語句>
end 開始標(biāo)記
3. 段的定義
數(shù)據(jù)段
.data
已初始化數(shù)據(jù)段,可讀可寫的已定義變量;
當(dāng)程序裝入完成時(shí),這些值就已經(jīng)在內(nèi)存中;
數(shù)據(jù)定義在.data段中會(huì)增加可執(zhí)行文件的大?。?
.data 段一般存放在可執(zhí)行文件的_DATA節(jié)區(qū)(Section)內(nèi);
.data?
未初始化數(shù)據(jù)段,可讀可寫的未定義變量,在可執(zhí)行文件中不占空間;
這些變量一般作為緩沖區(qū)或者在程序執(zhí)行后才開始使用。
數(shù)據(jù)定義在.data?數(shù)據(jù)段中不會(huì)增加可執(zhí)行文件的大?。?
.data? 段一般存放在可執(zhí)行文件的_BSS節(jié)區(qū)內(nèi);
.const
常量,可讀不可寫的變量;
代碼段
.code
所有的指令都必須寫在代碼段中;
Win32 中,數(shù)據(jù)段是不可執(zhí)行的,只有代碼段有可執(zhí)行的屬性;
對(duì)于運(yùn)行在特權(quán)級(jí)3的應(yīng)用程序,.code段不可寫。除非把可執(zhí)行文件PE頭部中的屬性位改成可寫;
對(duì)于運(yùn)行在特權(quán)級(jí)0的程序,所有的段都有讀寫權(quán)限,包括代碼段;
.code 代碼段一般存放在可執(zhí)行文件的_TEXT節(jié)區(qū)內(nèi);
堆棧段
.stack
與DOS匯編不同,Win32匯編不必考慮堆棧。系統(tǒng)會(huì)自動(dòng)分配堆??臻g;
堆棧段的內(nèi)存屬性是可讀寫并且可執(zhí)行;
靠動(dòng)態(tài)修改代碼的反跟蹤模塊可以拷貝到堆棧中去邊修改邊執(zhí)行;
緩沖區(qū)溢出技術(shù)也會(huì)用到這個(gè)特性;
4. 調(diào)用操作系統(tǒng)功能的方法:
DOS下
操作系統(tǒng)的功能通過各種軟中斷來實(shí)現(xiàn)。
應(yīng)用程序調(diào)用操作系統(tǒng)功能將經(jīng)歷如下三個(gè)過程:
把相應(yīng)的參數(shù)放在各個(gè)寄存器中再調(diào)用相應(yīng)的中斷;
程序控制權(quán)轉(zhuǎn)到中斷中去執(zhí)行;
完成以后通過iret中斷返回指令回到應(yīng)用程序中;
DOS 下調(diào)用系統(tǒng)功能方法的缺點(diǎn):
所有的功能號(hào)定義是難以記憶的數(shù)字;
80x86 系列處理器能處理的中斷最多只能有256個(gè);
通過寄存器來傳遞參數(shù),對(duì)于參數(shù)較多的函數(shù)很不方便;
Win32下
系統(tǒng)功能模塊放在Windows的動(dòng)態(tài)鏈接庫(DLL)中
作為Win32 API核心的3個(gè)DLL:
KERNEL32.DLL 系統(tǒng)服務(wù)功能。
GDI32.DLL 圖形設(shè)備接口。
USER32.DLL 用戶接口服務(wù)。
常用API的參數(shù)和函數(shù)聲明,查看文檔《Microsoft Win32 Programmer's Reference》
5. Win32 API 的函數(shù)原型聲明
函數(shù)原型聲明的匯編格式如下:
函數(shù)名 proto [距離] [語言] [參數(shù)1]:數(shù)據(jù)類型, [參數(shù)2]:數(shù)據(jù)類型,......
proto是函數(shù)聲明的偽指令
距離可以設(shè)置為NEAR、FAR、NEAR16、NEAR32、FAR16或FAR32,由于Win32中只有一個(gè)平坦的段,無所謂距離,所以在定義時(shí)可以忽略距離。
語言類型可是使用.model所定義的默認(rèn)值。
以消息對(duì)話框函數(shù)MessageBox為例
C格式如下:
int MessageBox(
HWND hWnd, // Handle to owner window
LPCTSTR lpText, // text in message box
LPCTSTR lpCaption, // message box title
UINT uType // message box style
);
匯編格式如下:
MessageBox Proto hWnd:dword,lpText:dword,lpCaption:dword,uType:dword
或者寫為
MessageBox Proto :dword,:dword,:dword,:dword
編譯器只對(duì)參數(shù)的數(shù)量和類型感興趣,參數(shù)的名稱只是增加可讀性,所以可以省略。
對(duì)于匯編語言來說,Win32環(huán)境中的參數(shù)實(shí)際上只有一種類型,就是一個(gè)32位的整數(shù)(dword,double word),雙字,四字節(jié)。
6. 調(diào)用 Win32 API
調(diào)用API有如下兩種方法:
1) invoke
invoke是MASM提供的偽指令;
invoke 偽指令的好處就是能夠提高代碼的可讀性,減少錯(cuò)誤;
invoke 做了下面三件事:
在編譯的時(shí)候,由編譯器把invoke偽指令展開成相應(yīng)的push指令和call指令;
進(jìn)行參數(shù)數(shù)量的檢查工作;
如果帶的參數(shù)數(shù)量和聲明時(shí)的數(shù)量不符,編譯器會(huì)報(bào)錯(cuò);
2) push和call的組合
80386處理器的指令
invoke MessageBox,NULL,offset szText,offset szCaption,MB_OK
也可寫為
push NULL
push offset szText
push offset szCaption
push MB_OK
call MessageBox
7. Win32 API 函數(shù)返回值的處理方法
對(duì)于匯編語言來說,Win32 API函數(shù)返回值的類型只有dword一種類型,它永遠(yuǎn)放在eax中。
如果要返回的內(nèi)容在一個(gè)eax中放不下,Win32 API采用如下方法來解決:
a) 一般是eax中返回一個(gè)指向返回?cái)?shù)據(jù)的指針;
b) 在調(diào)用參數(shù)中提供一個(gè)緩沖區(qū)地址,數(shù)據(jù)直接返回到這個(gè)緩沖區(qū)中去。類似變參的概念;
8. 與字符串相關(guān) Win32 API 的分類
在Win32環(huán)境中,根據(jù)兩個(gè)不同的字符集(ANSI字符集和Unicode字符集),可以把和字符串相關(guān)的API分成兩類:
a) 處理ANSI字符集的Win32 API函數(shù)
函數(shù)名稱的尾部帶一個(gè)“A”字符;
ANSI字符串是以NULL結(jié)尾的一串字符數(shù)組,每一個(gè)ANSI字符占一個(gè)字節(jié)的寬度;
例如:MessageBoxA Proto hWnd:dword,lpText:dword,lpCaption:dword,uType:dword
b) 處理Unicode字符集的Win32 API函數(shù)
函數(shù)名稱的尾部帶一個(gè)“W”字符;
每一個(gè)Unicode字符占兩個(gè)字節(jié)的寬度,所以可以同時(shí)定義65536個(gè)不同的字符;
例如:MessageBoxW Proto hWnd:dword,lpText:dword,lpCaption:dword,uType:dword
Windows 9x系列不支持Unicode版本的API,絕大多數(shù)的API只有ANSI版本。
只有Windows NT系列才完全支持Unicode版本的API。
為了編寫在幾個(gè)平臺(tái)中都能通用的程序,一般應(yīng)用程序都使用ANSI版本的API函數(shù)集。
提高程序可移植性的一個(gè)方法:
一般在源程序中不直接指明使用Unicode還是ANSI版本,而是使用宏匯編中的條件匯編功能來統(tǒng)一替換。
比如,在頭文件中做如下定義:
if UNICODE
MessageBox equ <MessageBoxW>
else
MessageBox equ <MessageBoxA>
endif
然后在源程序的頭部指定UNICODE=1或UNICODE=0,重新編譯后就能產(chǎn)生不同的版本。
9. include 語句
include語句的語法是:
include 文件名
include <文件名>
用“<>”將文件名括起來,可以避免黨文件名和MASM的關(guān)鍵字同名時(shí)引起編譯器混淆。
include語句的作用:
解決了所用到的Win32 API函數(shù)都必須預(yù)先聲明的麻煩。
把所有用到的Win32 API函數(shù)聲明預(yù)先放在一個(gè)頭文件中,然后用include語句包含進(jìn)源程序。
編譯器對(duì)include語句的處理方法,僅是簡(jiǎn)單地用指定的文件內(nèi)容把這行include語句替換掉而已。
和C語言中的#include作用類似。
10. includelib 語句
includelib語句的語法是:
includelib 庫文件名
includelib <庫文件名>
用“<>”將文件名括起來,同樣可以避免當(dāng)文件名和MASM的關(guān)鍵字同名時(shí)引發(fā)編譯器混淆。
includelib語句的作用是:
告訴鏈接器使用哪些導(dǎo)入庫。
導(dǎo)入庫
WIN32中,API函數(shù)的實(shí)現(xiàn)代碼放在DLL中,導(dǎo)入庫中只留有API函數(shù)的定位信息和參數(shù)數(shù)目等簡(jiǎn)單信息。
DOS下的函數(shù)庫是靜態(tài)庫
C語言的函數(shù)庫是典型的靜態(tài)庫
靜態(tài)庫的好處是節(jié)省大量的開發(fā)時(shí)間。
靜態(tài)庫的缺點(diǎn)是每個(gè)可執(zhí)行文件中都包含了要用到的相同函數(shù)的代碼,即占用了大量的磁盤空間,執(zhí)行的時(shí)候,這些代碼也會(huì)重復(fù)占用內(nèi)存。
includelib語句和include語句的處理不同,includelib不會(huì)把.lib文件的內(nèi)容插入到源程序中,它只是告訴鏈接器在鏈接的時(shí)候到指定的庫文件中去找Win32 API函數(shù)的位置信息而已。
11. MASM 中標(biāo)號(hào)和變量的命名規(guī)范
MASM中標(biāo)號(hào)和變量的命名規(guī)范是相同的,如下:
1) 可以用字母、數(shù)字、下劃線及符號(hào)@、$和?。
2) 第一個(gè)符號(hào)不能是數(shù)字。
3) 長(zhǎng)度不能超過240個(gè)字符。
4) 不能使用指令名等關(guān)鍵字。
5) 在作用域內(nèi)必須是唯一的。
12. 標(biāo)號(hào)
標(biāo)號(hào)有如下兩種定義方法:
標(biāo)號(hào)名: 目的指令 ; 方法1
標(biāo)號(hào)名::目的指令 ; 方法2
方法1和方法2是不同的
方法1
標(biāo)號(hào)名的后面跟一個(gè)冒號(hào),表示標(biāo)號(hào)的作用域是當(dāng)前的子程序。
在單個(gè)子程序中的標(biāo)號(hào)不能同名,不能從一個(gè)子程序中用跳轉(zhuǎn)指令跳到另一個(gè)子程序中。
方法2
標(biāo)號(hào)名的后面跟兩個(gè)冒號(hào),表示標(biāo)號(hào)的作用域是整個(gè)程序。
對(duì)任何其它子程序都是可見的。
在低版本MASM中,默認(rèn)標(biāo)號(hào)的作用域是整個(gè)程序。
在高版本MASM中,默認(rèn)標(biāo)號(hào)的作用域是當(dāng)前的子程序。
高版本MASM中的@@標(biāo)號(hào)
當(dāng)用@@做標(biāo)號(hào)時(shí),可以用@F和@B來引用;
@F表示本條指令后的第一個(gè)@@標(biāo)號(hào);
@B表示本條指令前的第一個(gè)@@標(biāo)號(hào);
不要在間隔太遠(yuǎn)的代碼中使用@@標(biāo)號(hào),源程序中@@標(biāo)號(hào)和跳轉(zhuǎn)指令之間的距離最好限制在編輯器能夠顯示的同一屏幕的范圍內(nèi)。
13. 全局變量
全局變量的作用域是整個(gè)程序
Win32匯編的全部變量定義在.data或.data?段內(nèi),這兩個(gè)段都是可寫的。可以同時(shí)定義變量的類型和長(zhǎng)度。
全局變量的定義格式如下:
變量名 類型 初始值1,初始值2,......
變量名 類型 重復(fù)數(shù)量 dup (初始值1,初始值2,......)
MASM支持的變量類型如下表:

名稱
表示方式
縮寫
長(zhǎng)度 ( 字節(jié) )
字節(jié)
Byte
db
1
word
dw
2
雙字(double word)
dword
dd
4
三字(far word)
fword
df
6
四字(quad word)
qword
dq
8
10字節(jié)BCD碼(ten byte)
tbyte
dt
10
有符號(hào)字節(jié)(sign byte)
sbyte
1
有符號(hào)字(sign word)
sword
2
有符號(hào)雙字(sign dword)
sdword
4
單精度浮點(diǎn)數(shù)
Real4
4
雙精度浮點(diǎn)數(shù)
Real8
8
10字節(jié)浮點(diǎn)數(shù)
Real10
10
注意:只有定義全局變量的時(shí)候,類型才可以用縮寫。
在byte類型變量的定義中,可以用引號(hào)定義字符串和數(shù)值定義的方法混用。
例如:szText db ‘Hello,world!’,0dh,0ah,’Hello again’,0dh,0ah,0
全局變量的初始化:
全局變量在定義中既可以指定初值,也可以只用問號(hào)預(yù)留空間。
全局變量定義在.data?段中時(shí),只能用問號(hào)預(yù)留空間,因?yàn)?data?段不能指定初始值。
定義時(shí)用問號(hào)指定的全局變量的初始值是0。
14. 局部變量
局部變量的好處是使程序的模塊結(jié)構(gòu)更加分明。
局部變量的缺點(diǎn)是因?yàn)榭臻g是臨時(shí)分配的,所以無法定義含有初始化值的變量,對(duì)局部變量的初始化一般在子程序中由指令完成。
局部變量的作用域是單個(gè)子程序。
局部變量定義在堆棧中。
局部變量的定義格式如下:
local 變量名1[[重復(fù)數(shù)量]][:類型],變量名2[[重復(fù)數(shù)量]][:類型] ......
local是MASM提供的偽指令,用于支持局部變量的定義。有了local偽指令降低不少難度。
定義局部變量需注意以下幾點(diǎn):
a) local偽指令必須緊接在子程序定義的偽指令proc后、其它指令開始之前,因?yàn)榫植孔兞康臄?shù)目必須在子程序開始的時(shí)候就確定下來;
b) 定義局部變量時(shí)數(shù)據(jù)類型不能用縮寫。如果要定義數(shù)據(jù)結(jié)構(gòu),可以用數(shù)據(jù)結(jié)構(gòu)的名稱當(dāng)作類型;
c) Win32匯編中,參數(shù)的默認(rèn)類型是dword,如果定義dword類型的局部變量,類型可以省略;
d) 當(dāng)定義數(shù)組類型的局部變量時(shí),重復(fù)數(shù)量可以用“[]”括起來,不能使用定義全局變量的dup偽指令。
e) 局部變量不能和已定義的全局變量同名。
f) 局部變量的作用域是當(dāng)前的子程序,所以在不同的子程序中可以有同名的局部變量。
局部變量的初始化:
局部變量無法在定義的時(shí)候指定初始化值,因?yàn)閘ocal偽指令只是為局部變量留出空間。
局部變量的初始值是隨機(jī)的,所以,對(duì)局部變量的值一定要初始化。
一般在子程序中使用指令來初始化局部變量。
RtlZeroMemory這個(gè)Win32 API函數(shù)實(shí)現(xiàn)將整個(gè)數(shù)據(jù)結(jié)構(gòu)填0的功能,類似C語言的memset。
在原來的DOS環(huán)境下,低版本的MASM中,所有的變量都相當(dāng)于現(xiàn)在所說的全局變量,都定義在數(shù)據(jù)段里面。
用匯編語言在堆棧中定義局部變量非常麻煩,需要作一張表,表上的內(nèi)容是局部變量名和 ebp指針的位置關(guān)系。
15. 使用局部變量的一個(gè)典型例子與反匯編得到指令的比較:
TestProc proc
local @loc1:dword,@loc2:word
local @loc3:byte
mov eax,@loc1
mov ax,@loc2
mov al,@loc3
TestProc endp
反編譯后得到以下指令:
:00401000 55 push ebp
:00401001 8BEC mov ebp,esp
:00401003 83C4F8 add esp,FFFFFFF8
:00401006 8B45FC mov eax,dword ptr [ebp-04]
:00401009 668B45FA mov ax,word ptr [ebp-06]
:0040100D 8A45F9 mov al,byte ptr [ebp-07]
:00401010 C9 leave
:00401011 C3 ret
其中的
push ebp ; 把原來ebp寄存器的值保存起來;
mov ebp,esp ; 把esp寄存器的值復(fù)制到ebp寄存器中,供存取局部變量時(shí)做指針用;
add esp,FFFFFFF8 ; 在堆棧中預(yù)留出空間(即重新設(shè)置堆棧指針),由于堆棧是向下增長(zhǎng),所以要把esp加上一個(gè)負(fù)值。
三條指令用于局部變量的準(zhǔn)備工作。
在堆棧中預(yù)留出空間時(shí),把esp加上(-8),而不是加上(-7),是因?yàn)樵?0386處理器中,以dword為界對(duì)齊時(shí)存取內(nèi)存的速度最快。以空間換時(shí)間。
leave是80386指令集中的一條指令,用于局部變量的掃尾工作。
一條leave指令就實(shí)現(xiàn)了mov esp,ebp和pop ebp兩條指令的功能。
mov esp,ebp ; ebp 寄存器中保存了正確的初始esp值,所以把正確的esp設(shè)置回去后,ret指令就能從堆棧中取出正確的地址返回。
pop ebp ; 執(zhí)行這條語句之后,堆棧就是正確的。
由于esp寄存器在程序的執(zhí)行過程中可能隨時(shí)會(huì)被用到,所以不可能用esp寄存器做指針來存取堆棧中的局部變量。
ebp寄存器也是以堆棧段為默認(rèn)數(shù)據(jù)段的,所以可以用ebp做指針來存取堆棧中的局部變量。
局部變量在堆棧中排列的順序如下表:

ebp 偏移
內(nèi)容
ebp+4
由call指令推入的返回地址。
ebp
push ebp指令推入的原ebp值,然后新的ebp就等于當(dāng)前的esp寄存器的值。
ebp-4
第一個(gè)局部變量@loc1:dword (4個(gè)字節(jié))
ebp-6
第二個(gè)局部變量@loc2:word (2個(gè)字節(jié))
ebp-7
第三個(gè)局部變量@loc3:byte (1個(gè)字節(jié))
使用局部變量時(shí)的注意點(diǎn):
a) ebp寄存器是關(guān)鍵,它起到保存原始esp寄存器值的作用;
b) 另外,ebp寄存器隨時(shí)用做存取局部變量的指針基址,所以絕不能把ebp寄存器用于別的用途;
c) ebp寄存器的值絕對(duì)不能被改變,把ebp寄存器的值改掉,程序就玩完;
16. 數(shù)據(jù)結(jié)構(gòu)
數(shù)據(jù)結(jié)構(gòu)相當(dāng)于一種自定義的數(shù)據(jù)類型,類似C語言中的struct定義。
匯編中,數(shù)據(jù)結(jié)構(gòu)的定義方法如下:
結(jié)構(gòu)名 struct
字段1 類型 ?
字段2 類型 ?
......
結(jié)構(gòu)名 ends
定義數(shù)據(jù)結(jié)構(gòu)并不會(huì)在某個(gè)段中產(chǎn)生數(shù)據(jù),只有使用數(shù)據(jù)結(jié)構(gòu)在數(shù)據(jù)段中定義數(shù)據(jù)后,才會(huì)產(chǎn)生數(shù)據(jù)。
使用數(shù)據(jù)結(jié)構(gòu)在數(shù)據(jù)段中定義數(shù)據(jù)的兩種方法如下:
第一種定義方法是未初始化的定義方法:
.data?
stWndClass WNDCLASS <>
......
第二種定義方法是定義的同時(shí)指定結(jié)構(gòu)中個(gè)字段的初始值:
.data
stWndClass WNDCLASS <1,1,1,1,1,1,1,1,1,1>
......
匯編中,對(duì)數(shù)據(jù)結(jié)構(gòu)變量的幾種引用方法如下:
a) 最直接的方法:
mov eax,stWndClass.lpfnWndProc
如果stWndClass結(jié)構(gòu)變量在內(nèi)存中的起始地址是403000h,那么這句指令會(huì)被編譯成mov eax,[403004h]
b) 在實(shí)際使用中,常有使用指針存取數(shù)據(jù)結(jié)構(gòu)變量的情況:
如果使用esi寄存器做指針尋址
mov esi,offset stWndClass
mov eax,[esi + WNDCLASS.lpfnWndProc]
第二句指令將被編譯成mov eax,[esi+4]
c) 使用assume偽指令把寄存器預(yù)先定義為結(jié)構(gòu)指針,在進(jìn)行操作:
mov esi,offset stWndClass
assume esi:ptr WNDCLASS
mov eax,[esi].lpfnWndClass
......
assume esi:nothing
編譯后產(chǎn)生同樣的代碼,不過程序的可讀性比較好。
注意:在不使用esi寄存器做指針的時(shí)候要用assume esi:nothing取消定義。
結(jié)構(gòu)的嵌套定義如下:
NEW_WNDCLASS struct
dwOption dword ?
oldWndClass WNDCLASS<>
NEW_WNDCLASS ends
引用嵌套的oldWndClass結(jié)構(gòu)變量的lpfnWndProc字段的方法:
assume esi:ptr NEW_WNDCLASS
mov eax,[esi].oldWndClass.lpfnWndProc
......
assume esi:nothing
windows.inc文件定義了大部分Win32 API所涉及的常量和數(shù)據(jù)結(jié)構(gòu)。
17. 以不同的類型訪問變量
MASM中以不同的類型訪問不會(huì)對(duì)變量造成影響。而C語言中的數(shù)據(jù)類型強(qiáng)制轉(zhuǎn)換過程中,數(shù)據(jù)的內(nèi)容已經(jīng)發(fā)生變化。
MASM中,如果要用指定類型之外的長(zhǎng)度訪問變量,必須顯式地指出要訪問的長(zhǎng)度,這樣,編譯器忽略語法上的長(zhǎng)度校驗(yàn),僅使用變量的地址。
訪問變量是顯式地指出要訪問長(zhǎng)度的方法是:
類型 ptr 變量名
例如:
mov ax,word ptr szBuffer
mov eax,dword ptr szBuffer
類型可以設(shè)置為byte、word、dword、fword、qword、real8和real10。
類型必須和操作的寄存器長(zhǎng)度匹配,否則無法通過編譯。
需要注意的是:
指定類型的訪問變量并不會(huì)去檢測(cè)長(zhǎng)度是否溢出。
80386的字節(jié)序是:
低位數(shù)據(jù)在低地址,高位數(shù)據(jù)在高地址
舉例:
下面這段代碼存在長(zhǎng)度溢出的問題。長(zhǎng)度溢出即越界存取到相鄰的其它變量。
.data
bTest1 db12h
wTest2 dw 1234h
dwTest3dd 12345678h
......
.code
......
mov al,bTest1
movax,word ptr bTest1
mov eax,dword ptr bTest1
......
通過反匯編后的內(nèi)容如下:
; .data段中的變量
:00403000 12 ; 從這里開始的1個(gè)字節(jié)是變量bTest1
:00403001 34 ; 從這里開始的2個(gè)字節(jié)是變量wTest2
:00403002 12
:00403003 78 ; 從這里開始的4個(gè)字節(jié)是變量dwTest3
:00403004 56
:00403005 34
:00403006 12
; .code段中的代碼
:00401000 A000304000 mov al,byte ptr [00403000]
:00401005 66A100304000 mov ax,word ptr [00403000]
:0040100B A100304000 mov eax,dword ptr [00403000]
運(yùn)行結(jié)果:
al等于12h
ax等于 3412h
eax 等于 78123412h
從例子可以看出,匯編中用ptr強(qiáng)制覆蓋變量長(zhǎng)度的時(shí)候,實(shí)質(zhì)上只用了變量的地址,編譯器并不會(huì)考慮定界的問題。
movzx 指令用于數(shù)據(jù)長(zhǎng)度的擴(kuò)展
movzx 指令是80386 處理器提供的擴(kuò)展指令 ,該指令總是將擴(kuò)展的數(shù)據(jù)位用0代替。
movzx指令是安全的強(qiáng)制類型轉(zhuǎn)換方式。
能夠像C語言的強(qiáng)制類型轉(zhuǎn)換一樣,把一個(gè)字節(jié)擴(kuò)展到一個(gè)字或一個(gè)雙字再放到ax或eax中,高位保持0而不是越界存取到其它的變量中。
movsx 指令可以完成帶符號(hào)位的擴(kuò)展
movsx指令是80386處理器提供的擴(kuò)展指令;
當(dāng)被擴(kuò)展數(shù)據(jù)的最高位為0時(shí),效果和movzx指令相同;當(dāng)最高位為1時(shí),則擴(kuò)展部分的數(shù)據(jù)位全部用1填充。
18. 變量的尺寸和數(shù)量
sizeof偽操作符可以取得變量、數(shù)據(jù)類型或數(shù)據(jù)結(jié)構(gòu)以字節(jié)為單位的長(zhǎng)度(尺寸)。
格式:
sizeof 變量、數(shù)據(jù)類型或數(shù)據(jù)結(jié)構(gòu)名
lengthof偽操作符可以取得變量、數(shù)據(jù)類型或數(shù)據(jù)結(jié)構(gòu)中數(shù)據(jù)的項(xiàng)數(shù)(數(shù)量)
格式:
length 變量、數(shù)據(jù)類型或數(shù)據(jù)結(jié)構(gòu)名
對(duì)字符串使用sizeof偽操作符,取得的長(zhǎng)度包括結(jié)束符0。
需要注意的是:
sizeof偽操作符和length偽操作符取得的數(shù)值是編譯期產(chǎn)生的,由編譯器直接替換到指令中去。所以,在反匯編得到的代碼中沒有sizeof或lengthof,而只有它們?nèi)〉玫臄?shù)值。
取得字符串長(zhǎng)度的一種特殊情況:
如果szHello的定義分成兩行:
szHello db ‘Hello’,0dh,0ah
db ‘World’,0
sizeof szHello得到的數(shù)值是7而不是13。
這種定義方式實(shí)質(zhì)為越界使用字符串變量。
MASM中的變量定義只認(rèn)一行,后一行db ‘World’,0實(shí)際上是另一個(gè)沒有名稱的數(shù)據(jù)定義。
要取得這種字符串的長(zhǎng)度時(shí),千萬不能用sizeof偽指令,最好是在程序中用lstrlen函數(shù)去計(jì)算。
19. 獲取變量地址
獲取全局變量地址和獲取局部變量地址的操作是不同的。
因?yàn)槿肿兞慷x在數(shù)據(jù)段中,而局部變量在堆棧中。全局變量的地址可以在編譯期確定,而局部變量的地址只能在運(yùn)行期確定。
全局變量的地址在編譯期已經(jīng)由編譯器確定了。
獲取全局變量的地址使用offset偽操作符,這個(gè)操作在編譯期而不是運(yùn)行期完成。
mov 寄存器,offset 變量名
不可能用offset偽操作符來獲取局部變量地址的原因是:
局部變量是用ebp來做指針訪問的,由于ebp的值隨著程序的執(zhí)行環(huán)境不同可能是不同的,所以局部變量的地址值在編譯期也是不確定的。
獲取局部變量的地址使用lea指令
lea指令是80386處理器指令集中的一條指令。
lea eax,[ebp-4]
在invoke偽指令的參數(shù)中用到某個(gè)局部變量的地址,使用MASM提供的偽操作符addr。
格式為:
addr 局部變量名和全局變量名
addr偽操作符即可用于局部變量,也可用于全局變量
<
分享到:
評(píng)論

WIN32匯編基礎(chǔ)


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦?。。?/p>

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 一级片影院| 奇米影视小说 | 黄色片网站视频 | 黄色毛片小视频 | 欧美日韩顶级毛片www免费看 | 91精品国产免费网站 | 欧美成人性videos | 国产成年人在线观看 | 97在线视频免费播放 | 99热成人精品国产免男男 | 伊人久久综合影院 | 亚洲光棍天堂 | 九九免费精品视频在这里 | 午夜精品久久久久久久久 | 亚洲精品亚洲人成人网 | 日韩一级a毛片欧美一级 | 成 人 黄 色视频免费播放 | 久草在线视频精品 | 久国产精品久久精品国产四虎 | 国产精品久久天天影视 | 激情五月综合综合久久69 | 一区二区精品久久 | 亚洲 日本 欧美 中文幕 | 四虎免费影院在线播放 | 国产亚洲午夜精品a一区二区 | 日本一级免费 | 日本免费黄色小视频 | 精品国产91| 日韩欧美二区在线观看 | 日韩最新视频一区二区三 | 欧美a级成人淫片免费看 | 四虎影视永久 | 色噜噜狠狠成人中文小说 | 国产精品久久精品 | 日本一级特黄大一片免 | 番茄视频在线观看黄版本免费 | 久久最新精品 | 国产精品二区三区 | 精品国产视频在线观看 | 国产精品日本一区二区不卡视频 | 久爱www成人网免费视频 |