《匯編語言》
前言
正式工作之后打算着手做一些逆向方面的研究,聽前輩們的建議,必須先把匯編學會,於是我用第一個月把《匯編語言》(第三版-王爽著)看了一遍,但是人的記憶力是有限的,所以打算以博客的形式再回憶一遍,相信通過這種形式,能讓自己對知識理解的更加模塊化和具體化,也方便自己日后復習,同時也方便了看到這篇博客的同道中人。
作用
匯編語言在整個計算機編程語言中的地位可以說是沒什么用,很少有人會直接拿匯編語言去寫項目,如果這么干的話,不得麻煩死(但是不排除確實有這種需求的時候的做法)。更多時候匯編語言的使用場景是在反編譯別人的二進制代碼之后,對匯編代碼的邏輯還原,並且對於計算機系統整體的理解,我相信沒有什么比學習匯編語言更快、更好。
名詞、匯編指令的示例和說明(8086cpu)
-
寄存器:用於存放cpu的數據信息,共14個,分別是:AX,BX,CX,DX,SI,DI,SP,BP,IP,CS,SS,DS,ES,PSW。
-
字節、字和雙字:1雙字(DWROD)=2字(WORD)=4字節(byte)。1字節(byte)=8位(bit),比如,寄存器ax是16位寄存器,在內部可以在分為ah和al,分別代表高8位和低8位。
-
內存訪問:一般利用寄存器ds,es,ss才可以間接訪問內存,比如要訪問2000:0200位置的數據,並將該位置的數據傳送入ax,應該這樣:
mov ax,2000h
mov ds,ax
mov si,200h
mov ax,ds:[si]
- 棧內存:在內存訪問中有一類特殊的訪問,就是棧內存,在操作棧的時候,最好指定一塊用於自己使用的內存區域,以便自定義棧的大小和位置。示例:
assume cs:code
stack segment
db 128 dup (0)
stack ends
code segment
start:
mov ax,stack
mov ss,ax
mov sp,128 ;目前棧頂在ss:sp處(即stack:128),也就是上面定義的stack中的128字節
mov ax,'a'
push ax ;將'a'入棧,棧頂在ss:sp處(即stack:126)
push ax
pop bx ;出棧,將數據保存在bx中
mov ax,4c00h
int 21h
code ends
end start
- 標志位:在內存中占1字節大小,用於運算和程序中的狀態等。常用的標志有ZF,PF,SF,CF,OF,DF
ZF:是否為0
PF:1的個數是否為偶數
SF:是否為負數
有符號整數,只看結果是不是大於7,也就是首位是不是0
CF:是否存在進位或借位(無符號整數)
直接進行無符號運算,首位進位就置1
OF:是否存在溢出(有符號整數)
16位cpu,有符號溢出范圍79H(127),-80H(128)
DF:方向標志位,與movsb配合使用
- 指令
數據傳送指令:
mov:數據傳送
示例:mov ax,bx
說明:將bx中的數據傳送到ax中,因為使用的是ax和bx,所以數據的長度是16位,下面的例子都相同,如果有操作數據中有寄存器,那么按照寄存器的數據長度計算
push:入棧
示例:push ax
解釋:將ax的數據入棧,傳送到ss:sp棧頂處
pop:出棧
示例:pop ax
解釋:將ss:sp位置的數據出棧,傳送入ax中
pushf:所有標志位入棧
示例:pushf
解釋:標志位入棧,防止后面的操作對標志位產生影響的通常做法
popf:標志位出棧
示例:popf
解釋:標志位出棧,用於還原入棧的標志位
xchg:交換,目前沒用到
算數運算指令
add:加法
示例:add ax,2
解釋:將ax中的數據加2,即ax+0002h
sub:減法
示例:sub ax,2
解釋:將ax中的數據減2,即ax-0002h
adc:加法(帶符號位)
示例:adc ax,2 ;CF=1
解釋:將ax中的數據加3,即ax+0002h+1h
sbb:減法(帶符號位)
示例:sbb ax,2 ;CF=1
解釋:將ax中的數據減3,即ax-0002h-1h
inc:自增
示例:inc si
解釋:將si中的數據加1,常用在循環或條件轉移中
dec:自減
示例:dec ax
解釋:將ax中的數據減1
cmp:比較
示例:cmp ax,0
解釋:相當於減法,ax-0,只不過不影響寄存器的值,而只影響標志寄存器,因為條件轉移指令是依據標志寄存器的指令,所以cmp常與條件轉移指令配合使用進行條件轉移
mul:乘法
示例:mul bx或mul bl
解釋:分為兩種情況:
1. 指令參數是8位寄存器如bl時,乘數1默認放在al寄存器中,另一個乘數2放在8位寄存器如bl中,結果存在ax中
2. 指令參數是16位寄存器如bx時,乘數1默認放在ax寄存器中,另一個乘數2是指定的16位寄存器如bx中的數據,結果的高16位存在dx,低16位存在ax中
div:除法
示例:div bx或div bl
解釋:同樣分為兩種情況:
1. 指令參數是8位寄存器時,被除數(除法前面那個數。。。)則為16位,默認存放於ax中,除數則是存放於指定8位寄存器如bl中,結果為:al存儲商,ah存儲余數
2. 指令參數是16位寄存器時,被除數則為32位,默認存放於ax和dx中,dx存高16位,ax存低16位,除數存放於指定16位寄存器如bx中,結果:ax存儲商,dx存儲余數
aaa:目前沒有用到
邏輯指令
and:邏輯與
示例:and al,11011111b
解釋:示例中結果是將al中第6位置為0,其他位保持不變,常用與簡化運算如,將小寫字母轉化為大寫字母,只需要將字母與11011111做邏輯與運算即可實現
or:邏輯或
示例:or al,00100000
解釋:示例中結果是將al中第6位置為1,其他位保持不變,同樣也能實現將大寫字母轉化為小寫字母的簡化運算,需要將字母與00100000做邏輯或運算即可實現
not:邏輯非,不常用
示例:
解釋:
xor:邏輯異或,目前不常用
示例:
解釋:
test:不常用
shl:邏輯左移
示例:shl al,1或mov cl,3;shl al,cl
解釋:邏輯左移的意思就是左移后,移出的數據存放在標志寄存器CF中,而最低位用0補齊,也分為兩種情況:
1. 左移1位:直接shl al,1即可
2. 左移超過1位:需要先將欲移動的位數據存入cl中,再通過左移cl個位的數據來實現
shr:邏輯右移
示例:shr al,1或move cl,2;shr al,cl
解釋:邏輯右移與邏輯左移類似,這里就不多講了,同樣也是兩種情況
sal
sar
rol
ror
rcl
rcr
轉移指令
無條件轉移指令
jmp:無條件轉移指令
示例:jmp short s;s:inc ax
解釋:jmp轉移指令可以實現段內短轉移、段內近轉移和段間轉移這三種基本需求
1. 段內短轉移:只對IP寄存器修改,修改范圍為-128~127,也就是說向前最多轉移+127個字節,向后最多轉移-128個字節
2. 段內近轉移:與短轉移基本相同,不過是16位的位移,即修改IP的范圍是-32768~32767
3. 遠轉移(段間轉移):可以轉移到指定的內存處,上面的兩個轉移只是在同一個段中的轉移,是根據位移定位的轉移方式,而遠轉移可以指定轉移的目的地址
條件轉移指令
jcxz:如果cx寄存器的值為0,則轉移到指定標號
示例:mov cx,0;jcxz s
解釋:如果條件滿足cx寄存器的值為0,則轉移到指定的標號處,常用的場景是:遍歷一個以'0'字符結尾的字符串,根據這個0判斷字符串是否到末尾的簡單實現方式
je:如果cmp得差結果等於0,則轉移到指定標號
示例:mov bx,3;cmp bx,3;je s
解釋:將會轉移到s處
jb:如果cmp的差結果小於0,則轉移到指定標號
示例:mov bx,3;cmp bx,4;jb s
解釋:將會轉移到s處
ja:如果cmp的差結果大於0,則轉移到指定標號
示例:mov bx,3;cmp bx,2;ja s
解釋:將會轉移到s處
jnb:如果cmp的差結果不小於0,則轉移到指定標號
示例:mov bx,3;cmp bx,3;jnb s
解釋:將會轉移到s處
jna:如果cmp的差的記過不大於0,則轉移到指定標號
示例:mov bx,3;cmp bx,3;jna s
解釋:將會轉移到s處
循環指令
loop:匯編語言中的循環語句
示例:mov cx,10;loop s
解釋:將s程序段循環執行10次(循環次數由cx的值指定)
過程
call:調用子程序,常與ret成對使用
示例:call s;s:ret
解釋:轉移到子程序,類似於轉移指令,但相當於執行了
push IP
jmp near s這兩條指令,記錄了轉移的位置,可以使用ret返回此IP的位置
ret:在子程序中同於返回call的指令處,常與call成對使用,並且是近轉移
示例:call s;s:ret
解釋:從子程序跳出,相當於執行了
pop IP,程序執行的下一條語句就是原來call的IP的地址,從而實現了近轉移
retf:在子程序中同於返回call的指令處,常與call成對使用,並且是遠轉移
示例:call s;s:retf
解釋:從子程序跳出,相當於執行了
pop IP
pop CS程序執行的下一條語句就是原來call的IP的地址,從而實現了遠轉移
中斷
int:系統中斷
示例:mov ax,4c00h;int 21h
解釋:BIOS和DOS都提供了一些默認的中斷進程,用於持續檢測中斷碼,如果接受到中斷碼,則會在TF=1的情況下在下一條指令去執行可屏蔽中斷進程,我們也可以自定義中斷進程去替代系統的中斷進程。示例中的中斷是去執行21號中斷的ah=4c的子程序,子程序為退出當前DOS
iret:與int配合使用,在子程序中返回,與ret,retf類似
處理機控制指令
cld:設置DF為0(即正向拷貝)
示例:cld;rep movsb
解釋:設置標志寄存器DF為0,即設置拷貝的方向為正向
std:這是DF為1(即反向拷貝)
示例:std;rep movsb
解釋:設置標志寄存器DF為1,即設置拷貝的方向為反向
cli:設置TF標志位為1
示例:cli
解釋:設置TF為1后,當接受到可屏蔽中斷時,會在下一條指令執行中斷
sti:設置TF標志位為0
示例:sti
解釋:設置TF為0后,當接受到可屏蔽中斷時,會忽略中斷繼續執行當前程序直至結束
nop:添加一個占位的一字節數據
示例:funcend:nop
解釋:常用來記錄子程序的段結束的位置,比如offset funcend就可以獲取func結束位置的偏移地址
clc
cmc
stc
hlt
wait
esc
lock
串處理指令
movsb:復制字符串
示例:rep movsb
解釋:可以正向或反向復制指定為的字符串到目標地址,參數必須將ds:si源地址、es:di目的地址、cx長度、標志DF指定,然后調用rep movsb
movsw
示例:
解釋:
cmps
scas
lods
stos
配合使用:
rep
repe
repne
書中的一些值得注意的問題(自己的筆記上的內容,某些描述可能不是很准確,可以選擇性跳過)
mov ax,0 可以用sub ax,ax替代,而且后者2字節,前者3字節
當16進制數,首位數字為英文,則需要在前面加上0,比如:0A432H
在匯編程序中,mov al,[0]指令與debug程序不同,匯編編譯器會將它解釋為mov al,0
DOS和合法程序都不會使用0:200~0:2ff這段256字節的空間
直接DEBUG顯示的CS就是當前程序段的字節長度
為什么mov 4c00h;int 21h長度為5字節
暫存數據的時候,一般用棧
bp寄存器默認使用ss作為段寄存器
沒有寄存器名存在的情況下指定內存單元的長度:mov word/byte ptr [bx],1
push/pop只對字操作
在10.1節,ret和retf的示例中,在執行命令之前,為什么都要mov bx,0
mul乘法,與div除法原理相似,都分為8位和16位的計算
16位cpu,有符號溢出范圍79H(127),-80H(128)
DF:方向標志位,與movsb配合使用
adc指令的意義:進行大數的加法或減法
inc和loop不影響cf位
cmp指令只會影響標志位
CF能說明操作符的大小
SF=1並不能說明運算的結果的正負,因為可能發生溢出
但SF和OF同時可以說明正負
SF=1,OF=1:(ah)<(bh)
SF=1,OF=0:(ah)>(bh)
SF=0,OF=1:(ah)<(bh)
SF=0,OF=0:(ah)>=(bh)
cld和std分別設置DF為0(正向)和1(反向)
Debug中的標志位對應關系
flag:1--0
OF:OV,NV
SF:NG,PL
ZF:ZR,NZ
PF:PE,PO
CF:CY,NC
DF:DN,UP
loop執行分兩步
cx--
cx不等於0,轉移
中斷過程
取得中斷類型嗎N
pushf,將標志寄存器入棧保存
TF=0,IF=0
push cs
push ip
(IP)=4*N,(CS)=4*N+2
動態獲取到一段代碼的長度,可以設置一個nop字節,獲取長度只需要:
mov cx,offset do0end-offset do0
do0:
mov xx,xxx
mov 4c00h
int 21h
do0end:nop
jmp short s占2字節內存
設置棧頂的ss和sp之間不會響應任何中斷,所以在實驗二的時候,執行mov ss,0后,mov ax,0執行了,只是debug不能將它中斷顯示
一般在中斷例程中還存在子程序,一般通過ah來指定
如果將字符串后的0寫成字符'0',則利用jcxz無法跳轉
在進行邏輯移位shl或shr時,移位大於1,必須用cl保存位移數,並將最后一次移出的保存到CF中
在CMOS RAM中,端口為70h(地址端口)和71h(數據端口)存儲時間信息的單元分別為:秒:0分:2時:4日:7月:8年:9,都占一個字節
在PC中,外中斷可分為可屏蔽中斷和不可屏蔽中斷
可屏蔽中斷在IF=1時,cpu會在執行完當前指令后響應中斷,IF=0則不響應此中斷
不可屏蔽中斷是cpu必須響應的中斷,一般不會使用
設置IF:sti,設置IF=1; cli,設置IF=0,IF=0不會執行屏蔽中斷
通碼:按下鍵盤的一個鍵產生的掃描碼;斷碼:松開一個鍵產生的掃描碼,
斷碼=通碼+80h
都被送到60h端口
掃描碼一個字節中,通碼的第7位為0,斷碼第7位為1
地址標號和數據標號
地址標號,只能在代碼段使用
數據標號,不僅表示內存單元的地址,還表示內存單元的長度,與地址標號不同是標號后無符號“:”
直接定址表:可以通過依據數據,直接計算出所要找的元素的位置的表
鍵盤緩沖區的字單元中,高位字節存儲掃描碼,低位字節存儲ASCII碼。
int 16h用於從鍵盤緩沖區讀取字符,ah存儲掃描碼(高位),al存儲字符(低位)