实验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;再次汇编、链接,运行结果如下:

  line19-22, line36-39,这组对称使用的push、pop,这样用的目的是什么?
  bx、cx、si、di寄存器在子程序中需要使用,将会改变它们的值。为了保存现场,不使调用程序出现问题,故用栈保存这些寄存器的值,在开始操作前压入栈中,在返回前弹出。这样,这些寄存器的值在调用子程序之前和之后将保持不变。
  line31的功能是什么?
  line27将[si],即字符信息写入了cl。line30将al,即色彩信息写入了ch。此时,cx中的数据即为完整的显示单元。故line31将cx写入es:[di],即显存中。因此,line31的功能为将组合完毕的显示内容写入显存,显示在屏幕上。
 

 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寄存器,使用时需要注意。

posted @ 2020-12-16 21:47  SpeakLessPoiMore  阅读(144)  评论(1编辑  收藏  举报