没有合适的资源?快使用搜索试试~ 我知道了~
首页Windows 32位汇编入门:Win32平台与MASM编程
Windows 32位汇编入门:Win32平台与MASM编程
需积分: 0 0 下载量 163 浏览量
更新于2024-06-30
收藏 459KB DOCX 举报
本资源主要围绕"Windows环境下32位汇编语言程序设计"展开,深入讲解了Windows系统与80x86系列处理器的相关知识以及在该平台上进行汇编编程的准备工作。章节一首先介绍了Win32的软硬件平台,涵盖了80x86系列处理器的历史发展、Windows操作系统的发展历程以及Wintel联盟(由微软和英特尔合作形成的联盟,推动了Windows与Intel处理器的紧密结合)的重要性。接着,章节一还概述了Windows的主要特色,如处理器工作模式、内存管理和特权保护,这些都是理解Win32编程基础的关键。 在准备编程环境部分,章节二详细指导了如何设置Win32可执行文件的开发流程,包括选择合适的编译器(如MASM、TASM和NASM),其中MASM因其易用性和兼容性被推荐。资源编译器的使用和所见即所得资源编辑器的介绍有助于创建程序资源。此外,make工具的使用也被详细讲解,包括其功能、nmake命令的运用以及描述文件的语法。获取资料方面,指出了Windows和Intel处理器相关资料的来源,强调了获取可靠资源的重要性。 在实际操作层面,章节三聚焦于使用MASM编写Win32汇编程序,如构造Win32程序的基本结构,包括Hello,world!程序的示例。这部分内容涉及数据段和代码段的定义,以及如何引用Windows库函数和链接所需的库文件。 这是一份全面的教程,适合对Windows环境下32位汇编语言编程感兴趣的读者,无论是初学者还是有一定经验的开发者,都能从中收获关于平台特性、工具选择和实际编程技巧的知识。
资源详情
资源推荐
![](https://csdnimg.cn/release/download_crawler_static/86345339/bge.jpg)
mov cx,1234h
cmp flag,1
jz @F
mov cx,1000h
@@:
…
loop @B
当用@@做标号时, 可以用@F 和@B 来引用它, @F 表示本条指令后的第一个@@标号,@B
表示本条指令前的第一个@@标号,程序中可以有多个@@标号,但@B 和@F 只寻找匹配最
近的一个。
不要在间隔太远的代码中使用@@标号,因为在以后的修改中,@@和@B,@F 中间可能会被无意
中插入一个新的@@,这样一来,@B 或@F 就会引用到错误的地方去,源程序中@@标号和跳转指令之
间的距离最好限制在编辑器能够显示的同一屏幕的范围内。
3.3.2 全局变量
1. 全局变量的定义
全局变量的作用域是整个程序,Win32 汇编的全局变量定义在 .data 或 .data?段内,可
以同时定义变量的类型和长度,格式是:
变量名 类型 初始值 1,初始值 2,……
变量名 类型 重复数量 dup (初始值 1,初始值 2,……)
MASM 中可以定义的变量类型相当多,具体如表 3-2 所示。
表 3-2 数据类型
名称
表示方式
缩写
长度(字节)
字节(byte)
Byte
db
1
字(word)
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
所有使用到变量类型的情况中,只有定义全局变量的时候类型才可以用缩写。
2. 全局变量的初始化值
全局变量在定义中既可以指定初值,也可以只用问号预留空间,在 .data?段中,只能用
问号预留空间,因为 .data?不能指定初始值,这里就有一个问题:既然可以用问号预留空间,
那么在实际运行的时候,这个未初始化的值是随机的还是确定的呢?答案是 0,所以用问号
指定的全局变量如果要以 0 为初始值的话,在程序中可以不必特地为它赋值。
![](https://csdnimg.cn/release/download_crawler_static/86345339/bgf.jpg)
3.3.3 局部变量
局部变量的作用域是单个子程序,在进入子程序的时候,通过修改堆栈指针 esp 来预
留出需要的空间,在用 ret 指令返回主程序之前,同样通过恢复 esp 丢弃这些空间,这些变
量就随之无效了。它的缺点就是因为空间是临时分配的,所以无法定义含有初始化值的变量,
对局部变量的初始化一般在子程序中由指令完成。
在 DOS 时代,低版本的宏汇编本来无所谓全局变量和局部变量,所有的变量都是定义
在数据段里面的,能被所有的子程序或主程序存取,就相当于现在所说的全局变量,用汇编
语言在堆栈中定义局部变量是很麻烦的一件事情。要和高级语言做混合编程的时候,程序员
往往很痛苦地在边上准备一张表,表上的内容是局部变量名和 ebp 指针的位置关系。
1. 局部变量的定义
MASM 用 local 伪指令提供了对局部变量的支持。定义的格式是:
local 变量名 1[[重复数量]][:类型],变量名 2[[重复数量]][:类型]……
local 伪指令必须紧接在子程序定义的伪指令 proc 后、其他指令开始前,这是因为局部变量
的数目必须在子程序开始的时候就确定下来,在一个 local 语句定义不下的时候,可以有多
个 local 语句,语法中的数据类型不能用表 3-2 中的缩写,如果要定义数据结构,可以用数
据结构的名称当做类型。Win32 汇编默认的类型是 dword,如果定义 dword 类型的局部变
量,则类型可以省略。当定义数组的时候,可以 [] 括号括起来,不能使用定义全局变量的
dup 伪指令。局部变量不能和已定义的全局变量同名。局部变量的作用域是当前的子程序,
所以在不同的子程序中可以有同名的局部变量。
这里有几个定义局部变量的例子:
local loc1[1024]:byte ;例 1
local loc2 ;例 2
local loc3:WNDCLASS ;例 3
下面是局部变量使用的一个典型的例子:
TestProc proc
local @loc1:dword,@loc2:word
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]
![](https://csdnimg.cn/release/download_crawler_static/86345339/bg10.jpg)
: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 指令集中有一条指令可以在一句中实现 mov esp,ebp 和 pop ebp 的功能,
就是 leave 指令,所以,编译器在 ret 指令之前只使用了一句 leave 指令。
明白了局部变量使用的原理,就很容易理解使用时的注意点:ebp 寄存器是关键,它起
到保存原始 esp 的作用,并随时用做存取局部变量的指针基址,所以在任何时刻,不要尝
试把 ebp 用于别的用途,否则会带来意想不到的后果。
Win32 汇编中局部变量的使用方法可以解释一个很有趣的现象: 在 DOS 汇编的时候, 如果在子程序
中的 push 指令和 pop 指令不配对,那么返回的时候 ret 指令从堆栈里得到的肯定是错误的返回地址,程序
也就死掉了。但在 Win32 汇编中,push 指令和 pop 指令不配对可能在逻辑上产生错误,却不会影响子程
序正常返回, 原因就是在返回的时候 esp 不是靠相同数量的 push 和 pop 指令来保持一致的, 而是靠 leave
指令从保存在 ebp 中的原始值中取回来的,也就是说,即使把 esp 改得一塌糊涂也不会影响到子程序的
返回,当然,“窍门”就在 ebp,把 ebp 改掉,程序就玩完了!
2. 局部变量的初始化值
显然,局部变量是无法在定义的时候指定初始化值的,因为 local 伪指令只是简单地把
空间给留出来,那么开始使用时它里面是什么值呢?和全局变量不一样,局部变量的起始值
![](https://csdnimg.cn/release/download_crawler_static/86345339/bg11.jpg)
是随机的,是其他子程序执行后在堆栈里留下的垃圾。
在 API 函数使用的大量数据结构中,往往用 0 做默认值,如果用局部变量定义数据结构,初始化
时只定义了其中的部分字段, 那么剩余字段的当前值可能是编程者预想不到的数值, 传给 API 函数后,
执行的结果可能是意想不到的,这是初学者很容易忽略的一个问题。所以最好的办法是:在赋值前首先
将整个 数 据 结构 填 0 , 然后 再 初始 化要 用 的字 段 ,这 样其 余 的字 段就 不 必一 个 个地 去填 0 了 ,
RtlZeroMemory 这个 API 函数就是实现填 0 的功能的。
3.3.4 数据结构
数据结构实际上是由多个字段组成的数据“样板”,相当于一种自定义的数据类型,数
据结构中间的每一个字段可以是字节、字、双字、字符串或所有可能的数据类型。比如在 API
函数 RegisterClass 中要使用到一个叫做 WNDCLASS 的数据结构,Microsoft 的手册中是如下
定义的:
typedef struct _WNDCLASS {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
} WNDCLASS, *PWNDCLASS;
注意,这是 C 语言格式的,这个数据结构包含了 10 个字段,字段的名称是 style,
lpfnWndProc 和 cbClsExtra 等, 前面的 UINT 和 WNDPROC 等是这些字段的类型, 在汇编中,
数据结构的写法如下:
结构名 struct
字段 1 类型 ?
字段 2 类型 ?
……
结构名 ends
上面的 WNDCLASS 结构定义用汇编的格式来表示就是:
WNDCLASS struct
style DWORD ?
lpfnWndProc DWORD ?
cbClsExtra DWORD ?
cbWndExtra DWORD ?
hInstance DWORD ?
剩余83页未读,继续阅读
![zip](https://img-home.csdnimg.cn/images/20210720083736.png)
![rar](https://img-home.csdnimg.cn/images/20210720083606.png)
![rar](https://img-home.csdnimg.cn/images/20210720083606.png)
![-](https://csdnimg.cn/download_wenku/file_type_column_c1.png)
![-](https://csdnimg.cn/download_wenku/file_type_column_c1.png)
![-](https://csdnimg.cn/download_wenku/file_type_column_c1.png)
![-](https://csdnimg.cn/download_wenku/file_type_column_c1.png)
![-](https://csdnimg.cn/download_wenku/file_type_column_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
安全验证
文档复制为VIP权益,开通VIP直接复制
![](https://csdnimg.cn/release/wenkucmsfe/public/img/green-success.6a4acb44.png)