实验4 汇编应用编程和c语言程序反汇编分析
四、实验结论
1.实验任务1
taks1.asm源代码如下:
assume cs:code, ds:data data segment db 'welcome to masm!' ;待输出字符 db 00000010B, 00100100B, 01110001B ;待输出字符样式 data ends code segment start: mov ax, data mov ds, ax ;数据段地址 mov ax, 0b800H mov es, ax ;附加段地址 mov bx, 0010H ; 颜色数据于数据段偏移地址 mov di, 1824; 第一行第一个字符w的在附加段中的偏移地址,即第12行第32列(12*160+64=1824) mov cx, 3 ;三行输出,三次循环 s1: mov si, 0 ;每次输出后都要初始到数据段开始重新输出目标字符 push cx ;将外层循环次数cx入栈保存 mov cx, 16 ;一行16个字符,循环16次 s2: mov al, ds:[si] ; 将数据段对应字符内容一一读出 mov ah, ds:[bx] ;读取字符样式 mov es:[di], ax ;将读出的对应样式的字符写入显存 inc si add di,2 loop s2 ;跳出内层循环,对应屏幕上打印完一行 add di, 128 ;算出下一行开始偏移地址+(160-32) inc bx pop cx ;在跳转s1前将外层cx出栈 loop s1 ;回到外层循环开始 mov ah, 4ch int 21h code ends end start
运行截图如下:

说明:本实验任务关键就在于计算屏幕中央对应的显存区域,已知是80*25,所以是第11-13行,然后每行16个字符,所以是32-48列,因此算出第一行开始偏移地址是1824,然后第二行第三行依次比上一行160即可,然后使用两层循环结构写入显存即可即可
2.实验任务2
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
运行截图如下:

str db 'another try', 0
将line12改为:
mov al, 4
然后运行程序截图如下:

答:(1)push将跳转用到的寄存器状态压入栈中,因为子程序中也用到了这些寄存器,防止因为子程序段修改这些寄存器的值导致跳转不能正常执行,因此压入栈中等待子程序段执行后再把压入的寄存器状态出栈。
(2)line30"mov es:[di], cx"中,es对应b800H是显存的基准地址,[di]则是偏移地址,表示将cx中存储的一定格式的字符数据写入显存对应区域
3.实验任务3
task3.asm源代码如下:
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
(1)子任务1
u命令对代码段反汇编截图如下:


使用g命令执行到Line15然后可以使用d命令可以看到数据段中写入数字字符串'1984':

(2)子任务2
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 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中数字随意修改后重新汇编链接运行如下:

补充:经过评论提示,对于输出到屏幕的内容与样式并没有完整设置好,在call prinstr之前增加了mov al,2设置颜色入口参数,以及增加mov si,offset str。设置si初始值使得第一次进入s段中打印时会跳过ASCII码对应内容,修改后的task3.asm代码如下:
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 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 mov si, offset str 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
运行结果如下:

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
测试运行截图如下,输入字符串hello并以#结束,程序会自动在#后输出一个相同字符串:

其中line12-19行实现了读取使用者从键盘中输入的字符串并且将其存放到data数据段中,在遇到'#'时结束跳转到next程序段
line21-27行 使用软中断指令Int21的二号子功能将data数据段中存储的字符串输出到屏幕上。
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); }
使用devc++进行debug:
可以看到sum程序段反汇编如下:

main程序段反汇编如下

可以看到main()中789行使用mov指令在ecx中存储了eax即DWORD PTR[rbq-0x4](值为2),对应的高级语言将a的值,和edx存储的是DWORD PTR[rbq-0x8](值为7)对应高级语言的b的值,然后通过eax读取sum()的返回值。可以看到sum()中3.4行通过读取ecx和edx的值读取了参数a,b的值,而寄存器eax存储sum()函数的结果,然后在main()中反汇编11行通过Mov指令将eax赋给DWORD[rbp-0xc]对应的就是高级语言中main()函数体第二行将sum(a,b)的返回值赋值给c,得出结论,高级语言通过寄存器来实现参数传递和返回值。

浙公网安备 33010602011771号