簡介 :
分析下面的程序 , 在運行前思考 , 這個程序可以正常返回嗎 ?
運行后再思考 , 為什么是這種結果 ?
通過這個程序加深對相關內容的理解
assume cs:code
code segment
mov ax, 4C00H
int 21H
start:
mov ax, 0000H
s:
nop
nop
mov di, offset s
mov si, offset s2
mov ax, cs:[si]
mov cs:[di], ax
s0:
jmp short s
s1:
mov ax, 0000H
int 21H
mov ax, 0000H
s2:
jmp short s1
nop
code ends
end start
分析 :
程序的執行流程是這樣的 :
; 從 start 標號開始
1. mov ax, 0000H
2. nop
3. nop
4. mov di, offset s
5. mov si, offset s2
6. mov ax, cs:[si]
7. mov cs:[di], ax
8. jmp short s
9. jmp short si ; 這句 jmp short s1 , 根據我們之前的分析 , 指令是用相對偏移來表示的
; 因此執行的操作并不是真的跳轉到 s1 這個標號 , 而是跳轉編譯時確定的 該指令到 s1 標號的偏移
; 所以我們要分析接下來程序的流程的話 , 就必須先編譯程序 , 然后要知道到底偏移是多少
; 然后再根據這個偏移確定程序下一步應該執行哪里的指令
; 根據下圖的編譯結果 , 可以發現 , jmp short s1 在編譯后得到的指令是 :
; 偏移是 : EB F6
; 這個數據是使用 補碼 來表示的 , 也就是說 , 是一個負數 , 然后符號位不變 , 其他位取反 , 然后加 1
; 然后 , 我們現在就知道了 , 這條指令是將 ip 的值加上 -10
; 我們再看看 ip - 10 指向的地址是哪里 ?
; 對 , 剛好就是 code segment 開始的位置
10. mov ax, 4C00H
11. int 21H
; 這樣程序就實現了正常的返回
反編譯結果 :
注意這里使用 debug 的 u 命令進行反匯編的時候要指定代碼段的偏移地址為 0
否則 debug 會自動從 start 標號的地方開始反匯編
Paste_Image.png
可以看到 :
jmp short s1 ; 這句匯編指令被翻譯成了 : EB F6 , 其中 EB 表示的是跳轉 , F6 表示偏移
F6 怎么理解呢 ?
1111 0110 (使用補碼來表示)
補碼轉換成原碼 , 符號位不變 , 其他位取反 , 然后加 1
1000 1001
1000 1010 ; 也就是 -10
也就是上面我們分析的讓 (ip) = (ip) - 0x0A
然后 , 這句指令被復制到 s 標號的開頭處
由于 nop 只占一個字節 , 因此兩個 nop 被完全替代
然后程序執行到 s0 , 又跳轉到 s 開始的地方
這個時候就要執行 : (這個時候 ip = 8)
EB F6
首先讀取這條指令到指令緩存器里
接下來 , (ip) = (ip) + len(EB F6) = (ip) + 8 = 10
然后執行這條指令 , 即為 (ip) = (ip) - 10 = 0
這樣 ip 就回到了 code segment 的起始處
這樣繼續執行
mov ax, 4C00H
int 21H
就實現了程序的正常返回