宏汇编

宏——具有宏名的一段汇编语句序列——宏定义时书写

宏指令——这段汇编语句序列的缩写——宏调用时书写

宏展开——宏指令处用这段宏代替的过程——宏汇编时实现

宏的功能强大,颇具特色

宏定义

1
2
3
4
宏名	macro [形参表]
宏定义体
endm
宏注释符 ;;
1
2
3
4
5
6
7
8
9
10
main	MACRO	;定义名为main的宏,无参数
mov ax,@data ;;宏定义体
mov ds,ax
ENDM ;;宏定义结束

return MACRO retnum ;;带有形参return
mov al,retnum ;;宏定义中使用参数
mov ah,4ch
int 21h
ENDM

宏调用

1
宏名 [实参表]
1
2
3
4
start:	main			;宏调用,建立DS内容
dispmsg string ;宏调用,显示字符串
return 0 ;宏调用,返回DOS
end start
  • 宏调用的实质是在汇编过程中进行宏展开
  • 宏展开的具体过程是:当汇编程序扫描源程序遇到已有定义的宏调用时,即用相应的宏定义体取代源程序的指令,同时用位置匹配的实参对形参进行取代
1
2
3
4
5
6
7
8
9
10
;宏定义
dstring macro string
db '&string&',0dh,0ah,'$'
endm
;宏调用
dstring < This is a example. >
dstring < 0 !< Number !< 10 >
;宏展开
db 'This is a example. ', 0dh,0ah,'$'
db '0 < Number < 10', 0dh,0ah, '$'

image-20240826163148198

示例代码

hello.asm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
include hello.inc
includelib user32.lib
includelib kernel32.lib

MyAdd2 macro n1,n2,n3
;db "&n3&",0
mov eax,n1
add eax,n2
endm

;代码区
.code
MyAdd1 proc n1:dword,n2:dword
mov eax,n1
add eax,n2
ret
MyAdd1 endp

START proc
local @pt:Point
local @pt2:Point
local @num:dword
local @num2:byte
local @ary[10]:dword

lea ebp,@ary
;mov @ary[2*type @ary[0]],1
mov word ptr[@ary+2*2],1

lea ebx,@pt ;mov ebp,dword ptr [ebp-8]
lea ecx,@pt2 ;mov ebp,dword ptr [ebp-8]
;mov ebx,addr @pt
ASSUME ebx:ptr Point
ASSUME ecx:ptr Point
mov [ebx].x,0
mov [ebx].y,0
mov [ecx].x,0
mov [ecx].y,0
ASSUME ebx:nothing ;取消
ASSUME ecx:nothing ;取消
mov @num,0
mov @num2,1

mov ebx,@num ;mov ebx,dword ptr [ebp-c]
mov al,@num2

;invoke MyAdd1,addr @pt,2 ;addr 获取局部变量地址

mov ebx,offset g_pt2

;假设
ASSUME ebx:ptr Point
mov eax,[ebx].x
mov eax,[ebx].y
ASSUME ebx:nothing ;取消

ASSUME eax:ptr Point3D
;mov eax,dword ptr [ebx+0] ;pt.x
;mov eax,dword ptr [ebx+4] ;pt.y

invoke MyAdd1,1,2
invoke MyAdd1,1,2
;MyMsg<MB_OK>
;MyMsg<MB_OK>
;MyAdd2 1,2,<hello world>
;MyAdd2 3,4,<hello world>
;MyAdd2 3,4,<!<hello world!>> ;转义
invoke ExitProcess,0
ret
START endp
end START

头文件和库的包含

1
2
3
include hello.inc
includelib user32.lib
includelib kernel32.lib
  • **include hello.inc**:包含一个名为 hello.inc 的头文件,通常包含宏、常量或其他汇编代码片段。
  • includelib user32.lib 和 **includelib kernel32.lib**:包含 Windows API 的库文件,用于链接程序中调用的 Windows API 函数,如 ExitProcess

宏定义

1
2
3
4
5
MyAdd2 macro n1,n2,n3
;db "&n3&",0
mov eax,n1
add eax,n2
endm
  • **MyAdd2 macro n1,n2,n3**:定义一个宏 MyAdd2,接受三个参数 n1n2n3。宏类似于函数,但在预处理阶段展开。
  • **mov eax,n1**:将第一个参数 n1 的值加载到 eax 寄存器中。
  • **add eax,n2**:将第二个参数 n2 的值加到 eax 寄存器中。
  • **;db "&n3&",0**:这行代码被注释掉了,可能是尝试将参数 n3 作为字符串插入,但被取消了。

函数定义

1
2
3
4
5
6
.code
MyAdd1 proc n1:dword,n2:dword
mov eax,n1
add eax,n2
ret
MyAdd1 endp
  • **.code**:指示代码段的开始,所有可执行的指令都在这个段中定义。
  • **MyAdd1 proc n1:dword,n2:dword**:定义一个名为 MyAdd1 的过程(函数),接受两个 DWORD 类型的参数 n1n2
  • **mov eax,n1**:将参数 n1 的值加载到 eax 寄存器中。
  • **add eax,n2**:将参数 n2 的值加到 eax 寄存器中。
  • **ret**:返回调用者,结束函数的执行。

主程序 START 的定义

1
2
3
4
5
6
START proc
local @pt:Point
local @pt2:Point
local @num:dword
local @num2:byte
local @ary[10]:dword
  • **START proc**:定义一个名为 START 的过程,程序的入口点。
  • local @pt:Point 和 **local @pt2:Point**:定义局部变量 @pt@pt2,它们的类型是 Point 结构体。
  • **local @num:dword**:定义一个局部变量 @num,类型为 DWORD(32位无符号整数)。
  • **local @num2:byte**:定义一个局部变量 @num2,类型为 BYTE(8位无符号整数)。
  • **local @ary[10]:dword**:定义一个 DWORD 类型的数组 @ary,包含 10 个元素。
1
2
lea ebp,@ary
mov word ptr[@ary+2*2],1
  • **lea ebp,@ary**:将局部数组 @ary 的地址加载到 ebp 寄存器中。
  • **mov word ptr[@ary+2*2],1**:将数组 @ary 的第三个元素(@ary[2])设置为 1。2*2 是偏移量,表示字节偏移。
1
2
3
4
5
6
7
8
9
10
lea ebx,@pt
lea ecx,@pt2
ASSUME ebx:ptr Point
ASSUME ecx:ptr Point
mov [ebx].x,0
mov [ebx].y,0
mov [ecx].x,0
mov [ecx].y,0
ASSUME ebx:nothing
ASSUME ecx:nothing
  • lea ebx,@pt 和 **lea ecx,@pt2**:将局部变量 @pt@pt2 的地址分别加载到 ebxecx 寄存器中。
  • ASSUME ebx:ptr Point 和 **ASSUME ecx:ptr Point**:告诉汇编器 ebxecx 寄存器指向 Point 结构体。
  • mov [ebx].x,0 和 **mov [ebx].y,0**:将 @pt 结构体的 xy 成员设置为 0。
  • mov [ecx].x,0 和 **mov [ecx].y,0**:将 @pt2 结构体的 xy 成员设置为 0。
  • ASSUME ebx:nothing 和 **ASSUME ecx:nothing**:取消之前的假设。
1
2
3
4
5
mov @num,0
mov @num2,1

mov ebx,@num
mov al,@num2
  • **mov @num,0**:将局部变量 @num 设置为 0。
  • **mov @num2,1**:将局部变量 @num2 设置为 1。
  • **mov ebx,@num**:将 @num 的值加载到 ebx 寄存器中。
  • **mov al,@num2**:将 @num2 的值加载到 al 寄存器中(aleax 的低8位部分)。
1
2
3
4
5
mov ebx,offset g_pt2
ASSUME ebx:ptr Point
mov eax,[ebx].x
mov eax,[ebx].y
ASSUME ebx:nothing
  • **mov ebx,offset g_pt2**:将全局变量 g_pt2 的地址加载到 ebx 寄存器中。
  • **ASSUME ebx:ptr Point**:假设 ebx 指向 Point 结构体。
  • **mov eax,[ebx].x**:将 g_pt2x 成员值加载到 eax 寄存器中。
  • **mov eax,[ebx].y**:将 g_pt2y 成员值加载到 eax 寄存器中(覆盖上一步的值)。
  • **ASSUME ebx:nothing**:取消假设。
1
2
3
ASSUME eax:ptr Point3D
;mov eax,dword ptr [ebx+0]
;mov eax,dword ptr [ebx+4]
  • **ASSUME eax:ptr Point3D**:假设 eax 寄存器指向 Point3D 结构体。
  • 注释掉的行代码本来是用来从 ebx 偏移量加载 Point 结构体的 xy 成员。

函数调用和程序结束

1
2
3
4
5
6
    invoke MyAdd1,1,2
invoke MyAdd1,1,2
invoke ExitProcess,0
ret
START endp
end START
  • **invoke MyAdd1,1,2**:调用 MyAdd1 函数,参数为 1 和 2。该函数会将两个数相加,并返回结果。
  • **invoke ExitProcess,0**:调用 Windows API 函数 ExitProcess,终止程序并返回状态码 0。
  • **ret**:返回调用者,结束 START 过程的执行。
  • **START endp**:标记 START 过程的结束。
  • **end START**:定义程序的入口点为 START

hello.inc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
.386    ;指令集
.model flat,stdcall ;平坦模式(不分段)
OPTION CASEMAP:none ;大小写敏感

;宏
NULL EQU 0
MB_OK EQU 0

;代码宏
MyMsg MACRO p1
mov eax,eax
endm

;函数声明
MessageBoxA proto hWnd:dword,lpText:dword,lpCaption:dword,uType:dword
ExitProcess proto uExitCode:dword

;常量区
.const
MY_MSG db "Hello World!",0
MY_TITLE db "51asm",0

;结构体
Point struct 4
test1 dword ?
x dword ?
y dword ?
Point ends

Point3D struct 4
pt Point<?>
z dword ?
Point3D ends

;初始化数据区
.data
g_pt1 Point<0,0>
g_pt2 Point<1,2>
g_pt3 Point<?>
g_pt4 Point3D<<?,?>>
;g_ptArt Point 10 dup(<4,5>);数组
;g_pt2 Point3D<<?,?>,?>
;g_pt3 Point3D<<1,2>,3>
;g_pt4 Point3D<<?,2>,3>

处理器和模式定义

1
2
3
.386    ;指令集
.model flat,stdcall ;平坦模式(不分段)
OPTION CASEMAP:none ;大小写敏感
  • **.386**:指示汇编器使用 80386 指令集,这意味着可以使用 32 位寄存器和指令。
  • **.model flat,stdcall**:指定使用平坦内存模型(即不分段),调用约定为 stdcall。在 stdcall 调用约定中,函数的参数由调用者传递,函数本身负责清理堆栈。
  • **OPTION CASEMAP:none**:设置汇编器为大小写敏感模式。

宏定义和常量

1
2
NULL EQU 0
MB_OK EQU 0
  • **NULL EQU 0**:定义 NULL 常量,值为 0,用于表示空指针或无效地址。
  • **MB_OK EQU 0**:定义 MB_OK 常量,值为 0,用于表示 MessageBox 函数的 OK 按钮标志。
1
2
3
MyMsg MACRO p1
mov eax,eax
endm
  • **MyMsg MACRO p1**:定义一个名为 MyMsg 的宏,接受一个参数 p1。宏在编译时展开为具体的代码片段。
  • **mov eax,eax**:这是一条无操作指令(NOP),它不改变任何状态。可用于占位或调试。

函数声明

1
2
MessageBoxA proto hWnd:dword,lpText:dword,lpCaption:dword,uType:dword
ExitProcess proto uExitCode:dword
  • **MessageBoxA proto hWnd:dword,lpText:dword,lpCaption:dword,uType:dword**:声明 MessageBoxA 函数的原型,表示它接受四个 DWORD 类型的参数。MessageBoxA 是 Windows API 用于显示一个消息框的函数。
  • **ExitProcess proto uExitCode:dword**:声明 ExitProcess 函数的原型,表示它接受一个 DWORD 类型的参数,用于终止当前进程。

常量区

1
2
3
.const
MY_MSG db "Hello World!",0
MY_TITLE db "51asm",0
  • **.const**:开始定义只读常量区。
  • **MY_MSG db "Hello World!",0**:定义一个字符串常量 MY_MSG,内容为 "Hello World!",以空字符 0 结尾。
  • **MY_TITLE db "51asm",0**:定义一个字符串常量 MY_TITLE,内容为 "51asm",以空字符 0 结尾。这通常用于消息框的标题。

结构体定义

1
2
3
4
5
Point struct 4
test1 dword ?
x dword ?
y dword ?
Point ends
  • **Point struct 4**:定义一个名为 Point 的结构体,使用 4 字节对齐方式。
  • **test1 dword ?**:定义一个名为 test1 的成员,类型为 DWORD(32 位无符号整数),初始值为不确定(?)。
  • x dword ? 和 **y dword ?**:分别定义 xy 成员,类型为 DWORD,初始值为不确定。
  • **Point ends**:标记 Point 结构体的结束。
1
2
3
4
Point3D struct 4
pt Point<?>
z dword ?
Point3D ends
  • **Point3D struct 4**:定义一个名为 Point3D 的结构体,使用 4 字节对齐方式。
  • **pt Point<?>**:定义一个名为 pt 的成员,类型为 Point 结构体,初始值为不确定。
  • **z dword ?**:定义 z 成员,类型为 DWORD,初始值为不确定。
  • **Point3D ends**:标记 Point3D 结构体的结束。

初始化数据区

1
2
3
4
5
.data
g_pt1 Point<0,0>
g_pt2 Point<1,2>
g_pt3 Point<?>
g_pt4 Point3D<<?,?>>
  • **.data**:定义数据段,存放全局变量或已初始化的数据。
  • **g_pt1 Point<0,0>**:定义一个全局变量 g_pt1,类型为 Point 结构体,并初始化 xy0
  • **g_pt2 Point<1,2>**:定义一个全局变量 g_pt2,类型为 Point 结构体,并初始化 xy12
  • **g_pt3 Point<?>**:定义一个全局变量 g_pt3,类型为 Point 结构体,但没有初始化。
  • **g_pt4 Point3D<<?,?>>**:定义一个全局变量 g_pt4,类型为 Point3D 结构体,且 ptz 都没有初始化。

注释部分

1
2
3
4
;g_ptArt Point 10 dup(<4,5>);数组
;g_pt2 Point3D<<?,?>,?>
;g_pt3 Point3D<<1,2>,3>
;g_pt4 Point3D<<?,2>,3>
  • **g_ptArt Point 10 dup(<4,5>)**:注释掉的代码,表示定义一个包含 10 个元素的 Point 结构体数组,每个元素的 xy 都初始化为 45
  • **g_pt2 Point3D<<?,?>,?>**:注释掉的代码,表示定义一个 Point3D 结构体,并初始化 ptz 为不确定值。
  • **g_pt3 Point3D<<1,2>,3>**:注释掉的代码,表示定义一个 Point3D 结构体,pt 初始化为 Point<1,2>z 初始化为 3
  • **g_pt4 Point3D<<?,2>,3>**:注释掉的代码,表示定义一个 Point3D 结构体,pt.x 初始化为不确定,pt.y 初始化为 2z 初始化为 3