【8086汇编】-- call 和 ret 指令 的应用和本质

2018-10-07  本文已影响0人  fanglaoda

在高级语言开发中会把一些功能封装成方法然后调用,下面我们来用汇编实现一个打印hello world的方法

先实现打印的功能

assume cs:code    ds:data   ;assume是伪指令 作用是申明本程序中cs为code ds为data


data segment                ;segment .... ends 定义一个段

   string db 'Hello World!$'  
    
data ends


code segment 
    
start:                      ;start 伪指令地址的标号

      
      mov ax, data
      mov ds, ax
          
       ;ds + dx 才能给打印函数传参
      mov dx,offset string
      
      ;当ah == 9h 的时候 调用系统的print函数 
      mov ah,9h  
      int 21h    
       
      ;当ah == 4ch 的时候 调用系统的退出函数
      mov ax, 4c00h
      int 21h

      
code ends

end start

发现是正常输出的

15373275595442.jpg

如果想把打印封装成一个函数,在汇编里面通过callret指令配合使用的

assume cs:code    ds:data    ss:stack
   

stack segment
       db 10 dup(0) ;申请10个字节,并且默认都是0 
stack ends       

data segment

   string db 'Hello World!$'  
    
data ends



code segment 
    
start:
      
      mov ax, data
      mov ds, ax 
      
      mov ax , stack
      mov ss ,ax
      mov sp , 10h
      
      call printFunc    
                      
      mov ax , 1122h                
      ;当ah == 4ch 的时候 调用系统的退出函数
      mov ax, 4c00h
      int 21h

printFunc: ; printFunc为伪指令,值为下面一行的地址
      
      ;ds + dx 才能给打印函数传参
      mov dx,offset string
      
      ;当ah == 9h 的时候 调用系统的print函数 
      mov ah,9h  
      int 21h    
             
       ret      
      
code ends

end start

通过callret指令就可以实现一个方法调用了,相信你已经发现了下面的代码增加了一个stack段,这就涉及到callret指令的本质了

callret指令的本质

call指令,相当于

  1. push IP // 具体应该说是call下面一行的ip
  2. jmp 标号处

ret指令,相当于

pop IP

call指令

我们来查看程序执行过程来证明下

点击下图执行代码

15373376970904.jpg

先来看看call代码的下一行的对应的cs:ip是多少,看下图

15373385706754.jpg

标号printFunc的cs:ip如图

15373387353222.jpg

由上图可以知道30那行代码的cs:ip为0712:0010,看下此时栈的内容

15373382064902.jpg

由于代码还没到那行,此时的栈为空栈所以此时的sp000A,当代码执行到call printFunc时,之前说到call指令,那么过了28行代码应该有入栈操作

15373384702103.jpg

我们发现了2个事情:

  1. 确实将30行的ip == 0010 入栈了;
  2. 代码通过cs:ip即 0712:0018 确实到了标号处

从而验证了call指令的正确性

ret指令

还是上面的代码当执行到ret指令时

15373390067711.jpg

此时的cs:ip为0712:001F,安照对ret指令的描述,那么此时应该是出栈操作并赋值给ip,我们过掉44行,发现代码执行到该行,如图

15373391951998.jpg

和我们猜想的一样,再看看此时栈中的情况

15373392362966.jpg

发现栈定的指针已经+2了,这也符合我们的逾期,至此,已经证明了ret指令的结论

自己实现函数调用

既然我们清楚了callret的本质,我们是可以不使用这2个指令也可以实现函数调用和返回的修改代码如下

assume cs:code    ds:data    ss:stack
   

stack segment
       db 10 dup(0)
stack ends       

data segment

   string db 'Hello World!$'  
    
data ends



code segment 
    
start:
      
      mov ax, data
      mov ds, ax 
      
      mov ax , stack
      mov ss ,ax
      
      mov sp , 10
      
      ;call printFunc
      push    0011h
      jmp    printFunc
                      
      mov ax , 1122h                
      ;当ah == 4ch 的时候 调用系统的退出函数
      mov ax, 4c00h
      int 21h

printFunc: ; printFunc为伪指令,值为下面一行的地址
      
      ;ds + dx 才能给打印函数传参
      mov dx,offset string
      
      ;当ah == 9h 的时候统的print函数  调用系
      mov ah,9h  
      int 21h    
             
       ;ret
       pop ax
       jmp ax      
      
code ends

end start

我将call printFunc换成了

 push    0011h // 0011h是 mov ax , 1122h 的ip
jmp    printFunc
      

ret换成了

 pop ax
 jmp ax  

同样实现了函数调用和返回

上一篇 下一篇

猜你喜欢

热点阅读