0x00 IA32處理器體系結構
微機的基本結構
指令執行周期
當指令使用了內存操作數時還需要兩個額外的步驟:取操作數和存儲輸出操作數。
機器指令的執行;
- 取指令
- 解碼
- 執行
操作模式
保護模式:處理器的基本模式。
虛擬8086模式:多任務環境中執行是地址模式的軟件。
實地址模式:用於運行那些需要直接訪問系統內存和硬件設備的MS-DOS程序。
系統管理模式:實現電源管理和系統安全等功能的機制。
基本執行環境
基本寄存器
寄存器中數據在內存中存放數據遵循高高低低的原則
8個通用寄存器
EAX EBX ECX EDX
EBP ESP ESI EDI
6個段寄存器
一個處理器狀態標志寄存器(EFLAGS)和一個指令指針(EIP)寄存器。
ESP:棧地址寄存器 任意時刻指向棧頂元素
EBP:擴展幀指針寄存器 指向堆棧上的函數參數和局部變量
EIP:指令指針寄存器
EIP寄存器不能作為MOV指令的⽬標操作數
EFLAGS:由控制CPU的操作或反映CPU某些運算的結果的獨立二進制位構成
狀態標志
Name | ||
---|---|---|
CF | 進位標志 | 進位或借位時CF=1 |
AC | 輔助進位標志 | 低4位進位或借位時A=1 |
PF | 奇偶標志 | 偶數P=1 |
ZF | 零標志 | 結果為0則Z=1 |
SF | 符號標志 | S = 符號位值(補碼時0=正,1=負) |
OF | 溢出標志 | 運算結果超界時O=1 |
DF | Direction Flag | |
IF | Intertupt Flag | |
TF | Trace Flag |
內存管理
實地址模式
可以尋址1MB內存 0~FFFFF
20位線性地址
linear address or abssolute address is 20 bits,range from 0 to FFFFF
用段-偏移地址表示
- CS:16位代碼段
- DS:16位數據段
- SS:16位堆棧段
- ES,FS,GS可指向其他數據段
保護模式
可以尋址4GB內存 0~FFFFFFFF
段寄存器指向段描述符表,操作系統使用段描述符表定位程序使用的段的位置。
0x01 匯編語言基礎
補碼的表示法
- 正數的補碼:與源碼相同
- 負數的補碼:反碼加1
尋址方式
基本元素
16進制數第一個是字母時要在前面加0
指令
一條匯編指令包括4個部分:
- 標號(可選)
- 助記符
- 操作數
- 注釋(可選)
INVOKE
相當於call,調用函數或過程
偽指令
偽指令課用於定義變量、宏以及過程,可用於執行命名段以及執行其他與匯編器相關任務。
.data?
:指明未初始化的數據段
NOP指令
占用一個字節的存儲,什么也不做。
程序模板
TITLE Program Template
; 程序描述:
; 作者:
; 創建日期:
; 修改:
; 日期: 修改者:
INCLUDE Irvine32.inc
.data
;(在此插入變量)
.code
main PROC
;(在此插入可執行代碼)
exit
main ENDP
;(在此插入其他子程序)
END main
匯編-鏈接-執行
定義數據
字符常量/字符串常量
- 以單引號或雙引號括起來的單個/一串字符
- 存儲為對應字符的ASCII碼
后綴 | 含義 |
---|---|
d | 十進制 |
b | 二進制 |
q | 八進制 |
h | 十六進制 |
數據定義語句
初始值可以用?
表示不確定,可以是表達式。
可以指定多個初始值,用逗號隔開,變量名代表第一個初始值的偏移。
DUP
可以為多個數據項分配存儲空間。
V1 BYTE 10 dup (0)
V1占用10個字節空間,初值均為0
符號常量
等號偽指令:名字=表達式
計算數組和字符串大小:
list BYTE 10, 20, 30
ListSize = ($ - list)
list word 10,20,30,40
ListSize = ($-list)/2
myString_len = ($ - myString)
EQU和TEXTEQU偽指令:
將符號名和整數表達式,文本聯系起來。
name EQU expression
name EQU symbol
name EQU <text>
rowSize = 5
count TEXTEQU %(rowSize * 5)
move TEXTEQU <mov>
setupAL TEXTEQU <move al, count>
setupAL將被匯編成mov al, 10
0x02 數據傳送,尋址,算術運算
小尾(小端)順序
intel處理器使用小端順序存儲,最低字節存儲在最低地址單元
Val DWORD 12345678h
數據傳送指令
操作數類型
- 立即操作數(immediate)
- 寄存器操作數(register)
- 內存操作數(memory)
MOV指令
MOV destination, source
- 兩個操作數尺寸必須一致
- 不能同時為內存操作數
- 目的操作數不能是CS, EIP,IP
- 立即數不能直接送至段寄存器
MOVZX
復制較小值至較大值中。
低八位原樣復制,高八位補0擴展,僅適用於無符號整數。
MOVSX
低八位原樣復制,高八位補F擴展,僅適用於有符號整數。
LAHF/SAHF
LAHF
將標志局存起EFLAGS
的低8位復制到AH
寄存器,SAHF
是將AH
復制到EFLAGS
XCHG指令
交換兩個操作數的內容。
XCHG reg, reg
XCHG reg, mem
XCHG mem, reg
算數指令
名稱 | 作用 | 影響標志位 |
---|---|---|
INC | 加1 | AF OF PF SF ZF 不影響CF |
DEC | 減1 | AF OF PF SF ZF 不影響CF |
ADD | 相加 | CF ZF SF OF AF PF |
SUB | 相減 | CF ZF SF OF AF PF |
NEG | 取相反數 | CF ZF SF OF AF PF |
加減法影響標志位
INC和DEC不會影響CF
標志位
NEG影響的標志位和ADD SUB一樣
名稱 | 作用 |
---|---|
CF進位位 | 無符號數是無溢出 |
OF溢出位 | 有符號數有無溢出 |
ZF零標位 | 判斷結果是否為0 |
SF符號位 | 結果正負 |
PF奇偶標志 | 最低有效字節內1的個數是否為偶數 |
AC輔助進位標志 | 最低有效字節的第三位向高位進位 |
加減法算術運算指令的操作數自身不區分有無符號數,程序通過判斷不同的標志位來實現對有符號數和無符號數的處理。
和數據相關的操作符和偽指令
名稱 | 作用 |
---|---|
OFFSET | 取偏移地址 |
ALIGN | 設置對齊值 |
PTR | 重載默認尺寸 |
TYPE | 返回單個元素大小 |
LENGTHOF | 計算數組中元素的數目 |
SIZEOF | 返回LENGTHOF*TYPE |
LABEL | 插入一個標號並賦予尺寸 |
加逗號可以多行定義
LABEL不會分配存儲空間
JMP和LOOP
JMP
無條件轉移與條件轉移
JMP 目的地址
功能:接着從目的地址開始執行指令
- 目的地址一般為標號
- 通常在當前過程內跳轉
LOOP
LOOP 目的地址
功能:將ecx
的值減1,接着與0比較,如果不等於0,就執行目的地址開始的指令,如果等於0 ,則不跳轉,接着執行緊跟在LOOP指令后的指令
- 通常,
ecx
里的值就是循環次數。但如果初值為0,因是先減1再判斷是否等於0,所以,實際實際循環次數就是1 00 00 00 00 H
。 LOOPD
也使用ecx控制循環,LOOPW
使用cx控制循環。- 實模式下,使用的是cx作為控制循環的寄存器
實例
數組求和:
INCLUDE irvine32.inc
.data
vb1 byte 1 , 2 , 3
.code
main proc
mov esi , offset vb1
mov ecx , lengthof vb1
mov al , 0
L1:
add al , [ esi ]
add esi , type vb1
loop L1
exit
main endp
end main
復制字符串:
INCLUDE irvine32.inc
.data
s1 byte "source string",0
s2 byte sizeof s1 dup(0)
.code
main proc
mov esi , 0
mov ecx , sizeof s1
L1:
mov al , s1[ esi ]
mov s2[esi] , al
inc esi
loop L1
exit
main endp
End main
尋址方式總結
操作數尋址方式
數據尋址的基本方式:
- 立即尋址
- 寄存器尋址
- 存儲器尋址
存儲器尋址有六種類型
用寄存器作為指針並操縱寄存器的值。操作數使用間接尋址則叫間接操作數。
0x03 過程
堆棧操作
運行時棧
運行時棧是CPU直接管理的內存數組,使用到兩個寄存器:SS和ESP
- 保護模式下,SS是段選擇子,應用程序不應該修改它
- ESP是指向棧的特定位置的一個32位偏移值
- 一般不會直接修改ESP,而是通過使用CALL,RET,PUSH,POP等指令,由這些指令間接操作ESP。
- ESP指向最后壓入到棧的數據
- 實模式下,使用的SS和SP
PUSH
PUSH r/m16
PUSH r/m32
PUSH imm32
壓棧,將操作數放入堆棧中:
- 將ESP減4
- 將要壓入的32位值拷貝到ESP指向的內存。
對於32位操作數,ESP減4,存到棧中的內容為雙字;對於16位操作數,ESP減2,存到棧中的內容為字
POP
POP r/m16
POP r/m32
出棧,從堆棧中取出操作數放到指令中的操作數中
- 將ESP所指向內存中的內容取出放到操作數中
- 將ESP加4
對於32位操作數,是先從棧中拷貝雙字到操作數中,然后ESP加4;對於16位操作數,是先從棧中拷貝字到操作數中,然后ESP加2。
PUSHFD 把32位標志寄存器壓入堆棧
POPFD 從堆棧中彈出32位值到標志寄存器中
兩指令無操作數
實模式下標志寄存器是16位的,入棧出棧指令分別是PUSHF,POPF。
PUSHAD 把八個32位通用寄存器按序全部壓入堆棧
POPAD是以上序反序從堆棧中依次彈出值到八個32位通用寄存器中
過程定義
PROC
main proc
...
main endp
一般過程需要返回指令ret,起始過程需要調ExitProcess
CALL和RET
call 過程名
將EIP壓棧(即當前CALL指令的下一條指令的地址),然后將過程名所在地址賦給EIP(相當於跳轉到過程名所在的代碼處)
RET
RET指令是從棧中取出32位地址,賦給EIP。
使用寄存器傳遞過程參數
.data
dArray DD 1, 2 , 3
dSum DD ?
.code
Main proc
mov ebx , offset dArray
mov ecx , lengthof dArray
call SumOf
mov dSum, eax
exit
Main endp
SumOf proc
push ebx
push ecx
mov eax , 0
L2: add eax , [ebx]
add ebx , 4
loop L2
pop ecx
pop ebx
ret
SumOf endp
End main
0x04 條件處理
布爾和比較指令
名稱 | 作用 |
---|---|
AND | 與 |
OR | 或 |
XOR | 異或 |
NOT | 非 |
TEST | 與,不改變目的操作數只改變標志位 |
BT,BTC,BTR,BTS | 求補/清零/置位 |
尺寸相同
AND
, OR
,XOR
總是清除溢出標志和進位標志(CF和OF)
NOT
不影響任何標志位
實例
小寫轉大寫:
同一字母的大寫字母和小寫字母的ASCII碼的區別只在第5位不同,其他各位相同,小寫字母第5位為1,大寫字母第5位為0
如要把小寫轉大寫,則可將小寫的ASCII碼與11011111B
相與
.data
aName byte “Abraham” , 0
nameSize=($-aName)-1
.code
Main proc
mov ecx , nameSize
mov esi , 0
L1:AND aName[esi] , 11011111B
inc esi
loop L1
Main endp
End Main
將0-9之間的整數轉換為對應數字符號的ASCII碼
.data
aNum byte 1,3,2,0
numSize=($-aNum)-1
.code
Main proc
mov ecx , numSize
mov esi , 0
L1:OR aNum[esi] , 110000B
inc esi
loop L1
exit
Main endp
End Main
CMP
功能:對兩個操作數作相減運算,不修改操作數,但會影響標志位。會修改OF、SF、ZF、CF、AF、PF。
設置和清除單個CPU狀態標志
條件跳轉
基於特定標志位
為真時跳轉 | 為假時跳轉 | 相關標志位 |
---|---|---|
JZ | JNZ | ZF |
JC | JNC | CF |
JO | JNO | OF |
JS | JNS | SF |
JP | JNP | PF |
基於相等比較
助記符 | 描述 |
---|---|
JE | 相等跳轉 同JZ |
JNE | 不相等跳轉 同JNZ |
JCXZ | CX=0跳轉 |
JECXZ | ECX=0跳轉 |
基於無符號數比較
助記符 | 描述 |
---|---|
JA | 大於跳轉 |
JB | 小於跳轉 |
JAE | 大於等於 |
JBE | 小於等於 |
JNA | 不大於 |
JNB | 不小於 |
JNBE | 同JA |
JNAE | 同JB |
基於有符號數比較
助記符 | 描述 |
---|---|
JG | 大於跳轉 |
JL | 小於跳轉 |
JGE | 大於等於 |
JLE | 小於等於 |
JNG | 不大於 |
JNL | 不小於 |
JNLE | 同JG |
JNGE | 同JL |
實例
將最小有符號數存到AX:
Mov ax,v1
Cmp ax,v2
JL L1
mov ax,v2
L1:cmp ax,v3
JL L2
mov ax, v3
L2:
數組的順序查找:
查找第一個非0值
INCLUDE Irvine32.inc
.data
intArray SWORD 0,0,0,0,5,20,35,-12,66,4,0
noneMsg BYTE "A non-zero value was not found", 0
.code
main PROC
mov ebx, OFFSET intArray
mov ecx, LENGTHOF intArray
L1: cmp WORD PTR [ebx], 0
jne found
add ebx, 2
loop L1
jmp notFound
found:
movsx eax, WORD PTR[ebx]
call WriteInt
jmp quit
notFound:
mov edx, OFFSET noneMsg
call WriteString
quit:
call Crlf
exit
main ENDP
END main
條件循環指令
指令 | 循環條件 |
---|---|
LOOPZ | ECX>0 && ZF=1 |
LOOPE | ECX>0 && ZF=1 |
LOOPNZ | ECX>0 && ZF=0 |
LOOPNE | ECX>0 && ZF=0 |
LOOPE和LOOPZ不影響任何狀態標志
.data
Array SWORD -3,-6,-1,-10,10,30,40,5
Sentinel SWORD 0
.code
; …
mov esi , offset array
mov ecx , lengthof array
L1:test word ptr [esi],8000h
pushfd ; pushfd不修改標志位
add esi , type array
popfd
loopnz L1 ; 注意:loopnz不修改標志位
jnz quit
sub esi , type array
Quit:
0x05 整數算術指令
移位和循環移位
指令 | 含義 |
---|---|
SHL | 邏輯左移 |
SHR | 邏輯右移 |
SAL | 算術左移 |
SAR | 算術右移 |
ROL | 循環左移 |
ROR | 循環右移 |
RCL | 帶進位的循環左移 |
RCR | 帶進位的循環右移 |
SHLD | 雙精度左移 |
SHRD | 雙精度右移 |
邏輯移位和算術移位
SHL/SAL
SHL 目的操作數, 移位位數
功能:對目的操作數執行左移操作,最低位補0,移出的最高位送入進位標志CF,原來的進位位將丟失。SHL和SAL功能完全一樣。
左移的SHL和SAL是等價的。算術移位不改變符號位,邏輯移位可能改變符號位
SHR
SHR 目的操作數, 移位位數
功能:將目的操作數邏輯右移,左邊空出的位添0,右邊最低位被移出,復制到CF位中
SHR可以實現無符號數的快速除法
SAR
有符號數的快速除法,右移過程中最高位保持不變
ROL/ROR/RCL/RCR
移出的位又送回另一端
SHLD/SHRD
應用
BinToAsc PROC uses eax ebx ecx esi
;將EAX中的數轉換成二進制ASCII碼存到ESI指向的數組中
Add esi , 31
Mov ecx ,32
Nxt:
Mov bl, al
And bl , 1
Add bl , 30H
Mov [esi],bl
Shr eax,1
Dec esi
Loop nxt
Ret
BinToAsc ENDP
乘法和除法指令
助記符 | 描述 |
---|---|
MUL | 無符號乘法 |
IMUL | 有符號乘法 |
DIV | 無符號除法 |
IDIV | 有符號除法 |
應用
Mov al, 30h
Mov bl, 4h
Mul bl ;AX =0C0H,CF=0
Mov ax , 2000h
Mov bx ,100h
Mul bx ;DX:AX=0020 0000h,CF=1
Mov al, -4
Mov bl, 4
IMUL bl ;AX=0FFF0H,CF=0
Mov ax, 30h
Mov bx, 4h
IMul bx ;DX:AX =0C0H,CF=0
Mov al, 48
Mov bl, 4
IMUL bl ;AX=00C0H(即十進制的192),CF=1
任意進制的碼制轉換
.data
ASCIICHAR BYTE '0123456789ABCDEFGHIJKLMNOPQRSTUVWZYX'
.code
ToASC PROC uses eax ebx ecx esi
;將EAX中的數按BL中指定的進制數,轉換成ASCII字符串放到ESI指向的數組中
mov ecx , 0 ;
mov cl , bl ; movzx ecx, bl
add esi , 31
nxt_ta:
mov edx , 0
div ecx
mov bl,ASCIICHAR[edx]
mov [esi],bl
dec esi
cmp eax , 0
jnz nxt_ta ; jne
ret
ToASC ENDP
0x06 高級過程
stack frame
給子過程傳遞參數的兩種基本方式
- 通過寄存器傳遞
- 執行效率高
- 代碼可能顯得混亂
- 寄存器數量有限
mov esi , offset array
mov ecx,lengthof array
mov ebx , type array
call DumpMem
- 通過堆棧傳遞
- 方式靈活通用
- 效率偏低
push offset array
push lengthof array
push type array
call DumpMem2
使用堆棧傳遞參數時壓入了兩類參數:
- 值參數(變量或常量的值)
- 引用/指針參數(變量的地址)
實例
傳遞值
.data
val1 dword 5
val2 dword 6
.code
push val2
push val1
call AddTwo
AddTwo(val1,val2)
傳遞引用
.data
val1 dword 5
val2 dword 6
.code
push offset val2
push offset val1
call AddTwo
AddTwo(&val1,&val2)
重點:參數訪問
.data
Val1 dword 5
Val2 dword 6
.code
Push val2
Push val1
Call AddTwo
…
AddTwo proc
push ebp
Mov ebp , esp
mov eax , [ebp + 12] ;取得val2
add eax , [ebp + 8] ;加上val1
pop ebp
ret
AddTwo endp
堆棧清理
因為在調用子過程前,給堆棧壓入了一些內容,在子過程返回時,必須調整堆棧指針。
- 在調用完子過程后通過加法指令改變ESP值
- 通過 RET imm 指令的形式
add方法:
.data
Val1 dword 5
Val2 dword 6
.code
Push val2
Push val1
Call AddTwo
Add esp , 8
ret方法,在子過程中調用:
.data
Val1 dword 5
Val2 dword 6
.code
Push val2
Push val1
Call AddTwo
AddTwo proc
push ebp
mov ebp,esp
mov eax,[ebp+12]
add eax,[ebp+8]
pop ebp
ret 8
AddTwo endp
采用uses操作符保存寄存器,則要注意uses指令是將寄存器的壓棧指令放在子過程的開始,即在堆棧幀里push ebp語句之前,這時,參數偏移地址計算將會受到影響
0x07 字符串和數組
CLD
清除方向標志
STD
設置方向標志
MOVSB,MOVSW,MOVSD
指令 | 功能 | ESI和EDI修改量 |
---|---|---|
MOVSB | 復制字節 | 1 |
MOVSW | 復制字 | 2 |
MOVSD | 復制雙字 | 4 |
復制雙字數組
.data
source dword 20 dup(0ffh)
target dword 20 dup(?)
.code
; …
cld
mov ecx , lengthof source
mov esi , offset source
mov edi , offset target
rep movsd ;將source開始的20個雙字復制到target中
; …
CMPSB,CMPSW,CMPSD
指令 | 操作 |
---|---|
CMPSB | 比較字節 |
CMPSW | 比較字 |
CMPSD | 比較雙字 |
單個比較
.data
source dword 1234h
target dword 5678h
.code
; …
mov esi , offset source
mov edi , offset target
cmpsd ;比較雙字
ja L1 ;如果source>targe跳轉至L1
jmp L2 ;如果source<=target跳轉至L2,本例即是
; ….
字符串比較
.data
CmpsTestSource byte "ABCDE"
CmpsTestTarget byte "AB "
.code
CMPSTEST proc
cld
mov esi , offset CmpsTestSource
mov edi , offset CmpsTestTarget
mov ecx, lengthof CmpsTestSource ;最多比較次數,此例為5
repe cmpsb ; 比較到第三個字母時,因兩者不等,重復不再繼續,但當前串
; 操作執行完,esi和edi還會增加。所以,最后,esi和edi會指向
; 第四個字母的位置。
ret
CMPSTEST endp
SCASB,SCASW,SCASD
將AL的值與EDI指向的內存內容相比較(相當於cmp AL , [edi]),即相當於是做查找操作,通常會跟重復前綴
- 如果使用repe前綴,則將查找到EDI開始的內存中第一個不等於AL時中止重復;
- 如果使用repne前綴,則將查找到EDI開始的內存中第一個等於AL時中止重復;
- 當然,如果ecx減到0,也會結束查找
SCASW
是用AX作字查找,SCASD
是用EAX作雙字查找
掃描一個匹配字符
.data
alpha byte “ABCDEFGH”,0
.code
mov edi , offset alpha
mov al , ‘F’
mov ecx , lengthof alpha
cld
repne scasb ;不相等則重復,即找到第一個相等的
jnz quit ; 如果這個條件滿足,表示是找完整個ecx長度,也沒有找到
dec edi ;回減一,讓edi指向找到第一個相等的位置
…
Quit:
STOSB,STOSW,STOSD
把AL/AX/EAX的內容存儲在EDI指向的內存單元中,同時EDI的值根據方向標志的值增加和減少。
Stosb是存儲AL,stosw存儲AX,stosd存儲EAX 使用rep前綴可以對一段內存進行填充
LODSB,LODSW,LODSD
將從esi指向的內存內容取出存到累加器中,同時,修改esi的值。
lodsb
是取出一個字節存到AL中,lodsw
是取出一個字存到AX中,lodsd
是取出一個雙字存到EAX中。
該指令一般不會跟重復前綴
串操作指令對標志位的影響
cmps
和scas
指令會對標志位有影響,影響效果如同CMP指令。
movs
,lods
,stos
不會影響標志位。