---王爽 (實驗 8)分析一個奇怪的程序

簡介 : 分析下面的程序 , 在運行前思考 , 這個程序可以正常返回嗎 ? 運行後再思考 , 爲什麼是這種結果 ?
通過這個程序加深對相關內容的理解

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 s1 ; 這句 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
標號的地方開始反彙編

這裏寫圖片描述

可以看到 : 
jmp short s1 ; 這句彙編指令被翻譯成了 : EB F6 , 其中 EB 表示的是跳轉 , F6 表示偏移
F6 怎麼理解呢 ? 
1111 0110 (使用補碼來表示)
補碼轉換成原碼,先減一,然後按位取反得到
1000 1010 ; 也就是 -10
也就是上面我們分析的讓 (ip) = (ip) - 0x0A
然後 , 這句指令被複制到 s 標號的開頭處
由於 nop 只佔一個字節 , 因此兩個 nop 被完全替代
然後程序執行到 s0 , 又跳轉到 s 開始的地方
這個時候就要執行 : (這個時候 ip = 8)
EB F6
首先讀取這條指令到指令緩存器裏
接下來 , (ip) = (ip) + len(EB F6) = (ip) + 2 = 10
然後執行這條指令 , 即爲 (ip) = (ip) - 10 = 0
這樣 ip 就回到了 code segment 的起始處
這樣繼續執行 
mov ax, 4C00H
int 21H
就實現了程序的正常返回

原文作者 王一航

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章