请问mov EBP,ESP ESP怎么找呀

如题所述

sub esp,1 在堆栈中留出局部变量的空间

看看下面的文章对你的汇编语言一定有帮助!

汇编中参数的传递和堆栈修正

在 Win32汇编中,我们经常要和 Api 打交道,另外也会常常使用自己编制的类似于 Api 的带参数的子程序,本文要讲述的是在子程序调用的过程中进行参数传递的概念和分析。一般在程序中,参数的传递是通过堆栈进行的,也就是说,调用者把要传递给子程序(或者被调用者)的参数压入堆栈,子程序在堆栈取出相应的值再使用,比如说,如果你要调用 SubRouting(Var1,Var2,Var3),编译后的最终代码可能是

push Var3
push Var2
push Var1
call SubRouting
add esp,12

也就是说,调用者首先把参数压入堆栈,然后调用子程序,在完成后,由于堆栈中先前压入的数不再有用,调用者或者被调用者必须有一方把堆栈指针修正到调用前的状态。参数是最右边的先入堆栈还是最左边的先入堆栈、还有由调用者还是被调用者来修正堆栈都必须有个约定,不然就会产生不正确的结果,这就是我在前面使用“可能”这两个字的原因:各种语言中调用子程序的约定是不同的,它们的不同点见下表:

C SysCall StdCall Basic Fortran Pascal
参数从左到右 是 是 是
参数从右到左 是 是 是
调用者清除堆栈 是
允许使用:VARARG 是 是 是

VARARG 表示参数的个数可以是不确定的,有一个例子就是 C 中的 printf 语句,在上表中,StdCall 的定义有个要说明的地方,就是如果 StdCall 使用 :VARARG 时,是由调用者清除堆栈的,而在没有:VARARG时是由被调用者清除堆栈的。
在 Win32 汇编中,约定使用 StdCall 方式,所以我们要在程序开始的时候使用 .model stdcall 语句。也就是说,在 API 或子程序中,最右边的参数先入堆栈,然后子程序在返回的时候负责校正堆栈,举例说明,如果我们要调用 MessageBox 这个 API,因为它的定义是 MessageBox(hWnd,lpText,lpCaption,UType) 所以在程序中要这样使用:

push MB_OK
push offset szCaption
push offset szText
push hWnd
call MessageBox
...

我们不必在 API 返回的时候加上一句 add sp,4*4 来修正堆栈,因为这已经由 MessageBox 这个子程序做了。在 Windows API 中,唯一一个特殊的 API 是 wsprintf,这个 API 是 C 约定的,它的定义是 wsprintf(lpOut,lpFormat,Var1,Var2...),所以在使用时就要:

push 1111
push 2222
push 3333
push offset szFormat
push offset szOut
call wsprintf
add esp,4*5

下面要讲的是子程序如何存取参数,因为缺省对堆栈操作的寄存器有 ESP 和 EBP,而 ESP是堆栈指针,无法暂借使用,所以一般使用 EBP 来存取堆栈,假定在一个调用中有两个参数,而且在 push 第一个参数前的堆栈指针 ESP 为 X,那么压入两个参数后的 ESP 为 X-8,程序开始执行 call 指令,call 指令把返回地址压入堆栈,这时候 ESP 为 X-C,这时已经在子程序中了,我们可以开始使用 EBP 来存取参数了,但为了在返回时恢复 EBP 的值,我们还是再需要一句 push ebp 来先保存 EBP 的值,这时 ESP 为 X-10,再执行一句 mov ebp,esp,根据右图可以看出,实际上这时候 [ebp + 8] 就是参数1,[ebp + c]就是参数2。另外,局部变量也是定义在堆栈中的,它们的位置一般放在 push ebp 保存的 EBP 数值的后面,局部变量1、2对应的地址分别是 [ebp-4]、[ebp-8],下面是一个典型的子程序,可以完成第一个参数减去第二个参数,它的定义是:

MyProc proto Var1,Var2 ;有两个参数
local lVar1,lVar2 ;有两个局部变量

注意,这里的两个 local 变量实际上没有被用到,只是为了演示用,具体实现的代码是:

MyProc proc

push ebp
mov ebp,esp

sub esp,8

mov eax,dword ptr [ebp + 8]
sub eax,dword ptr [ebp + c]

add esp,8

pop ebp
ret 8

MyProc endp

现在对这个子程序分析一下,push ebp/mov ebp,esp 是例行的保存和设置 EBP 的代码,sub esp,8 在堆栈中留出两个局部变量的空间,mov /add 语句完成相加,add esp,8 修正两个局部变量使用的堆栈,ret 8 修正两个参数使用的堆栈,相当于 ret / add esp,8 两句代码的效果。可以看出,这是一个标准的 Stdcall 约定的子程序,使用时最后一个参数先入堆栈,返回时由子程序进行堆栈修正。当然,这个子程序为了演示执行过程,使用了手工保存 ebp 并设置局部变量的方法,实际上,386 处理器有两条专用的指令是完成这个功能用的,那就是 Enter 和 Leave,Enter 语句的作用就是 push ebp/mov ebp,esp/sub esp,xxx,这个 xxx 就是 Enter 的,Leave 则完成 add esp,xxx/pop ebp 的功能,所以上面的程序可以改成:

MyPorc proc
enter 8,0

mov eax,dword ptr [ebp + 8]
sub eax,dword ptr [ebp + c]

leave
ret 8
MyProc endp

好了,说到这儿,参数传递的原理也应该将清楚了,还要最后说的是,在使用 Masm32 编 Win32 汇编程序的时候,我们并不需要记住 [ebp + xx] 等麻烦的地址,或自己计算局部变量需要预留的堆栈空间,还有在 ret 时计算要加上的数值,Masm32 的宏指令都已经把这些做好了,如在 Masm32 中,上面的程序只要写成为:

MyProc proc Var1,Var2
local lVar1,lVar2

mov eax,Var1
sub eax,Var2
ret

MyProc endp

编译器会自动的在 mov eax,Var1 前面插上一句 Enter 语句,它的参数会根据 local 定义的局部变量的多少自动指定,在 ret 前会自动加上一句 Leave,同样,编译器会根据参数的多少把 ret 替换成 ret xxx,把 mov eax,Var1 换成 mov eax,dword ptr [ebp + 8] 等等。

最后是使用 Masm32 的 invoke 宏指令,在前面可以看到,调用带参数的子程序时,我们需要用 push 把参数压入堆栈,如果不小心把参数个数搞错了,就会使堆栈不平衡,从而使程序从堆栈中取出错误的返回地址引起不可预料的后果,所以有必要有一条语句来完成自动检验的任务,invoke 就是这样的语句,实际上,它是自动 push 所有参数,检测参数个数、类型是否正确,并使用 call 来调用的一个宏指令,对于上面的 push/push/call MyProc 的指令,可以用一条指令完成就是:

invoke MyProc,Var1,Var2

当然,当程序编译好以后你去看机器码会发现它被正确地换成了同样的 push/push/call 指令。但是,在使用 invoke 之前,为了让它进行正确的参数检验,你需要对函数进行申明,就象在 C 中一样,申明的语句是:

MyProc proto :DWORD,:DWORD

语句中 proto 是关键字,表示申明,:DWORD 表示参数的类型是 double word 类型的,有几个就表示有几个参数,在 Win32 中参数都是 double word 型的,申明语句要写在 invoke 之前,所以我们一般把它包括在 include 文件中,好了,综合一下,在 Masm32 中使用一个带参数的子程序或者 Api ,我们只需用:

...
MyProc proto :dword,:dword
...
.data
x dd ?
y dd ?
dwResult dd ?
...
mov x,1
mov y,2
invoke MyProc x,y
mov dwResult,eax
温馨提示:内容为网友见解,仅供参考
无其他回答

请问mov EBP,ESP ESP怎么找呀
我们可以开始使用 EBP 来存取参数了,但为了在返回时恢复 EBP 的值,我们还是再需要一句 push ebp 来先保存 EBP 的值,这时 ESP 为 X-10,再执行一句 mov ebp,esp,根据右图可以看出,实际上这时候 [ebp + 8] 就是参数1,[ebp + c]就是参数2。

请教如何查看dll中有哪些函数
1。先打开需要分析的DLL,然后通过菜单功能-》出口来找到需要分析的函数,双击就可以了。它可以直接定位到该函数。2。看准该函数的入口,一般函数是以以下代码作为入口点的。push ebp mov ebp, esp ...3。然后往下找到该函数的出口,一般函数出口有以下语句。...ret xxxx;\/\/其中xxxx就是函...

关于 函数返回值储存和栈的问题
void __declspec(naked) fun( int a, int b ){ __asm { push ebp ;保存调用函数的栈顶 mov ebp, esp ;fun的栈顶保存在ebp里 sub esp, 10h ;减出来的这段空间就用来存局部变量,这里一共16字节的空间 mov [ebp-4], 1 ;把这16字节用作4个32位存储空间,存入4个int mov...

MOV DWORD PTR FS:[EAX],ESP
MOV是一个赋值语句,这句话的意思是:将EBP寄存器的值减1CH,然后在SS(堆栈段)寻址,找到EBP这个地址的值,然后将这个值转换成 DWORD类型,也就是32位,取4字节,然后存在EAX这个寄存器里。

栈帧(ebp与esp)
首先执行add esp, 8指令,将栈顶去除八个字节,这八个字节用于存储FunAdd的入栈参数。由于编译器默认采用的__cdecl约定,因此由调用函数main来清理入栈的函数参数。EBP-0Ch地址存储的是iRes,从第二步中可知,将返回结果存储在EAX,然后使用mov dword ptr [ebp-0Ch],eax将返回结果存储到iRes中。

esp寄存器到底指在什么位置?
ESP指向的是内存单元不是一条线,可以假设ESP每个值所指向的是一个字节的内存单元这样想就简单了,新栈帧底部就是ESP当前所指向的值,栈顶是相对于还没有使用的栈,新的栈帧底部是相对己经使用的栈。

关于C++中函数调用的执行过程
1、参数传递:将函数参数压栈:mov eax,dword ptr [n] ;(n为参数变元)。2、操作:将帧指针压入栈中:push ebp;使得帧指针等于栈指针:mov ebp,esp;使栈指针自减,自减得到的内存地址应当能够(足够)用来存储被调用函数的本地状态:sub esp,0CCh。3、传入保存状态:push ebx ;保存ebx...

push ebp mov ebp,esp sub esp,4C mov dword ptr ss:[ebp-4],0 push...
]; 一般也是作为函数的静态参数使用。函数调用部分就不说了,给你讲下函数调用完后返回的操作 1,mov esp ebp; 这就是步骤2的恢复,使得esp指向步骤二的栈顶;2, pop ebp; 这就是把开始保存在堆栈中的ebp 恢复到ebp里面;此时esp <- esp + 4;就是恢复步骤一的状态,此次函数调用完毕;

esp定律广义ESP定律
在程序执行过程中,我们遇到访问异常,但只需忽略此异常继续执行。在经过一系列操作后,我们达到关键指令集,如 'mov ebp,esp',这标志着程序执行已抵达目标位置。利用此方法,我们能够准确定位到程序的重要部分,从而进行深入的调试和分析。综上所述,ESP 定律的广义应用在于通过巧妙设置硬件断点,实现对...

汇编语言寄存器都叫什么?
Mov ebx, [eax+edx*4+300] Mov ax, [esp] 由于32位寻址方式能使用所有的通用寄存器,所以,和该有效地址相组合的段寄存器也就有新的规定,具体 规定如下: 1. 地址中寄存器的书写顺序决定该寄存器是基址寄存器还是变址寄存器。 如:[ebx+ebp]中的ebx是基址寄存器,ebp是变址寄存器,而[ebp+ebx]中的ebp是基址寄存...

相似回答