實驗4 匯編應(yīng)用編程和c語言程序反匯編分析
一、實驗結(jié)論
1.實驗任務(wù)1
編程:在屏幕中間分別顯示綠色、綠底紅色、白底藍(lán)色的字符串'welcome to masm!'
實現(xiàn)以上實驗任務(wù)的源代碼task1.asm如下所示:
1 assume cs:code, ds:data 2 data segment 3 db 'welcome to masm!' ;存放字符的數(shù)據(jù) 4 db 00000010B, 00100100B, 01110001B ;存放字符顏色屬性的數(shù)據(jù) 5 data ends 6 code segment 7 start:mov ax, data 8 mov ds, ax ;設(shè)置數(shù)據(jù)段地址,指明數(shù)據(jù)從哪里來 9 mov ax, 0b800H 10 mov es, ax ;設(shè)置附加段地址,指明數(shù)據(jù)到哪里去 11 mov bx, 16 ;獲取顏色數(shù)據(jù)的偏移地址 12 mov di, 1824 ;附加段數(shù)據(jù)的偏移地址 13 mov cx, 3 ;設(shè)置循環(huán)次數(shù)為3 14 s1: mov si, 0 ;數(shù)據(jù)段數(shù)據(jù)的偏移地址 15 push cx ;使用cx之前保存其原來的數(shù)據(jù) 16 mov cx, 16 ;設(shè)置循環(huán)次數(shù)為16 17 s2: mov al, ds:[si] ;取第(si+1)個字符數(shù)據(jù) 18 mov ah, ds:[bx] ;取第(bx-15)個顏色屬性數(shù)據(jù) 19 mov es:[di], ax ;將攜帶顏色屬性的字符寫入到顯存中 20 add di ,2 ;附加段的偏移地址自增2,以便下一次寫入字符數(shù)據(jù) 21 inc si ;取下一個字符的ASCII碼 22 loop s2 23 add di, 128 ;開始寫入下一行 24 inc bx ;改變下一行字符的顏色屬性 25 pop cx ;恢復(fù)cx的原先值 26 loop s1 27 mov ah, 4ch 28 int 21h 29 code ends 30 end start
將上述源程序task1.asm匯編、鏈接為程序task1.exe,其運行結(jié)果如下所示:

實現(xiàn)思路:
對于本實驗任務(wù)來說,最為關(guān)鍵的還是需要理解 80×25 彩色模式是怎樣的一個模式。而了解了80×25 彩色模式之后就可以推算出字符應(yīng)該寫入到
顯存的何處位置,才會有中央展示的效果。80×25的彩色字符模式,即一屏25行80列。每一行顯示一個字符需要兩個字節(jié)(低位字節(jié)存放字符的ASCⅡ
碼值,高位字節(jié)存放字符的顯示屬性),一共160個字節(jié)。默認(rèn),在顯存第0列顯示時,對應(yīng)的顯存空間為B8000H ~ B8F9FH。這里展示我們需要寫入
三行數(shù)據(jù)的顯存偏移地址:

由80×25的彩色組織模式可以得出,我們需要連續(xù)寫入32個字節(jié)的數(shù)據(jù)。從上述表中可以看出我們需要在偏移地址為1760位置處開始寫入數(shù)據(jù),但是在
1760處開始寫入數(shù)據(jù)的話,顯示的數(shù)據(jù)就不是在屏幕中央了。所以,根據(jù)寫入的32字節(jié)數(shù)據(jù)以及1760~1919這個范圍,我們可以計算出在中央顯示數(shù)據(jù)
的顯存偏移地址為[(1760+80)-16]=1824,同時也可以計算出下一行數(shù)據(jù)應(yīng)該寫入的顯存偏移地址為(1824+160)=1984。但是為了只使用一個di寄存器來
間接尋址,我們需要考慮第一行最后一個數(shù)據(jù)后到第二行第一個數(shù)據(jù)開始所相差的字節(jié)數(shù)為[(80-16)*2] = 128。這便是“add di, 128”這條語句的由來。解
決了這些問題之后,只需要考慮使用雙重循環(huán)即可,內(nèi)層循環(huán)負(fù)責(zé)寫入一行數(shù)據(jù),外層循環(huán)控制寫入幾行數(shù)據(jù)。設(shè)置雙重循環(huán)期間需要注意cx寄存器的
使用,在使用cx控制內(nèi)層循環(huán)的時候需要將原cx的值保存到棧當(dāng)中,然后再去設(shè)置cx寄存器的值。
2. 實驗任務(wù)2
實驗任務(wù):編寫子程序printStr,實現(xiàn)以指定顏色在屏幕上輸出字符串。調(diào)用它,完成字符串輸出。
使用任意一款文本編輯器,編寫8086匯編源程序task2.asm。源代碼如下:
1 assume cs:code, ds:data 2 data segment 3 str db 'try', 0 4 data ends 5 6 code segment 7 start: 8 mov ax, data 9 mov ds, ax 10 11 mov si, offset str ;data數(shù)據(jù)段的間接尋址方式 12 mov al, 2 ;設(shè)置顏色入口參數(shù)為綠色 13 call printStr ;調(diào)用子程序printStr 14 15 mov ah, 4ch 16 int 21h 17 18 printStr: 19 push bx 20 push cx 21 push si 22 push di 23 ;將寄存器的狀態(tài)保存到棧中 24 mov bx, 0b800H 25 mov es, bx ;設(shè)置附加段地址,指明數(shù)據(jù)到哪里去 26 s: mov cl, [si] ;將data數(shù)據(jù)段中偏移地址為si的字節(jié)數(shù)據(jù)存放到cl寄存器中 27 mov ch, 0 ;將寄存器ch設(shè)置為0 28 jcxz over ;如果cx寄存器的值為零則跳轉(zhuǎn)到over繼續(xù)執(zhí)行;否則,不作任何操作 29 mov ch, al ;將顏色入口參數(shù)綠色放到ch寄存器中 30 mov es:[di], cx ;將帶有顏色屬性的字符寫入到顯存空間中 31 inc si ;取data數(shù)據(jù)段中的下一個字節(jié)數(shù)據(jù) 32 add di, 2 ;附加段的偏移地址自增2,以便下一次寫入 33 jmp s ;無條件跳轉(zhuǎn)到s繼續(xù)執(zhí)行 34 35 over: ;從棧中恢復(fù)寄存器的狀態(tài) 36 pop di 37 pop si 38 pop cx 39 pop bx 40 ret 41 42 code ends 43 end start
將上述匯編源程序task2.asm匯編、鏈接成程序task2.exe,其運行結(jié)果如下所示:

對源程序task2.asm做如下的修改:
把line3改為:
1 str db 'another try', 0
把line12改為:
1 mov al, 4
將更改后的匯編源程序task2.asm匯編、鏈接成程序task2.exe,其運行結(jié)果如下所示:

問題分析:
(1)line19-22, line36-39,這組對稱使用的push、pop,這樣用的目的是什么?
答:由于line24~line34之間的匯編代碼改變了bx、cx、si、di這四個寄存器的值,而且這些寄存器的改變是出現(xiàn)在子程序當(dāng)中的。那么在子程序被調(diào)用的
時候這四個寄存器的原有值會被更改,當(dāng)子程序返回之前將四個寄存器的原值從棧中恢復(fù),就不會導(dǎo)致主調(diào)函數(shù)中的寄存器值出現(xiàn)意外的值。其目
的就是在子程序被調(diào)用之后將各寄存器的狀態(tài)恢復(fù)到子程序被調(diào)用之前的狀態(tài)。
(2)ine30的功能是什么?
答:line30行的代碼內(nèi)容為“mov es:[di], cx”。首先,根據(jù)源代碼先前描述知道寄存器es的值為B800H,而di初始值為0。其次,cx的低位是存儲字符的
ASCII值(字符的ASCII值來自于data數(shù)據(jù)段),而cx的高位是存儲字符顏色的屬性值(屬性值來自于入口參數(shù)al)。那么“mov es:[di], cx”的功能
就是“將帶有顏色屬性的字?jǐn)?shù)據(jù)字符寫入到地址為B800:0的顯存空間中”。
3. 實驗任務(wù)3
使用任意一款文本編輯器,編寫8086匯編源程序task3.asm。源代碼如下:
1 assume cs:code, ds:data 2 data segment 3 x dw 1984 4 str db 16 dup(0) 5 data ends 6 7 code segment 8 start: 9 mov ax, data 10 mov ds, ax 11 mov ax, x 12 mov di, offset str 13 call num2str 14 15 mov ah, 4ch 16 int 21h 17 18 num2str: 19 push ax 20 push bx 21 push cx 22 push dx 23 24 mov cx, 0 25 mov bl, 10 26 s1: 27 div bl 28 inc cx 29 mov dl, ah 30 push dx 31 mov ah, 0 32 cmp al, 0 33 jne s1 34 s2: 35 pop dx 36 or dl, 30h 37 mov [di], dl 38 inc di 39 loop s2 40 41 pop dx 42 pop cx 43 pop bx 44 pop ax 45 46 ret 47 code ends 48 end start
實驗任務(wù):
(1)對task3.asm進(jìn)行匯編、鏈接,得到可執(zhí)行程序后,在debug中使用u命令反匯編,使用g命令執(zhí)行到line15(程序退出之前),使用d命令查看數(shù)據(jù)段
內(nèi)容,觀察是否把轉(zhuǎn)換后的數(shù)字字符串'1984'存放在數(shù)據(jù)段中str標(biāo)號后面的單元。

(2)對task3.asm源代碼進(jìn)行修改、完善,把task2.asm中用于輸出以0結(jié)尾的字符串的子程序加進(jìn)來,實現(xiàn)對轉(zhuǎn)換后的字符串進(jìn)行輸出。
答:實現(xiàn)對轉(zhuǎn)換后的字符串進(jìn)行輸出的task3.asm完整源程序如下所示:
1 assume cs:code, ds:data 2 data segment 3 x dw 1984 4 str db 16 dup(0) 5 data ends 6 7 code segment 8 start: 9 mov ax, data 10 mov ds, ax 11 mov ax, x 12 mov di, offset str 13 call num2str 14 mov al, 2 ;設(shè)置顏色入口參數(shù)為綠色 15 call printStr ;調(diào)用printStr子程序 16 17 mov ah, 4ch 18 int 21h 19 20 num2str: 21 push ax 22 push bx 23 push cx 24 push dx 25 26 mov cx, 0 27 mov bl, 10 28 s1: 29 div bl 30 inc cx 31 mov dl, ah 32 push dx 33 mov ah, 0 34 cmp al, 0 35 jne s1 36 s2: 37 pop dx 38 or dl, 30h 39 mov [di], dl 40 inc di 41 loop s2 42 43 pop dx 44 pop cx 45 pop bx 46 pop ax 47 48 ret 49 50 printStr: 51 push bx 52 push cx 53 push si 54 push di 55 56 mov bx, 0b800H 57 mov es, bx 58 mov di, 0 59 mov si, offset str ;將數(shù)據(jù)段的偏移地址指向存放數(shù)字字符的str 60 s: mov cl, [si] 61 mov ch, 0 62 jcxz over 63 mov ch, al 64 mov es:[di], cx 65 inc si 66 add di, 2 67 jmp s 68 69 over: pop di 70 pop si 71 pop cx 72 pop bx 73 ret 74 75 code ends 76 end start
將更改后的匯編源程序task3.asm匯編、鏈接成程序task3.exe,其運行結(jié)果如下所示:

4. 實驗任務(wù)4
使用任意一款文本編輯器,編寫8086匯編源程序task4.asm。源代碼如下:
1 assume cs:code, ds:data 2 data segment 3 str db 80 dup(?) 4 data ends 5 6 code segment 7 start: 8 mov ax, data 9 mov ds, ax 10 mov si, 0 11 12 s1: 13 mov ah, 1 14 int 21h 15 mov [si], al 16 cmp al, '#' 17 je next 18 inc si 19 jmp s1 20 next: 21 mov cx, si 22 mov si, 0 23 s2: mov ah, 2 24 mov dl, [si] 25 int 21h 26 inc si 27 loop s2 28 29 mov ah, 4ch 30 int 21h 31 code ends 32 end start
將上述的task4.asm匯編、鏈接得到程序task4.exe。運行此程序然后輸入一個字符串并以#結(jié)束,程序運行結(jié)果如下所示:

實驗任務(wù):
(1)line12-19實現(xiàn)的功能是?
1 mov ah, 1 2 int 21h ;使用軟中斷的1號子功能,從鍵盤中讀入一個字符 3 mov [si], al ;將從鍵盤中讀入的一個字符存放到data數(shù)據(jù)段中 4 cmp al, '#' ;將讀入的字符與'#'比較 5 je next ;如果讀入的字符是'#',那么跳轉(zhuǎn)到next程序段繼續(xù)執(zhí)行;否則不作任何操作 6 inc si ;data數(shù)據(jù)段的偏移地址自增1 7 jmp s1 ;繼續(xù)讀入下一個字符
答:綜合上述匯編代碼注釋,line12-19實現(xiàn)的功能是“一直從鍵盤讀入字符,并將讀入的字符存放到data數(shù)據(jù)段當(dāng)中,直到遇到輸入符號為'#'為止”。
(2)line21-27實現(xiàn)的功能是?
1 mov cx, si ;設(shè)置循環(huán)的執(zhí)行次數(shù)為讀入字符的個數(shù) 2 mov si, 0 ;數(shù)據(jù)段中取數(shù)據(jù)的間接尋址方式 3 s2: mov ah, 2 4 mov dl, [si] ;將數(shù)據(jù)段中偏移量為si的字節(jié)數(shù)據(jù)存放到dl寄存器中 5 int 21h ;使用軟中斷的2號子功能,在顯示屏上輸出存放在dl中的字符 6 inc si ;取data段的下一個字節(jié)數(shù)據(jù) 7 loop s2
答:綜合上述匯編代碼注釋,line21-27實現(xiàn)的功能是“將存放在data數(shù)據(jù)段中的字符打印到顯示屏上,打印的字符個數(shù)就是讀入的字符個數(shù)”。
5. 實驗任務(wù)5
在visual studio集成環(huán)境中,編寫一個簡單的包含有函數(shù)調(diào)用的c程序。代碼如下:
1 #include<stdio.h> 2 int sum(int, int); 3 4 int main() { 5 int a = 2, b = 7, c; 6 c = sum(a, b); 7 return 0; 8 } 9 10 int sum(int x, int y) { 11 return (x + y); 12 }
在line6, line11分別設(shè)置斷點,在調(diào)試模式下,查看反匯編代碼。line6的反匯編代碼如下圖所示:

line11的反匯編代碼如下圖所示:

實驗任務(wù):
分析反匯編代碼,從匯編的角度,觀察高級語言中參數(shù)傳遞和返回值是通過什么實現(xiàn)的,以及參數(shù)的入棧順序,返回值的帶回方式。
line6反匯編代碼的部分注釋:
5: int a = 2, b = 7, c; mov dword ptr [ebp-8],2 ;在main函數(shù)棧幀中距棧底為8的位置定義雙字變量a并為其賦初值2 mov dword ptr [ebp-14h],7 ;在main函數(shù)棧幀中距棧底為14的位置定義雙字變量b并為其賦初值7 6: c = sum(a, b); mov eax,dword ptr [ebp-14h] push eax ;先將雙字變量b壓棧 mov ecx,dword ptr [ebp-8] push ecx ;然后將雙字變量a壓棧 call 0040137F add esp,8 mov dword ptr [ebp-20h],eax ;在main函數(shù)棧幀中距棧底為20的位置定義雙字變量b并為其賦sum函數(shù)的返回值eax 7: return 0; xor eax,eax ;將eax快速置零 8: }
line11反匯編代碼的部分注釋:
10: int sum(int x, int y) { push ebp ;使用棧底指針寄存器之前,先將其原先值保存到棧中 mov ebp,esp ;原棧頂變?yōu)樾聴5祝梢钥醋鱯um函數(shù)壓棧,此時棧底為main方法棧幀 sub esp,0C0h ;重新設(shè)置棧頂指針寄存器的值,為新棧開辟內(nèi)存空間 push ebx push esi push edi ;保存寄存器狀態(tài)以便恢復(fù) lea edi,[ebp+FFFFFF40h] mov ecx,30h mov eax,0CCCCCCCCh rep stos dword ptr es:[edi] ;串傳送指令,將eax寄存器的值轉(zhuǎn)送到es:[edi]的內(nèi)存空間中,傳送方向取決于DF標(biāo)志寄存器的值,而傳送次數(shù)取決于ecx的值。 mov ecx,40C003h call 0040120D 11: return (x + y); mov eax,dword ptr [ebp+8] ;在main函數(shù)棧幀中取距原esp為8的雙字?jǐn)?shù)據(jù)即副本a,注意main函數(shù)中定義的a變量存儲在原ss:[ebp-8]的位置。 add eax,dword ptr [ebp+0Ch];在main函數(shù)棧幀中取距原esp為12的雙字?jǐn)?shù)據(jù)即副本b,然后與eax即副本a做加法運算并將運算結(jié)果送回eax中, ;注意main函數(shù)中定義的變量b存儲在原ss:[ebp-14]的位置。 12: } pop edi pop esi pop ebx ;將寄存器的狀態(tài)復(fù)原 add esp,0C0h ;還原esp,與“sub esp,0C0h”操作相對應(yīng) cmp ebp,esp call 00401217 mov esp,ebp ;新棧底恢復(fù)為原棧頂,相當(dāng)于sum函數(shù)出棧,此時棧中只有main方法 pop ebp ;還原原棧底
ret
答:根據(jù)上述反匯編代碼的注釋分析,從匯編的角度來看,高級語言中參數(shù)傳遞是通過棧來實現(xiàn)的,并且靠近主調(diào)函數(shù)棧頂中的參數(shù)相當(dāng)于是形參,
而靠近主調(diào)函數(shù)棧底中的參數(shù)相當(dāng)于實參,這就是在高級語言的值傳遞過程中,形參和實參互不影響的原因。然后被調(diào)函數(shù)的返回值在該例中
是通過eax寄存器來存儲的,程序返回到主調(diào)函數(shù)可以通過eax寄存器來獲取被調(diào)函數(shù)的返回值。至于函數(shù)調(diào)用過程中參數(shù)的如入棧順序可以從
line6的反匯編代碼中看出,在該例主調(diào)函數(shù)的函數(shù)調(diào)用過程中,參數(shù)b先入棧,參數(shù)a后入棧;而在被調(diào)函數(shù)的執(zhí)行過程中,副本a先被訪問,
副本b后被訪問。
浙公網(wǎng)安備 33010602011771號