博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用MASM09 - Win32汇编语言017
阅读量:6333 次
发布时间:2019-06-22

本文共 2494 字,大约阅读时间需要 8 分钟。

使用MASM09

 

让编程改变世界

Change the world by program


 

局部变量的使用

  [codesyntax lang="asm"]
TestProc    proc   ;名为TestProc的子程序	local        @loc1:dword, @loc2:word	local        @loc3:byte    ;用local语句定义了3个变量	mov eax, @loc1    ;对应类型进行存储,然后返回	mov ax, @loc2	mov al, @loc3	retTestProc    endp
[/codesyntax]   我们来看看它反汇编之后是什么样子的: [codesyntax lang="asm"]
:00401000   55        push ebp:00401001   8BEC      mov ebp, esp:00401003   83C4F8    add esp, FFFFFFF8:00401006   8B45FC    mov eax, dword ptr [ebp-04]:00401009   668B45FA  mov ax, word ptr [ebp-06]:0040100D   8A45F9    mov al, byte ptr [ebp-07]:00401010   C9        leave:00401011   C3        ret
[/codesyntax]   可以看到,反汇编后的指令比源程序多了前后两段指令,它们是: [codesyntax lang="asm"]
:00401000 55          push ebp:00401001 8BEC        mov ebp, esp:00401003 83C4F8      add esp, FFFFFFF8:00401010 C9          leave
[/codesyntax]   这些就是使用局部变量所必需的指令,分别用于局部变量的准备工作和扫尾工作。   【分析过程】 当调用者执行了call TestProc指令后,CPU把返回的地址(当前地址)压入堆栈,再转移(jmp)到子程序执行。 esp在程序的执行过程中可能随时用到,不可能用esp来随时存取局部变量,ebp寄存器(可以理解为小三)是以堆栈段为默认数据段的,所以,可以用ebp做指针指向堆栈替代esp。 于是,在初始化前,先用一句push ebp指令把原来的dbp保存起来,然后把esp的值放到ebp中。 (介绍局部变量怎么腾出空间的)再后面就是堆栈中预留空间了,由于堆栈是向下增长的。所以要在esp中加一个负值,FFFFFFF8就是-8。   我们来考虑另一个问题:一个dword加一个word加一个byte不是7吗,为什么刚刚我们在堆栈为局部变量让出了8个字节的空间呢? 这是因为在80386处理器中,以dword (32位)为界对齐时存取内存速度最快,所以MASM宁可浪费一个字节,执行了这3句指令后,初始化完成,就可以进行正常的操作了,从指令中可以看出局部变量在堆栈中的位置排列。   在程序退出的时候,必须把正确的esp设置回去,否则,ret指令会从堆栈中取出错误的地址返回,看程序可以发现,ebp就是正确的esp值。 因为子程序开始的时候已经有一句mov ebp,esp,所以要返回的时候只要先mov esp,ebp,然后再pop ebp,堆栈就是正确的了。   在80386指令集中有一条指令可以在一句中实现这些功能,就是leave指令,所以,编译器在ret指令之前只使用了一句leave指令。 明白了局部变量使用的原理,就很容易理解使用时的注意点:ebp寄存器是关键(第一次听说小三是关键)   因为它起到保存原始esp的作用,并随时用做存取局部变量的指针基址,所以在任何时刻,不要尝试把ebp用于别的用途,否则会带来意想不到的后果。(在任何时候不要做对不起小三的事情,不然后果很严重 T_T)  

闲言碎语

  Win32汇编中局部变量的使用方法可以解释一个很有趣的现象:在DOS汇编的时候,如果在子程序中的push指令和pop指令不配对,那么返回的时候ret指令从堆栈里得到的肯定是错误的返回地址,程序也就死掉了。 但在Win32汇编中,push指令和pop指令不配对可能在逻辑上产生错误,却不会影响子程序正常返回,原因就是在返回的时候esp不是靠相同数量的push和pop指令来保持一致的,而是靠leave指令从保存在ebp中的原始值中取回来的。 也就是说,即使把esp改得一塌糊涂也不会影响到子程序的返回,当然,窍门就在ebp,把ebp改掉,程序就玩完了!  

局部变量的初始化值

  显然,局部变量是无法在定义的时候指定初始化值的,因为local伪指令只是简单地把空间给留出来,那么开始使用时它里面是什么值呢? 和全局变量不一样,局部变量的初始值是随机的,是其他子程序执行后在堆栈里留下的垃圾(因为我们知道,腾出空间只是改变栈指针esp)。 所以,对局部变量的值一定要初始化,特别是定义为结构后当参数传递给API函数的时候。   在API函数使用的大量数据结构中,往往用0做默认值,如果用局部变量定义数据结构,初始化时只定义了其中的一些字段,那么其余字段的当前值可以是编程者预想不到的数值,传给API函数后,执行的结果可能是意想不到的,这是初学者很容易忽略的一个问题。 所以最好的办法是:在赋值前首先将整个数据结构填0,然后再初始化要用的字段,这样其余的字段就不必一个个地去填0了,RtlZeroMemory这个API函数就是实现填0的功能的。 [buy]   [/buy] [Downlink href='http://urlxf.qq.com/?qUVr227']视频下载[/Downlink]

转载于:https://www.cnblogs.com/LoveFishC/archive/2011/10/17/3847136.html

你可能感兴趣的文章
Ext.form.field.Number numberfield
查看>>
Linux文件夹分析
查看>>
解决部分月份绩效无法显示的问题:timestamp\union al\autocommit等的用法
查看>>
nginx 域名跳转 Nginx跳转自动到带www域名规则配置、nginx多域名向主域名跳转
查看>>
man openstack >>1.txt
查看>>
linux几大服务器版本大比拼
查看>>
在BT5系统中安装postgresQL
查看>>
Can't connect to MySQL server on 'localhost'
查看>>
【Magedu】Week01
查看>>
写给MongoDB开发者的50条建议Tip25
查看>>
PostgreSQL学习手册(四) 常用数据类型
查看>>
为什么要让带宽制约云计算发展
查看>>
[iOS Animation]-CALayer 绘图效率
查看>>
2012-8-5
查看>>
VS中ProjectDir的值以及$(ProjectDir)../的含义
查看>>
我的友情链接
查看>>
PHP实现排序算法
查看>>
Business Contact Mnanager for Outlook2010
查看>>
9种用户体验设计的状态是必须知道的(五)
查看>>
解决WIN7下组播问题
查看>>