实验4 汇编应用编程和c语言程序反汇编分析
1. 实验任务1
教材「实验9 根据材料编程」(P187-189)
编程:在屏幕中间分别显示绿色、绿底红色、白底蓝色的字符串\’welcome to masm!\’。
程序正确编写后,预期输出结果如下:
实验代码4如下:
1 ;task1.ASM 2 assume cs:code, ds:data 3 data segment 4 db \'welcome to masm!\' 5 db 2h,24h,71h 6 ;绿色:2h,绿底红字:24h,白底蓝字:71h 7 data ends 8 9 code segment 10 start: 11 mov ax, data 12 mov ds, ax ;传递data段的段地址给ds 13 mov ax,0b872h ;根据提示得出的显示器中心区域的显存地址(第十二行) 14 mov es,ax ;字符将要存入的显存地址段 15 16 mov di,0 17 mov ah,ds:[di+10h] ;将数据段中用于修改显示颜色的数据存入寄存器中 18 mov al,ds:[di+11h] 19 mov bh,ds:[di+12h] 20 mov si,0 21 mov cx,16 ;循环次数 22 s: mov bl,ds:[si] ;从数据段中取数据 23 mov es:[di],bl ;存入显存地址段中(在第十二行显示) 24 mov es:[di+1],ah ;修改字符颜色 25 mov es:[di+0a0h],bl ;存入显存地址段中(在第十三行显示) 26 mov es:[di+0a1h],al ;修改字符颜色 27 mov es:[di+140h],bl ;存入显存地址段中(在第十四行显示) 28 mov es:[di+141h],bh ;修改字符颜色 29 inc si 30 add di,2 31 loop s 32 33 mov ax,4c00h 34 int 21h 35 code ends 36 end start
编译、连接后执行task1.exe,结果如图:
根据提示:doSbox显示窗口是80×25彩色字符模式显示,重复三次显示\’welcom to masm!\’需要将数据存入的现存地址为:
十二行中心区域:b800:0720~b800:0740(32个字节(十六个字符十六个颜色))
十三行中心区域:b800:07C0~b800:07E0
十四行中心区域:b800:0860~b800:0880
使用debug,在使用g指令执行完程序后查看对应地址的数据:(虚拟机中win7系统的cmd命令窗口)
2. 实验任务2 编写子程序printStr,实现以指定颜色在屏幕上输出字符串。调用它,完成字符串输出。
编写子程序printStr,实现以指定颜色在屏幕上输出字符串。调用它,完成字符串输出。
子程序printSar
功能:以指定颜色在屏幕上(从屏幕左上角开始)输出字符串
要求:字符串以0结尾
入口参数
字符串的起始地址—> ds: si (其中,字符串所在段的段地址—> ds, 字符串起始地址的偏移地址—> si,字符串颜色—> al
出口参数:无
使用任意文本编辑器,录入汇编程序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数据段的偏移地址 12 mov al, 2 ;用于显示字符的颜色 13 call printStr ;跳转到标号printStr(原IP入栈,再给IP赋新值,即标号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 24 mov bx, 0b800H ;字符显示处的地址段 25 mov es, bx ;es记录显存地址 26 mov di, 0 27 s: mov cl, [si] ;取出数据段中的段数据(存入cx的低位中) 28 mov ch, 0 ;cx高位为0 29 jcxz over ;cx为0则转到标号over处执行(当data数据段的取完即取出的是最后一个数据(0)时跳转) 30 mov ch, al ;已经判断cx不为0,data数据段的数据未取完 31 mov es:[di], cx 32 inc si ;移向下一个字节 33 add di, 2 34 jmp s ;无条件跳转 35 36 over: pop di ;跳转到此处说明data段的字符已经显示在显示器上 37 pop si ;调用完成,恢复到调用前状态 38 pop cx 39 pop bx 40 ret ;利用栈中的数据修改IP(使用 call指令时将IP压入栈中) 41 42 code ends 43 end start
汇编、运行程序,观察运行结果。
对源程序做如下修改:
把line3改为:
str 1 db \'another try\', 0
把line12改为:
mov al, 4
再次汇编、运行程序,观察运行结果。
基于运行结果,理解源代码,以及,组合使用转移指令call和ret实现子程序的原理与方法。具体地,在line18-40中:
line19-22, line36-39,这组对称使用的push、pop,这样用的目的是什么?line31的功能是什么?
ine19-22中使用push将寄存器的值压入栈中,目的是保存程序跳转前的状态;
line36-39,使用pop指令对数据进行出栈操作,目的是回到程序跳转前的状态;
line31:mov es:[di] ,cx作用是将取出的data段中的字符数据以及该字符显示的颜色数据存入显存地址中。
实验代码中用cl存储data段中取出的数据,ch先置为0,经过jcxz指令通过后在将控制颜色的数据传给ch;
当cl还未取到data数据段的最后一个数据时,cx的值就不为零,在将控制颜色的数据传给ch,然后将cx的数据存入显存地址段中。
当cl取到data数据段的末尾时(最后一个数据为0),在执行jcxz指令时cx为0 ,跳转(同时说明数据取完)。
3. 实验任务3
使用任意文本编辑器,录入汇编源程序task3.asm。
子程序num2str:
功能:把0~2559之间的任意整数转换成数字字符串,例如,把1984转换成\’1984\’
入口参数
要转换的整数 —> ax
数字字符串的起始地址 —> ds:di (其中:数字字符串所在段的段地址—> ds,字符串起始地址的偏移地址—>di)
出口参数:无 1 assume cs:code, ds:data 2 data segment
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 ;做除法,除数8位,被除数默认ax中,结果:al商,ah余数 28 inc cx ;统计个数 29 mov dl, ah ;将余数存入dl中(整数中拆分出来的单个数字) 30 push dx ;商压入栈中 31 mov ah, 0 ;ah置为零 32 cmp al, 0 ;比较al是否为0 33 jne s1 ;不等于则转移到s1(为0说明整数全部都被拆分出来了) 34 35 mov ax,0b800h 36 mov es,ax 37 mov si,0 38 mov ah,2h 39 s2: 40 pop dx ;出栈,取拆分出来的数字(在bl中) 41 or dl, 30h ;或运算,转换数字对应的字符(0000 xxxx或0011 0000,0011 xxxx)数子字符30H~39H(0~9) 42 mov [di], dl ;将拆分出来的数字字符送入data数据段中 43 mov es:[si],dl 44 mov es:[si+1],ah 45 add si,2h 46 inc di 47 loop s2 48 49 pop dx 50 pop cx 51 pop bx 52 pop ax 53 54 ret 55 code ends 56 end start
阅读源代码,理解子程序num2str的汇编实现。
子任务1
对task3.asm进行汇编、链接,得到可执行程序后,在debug中使用u命令反汇编,使用g命令执行
到line15(程序退出之前),使用d命令查看数据段内容,观察是否把转换后的数字字符串\’1984\’存放
在数据段中str标号后面的单元。
子任务2
对task3.asm源代码进行修改、完善,把task2.asm中用于输出以0结尾的字符串的子程序加进来,
实现对转换后的字符串进行输出。
预期输入结果如下:
把task3.asm源代码中,line3中整数改成0~2559之间的任意数值,运行测试,观察结果。
任务1:
对task3.asm进行汇编、链接,得到可执行程序后,在debug中对程序进行调试:
1、使用u命令反汇编:
2、使用g命令执行到line15(程序退出之前),使用d命令查看数据段内容,
子任务2
对task3.asm源代码进行修改、完善,把task2.asm中用于输出以0结尾的字符串的子程序加进来,实现对转换后的字符串进行输出。
在代码中增加line35~38,line43~45,将拆分出来的数字显示在窗口上,如图:
将line3中的整数改为2000,编译连接后运行结果如图:
l
将line3中的整数改为199,编译连接后运行结果如图:
4. 实验任务4
使用任意文本编辑器,录入汇编源程序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 ;int 21的1号子功能,从键盘输入单个字符 14 int 21h ;出口函数al,键入的字符,存放在al中 15 mov [si], al ;将键入的字符存入data数据段中 16 cmp al, \'#\' ;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 ;int 21的2号子功能,将dl寄存器中的内容在屏幕上输出 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
汇编、链接、运行程序,输入一个字符串并以#结束(比如,2020, bye#)观察运行结果。
结合运行结果,理解程序功能,了解软中断指令。具体地:
line12-19实现的功能是?
line21-27实现的功能是?
编译连接后运行结果如图:输入字符串为2020,bye#
输入结束符号#后程序立即输出出入的字符串(除#)
line12-19实现的功能是:使用int 21h的1号子功能从键盘中输入字符,保存到data数据段中,输入的字符以#结尾(#不存入,也不做输出)。
line21-27实现的功能是:使用int 21h的2号子功能将存入data数据段中的字符在屏幕上输出。
5. 实验任务5
在visual studio集成环境中,编写一个简单的包含有函数调用的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 }
在line7, line13分别设置断点,在调试模式下,查看反汇编代码。
分析反汇编代码,从汇编的角度,观察高级语言中参数传递和返回值是通过什么实现的,以及,参数入
栈顺序,返回值的带回方式,等等。
该段c程序反汇编结果如图:
程序开始:将栈底指针ebp压入栈中,以栈底指针作为新的栈顶指针将各寄存器的状态压入栈中
传值:将值存入地址单元中。然后执行到语句调用sum函数
在地址单元中取出零个参数的值,存放在eax,ecx中,同时将连个寄存器的值压入栈中(参数副本),
在调试中查看入栈时的地址空间如图:
call跳转到地址00321078,如图:
再次跳转到003216f:调用sum函数反汇编如图:
调用sum函数,也是先保存寄存器状态,再执行。执行完之后再还原寄存器状态,返回到main函数。
以下两条汇编语句:取值得内存单元为:115320和115324(参数副本存入得内存单元)
sum函数得结果存放在eax寄存器中。
main函数的返回语句:
程序执行结束,恢复相关寄存器的值:
退出程序。