实验4 汇编应用编程和c语言程序反汇编分析

一、实验目的

1. 理解80×25彩色字符模式显示原理

2. 理解转移指令jmp, loop, jcxz的跳转原理,掌握使用其实现分支和循环的用法

3. 理解转移指令call, ret, retf的跳转原理,掌握组合使用call和ret/retf编写汇编子程序的方法,掌握 参数传递方式

4. 理解标志寄存器的作用

5. 理解条件转移指令je, jz, ja, jb, jg, jl等的跳转原理,掌握组合使用汇编指令cmp和条件转移指令实 现分支和循环的用法

6. 了解在visual studio/Xcode等环境或利用gcc命令行参数反汇编c语言程序的方法,理解编译器生成 的反汇编代码

7. 综合应用寻址方式和汇编指令完成应用编程

 

二、实验准备

实验前,请复习/学习教材以下内容:

第9章 转移指令的原理

第10章 call和ret指令

第11章 标志寄存器

 

三、实验内容

1. 实验任务1 教材「实验9 根据材料编程」(P187-189)

编程:在屏幕中间分别显示绿色、绿底红色、白底蓝色的字符串'welcome to masm!'。

代码:

assume cs:code
data segment
  db 'welcome to masm!'
  db 2,36,113          ;分别对应示绿色、绿底红色、白底蓝色
data ends
code segment
start:
        mov ax,data
        mov ds,ax

        mov bx,0b872H
        mov es,bx
        mov cx,3      ;设置外层循环次数
        mov bx,0      ;记录当前输出的行数
    
s1:     push cx       ;双层循环利用栈
    
        mov cx,16     ;根据输出的字符长度确定内层循环的次数
        mov di,0    
        mov si,0

s:      mov al,[si]    ;存放内容信息
        mov es:[di],al
        mov ah,[16+bx] ;存放颜色信息
        mov es:[di+1],ah
        inc si
        add di,2
        loop s
    
        inc bx
        mov ax,es     ;换行
        add ax,0ah
        mov es,ax
    
        pop cx
        loop s1
    
        mov ax,4c00h
        int 21h
code ends
end start

 

 

80×25的彩色字符模式,即一屏25行80列。要输出在屏幕中央并且输出三行,应该从第十一行开始输出,第十一行的偏移地址为1760(十进制),需要输出在中央,所以字符也应当水平居中,要输出的字符占32字节(包括颜色属性)。一行160字节,(160-32)/2=64,64+1760=1824,对应的十六进制为720h,所以显存中的段地址为B872h。

 

2. 实验任务2

编写子程序printStr,实现以指定颜色在屏幕上输出字符串。调用它,完成字符串输出。

子程序printSar 功能:

以指定颜色在屏幕上(从屏幕左上角开始)输出字符串

要求:字符串以0结尾

入口参数 字符串的起始地址—> ds: si 

其中,字符串所在段的段地址—> ds, 字符串起始地址 的偏移地址—> si

字符串颜色—> al 出口参数:无

使用任意文本编辑器,录入汇编源程序task2.asm

assume cs:code, ds:data
data segment
    str db 'try', 0
data ends

code segment
start:  
    mov ax, data
    mov ds, ax

    mov si, offset str
    mov al, 2
    call printStr

    mov ah, 4ch
    int 21h

printStr:
    push bx
    push cx
    push si
    push di

    mov bx, 0b800H
    mov es, bx
    mov di, 0
s:      mov cl, [si]
    mov ch, 0
    jcxz over
    mov ch, al
    mov es:[di], cx
    inc si
    add di, 2
    jmp s

over:   pop di
    pop si
    pop cx
    pop bx
    ret

code ends
end start

 汇编、运行程序,观察运行结果。

对源程序做如下修改:

把line3改为:str db 'another try', 0

把line12改为:mov al, 4

再次汇编、运行程序,观察运行结果。

基于运行结果,理解源代码,以及,组合使用转移指令call和ret实现子程序的原理与方法。具体地,在 line18-40中:

line19-22, line36-39,这组对称使用的push、pop,这样用的目的是什么? line30的功能是什么?

修改后运行结果:

 对称使用push,pop的目的是暂存寄存器的内容防止程序运行时修改内容无法恢复。line30的目的是将字符和颜色属性送入显存中。

 

3. 实验任务3

使用任意文本编辑器,录入汇编源程序task3.asm。

子程序num2str:

功能:把0~2559之间的任意整数转换成数字字符串,例如,把1984转换成'1984'

入口参数 要转换的整数 —> ax

数字字符串的起始地址 —> ds:di (其中:数字字符串所在段的段地址—> ds,字符串 起始地址的偏移地址—>di)

出口参数:无

assume cs:code, ds:data
data segment
        x dw 1984
        str db 16 dup(0)
data ends

code segment
start:  
        mov ax, data
        mov ds, ax
        mov ax, x
        mov di, offset str
        call num2str

        mov ah, 4ch
        int 21h

num2str:
        push ax
        push bx
        push cx
        push dx
        
        mov cx, 0
        mov bl, 10
s1:      
        div bl
        inc cx
        mov dl, ah
        push dx
        mov ah, 0
        cmp al, 0
        jne s1
s2:        
        pop dx
        or dl, 30h
        mov [di], dl
        inc di
        loop s2
        
        pop dx
        pop cx
        pop bx
        pop ax

        ret
code ends
end start

阅读源代码,理解子程序num2str的汇编实现。

子任务1

对task3.asm进行汇编、链接,得到可执行程序后,在debug中使用u命令反汇编,使用g命令执行 到line15(程序退出之前),使用d命令查看数据段内容,观察是否把转换后的数字字符串'1984'存放 在数据段中str标号后面的单元。

 检察是否把转换后的数字字符串'1984'存放 在数据段中str标号后面的单元:

 

子任务2

对task3.asm源代码进行修改、完善,把task2.asm中用于输出以0结尾的字符串的子程序加进来, 实现对转换后的字符串进行输出。

assume cs:code, ds:data
data segment
        x dw 1984
        str db 16 dup(0)
data ends

code segment
start:  
        mov ax, data
        mov ds, ax
        mov ax, x
        mov di, offset str
        call num2str
      
       mov si, offset str
        mov al, 2
        call printStr

        mov ah, 4ch
        int 21h

num2str:
        push ax
        push bx
        push cx
        push dx
        
        mov cx, 0
        mov bl, 10
s1:      
        div bl
        inc cx
        mov dl, ah
        push dx
        mov ah, 0
        cmp al, 0
        jne s1
s2:        
        pop dx
        or dl, 30h
        mov [di], dl
        inc di
        loop s2
        
        pop dx
        pop cx
        pop bx
        pop ax

        ret

printStr:
    push bx
    push cx
    push si
    push di

    mov bx, 0b800H
    mov es, bx
    mov di, 0
s:              mov cl, [si]
    mov ch, 0
    jcxz over
    mov ch, al
    mov es:[di], cx
    inc si
    add di, 2
    jmp s

over:   pop di
    pop si
    pop cx
    pop bx
    ret
code ends
end start

运行截图:

 改变line3的值为2020:

 

4. 实验任务4

使用任意文本编辑器,录入汇编源程序task4.asm。

assume cs:code, ds:data
data segment
        str db 80 dup(?)
data ends

code segment
start:  
        mov ax, data
        mov ds, ax
        mov si, 0

s1:        
        mov ah, 1
        int 21h
        mov [si], al
        cmp al, '#'
        je next
        inc si
        jmp s1
next:
        mov cx, si
        mov si, 0
s2:     mov ah, 2
        mov dl, [si]
        int 21h
        inc si
        loop s2

        mov ah, 4ch
        int 21h
code ends
end start

汇编、链接、运行程序,输入一个字符串并以#结束(比如,2020, bye#)观察运行结果。

结合运行结 果,理解程序功能,了解软中断指令。

具体地: line12-19实现的功能是? line21-27实现的功能是?

运行结果:

 

  line12-19实现的功能是调用int21 1号子功能从键盘读入字符,若字符不等于#则继续读入,若字符等于#则停止读入执行next代码段。

line21-27实现的功能是调用int21 2号子功能在屏幕上输出第一次输入的内容。

 

5. 实验任务5

在visual studio集成环境中,编写一个简单的包含有函数调用的c程序。代码如下:

#include <stdio.h>
int sum(int, int);
int main() {
    int a = 2, b = 7, c;
    c = sum(a, b);
    return 0;
}
int sum(int x, int y) {
    return (x + y);
}

在line7, line13分别设置断点,在调试模式下,查看反汇编代码。

分析反汇编代码,从汇编的角度,观察高级语言中参数传递和返回值是通过什么实现的,以及,参数入 栈顺序,返回值的带回方式,等等。

设置断点:

 

 反汇编代码:

#include <stdio.h>
int sum(int, int);
int main() {
00F217B0  push        ebp  
00F217B1  mov         ebp,esp  
00F217B3  sub         esp,0E4h  
00F217B9  push        ebx  
00F217BA  push        esi  
00F217BB  push        edi  
00F217BC  lea         edi,[ebp-0E4h]  
00F217C2  mov         ecx,39h  
00F217C7  mov         eax,0CCCCCCCCh  
00F217CC  rep stos    dword ptr es:[edi]  
00F217CE  mov         ecx,offset _D0D8FC35_源@cpp (0F2C003h)  
00F217D3  call        @__CheckForDebuggerJustMyCode@4 (0F2130Ch)  
    int a = 2, b = 7, c;
00F217D8  mov         dword ptr [a],2  
00F217DF  mov         dword ptr [b],7  
    c = sum(a, b);
00F217E6  mov         eax,dword ptr [b]  
00F217E9  push        eax  
00F217EA  mov         ecx,dword ptr [a]  
00F217ED  push        ecx  
00F217EE  call        sum (0F2116Dh)  
00F217F3  add         esp,8  
00F217F6  mov         dword ptr [c],eax  
    return 0;
00F217F9  xor         eax,eax  
}
00F217FB  pop         edi  
00F217FC  pop         esi  
00F217FD  pop         ebx  
00F217FE  add         esp,0E4h  
00F21804  cmp         ebp,esp  
00F21806  call        __RTC_CheckEsp (0F21235h)  
00F2180B  mov         esp,ebp  
00F2180D  pop         ebp  
00F2180E  ret  
int sum(int x, int y) {
00F21740  push        ebp  
00F21741  mov         ebp,esp  
00F21743  sub         esp,0C0h  
00F21749  push        ebx  
00F2174A  push        esi  
00F2174B  push        edi  
00F2174C  lea         edi,[ebp-0C0h]  
00F21752  mov         ecx,30h  
00F21757  mov         eax,0CCCCCCCCh  
00F2175C  rep stos    dword ptr es:[edi]  
00F2175E  mov         ecx,offset _D0D8FC35_源@cpp (0F2C003h)  
00F21763  call        @__CheckForDebuggerJustMyCode@4 (0F2130Ch)  
    return (x + y);
00F21768  mov         eax,dword ptr [x]  
00F2176B  add         eax,dword ptr [y]  
}
00F2176E  pop         edi  
00F2176F  pop         esi  
00F21770  pop         ebx  
00F21771  add         esp,0C0h  
00F21777  cmp         ebp,esp  
00F21779  call        __RTC_CheckEsp (0F21235h)  
00F2177E  mov         esp,ebp  
00F21780  pop         ebp  
00F21781  ret  

参数传递的方式是先将参数存入寄存器,再从右向左压入栈中,通过call命令调用函数,执行加法后将结果存入存储器最后返回主函数。

posted @ 2020-12-14 21:22  敲代码了吗  阅读(258)  评论(3编辑  收藏  举报