檢測點10.1
補全程序,實現從內存1000:0000處開始執行指令
匯編源代碼check10-1.asm
assume cs:code
stack segment
db 16 dup (0)
stack ends
code segment
start: ;建棧
mov ax,stack
mov ss,ax
mov sp,16
;壓棧
mov ax,1000H
push ax
mov ax,0000H
push ax
retf
mov ax,4c00H
int 21H
code ends
end start
程序分析:
1. retf指令作用(CPU角度):從棧中彈出2個字單元,並修改CS(第二個字)和IP(第一個字);首先它彈出的是IP,其次是CS,故在壓棧時,CS的值首先入棧,IP再入棧。
在匯編編程角度,retf實現了遠轉移。
講解:在匯編代碼這個層次,retf指令作用是修改CS和IP的值,進而使指令從修改后的地址處開始執行。由於它所依賴的是棧中存儲的內容,故在壓棧過程中要搞清楚入棧的順序、入棧的值。
2.熟悉ret指令和RETF指令執行的操作。
我們編譯鏈接后,debug跟蹤check10-1.exe
-d ss:0
0B66:0000 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
在stack數據區中初始化了16個0,此時它未成為棧結構。直到初始化棧的結構。
執行代碼t t(二次)
AX=0B66 BX=0000 CX=0026 DX=0000 SP=0010 BP=0000 SI=0000 DI=0000
DS=0B56 ES=0B56 SS=0B66 CS=0B67 IP=0008 NV UP EI PL NZ NA PO NC
0B67:0008 B80010 MOV AX,1000
-d ss:00
0B66:0000 00 00 00 00 00 00 66 0B-00 00 08 00 67 0B 68 05 ......f.....g.h.
初始化一個棧sp=0010H(16),棧地址:ss=0b66,這里我們發現一些不應該有的數據(不理會它,它是中斷的信息)
繼續執行代碼:
-d ss:0
0B66:0000 00 00 00 00 00 00 10 00-67 0B 68 05 00 00 00 10 ........g.h.....
發現從高位開始存儲10 00 00 00四個字節的數據,(體會棧是從高地址向低地址發展的,sp指針從10H減少到了0cH)
此時的CS=0B67 IP=0010
執行retf代碼:
我們發現:CS=1000 IP=0000,CS和IP的值改變了。
總結:ret和RETF依賴於棧的結構存儲一個程序執行點(IP或CS和IP),當執行這個代碼時,可以恢復到這個程序的執行點(將棧中的數據修改IP或CS和IP,使CPU指向新的CS:IP)
檢測點10.2
下面的程序執行后,ax中的數值為多少?
內存地址: 機器碼 匯編指令
1000: 0 b8 00 00 mov ax,0
1000:3 e8 01 00 call s
1000: 6 40 inc ax
1000:7 58 s:pop ax
程序分析:
1.熟悉call指令的操作過程。call指令在執行時,首先push ip(此時ip應為6 ,CPU將要執行的地址),然后jmp 標號,執行標號處的代碼,pop ax (彈棧寫入ax,(ax)= 6),我們發現inc ax沒有執行。
此時使用的棧空間是系統自動創建的。
2.結論:(ax)= 6
檢測點10.3
內存地址 機器碼 匯編指令 執行后情況
1000:0 b8 00 00 mov ax,0 ax=0,ip指向1000:3
1000:3 9a 09 00 00 10 call far ptr s push cs,push ip,ip指向1000:9
1000:8 40 inc ax
1000:9 58 s:pop ax ax=8h
add ax,ax ax=10h
pop bx bx=1000h
add ax,bx ax=1010h
程序分析:
1.關鍵還要明白是什么段地址和偏移地址壓棧?遇到CALL指令,老樣子,首先將當前的CS和IP值壓棧,(cs)=1000H(由CALL的機器碼得知),此時IP指向了0008H。它們統統壓棧(cs先入棧,ip在棧頂)。(ax)=0
2.轉移到標號S處繼續執行代碼,pop ax出棧寫入到ax,(IP)先出棧(0008H),(ax)=0008H
3.add ax,ax 0008H+0008H=0010H
4.pop bx 將(cs)=1000H出棧,並寫入bx中,(bx)=1000H
5.add ax,bx 0010H+1000H =1010H
6.(ax)=1010H;代碼:inc ax沒有執行
檢測點 10.4
下面的程序執行后,ax中的數值為多少?
內存地址 機器碼 匯編指令 執行后情況
1000:0 b8 06 00 mov ax,6 (ax)=6,ip指向1000:3
1000:3 ff d0 call ax push ip(此時IP值為5),ip轉移指向1000:6
1000:5 40 inc ax
1000:6 58 mov bp,sp (bp)=(sp)=fffeh
add ax,[bp] (ax)=6+(ss:bp)=6+5=0bh
程序分析:
1. 遇到CALL指令,老樣子,(ip)(此時是5),壓棧;這里我們可以不管sp是多少(我的debug是0000H),目前我們確定的就是IP的值是5(壓棧的數據)。此時棧中有一個字就是0005H。(sp)=(sp)-2 =>>0000H-2=fffeH
2.由於是CALL ax,(ax)=0006H,直接轉移到1000:0006處執行,將sp的值賦值給bp。
3.由於bp默認隸屬於ss段寄存器,故[bp]指向ss段的物理內存,也就是棧結構的空間,此時棧中就一個字0005H,那么(ss:[bp])=0005H(讀取ss棧中的內容)。
4.add ax,[bp] ,0006H+0005H=000BH
5.結果:ax的值是000BH。inc ax指令依然沒有執行。
檢測點10.5
(1)下面的程序執行后,ax中的數值為多少?
assume cs:code
stack segment
dw 8 dup (0)
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,16
mov ds,ax
mov ax,0
call word ptr ds:[0eh]
inc ax
inc ax
inc ax
mov ax,4c00h
int 21h
code ends
end start
程序分析:
1. 在棧段初始化並定義了8個字,16個字節的空間。是dw指令。
2. 初始化棧,將棧段地址也賦值給了ds;(ss)=(ds),數據段和棧段是同一個內存空間段。
3. 遇到了call,老樣子,將(cs)壓棧(這個值肯定是call指令后面的那個指令的cs和ip我們可以不關心它,依據你的debug程序)、將(ip)壓棧,此時的ip值應該是第一個inc ax的偏移地址。然后jmp到ds:[0eh]內存單元內容作為偏移地址的點執行代碼。由於ds和ss都是同一個段,call的是一個字單元,故ds:[0eh]內存單元是指向的一個字(ds:[0eh]~ds:[0fh]內存單元),此單元正好是棧空間棧頂的存儲單元,存儲着最后壓入的ip的值,這個值就是代碼inc ax的偏移地址,那么開始執行inc ax
4.執行3次inc ax后, (ax)=3。
5.總結:這個考察了我們對於CALL 內存空間這個指令的熟悉程度,也考察了棧段和數據段在同一段地址下,怎樣讀出棧空間單元內容。
為什么不讓使用debug來逐步調試?因為在執行到CALL指令時,顯示ds:[0eh]=065D(在我的環境中debug后),ds:【oeh】中的內容是中斷的信息,並不是我們程序所希望的0011H,它一下就jmp到065D的地址去了,而不是cs:11地址執行。
stack段初始的時候定義的全是0,怎么初始化棧段后有數據了?因為中斷信息。
(2)下面的程序執行后,ax和bx中的數值為多少?
assume cs:codesg
stack segment
dw 8 dup(0) ;初始化8個雙字,16個字的內存作為棧空間
stack ends
codesg segment
start:
mov ax,stack
mov ss,ax
mov sp,10h ;初始化棧頂
mov word ptr ss:[0],offset s ;(ss:[0])=001ah
mov ss:[2],cs ;(ss:[2])=cs
call dword ptr ss:[0] ;cs入棧,ip=19h入棧,轉到cs:1ah處執行指令
;(ss:[4])=cs,(ss:[6])=ip
nop
s: mov ax,offset s ;ax=1ah
sub ax,ss:[0ch] ;ax=1ah-(ss:[0ch])=1ah-19h=1
mov bx,cs ;bx=cs=0c5bh
sub bx,ss:[0eh] ;bx=cs-cs=0
mov ax,4c00h
int 21h
codesg ends
end start