马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
Leave指令相当于move esp,ebp和pop ebp。 Enter SRC1,SRC2 也不复杂,只是不了解的话动态调试起来会很晕,Enter作了下面的事。 push ebp mov ebp,esp 现在栈顶上是上一个函数的基地址(就是上一个函数里的ebp,刚被压入;这个ebp很重要,习惯上进入一个子函数时,在call的时候自动压入call后面那条语句的epi,然后压入ebp来保存这个即将被改变的ebp值,然后把ebp指向当前的栈顶,这样调用这个函数领空里用到的所有本地局部变量用ebp就能找到了,所以这时ebp的值叫基址),用这个刚保存的基址,去栈的深处(也不深,也就是调用本函数的父函数的领空)把这个基址上SRC2-1个内容复制到栈顶往上的相同大小的空间里,最后再把ebp的值压上去。 上面这段话里莫名其妙的操作正好使用了SRC2那么大的栈空间。 sub esp,SRC1 就走了这四步,而除了第三步,剩下那三步大家都很熟悉(如果不熟这篇文章还不是你该看的时候)并且经常自己会写的。关键是这个第三步有什么用? 很多书上都叫第二参数是“嵌套层数”。Bingo,很正解。比如说主程序调用了proc1,而proc1调用了proc2,而proc2调用了proc3……总之突然procN要调用procM(当然N>M)中的一个局部变量,按照传统的调用子函数编写方法,这个访问实现起来简直无计可施。而如果你在proc1中用了个 enter SRC1,1 ,proc2中用了个 enter SRC1,2 ……这样就有个简单的方法了。假设procN中要被赋值的的变量是第一个,procM中要被读取的变量也是第一个,在要赋值时这样就可以: mov eax, DWORD PTR [ebp-4*M] ;把procM的基地址暂存到eax里 push DWORD PTR [eax-4*M-4] ;把按这个地址找到的procM中的变量入栈(草,寄存器不够使了) pop DWORD PTR [ebp-4*N-4] ;出栈,把值赋给procN中的那个饥渴的变量。 思考一下吧,很简单。可见要使用enter必须保证要访问的那个proc到现在所处的proc之间的一系列proc都在开头用了enter。当然,当SRC2为零时就无所谓了,而且比自己写push ebp 和 mov ebp , esp省劲的多。这样在一系列的enter作用下,到了procN时,栈中最上面的一部分是procN的领空,而这个领空的内容是这样的(从下往上):返回procN-1(调用procN的proc)的地址(call是epi应有的内容);刚进入procN的ebp,即procN-1的基址;大小为SRC2*4的一段指针列表(指向前面各个用了enter的proc的基址),这段类表的原理就是递推的方法,其中SRC2*4-4的内容是从procN-1的领空中复制的,为了能让procN+1也能用enter创建一断指针列表,列表最上面也要压入指向procN自己基址的指针,虽然在procN中,这最后一个没什么用处;然后是依据SRC1开辟的本地局部变量区域。其实这个结构有个名字,堆栈桢,貌似《编译原理》里会学到这个概念。利用堆栈桢,就可以方便的访问之前嵌套了自己的各辈函数的本地局部变量。总之,要使用这个功能,必须保证一路上都用了enter,否则即使procN用了enter,在复制procN-1的指针段的时候复制的东西根本不是指针,而是,比如procN-1的本地局部变量什么的(因为procN-1根本没有这么一段指针表啊)。 下面给段代码,可以动态调试一下便于理解 .486 ; create 32 bit code
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
.code
proc5 proc
enter 8,5
mov DWORD PTR [ebp-4-4*5],54
mov eax,DWORD PTR [ebp-4-4*0]
push DWORD PTR [eax-4-4*1]
pop DWORD PTR [ebp-4-4*5-4]
;这里把第二个本地变量设为proc1的值
leave
ret
proc5 endp
proc4 proc
enter 4,4
mov DWORD PTR [ebp-4-4*4],53
call proc5
leave
ret
proc4 endp
proc3 proc
enter 8,3
mov DWORD PTR [ebp-4-4*3],52
mov eax,DWORD PTR [ebp-4-4*1]
push DWORD PTR [eax-4-4*2]
pop DWORD PTR [ebp-4-4*3-4]
;这里把第二个本地变量设为proc2的值
call proc4
leave
ret
proc3 endp
proc2 proc
enter 4,2
mov DWORD PTR [ebp-4-4*2],51
call proc3
leave
ret
proc2 endp
proc1 proc
enter 4,1
mov DWORD PTR [ebp-4-4*1],50
call proc2
leave
ret
proc1 endp
start:
call proc1
ret
end start
|