1.标号&变量&数据结构
1.1 标号
格式1: 标号: 目的指令
格式2: 标号::目的指令
在WIN32编程使用的高版本MASM中,格式1的标号只在同一个子程序中唯一,所以不能用格式1的标号跳转到其他子程序。格式2的标号整个程序唯一,所以可以跳到其他子程序。但这种跳转就像在c语言中写goto一样,最好少用。
@@:,@B, @F是常用的简便方法,避免每一个小跳转都要起名。JMP @B会跳转到前一个@@,JMP @F会调转到后一个@@处。使用@@不应该距离太远,避免程序维护时在中间加入了新的@@导致出错。
1.2 全局变量
全局变量定义在.data段 或.data?段,变量类型如下表
名称 | 表示方式 | 缩写 | 长度(字节) |
字节 | byte | db | 1 |
字 | word | dw | 2 |
双字(double word) | dword | dd | 4 |
三字(far word) | fword | df | 6 |
四字(quad word) | qword | dq | 8 |
十字节BCD码(ten byte) | tbyte | dt | 10 |
有符号字节(sign byte) | sbyte |
| 1 |
有符号字(sign word) | sword |
| 2 |
有符号双字(sign dword) | sdword |
| 4 |
单精度浮点数 | real4 |
| 4 |
双精度浮点数 | real8 |
| 8 |
10字节浮点数 | real10 |
| 10 |
只有在定义全局变量的时候可以用缩写。
.data?段定义的变量不能在定义的时候指定初值,这个初值是固定的0,所以如果初值本来就要设置为0的话可以省略初始化的步骤。这和局部变量就不同了,局部变量一定要清零处理,否则可能出现不确定的异常。
1.3 局部变量
局部变量是存储在子程序的堆栈中的。进入程序时,修改栈顶指针esp留出存储局部变量所需要的空间,然后将局部变量入栈。子程序返回时,恢复esp,这段用于存储局部变量的栈就被弃用,这样就可以实现变量的无效化处理。
MASM提供了一个伪指令用于定义局部变量:
local 变量名1[[重复数量]][:类型],变量名2[重复数量][:类型]……
如:
local loc1[1024] :byte, loc 2:WNDCLASS
含有局部变量的子程序调试时可以看到这样的特征:
push ebp
mov ebp,esp
sub esp,xxxx
……
leave
这里的leave就等于mov esp,ebp和pop ebp,等于恢复了子程序执行前ebp和esp的状态。
2.数据结构&变量使用&子程序
2.1 数据结构
类似于C语言中的结构体,是自定义的数据类型,作为一种数据的模板。定义的格式如下:
结构名 struct
字段1 类型 ?
字段2 类型 ?
……
结构名 ends
在数据段中使用数据结构定义的方法:
.data?
变量名 结构名 <>
.data
变量名 结构名 <初值1,初值2,……>
要使用结构体成员有三种方法:
(1)变量名.成员名
(2)[指针+结构名.成员名] 相当于基地址加偏移
(3)mov esi, offset 变量名
assume esi:ptr 结构名
[esi].成员名
不用的时候assume esi:nothing
2.2 变量使用
2.2.1 以不同的类型访问变量
类似于C语言中的强制类型转换。但在WIN32汇编中不会改变数据类型,只是在执行指令时不检测数据的长度。使用的方法:
类型 ptr 变量名
如:
mov ax,word ptr szBuffer
这样就会把szBuffer起始地址开始的两个字节存入ax中
应当注意的是这种方式访问数据不会检测长度是否溢出了,所以有可能会读到别的变量的数据,如果只是想进行字节的扩展,可以使用movzx,movsx指令。
2.2.2 变量的尺寸和数量
sizeof 伪指令可以取得变量、数据类型或数据结构以字节为单位的长度,lengthof 可以取得变量中数据的项数。
应该注意的是设计字符串的长度最好用lstrlen计算,因为MASM中变量定义只识别一行,如:
szHello db 'Hello', 0ah, 0ah
db 'World' ,0
MASM只会识别第一行为szHello,第二行识别为没有命名的另一个变量,所以对szHello使用sizeof就只能得到第一行的长度
2.2.3 获取变量地址
对于全局变量,可以使用 mov 寄存器,offset 全局变量名,因为全局变量的地址是固定的,所以在编译时编译器就直接将offset 全局变量名替换为地址写入程序。但对于局部变量,只有在程序加载进内存后才能确定地址,所以不能用offset获取地址。一般使用ebp做指针进行访问,用lea指令传地址。
MASM提供了一个伪指令addr 局部变量名,编译器将局部变量的地址用lea传入eax寄存器中,在用eax代替变量地址使用,
addr伪指令只能使用在invoke语句中,如mov eax,addr 变量名这样的用法是错误的。
与此同时,因为使用了eax寄存器,所以在用了addr的invoke语句中就最好不要再使用eax传参了,否则会出现数据的覆盖。
2.3 子程序
2.3.1 子程序的定义
2.3.2 参数传递和堆栈平衡
这两个部分前面都记录过了,这里就省略了。