尽管这两条指令看起来很简单,但是很多初学者并不能完全领会这两条指令的本质。所以我把它们作为一个单独的章节来介绍,我认为这样做是可取的。
Call指令是将转移到指定的子程序处,它的操作数就是给定的地址。例如:
Call 401362 表示将转移到地址401362处,将调用401362处的子程序,一旦子程序调用完毕就返回到Call指令的下一条语句处。
在这种情况下,完成401362的子程序调用以后,则会返回到40124A地址处。

我们这个子程序起始地址是401362,那么哪里是子程序的结束呢?这里我们下面第一个出现的ret指令就是子程序的结束。OD中还可以写成retn。执行完ret指令以后,程序就会返回到call指令的下一条指令40124A处。

为了让子程序执行完ret指令后,知道要返回到哪里,返回地址将要存到堆栈当中。这个程序可能包含了成千上万个堆栈操作(push,pop等),在堆栈中添加或者删除了各种各样的值,但是当我们执行到ret指令的时候,栈顶存放的一般是子程序的返回地址。我们一直按F8不跟进call指令里面,直到遇到了ret指令停止。

因此,我们可以知道ret指令是子程序的结束,也就是说,如果我们call跟进的话,那么ret就能返回到call指令的下一条语句处。我这里补充一点,ret指令可不仅仅用于子程序的返回,例如:
PUSH 401256
RET
这里将401256压入堆栈。下面的ret指令会将401256当做子程序的返回地址,其实它并不是返回地址,但是执行ret指令后我们依然可以转移到401256地址处。这段代码和JMP 401256指令的功能是一样的。
网友评论