实验4 汇编应用编程和c语言程序反汇编分析
一、实验结论
1、实验任务1
本程序的设计思路为顺序扫描字符串1次,每次扫描时将字符写入al,将需要的颜色和辅助信息写入ah,然后将ax写入es:[bx]。随后改变ah的值,将ax写入es:[bx+160],即下一行处。然后再次改变ah的值,将ax写入es:[bx+320],即下两行处。随后开始下一个字符的处理。这样,完整扫描字符串一次后,即可将其以三种色彩显示在屏幕上的三行。其中,由于需要将长度为16个字符的字符串显示在屏幕中间,即显示在第11、12、13行的第32-47个字符(均以0为起始),故显存偏移地址的起始值为11*160+32*2=1824。
代码如下:
1 assume cs:code, ds:data 2 3 data segment 4 db 'welcome to masm!' 5 data ends 6 7 code segment 8 start: mov ax,data 9 mov ds,ax 10 mov ax,0b800h 11 mov es,ax 12 mov si,0 13 mov bx,1824 14 mov cx,16 15 16 s: mov ah,2h; 17 mov al,[si] 18 mov es:[bx],ax 19 mov ah,24h 20 mov es:[bx+160],ax 21 mov ah,71h 22 mov es:[bx+320],ax 23 inc si 24 add bx,2 25 loop s 26 27 mov ah,4ch 28 int 21h 29 code ends 30 31 end start
运行结果如下:
2、实验任务2
源程序如下:
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 12 mov al, 2 13 call 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 26 mov di, 0 27 s: mov cl, [si] 28 mov ch, 0 29 jcxz over 30 mov ch, al 31 mov es:[di], cx 32 inc si 33 add di, 2 34 jmp s 35 36 over: pop di 37 pop si 38 pop cx 39 pop bx 40 ret 41 42 code ends 43 end start
运行结果如下:
将line3改为: str db 'another try', 0;将line12改为:mov al, 4;再次汇编、链接,运行结果如下:
3、实验任务3
①子任务1
汇编、链接task3.asm,并使用debug进入调试。使用u命令进行反汇编,可见应执行至076C:000E。使用g E命令执行至076C:000E,如下图:
结合程序,由DI=0006可知,应查看DS:0002到DS:0005中的数据。使用d 2 5命令查看,如下图:
可见,程序成功实现了将数字转换为字符串的功能。
②子任务2
修改后源程序如下:
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 14 call num2str 15 16 mov si, offset str 17 mov al, 2 18 call printStr 19 20 mov ah, 4ch 21 int 21h 22 23 num2str: 24 push ax 25 push bx 26 push cx 27 push dx 28 29 mov cx, 0 30 mov bl, 10 31 s1: 32 div bl 33 inc cx 34 mov dl, ah 35 push dx 36 mov ah, 0 37 cmp al, 0 38 jne s1 39 s2: 40 pop dx 41 or dl, 30h 42 mov [di], dl 43 inc di 44 loop s2 45 46 mov byte ptr [di], 0 47 pop dx 48 pop cx 49 pop bx 50 pop ax 51 52 ret 53 54 printStr: 55 push bx 56 push cx 57 push si 58 push di 59 60 mov bx, 0b800H 61 mov es, bx 62 mov di, 0 63 s: mov cl, [si] 64 mov ch, 0 65 jcxz over 66 mov ch, al 67 mov es:[di], cx 68 inc si 69 add di, 2 70 jmp s 71 72 over: pop di 73 pop si 74 pop cx 75 pop bx 76 ret 77 78 code ends 79 end start
其中,line16、line17的功能是为printStr子程序提供必要的输入信息。line46的功能为在转换完毕的字符串后添加0,使其封闭,符合printStr子程序的要求。printStr子程序本身与任务2中一致。
运行结果如下:
改变数据,再次测试,如下图:
可见,程序实现了所需的功能。
4、实验任务4
程序源代码如下:
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
运行结果如下:
line12-19实现的功能是?
其功能为使用21号中断的1号子功能,从键盘读取输入字符,并写入内存中。同时,将输入字符与'#'比较,若相同则退出循环进入下一个阶段,否则循环继续执行。
line21-27实现的功能是?
line21将si的值写入cx,作为循环次数。line22将si的值置为0,从头开始读取字符串。line23-27使用循环结构,利用21号中断的2号子功能,将字符输出在屏幕上,循环执行直到字符串输出完毕。
5、实验任务5
程序源代码如下:
1 #include <stdio.h> 2 3 int sum(int, int); 4 5 int main() { 6 int a = 2, b = 7, c; 7 c = sum(a, b); 8 return 0; 9 } 10 11 int sum(int x, int y) { 12 return (x + y); 13 }
反汇编结果如下:
#include <stdio.h> int sum(int, int); int main() { 00E11700 push ebp 00E11701 mov ebp,esp 00E11703 sub esp,0E4h 00E11709 push ebx 00E1170A push esi 00E1170B push edi 00E1170C lea edi,[ebp-0E4h] 00E11712 mov ecx,39h 00E11717 mov eax,0CCCCCCCCh 00E1171C rep stos dword ptr es:[edi] 00E1171E mov ecx,offset _A6FD8219_test@c (0E1C003h) 00E11723 call @__CheckForDebuggerJustMyCode@4 (0E1120Dh) int a = 2, b = 7, c; 00E11728 mov dword ptr [a],2 00E1172F mov dword ptr [b],7 c = sum(a, b); 00E11736 mov eax,dword ptr [b] 00E11739 push eax 00E1173A mov ecx,dword ptr [a] 00E1173D push ecx 00E1173E call _sum (0E110C8h) 00E11743 add esp,8 00E11746 mov dword ptr [c],eax return 0; 00E11749 xor eax,eax } 00E1174B pop edi 00E1174C pop esi 00E1174D pop ebx 00E1174E add esp,0E4h 00E11754 cmp ebp,esp 00E11756 call __RTC_CheckEsp (0E11217h) 00E1175B mov esp,ebp 00E1175D pop ebp 00E1175E ret int sum(int x, int y) { 00E11780 push ebp 00E11781 mov ebp,esp 00E11783 sub esp,0C0h 00E11789 push ebx 00E1178A push esi 00E1178B push edi 00E1178C lea edi,[ebp-0C0h] 00E11792 mov ecx,30h 00E11797 mov eax,0CCCCCCCCh 00E1179C rep stos dword ptr es:[edi] 00E1179E mov ecx,offset _A6FD8219_test@c (0E1C003h) 00E117A3 call @__CheckForDebuggerJustMyCode@4 (0E1120Dh) return (x + y); 00E117A8 mov eax,dword ptr [x] 00E117AB add eax,dword ptr [y] } 00E117AE pop edi 00E117AF pop esi 00E117B0 pop ebx 00E117B1 add esp,0C0h 00E117B7 cmp ebp,esp 00E117B9 call __RTC_CheckEsp (0E11217h) 00E117BE mov esp,ebp 00E117C0 pop ebp 00E117C1 ret
可见,从汇编的角度来说,高级语言中的参数传递是通过栈实现的。返回值的返回通过通用寄存器eax实现。具有多个参数时,入栈顺序为自右向左入栈。函数调用栈使用EBP、ESP寄存器记录,入栈顺序为:实参N~1→主调函数返回地址→主调函数帧基指针EBP→被调函数局部变量1~N。
二、实验总结
X86平台带有中断机制,其中21号中断是DOS系统中断,提供了大量可在汇编语言编程中调用的功能。使用时,在AH寄存器中写入需要使用的功能号,再执行INT 21H命令。目前在实验中使用过的功能包括1号(从键盘读入字符)、2号(在屏幕上显示单个字符)、4C号(带返回值返回)。需要注意的是,有资料指出使用2号功能时除显示字符外还会将该字符写入AL寄存器,使用时需要注意。