MASM程序第一部分是模式和源程序格式
.386
.mode flat,stdcall
option casemap:none
带p的伪指令表示程序中可以使用特权指令
为了使用MMX指令,除了定义.586之外,还需要加上.mmx伪指令;
.model内存模式:tiny, small, medium,compact,large,huge,flat
tiny:用来建立.com文件,所有的代码、数据和堆栈都在64KB段内;
small:建立代码和数据分别用一个64KB段的.exe文件
medium:代码段可以使用多个64KB段,数据段只有一个64KB段;
compact:代码段只有一个64KB段,数据段可以使用多个64KB段;
large:代码和数据都可使用多个64KB段;
huge:数据段中一个数组可以超过64KB;
flat:WIN32程序使用的模式,代码段和数据段共同使用一个4GB段;
option casemap:none:定义程序中变量和子程序名是否对大小写敏感;
段的定义
.stack ;堆栈段的大小
.data ;一些初始化的变量定义
.data? ;一些未初始化的变量定义
.const ;一些常量定义
.code ;代码定义
.data .data? .const段都是数据段。.code是代码段;系统为程序分配一个向下扩展的、足够大的段作为堆栈段,所以.stack段
定义会被忽略;
.data段数据值在程序装载完成后存入内存中;.data段一般存放在可执行文件的_DATA节区内;
.data?段不会增大.exe文件的大小;
.const段中的变量只可读,进行写操作直接报错;
.code段是默认是只读的,代码段的属性有可执行文件的PE头部中的属性位决定;代码段一般放在_TEXT节区中的;
.stack:可读写并可执行
----------------------------------------------------------------------------------------------------------
程序结束和程序入口:
程序员可指定从代码段的任何位置开始执行,这个位置由程序最后语句end语句决定;
WIN32汇编注释符(;)换行符(\);
----------------------------------------------------------------------------------------------------------
调用API:
(1)CALL printString, addr szHello
(2)使用invoke语句:
invoke 函数名 , 参数1, 参数2
invoke MessageBox,NULL, offset szText, offset szCaption, MB_OK
对于无参数的函数,既可以使用call,也可以使用invoke;
(3)API返回值:
返回值存放在eax寄存器中或者参数中提供的一个缓冲区地址;
include语句:
include user32.inc
导入库:只包含函数的定位信息和参数数目等简单信息,这种库文件称作导入库,一个DLL对应一个导入库;
如User32.dll 对应的导入库是User32.lib;
需要告诉链接程序使用哪个导入库,使用
includelib <导入库文件名>
----------------------------------------------------------------------------------------------------------
标号和变量的命名规范:
可以用字母、数字、下划线以及符号@、$和?。
标号:用来表示跳转的目的地;标号既可以定义在目的指令同一行的头部,也可以在前独立一行定义,格式如下:
标号名: 目标指令 ;方法1
标号名:: 目标指令 ;方法2 从一个子程序跳到另一个子程序的标号时使用,此时标号的作用域时整个程序;
----------------------------------------------------------------------------------------------------------
全局变量:
全局变量的定义:
定义在.data或者.data?段内,语法如下:
变量名 类型 初始值1,初始值2
变量名 类型 重复数量 dup (初始值1,初始值2)
类型: 缩写 长度(字节)
byte db 1
word dw 2
dword dd 4
fword df 6
qword dq 8
tbyte dt 10
sbyte 1 ;有符号字节
sword 2
sdword 4
Real4 4
Real8 8
Real10 10
类型缩写只有在定义变量时才可用;
byte类型变量定义中,引号定义字符串和数值定义可混用;
局部变量:
局部变量的作用域时单个子程序,在进入子程序的时候,通过修改堆栈指针esp来预留出需要的空间,在用
ret指令返回主程序之前,通过恢复esp丢弃这些空间,这些变量就随之无效了。
缺点是无法为定义含有初始值的变量;对局部变量的初始化一般在子程序中由
指令完成。
MASM用local伪指令提供对局部变量的支持。
local 变量名 :类型, 变量2 :类型
local loc[1024]:byte ;定义1024字节的局部变量loc
-------子程序定义案例:
TestProc proc
local @loc1:dword,@loc2:word
local @loc3:byte
mov eax,@loc1
mov ax, @loc2
mov al, @loc3
ret
TestProc endp
-------------------------------------------
转换后的汇编代码如下:
push ebp ;把返回的地址压入栈中,
mov ebp, esp ;转到子程序运行;
add esp, FFFFFFF8 ;预留8字节的空间;
mov eax, dword ptr [ebp-04]
mov ax, word ptr [ebp-06]
mov al, byte ptr [ebp-07]
leave
ret
ebp寄存器也是以堆栈段为默认数据段;
局部变量一定要初始化,
数据结构:
汇编数据结构定义如下:
结构名 struct
字段1 类型 ?
字段2 类型 ?
结构名 ends
案例:
WINCLASS struct
style DOWRD ?
lpfnWndProc DWORD ?
cbClsExtra DWORD ?
hInstance DWORD ?
hIcon DWORD ?
hCursor DWORD ?
hbrBackground DWORD ?
lpszMenuName DWORD ?
lpszClassName DWORD ?
WINCLASS ends
stWndClass WINCLASS <1,1,1,1,1,1,1,1,1,1>
使用结构中的字段用法
mov eax stWndClass.lpfnWndProc
实际使用中,常使用指针存取数据结构,如果使用esi寄存器做指针寻址,
mov esi, offset stWndClass
mov eax, [esi + WNDCLASS.lpfnWndProc]
MASM还提供一个用法,使用assume伪指令将寄存器预先定义为结构指针,再进行操作:
mov esi, offset stWinClass
assume esi:ptr WNDCLASS
mov eax, [esi].lpfnWndProc
assume esi:nothing
数据结构支持嵌套,
NEW_WNDCLASS struct
dwOptiond dword ?
oldWndClass WNDCLASS <>
NEW_WNDCLASS ends