t,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.code
main PROC
; 定义局部变量,自动压栈/平栈
LOCAL var_byte:BYTE,var_word:WORD,var_dword:DWORD
LOCAL var_array[3]:DWORD
; 填充局部变量
mov byte ptr ds:[var_byte],1
mov word ptr ds:[var_word],2
mov dword ptr ds:[var_dword],3
; 填充数组方式1
lea esi,dword ptr ds:[var_array]
mov dword ptr ds:[esi],10
mov dword ptr ds:[esi + 4],20
mov dword ptr ds:[esi + 8],30
; 填充数组方式2
mov var_array[0],100
mov var_array[1],200
mov var_array[2],300
invoke ExitProcess,0
main ENDP
END main
在上述示例代码中,main
过程使用LOCAL
指令定义了几个局部变量,包括一个字节类型的变量var_byte
、一个字类型的变量var_word
、一个双字类型的变量var_dword
和一个包含三个双字元素的数组var_array
。
在代码中,我们使用mov
指令填充这些变量的值。对于字节类型、字类型和双字类型的变量,使用mov byte ptr ds:[var_byte], 1
、mov word ptr ds:[var_word], 2
和mov dword ptr ds:[var_dword], 3
指令将相应的常数值存储到变量中。在填充数组时,分别使用了两种不同的方式。一种方式是使用lea
指令将数组的地址加载到esi
寄存器中,然后使用mov dword ptr ds:[esi],10
等指令将相应的常数值存储到数组中。另一种方式是直接访问数组元素,如mov var_array[0], 100
等指令。需要注意,由于数组元素在内存中是连续存储的,因此可以使用[]
操作符访问数组元素。
在汇编中使用LOCAL
伪指令来实现自动计算局部变量空间,以及最后的平栈操作,将会极大的提高开发效率。
10.4 USES/ENTER
USES是汇编语言中的伪指令,用于保存一组寄存器的状态,以便函数调用过程中可以使用这些寄存器。使用USES时,程序可以保存一组需要保护的寄存器,汇编器将在程序入口处自动向堆栈压入这些寄存器的值。读者需注意,我们可以在需要保存寄存器的程序段中使用USES来保护寄存器,但不应在整个程序中重复使用寄存器。
ENTER也是一种伪指令,用于创建函数调用过程中的堆栈帧。使用ENTER时,程序可以定义一个名为ENTER的指定大小的堆栈帧。该指令会将新的基准指针ebp 压入堆栈同时将当前的基准指针ebp存储到另一个寄存器ebx中,然后将堆栈指针esp减去指定大小的值,获取新的基地址,并将新的基地址存储到ebp 中。之后,程序可以在此帧上创建和访问局部变量,并使用LEAVE指令将堆栈帧删除,将ebp恢复为旧的值,同时将堆栈指针平衡。
在使用USES和ENTER指令时,需要了解这些指令在具体的平台上的支持情况,以及它们适用的调用约定。通常情况下,在函数开头,我们将使用ENTER创建堆栈帧,然后使用USES指定需要保护的寄存器。在函数末尾,我们使用LEAVE删除堆栈帧。
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.code
; USES 自动压入 eax,ebx,ecx,edx
my_proc PROC USES eax ebx ecx edx x:DWORD,y:DWORD
enter 8,0 ; 自动保留8字节堆栈空间
add eax,ebx
leave
my_proc endp
main PROC
mov eax,10
mov ebx,20
call my_proc
int 3
main ENDP
END main
10.5 STRUCT/UNION
STRUCT和UNION是汇编语言中的数据类型,STRUCT是一种复合数据类型,它将多个不同类型的变量按顺序放置在一起,并使用单个名称来引用集合。使用STRUCT时,我们可以将不同类型的变量组合成一个结构体并定义其属性,如结构体中包含的成员变量的数据类型、名称和位置。
例如,下面是一个使用STRUCT定义自定义类型的示例:
; 定义一个名为 MyStruct 的结构体,包含两个成员变量。
MyStruct STRUCT
Var1 DWORD ?
Var2 WORD ?
MyStruct ENDS
在上述示例代码中,我们使用STRUCT
定义了一个名为MyStruct
的结构体,其中包含两个成员变量Var1
和Var2
。其中,Var1
是DWORD
类型的数据类型,以问号?
形式指定了其默认值,Var2
是WORD
类型的数据类型。
另一个数据类型是UNION
,它也是一种复合数据类型,用于将多个不同类型的变量叠加在同一内存位置上。使用UNION
时,程序内存中的数据将只占用所有成员变量中最大的数据类型变量的大小。与结构体不同,联合中的所有成员变量共享相同的内存位置。我们可以使用一种成员变量来引用内存位置,但在任何时候仅能有一种成员变量存储在该内存位置中。
例如,下面是一个使用UNION定义自定义类型的示例:
; 定义一个名为 MyUnion 的联合,包含两个成员变量。
MyUnion UNION
Var1 DWORD ?
Var2 WORD ?
MyUnion ENDS
在上述示例代码中,我们使用UNION
定义了一个名为MyUnion
的联合,其中包含两个不同类型的成员变量Var1
和Var2
,将它们相对应地置于联合的同一内存位置上。
读者在使用STRUCT
和UNION
时,需要根据内存分布和变量类型来正确访问成员变量的值。在汇编语言中,结构体和联合主要用于定义自定义数据类型、通信协议和系统数据结构等,如下一段代码则是汇编语言中实现结构体赋值与取值的总结。
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
; 定义坐标结构
MyPoint Struct
pos_x DWORD ?
pos_y DWORD ?
pos_z DWORD ?
MyPoint ends
; 定义人物结构
MyPerson Struct
Fname db 20 dup(0)
fAge db 100
fSex db 20
MyPerson ends
.data
; 声明结构: 使用 <>,{}符号均可
PtrA MyPoint <10,20,30>
PtrB MyPoint {100,200,300}
; 声明结构: 使用MyPerson声明结构
UserA MyPerson <'lyshark',24,1>