罗云彬win32汇编教程笔记 Win32汇编的全局变量与局部变量

Win32汇编的全局变量与局部变量

变量的值在程序运行中是需要改变的,所以它必须定义在可写的段内,如 .data和 .data?,或者在堆栈内。
按照定义的位置不同,MASM中的变量也分为全局变量和局部变量两种。


1.  全局变量的定义
全局变量的作用域是整个程序,Win32汇编的全局变量定义在 .data或 .data?段内,可以同时定义变量的类型和长度,格式是:

变量名      类型    初始值1, 初始值2,……
变量名      类型    重复数量 dup (初始值1,初始值2,……)
MASM中可以定义的变量类型相当多,具体如下:

名    称                                    表示方式      缩    写          长度(字节)
字节                                        byte              db                   1
字                                            word             dw                  2
双字(doubleword)           dword           dd                   4
三字(farword)                   fword            df                    6
四字(quadword)               qword           dq                   8
十字节BCD码(tenbyte)   tbyte              dt                   10
有符号字节(signbyte)      sbyte                                   1
有符号字(signword)         sword                                  2
有符号双字(signdword)   sdword                                4
单精度浮点数                         real4                                    4
双精度浮点数                         real8                                    8
10字节浮点数                        real10                                  10
所有使用到变量类型的情况中,只有定义全局变量的时候类型才可以用缩写

.data
wHour               dw       ?                         ;定义了一个未初始化的word类型变量,名称为wHour。
wMinute            dw      10                       ;定义了一个名为wMinute的word类型变量
_hWnd              dd       ?                         ;定义了一个双字类型的变量_hWnd
word_Buffer      dw      100 dup (1,2)    ;定义了一组字,以0001,0002,0001,0002,…的顺序在内存中重复100遍,一共是200个字
szBuffer             byte    1024 dup (?)     ;定义了一个1 024字节的缓冲区
szText                db       'Hello,world!'     ;定义了一个字符串,总共占用了12字节。两头的单引号是定界的符号,并不属于字符串中真正的内容

在byte类型变量的定义中,可以用引号定义字符串和数值定义的方法混用,假设要定义两个字符串“Hello,World! ”和“Hello again”,每个字符串后面跟回车和换行符,最后以一个0字符结尾,可以定义如下:
szText          db      'Hello,world!',0dh,0ah,'Hello again',0dh,0ah,0

 

2. 全局变量的初始化值

全局变量在定义中既可以指定初值,也可以只用问号预留空间,在 .data?段中,只能用问号预留空间,因为 .data?不能指定初始值,这里就有一个问题:
既然可以用问号预留空间,那么在实际运行的时候,这个未初始化的值是随机的还是确定的呢?
在全局变量中,这个值就是0,所以用问号指定的全局变量如果要以0为初始值的话,在程序中可以不必为它赋值。


3. 局部变量

局部变量的作用域是单个子程序,在进入子程序的时候,通过修改堆栈指针esp来预留出需要的空间,在用ret指令返回主程序之前,同样通过恢复esp丢弃这些空间,这些变量就随之无效了。
 临时变量的空间是临时分配的,所以无法定义含有初始化值的变量,对局部变量的初始化一般在子程序中由指令完成。

局部变量的初始化值就是需要手动初始化.

 

局部变量的定义
MASM用local伪指令提供了对局部变量的支持。定义的格式是:

local       变量名1[[重复数量]][:类型],变量名2[[重复数量]][:类型]……

1. local伪指令必须紧接在子程序定义的伪指令proc后、其他指令开始前,这是因为局部变量的数目必须在子程序开始的时候就确定下来,
2. 在一个local语句定义不下的时候,可以有多个local语句,语法中的数据类型不能用类型的缩写,
3. 如果要定义数据结构,可以用数据结构的名称当做类型。
4. Win32汇编默认的类型是dword,如果定义dword类型的局部变量,则类型可以省略。
5. 当定义数组的时候,可以 [] 括号括起来。
6. 不能使用定义全局变量的dup伪指令(因为局部变量没有默认的初始值, 需要手工初始化)。
7. 局部变量不能和已定义的全局变量同名。
8. 局部变量的作用域是当前的子程序,所以在不同的子程序中可以有同名的局部变量。

这里有几个定义局部变量的例子:
local      loc1[1024]:byte        ;定义了一个1 024字节长的局部变量loc1。
local      loc2                           ;定义了一个名为loc2的局部变量,类型是默认值dword
local      loc3:WNDCLASS   ;定义了一个WNDCLASS数据结构,名为loc3
下面是局部变量使用的一个典型的例子:
TestProc         proc
                 local  @loc1:dword,@loc2:word  ;定义局部变量 @loc1
                 local  @loc3:byte

                 mov    eax, @loc1
                 mov    ax, @loc2
                 mov    al, @loc3
                 ret    
TestProc         endp

这是一个名为TestProc的子程序,用local语句定义了3个变量,@loc1是dword类型,@loc2是word类型,@loc3是byte类型,在程序中分别有3句存取3个局部变量的指令,然后就返回了,
编译成可执行文件后,再把它反汇编就得到了以下指令:
: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

可以看到,反汇编后的指令比源程序多了前后两段指令,它们是:
:00401000 55                        push ebp
:00401001 8BEC                  mov ebp, esp
:00401003 83C4F8              add esp, FFFFFFF8
            …
:00401010 C9                       leave

这些就是使用局部变量所必需的指令,分别用于局部变量的准备工作和扫尾工作。执行了call指令后,CPU把返回的地址压入堆栈,再转移到子程序执行,
esp在程序的执行过程中可能随时用到,不可能用esp来随时存取局部变量,大家一定有印象,在介绍寄存器的时候提到过ebp寄存器也是以堆栈段为默认数据段的,
所以,可以用ebp做指针,于是,在初始化前,先用一句push ebp指令把原来的ebp保存起来,然后把esp的值放到ebp中,供存取局部变量做指针用,
再后面就是在堆栈中预留空间了,由于堆栈是向下增长的,所以要在esp中加一个负值,FFFFFFF8就是–8。慢着!一个dword加一个word加一个字节不是7吗,
为什么是8呢?这是因为在80386处理器中,以dword为界对齐时存取内存速度最快,所以MASM宁可浪费一个字节,执行了这3句指令后,初始化完成,
就可以进行正常的操作了,从指令中可以看出局部变量在堆栈中的位置排列,如表3.3所示。

表3.3  上例中局部变量排列的顺序
ebp偏移                内    容
ebp+4                   由call推入的返回地址
Ebp                       push ebp指令推入的原ebp值,然后新的ebp=现在的esp
ebp-4                    第一个局部变量
ebp-6                    第二个局部变量
ebp-7                    第三个局部变量
在程序退出的时候,必须把正确的esp设置回去,否则,ret指令会从堆栈中取出错误的地址返回,看程序可以发现,ebp就是正确的esp值,
因为子程序开始的时候已经有一句mov ebp,esp,所以要返回的时候只要先mov esp,ebp,然后再pop ebp,堆栈就是正确的了。

在80386指令集中有一条指令可以在一句中实现这个功能,就是leave指令,所以,编译器在ret指令之前只使用了一句leave指令。

明白了局部变量使用的原理,就很容易理解使用时的注意点:ebp寄存器是关键,它起到保存原始esp的作用,并随时用做存取局部变量的指针基址,所以在任何时刻,
不要尝试把ebp用于别的用途,否则会带来意想不到的后果。

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

 

------------罗云彬win32汇编教程 笔记

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
-------------------------- 我是汇编爱好者 QQ695367480 高手别找我了 我是菜鸟。 -------------------------- 内容简介: Windows环境下32位汇编语言是一种全新的编程语言。它使用与C++语言相同的API接口,不仅可以用来开发出大型的软件,而且是了解操作系统运行细节的最佳方式。本书从编写应用程序的角度,从“Hello World!”这个简单的例子开始到编写多线程、注册表和网络通信等复杂的程序,通过60多个实例逐渐深入Win32汇编语言的方方面面。本书作者罗云彬拥有十余年汇编语言编程经验,是汇编编程网站http://asm.yeah.net和汇编编程论坛http://win32asm.yeah.net的站长。本书是作者多年来编程工作的总结,适合于欲通过Win32汇编语言编写Windows程序的读者。 第1章 背景知识 1.1 Win32的软硬件平台(1) 1.1 Win32的软硬件平台(2) 1.2 Windows的特色 1.3 必须了解的东西(1) 1.3 必须了解的东西(2) 1.3 必须了解的东西(3) 1.3 必须了解的东西(4) 1.3 必须了解的东西(5) 第2章 准备编程环境 2.1 Win32可执行文件的开发过程 2.2 编译器和链接器(1) 2.2 编译器和链接器(2) 2.2 编译器和链接器(3) 2.3 创 建 资 源 2.4 make工具的用法(1) 2.4 make工具的用法(2) 2.5 获 取 资 料 2.6 构建编程环境 第3章 使用MASM 当搭建编译和对编译器的使用不再成为绊脚石的时候,初学者的问题往往集中在对Windows程序结构的迷惑上,消息驱动体系、窗口过程、与硬件隔绝的图形接口及资源文件等相对于DOS程序来说都是全新的内容,接下来的4章将深入讨论这些内容,通过这几章,读者应该开始习惯以Windows的方式考虑问题了(脑海中的DOS逐渐远去...),这就是本书的初级篇: 3.1 Win32汇编源程序的结构(1) 3.1 Win32汇编源程序的结构(2) 3.1 Win32汇编源程序的结构(3) 3.2 调用API(1) 3.2 调用API(2) 3.2 调用API(3) 3.3 标号、变量和数据结构(1) 3.3 标号、变量和数据结构(2) 3.3 标号、变量和数据结构(3) 3.3 标号、变量和数据结构(4) 3.3 标号、变量和数据结构(5) 3.4 使用子程序 3.5 高 级 语 法(1) 3.5 高 级 语 法(2) 3.6 代 码 风 格(1) 3.6 代 码 风 格(2) 第4章 第一个窗口程序 4.1 开始了解窗口(1) 4.1 开始了解窗口(2) 4.1 开始了解窗口(3) 4.2 分析窗口程序(1) 4.2 分析窗口程序(2) 4.2 分析窗口程序(3) 4.2 分析窗口程序(4) 4.2 分析窗口程序(5) 4.3 窗口间的消息互发 4.4 实 验(1) 4.4 实 验(2) 4.4 实 验(3) 第5章 使用资源 5.1 菜单和加速键(1) 5.1 菜单和加速键(2) 5.1 菜单和加速键(3) 5.1 菜单和加速键(4) 5.1 菜单和加速键(5) 5.1 菜单和加速键(6) 5.1 菜单和加速键(7) 5.2 图标和光标(1) 5.2 图标和光标(2) 5.3 位 图 5.4 对 话 框(1) 5.4 对 话 框(2) 5.4 对 话 框(3) 5.4 对 话 框(4) 5.4 对 话 框(5) 5.4 对 话 框(6) 5.4 对 话 框(7) 5.4 对 话 框(8) 5.4 对 话 框(9) 5.4 对 话 框(10) 5.4 对 话 框(11) 5.5 字符串资源/5.6 版本信息资源(1) 5.6 版本信息资源(2) 5.7 二进制资源和自定义资源 第6章 定时器 6.1 定时器简介/6.2 定时器的使用(1) 6.2 定时器的使用(2) 6.3 取Windows时间 第7章 图形操作 Windows系统不像DOS系统,它的应用程序界面是规范化的,统一的界面来自大量的系统界面控件,学习这些控件就等于学习如何编写Windows界面,下面的界面篇中的两章将探讨这方面的内容: 7.1 GDI原理(1) 7.1 GDI原理(2) 7.1 GDI原理(3) 7
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值