王爽 汇编语言第三版 第9章 转移指令的原理
?
?
?
第九章? 轉移指令的原理
?
匯編代碼:
assume cs:codesgcodesg segments: mov ax,bx ; mov ax,bx 的機器碼占兩個字節mov si, offset smov di, offset s0mov ax, cs:[si]mov cs:[di], axs0:nop ; nop 機器碼占一個字節nopmov ax, 4c00hint 21h codesg ends end s?
?
?
?
9.3?依據 位移 進行 轉移的?jmp?指令
?
?
?
?
?
9.5?轉移地址 在 寄存器 中的?jmp?指令
?
?
?
9.6?轉移地址在內存中的?jmp?指令
?
檢測點 9.1?
?
分析:jmp word ptr [bx+1] 為段內轉移,要CS:IP指向程序的第一條指令,應設置ds:[bx+1]的字單元(2個字節)存放數據應為0,則(ip)=ds:[bx+1]=0。簡單來說就是,只要 ds:[bx+1] 起始地址的兩個字節為 0 就可以了。
答案 1:db 3 dup (0)。 答案 2:dw 2 dup (0)。 答案 3:dd 0。?答案 4:dd 16 dup (0)
驗證代碼:
assume cs:codesg, ds:datasgdatasg segmentdb 16 dup (0) datasg endscodesg segment start: mov ax, datasgmov ds, axmov bx,0jmp WORD ptr ds:[0]mov ax,4c00hint 21h codesg ends end start驗證結果截圖:
?
程序 2:
第一空參考答案:
mov [bx], bx ; 因為 bx 為 0,所以可以把 寄存器 bx 值賦值給 內存[bx] mov [bx], word ptr 0 ; 指定 0 是一個 字類型 0000h, mov [bx], offset start ; 取得 標號 start 相對 cs 段的偏移地址,即 0第二空參考答案:
mov [bx+2], cs mov [bx+2], codesg驗證代碼:
; jmp 段 間 轉移。轉移到 cs:ip 的第一條指令 ; 高地址 : 轉移的目的段地址 ; 低地址 : 轉移的目的偏移地址assume cs:codesg, ds:datasgdatasg segmentdd 12345678H datasg endscodesg segmentstart:mov ax, datasgmov ds, axmov bx, 0mov [bx], WORD ptr 0mov [bx+2], csjmp DWORD ptr ds:[0]mov ax, 4c00hint 21h codesg ends end start運行結果截圖:
?
?
?
?
?
實驗 8 分析一個奇怪的程序:
https://www.jianshu.com/p/7e5dfea72b65
assume cs:code code segmentmov ax, 4C00Hint 21Hstart:mov ax, 0000H s:nopnopmov di, offset smov si, offset s2mov ax, cs:[si]mov cs:[di], ax s0: jmp short s s1: mov ax, 0000Hint 21Hmov ax, 0000H s2: jmp short s1nop 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 標號的地方開始反匯編 可以看到 : 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 就實現了程序的正常返回說明:這題要是把 s 當成 jmp short s1 那就錯了,如果是那樣的話 ,你就會想當然的跳到 s1,但其實不是的,前邊復制的 是內存里的內容?,也就是說復制的是 jmp short s1 的機器碼 假如機器碼是這樣的: EB -10(十進制負數先理解嘛~) 也就是向上走10字節,?那么?現在復制到 s 那 也是向上走10?個字節,所以是跳轉到?mov ax, 4c00h。
這道題主要理解:程序 只是 復制 內存里面的數據。
?
?
匯編語言是按行一條一條指令進行執行的,可以按高級語言進行縮進來寫匯編程序 (?例如可以按照?Pyhton?的空格縮進?),最后再把縮進給去掉,這樣可以更好的理解匯編的邏輯和層級關系。
?
?
實驗 9?
每一個字符占用兩個字節 , 低地址為 ASCII 碼 ,高地址為屬性
示例代碼 1:
assume cs:code,ds:data; 顯存地址 : B8000H - BFFFFH ; 顯示尺寸 : 80 x 25 個字符 ; 其中每一個字符占用兩個字節 , 低地址為 ASCII 碼 , 高地址為屬性 ; 也就是說 每一個字符 可以設置的屬性有 256 種 ; 屬性 : ; 是否閃爍 背景色R 背景色G 背景色B 是否高亮 前景色R 前景色G 前景色B ; 因此一個屏幕總共有 80 x 25 = 2000 個字符 , 需要 4000 個字節來存儲 (約為4K) ; 然后系統的顯存總共是 32K , 也就是說可以儲存 8 個頁面 , 默認顯示第一個頁面data segmentdb 'Hello world', 33 ; 33 是感嘆號的 ASCII 碼 data endscode segmentstart:mov ax, datamov ds, axmov ax, 0B800Hmov es, ax ; 保存顯存的段地址mov si, 0000H ; 數據段偏移地址mov di, 10*160+80 ; 顯存偏移地址mov cx, 000CH ; 設置循環次數 , "Hello world!" 長度為 12print_green:mov al, ds:[si] ; 讀取數據段中的數據mov ah, 00100000B ; 設置字體的屬性 (黑底綠字)mov es:[di], ax ; 寫入顯存inc si ; 數據段偏移地址自增 1 add di, 0002H ; 顯存偏移地址自增 2loop print_greenfinish:mov ax,4c00Hint 21Hcode endsend start運行截圖:
示例代碼 2:
assume cs:codesg, ds:datasgdatasg segmentdb 'welcome to masm!' ; 顯示的字符db 2,24h,71h ; 字符屬性 datasg endscodesg segment start:; 設置 ds 段mov ax, datasgmov ds, ax; 設置 es 段 為 顯存地址段mov ax, 0b800hmov es, ax; 設置循環次數 mov cx, 16mov si, 0 ; 字符的偏移量mov di, 10*160+80 ; 顯存的偏移量s:mov al, ds:[si]mov ah, 2 ; 設置字符的屬性 為 2mov es:[di], ax ; 往顯存里面寫數據inc si ; 字符向后偏移 1 位add di, 2 ; 顯存向后偏移 2 位loop smov ax, 4c00hint 21h codesg ends end start運行截圖:
?
實驗 9?代碼 1:
http://blog.sina.com.cn/s/blog_171daf8e00102xcbv.html
assume cs:codesgdata segmentdb 'welcome to masm!'db 02H,24H,71H ;字符顯示的屬性值 data endsstack segmentdb 16 dup(0); 也可以是下面的定義法:; dw 8 dup(0) stack endscodesg segment start: ;初始化data數據段,es:di指向datamov ax, datamov es, axmov di, 0;初始化顯示緩沖區,ds:bx指向顯示緩沖區。mov ax, 0b800Hmov ds, ax;25行取中是12、13、14行,80列取中開始是61列;12行的偏移量是12*160=1920 (1行總共80個帶屬性的字符,即 160個字節);總偏移量為(偏移地址)1920+60=1980?mov bx, 1980 mov si, 16 ;字符的屬性在數據段中的偏移量 mov ax, stack ;建棧,并初始化棧頂,熟悉棧結構。其實這里都不用人工建棧,有系統自動的。mov ss, axmov sp, 16 ;指向棧頂 mov cx, 3 ;計數器初始化為3(循環顯示3次)s: push cx ;入棧保護 CX,在stack中 mov cx, 16 ;內循環為16次,16個字符output: ;將字符寫入顯存中mov al, es:[di]mov [bx], al;將字符屬性寫入顯存中mov ah, es:[si]mov [bx+1], ah inc diadd bx, 2loop outputadd bx, 128 ;每行輸出的偏移量為128字節 mov di, 0inc sipop cx ;出棧恢復cx計數器值loop s mov ax,4c00Hint 21H codesg ends end start運行截圖:
總結:
- 1. 合理利用棧結構保存寄存器變量的值。
- 2. 熟練掌握[bx+idata]這種CPU尋址的方式。
- 3. 在顯存中,甚至是內存中,它們都是線性存儲的,以列的形式存儲的。不存在行的概念的,只不過在計算機屏幕上,還有debug中有行的概念,為了顯示方便。
示例代碼:
https://www.cnblogs.com/nojacky/p/9497704.html
assume cs:code data segmentdb 'welcome to masm!'db 02h,24h,71h ; 要求的三個顏色對應的16進制代碼 data endsstack segmentdb 16 dup(0); 也可以是下面的定義法:; dw 8 dup(0) stack endscode segmentstart: ; 設置data段,以及ds:bx指向data段的第一個單元,; 即ds:[bx]的內容就是data段第一個單元的內容 mov ax,datamov ds,ax;設置顯示緩存區段mov ax,0b800h ;設置起始緩存mov es,ax ;設置棧段mov ax,stackmov ss,axmov sp,10h ;指向棧頂;初始化三個寄存器mov bx,780h ; 行 從12-14行(注意:從第1行開始計數)mov si,10h ; 顏色的偏移量,三次循環每次; 增加 1h 指向下一個顏色mov cx,3 ; 三次循環改變行s: mov ah, ds:[si] ;顏色事先存放在ah中push cx push simov cx, 16 ; 16次循環改變列 mov si, 64 ; 這里的si的意義是多少列,; 為什么從64列開始呢?; (1)字符串為32字節,16字節ASCLL碼,16字節屬性; (2)每一行有160列,那么余下有 160-32=128列為空白; 要使得字符串居中顯示,那么字符串的左邊和右邊; 都應該是64字節(128/2),而列數是從0開始計數,; 所以左邊的64字節為0-63,所以這里偏移量為64mov di,0 s0: mov al,ds:[di] ;將date段中的字符一個一個傳入es中mov es:[bx+si],al ; 低位存放字符mov es:[bx+si+1],ah ; 高位存放顏色 add si,2 ;顯示緩存區字符ASCII碼偏移量為2add di,1 ;data段字符的偏移量,每次加 1 loop s0pop si pop cx ;后進先出,先出棧si,再出棧cxadd si,1h ;指向下一個顏色add bx,0a0h ;指向下一行 160=0a0hloop smov ax,4c00hint 21h code endsend start示例代碼:
https://www.cnblogs.com/zhenzhenhuang/p/6898813.html
assume cs:codedata segmentdb 'welcome to masm!'db 2,24h,71h data endsstack segmentdb 16 dup(0) stack endscode segment start:mov ax,stackmov ss, axmov sp, 16mov ax,datamov ds,axmov cx,3mov ax,0B800Hmov es,axmov si,10hmov ax,0s:mov ah,ds:[si]push cxpush simov cx,16mov si,0add si,160*10+80mov bx,0mov di,1add di,160*10+80s1:mov al,[bx]mov es:[bx+si],almov es:[bx+di],ahinc bxinc siinc diloop s1pop siinc sipop cxmov dx,esadd dx,0ahmov es,dxloop sfinish:mov ax,4c00hint 21h code ends end start?
實驗9?代碼 2:
http://www.cppblog.com/Tim/archive/2012/06/04/177420.html
datasg segment ;對80*25的屏幕,每行的字節數為80*2=160.;要求顯示在屏幕中間,先計算行和列的偏移;行偏移:(25-3)/2=11.所以顯示在第11,12,13行。偏移值分別為1760,1920,2080。計算方法為 行數*160(每行的字節數) 。;列偏移:由于要顯示的字符數為16個,所以開始顯示的列偏移為(80-16)/2*2=64。dw 1760,1920,2080,64;要顯示的字符 16個db 'welcome to masm!';字符屬性:第11行顯示綠色字,第12行顯示綠底紅色,第13行顯示白底藍色;在全屏模式下能看到字符的閃爍效果db 82h,0ach,0f9h datasg endscodesg segment assume cs:codesg, ds:datasg start:mov ax, datasg ; 設置ds段為datasg數據段mov ds, axmov ax,0b800h ; 設置es段為顯存段mov es,ax mov cx,3 ; 設置外循環3次mov di,0s1: ; 外循環開始, 循環 行 mov ax, dimov bl, 2div bl ; 16被除數,ax 的高位是余數,低位是商。mov si, axmov ah, [24+si] ;取顏色屬性mov si, ds:[6] ;列 ,對應 64mov bp, [di] ;行 ,分別 對應 1760,1920,2080mov dx, cxmov bx, 0 ; 遍歷每一行的字符mov cx, 16 s2:mov al, [bx+8] ;取字符mov es:[bp+si], al ;寫字符mov es:[bp+si].1, ah ;設置顏色屬性inc bxadd si, 2loop s2mov cx,dxadd di,2loop s1mov ax, 4c00Hint 21H codesg endsend start運行結果截圖:
示例代碼:
https://www.lscx.org/2718.html
assume cs:codesg,ds:datasg,ss:stacksgdatasg segmentdb 'welcome to masm!'db 82h,0a4h,0f1h datasg endsstacksg segmentdb 8 dup (0) stacksg endscodesg segment start:mov ax,datasgmov ds,axmov ax,0B86Ehmov es,axmov cx,3mov si,0s1: push cxmov bx,0mov bp,0mov cx,16s: mov al,ds:[bp]mov es:[bx].40h,almov al,ds:16[si]mov es:[bx].40h[1],alinc bpadd bx,2loop spop cxmov ax,esadd ax,10mov es,axinc siloop s1mov ax,4c00hint 21h codesg ends end start?
?
實驗9?代碼 3:
assume cs:codesg, ds:datasg, ss:stacksgdatasg segmentdb "welcome to masm!"db 2,24h,71h datasg endsstacksg segmentdb 16 dup(0) stacksg endscodesg segment start:mov ax,datasgmov ds,axmov si,10hmov di,1824mov ax,0b800hmov es,ax mov cx,3 s1:mov bx,0push cxmov cx,16 s:mov al,[bx]mov ah,[si]mov WORD ptr es:[di],axadd bx,1add di,2 loop sinc siadd di,128pop cxloop s1 all:jmp short allmov ax,4c00hint 21h codesg ends end start實驗9?代碼4:
https://blog.csdn.net/Ryannn_/article/details/84190330
assume cs:code,ds:datadata segment ;在數據段定義字符串db 'Welcome to masm!' data endscode segment start: mov ax,datamov ds,axmov ax,0b800hmov es,ax ; 使用ds和es寄存器作為段寄存器mov si,0mov bx,0mov bp,07b0h ; 由計算出的字符串所在位置給bp賦值mov cx,16 ; 16個字符數據故loop16次s: mov al, [bx] ; 以下采用相對基址變址尋址方式mov es:[bp+si], al ; 經計算得第1行偏移地址應為b87b0h至b87cfhmov es:[bp+si+0a0h], al ; 第2行偏移地址應為b8850h至b886fhmov es:[bp+si+140h], al ; 第3行偏移地址應為b88f0h至b890fhmov al, 00000010b ; 分別給三行設置屬性字節mov es:[bp+si+1], almov al, 00100100bmov es:[bp+si+0a0h+1], almov al, 01110001bmov es:[bp+si+140h+1], alinc bxadd si,2 ; 每個字符占2個字節loop s mov ah,4chint 21h code ends end start?
?
?
總結
以上是生活随笔為你收集整理的王爽 汇编语言第三版 第9章 转移指令的原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《 FRIDA系列文章 》
- 下一篇: 命令行下 pdb 调试 Python 程