win32 汇编 例1:win32 汇编输出字符串 “hello world!”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ;----------------------------------- ;程序名:hello.asm ;功能:我的第一个基于Win32的汇编程序 ;作者:9unk ;编写时间:2022.11.4 ;------------------------------------ .386 .model flat,stdcall option casemap:none include kernel32.inc includelib kernel32.lib include masm32.inc includelib masm32.lib .data mess db 'hello world!',0 .code main PROC invoke StdOut,offset mess invoke ExitProcess,0 main ENDP END main
编译
ml /c /coff hello.asm
参数解释:
/c:仅编译生成obj文件,不自动链接为exe文件
/coff:生成COFF格式的obj文件
链接
link /subsystem:console hello.obj
参数解释:
/subsystem:windows 链接成PE文件
/subsystem:console 链接成控制台文件
option 语句 用 option 语句定义的选项有很多,如 option language 定义和 option segment 定义等,在 win32 汇编程序中,需要的只是定义 option casemap:none,这个语句定义了程序中的变量和子程序名是否对大小写敏感,由于 Win32 API 中的 API 名称是区分大小写的,所以必须指定这个选项,否则在调用 API 的时候会有问题。
程序反汇编 1 2 3 4 push hello.403000 call StdOut push 0 call <JMP.&ExitProcess>
StdOut 子函数 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 push ebp mov ebp,esp add esp,FFFFFFF4 push FFFFFFF5 call <JMP.&GetStdHandle> ;获取标准输出设备的句柄 ;0xFFFFFFF5 = -11 = “STD_OUTPUT_HANDLE” 表示活动控制台屏幕缓冲区。 ;获取当前活动的 console 窗口的句柄。 ;返回值:如果函数成功,则返回值是指定设备的句柄;如果函数失败,则返回值为无效句柄值。 push 0xFFFFFFF5 call GetStdHandle ;将返回值传递给局部变量 [ebp-4] mov dword ptr ss:[ebp-4],eax ;计算字符串长度 push dword ptr ss:[ebp+8] call Strlen ; mov dword ptr ss:[ebp-C],eax push 0 lea eax,dword ptr ss:[ebp-8] push eax push dword ptr ss:[ebp-C] push dword ptr ss:[ebp+8] push dword ptr ss:[ebp-4] call WriteFile mov eax,dword ptr ss:[ebp-8] leave ret 4
StdOut 函数中主要用到了 GetStdHandle 和 WriteFile 函数。
win32 汇编输出字符串 “hello world!” 不使用 StdOut 函数
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 ;----------------------------------- ;程序名:HelloWorld.asm ;功能:我的第一个基于Win32的汇编程序 ;作者:9unk ;编写时间:2022.10.9 ;------------------------------------ ;include user32.inc ;includelib user32.lib ;include Irvine32.inc ;includelib irvine32.lib STD_OUTPUT_HANDLE = -11 NULL = 0 .386 .model flat,stdcall option casemap:none ;include windows.inc ;其中包含了 STD_OUTPUT_HANDLE 和 NULL 的定义 include kernel32.inc includelib kernel32.lib include masm32.inc includelib masm32.lib .data mess db 'hello world!',0 .data? buffer db 100 dup(?) .code main PROC ;invoke StdOut,offset mess invoke GetStdHandle,STD_OUTPUT_HANDLE ;WriteFile 和 WriteConsole 的功能是相同的 invoke WriteFile,eax,addr mess,sizeof mess,ebx,NULL invoke ExitProcess,0 main ENDP END main
1 2 3 4 5 6 7 8 9 invoke ExitProcess,0 ;相当于8086汇编中的 push 0 int 20h 或者 mov ax,4c00h int 21h
汇编语言的基本元素 整数常量 整数常量由符号(可选)开头,后跟一个或多个数字(digit)以及一个表示数制基数(radix)的字符后缀。
符号 数字[基数]
基数后缀(大小写均可)
1 2 3 4 5 6 7 8 9 h 十六进制 q/o 八进制 d 十进制 b 二进制 r 编码实数 这两个可选不知道有什么用 t 十进制(可选) y 二进制(可选)
如果整数常量后面没有后缀,就认为是十进制数。
范例:
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 ;--------------------------- ;程序名:IntCon.asm ;功能:演示整数常量的定义 ;作者:9unk ;编写时间:2022-11-5 ;---------------------------- ;.386 ;Irvine32.inc 中已经定义了这些模式 ;.model flat,stdcall ;option casemap:none include Irvine32.inc .data var1 db 26 ;十进制数 var2 db 26d ;十进制数 var3 db 11010011b ;二进制数 db 42q ;八进制数 var4 db 42o ;八进制数 db 1Ah ;十六进制数 var6 db 0A3h ;十六进制数 .code main PROC mov al,var1 mov ah,var2 mov ebx,offset var3 mov cl,var4 mov ch,[ebx+3] mov dx,word ptr var6 call DumpRegs exit ;invoke ExitProcess,0 main ENDP END main
以字母开头的十六进制常量前面必须加上一个 0,以防止汇编器将其解释为标识符。
编译
ml /c /coff IntCon.asm
链接
link /subsystem:console irvine32.lib kernel32.lib user32.lib IntCon.obj
注意:添加 include Irvine32.inc 库后,链接时要手动链接 irvine32.lib kernel32.lib user32.lib 库
整数表达式 整数表达式是包含整数值和算数运算符的数学表达式。
实数常量 有两种类型的实数变量:十进制实数和十六进制实数。
进制实数常量: 由符号、整数部分、小数点、表示小数的整数和指数部分组成。如:
实数常量应该至少有一个数字和一个小数点,如果没有小数点,那它就是一个整数常量。
十六进制实数:以一个十六进制数表示一个实数,遵循 IEEE 浮点数格式。如十进制实数 +1.0 的二进制表示如下:
0011 1111 1000 0000 0000 0000 0000 0000
汇编语言中同样的值将被编码为单精度实数:
3F80000r
字符常量 字符常量是以单引号或双引号括起来的单个字符。汇编器将其转换为与之对应的二进制数 ASCII 码,例如: ‘A’ “d”
字符串常量 字符串常量是以单引号或双引号括起来的一串字符: ‘ABC’ ‘X’ “Goodnight, Gracie” ‘4096’ “This isn’t a test” ‘Say “Goodnight,” Gracie’
保留字 MASM 中有一些特殊含义的保留字,保留字只能适用于合适的上下文环境中,有如下不同类别的保留字:
指令助记符,MOV、ADD等
伪指令,offset、proc 等
属性:BYTE、WORD等
运算符
预定义符号:@data等
标识符 标识符是程序员选择的名字,用来标识变量、常量、过程或代码标号。创建标识符时要注意以下几点:
标识符可包含 1~247 个字符
标识符大小写不敏感(MASM 默认)
标识符的第一个字符必须是字母(A~Z和a~z)、下划线、@、?、$,后续字符可以是数字
标识符不能与汇编器的保留字相同
标号名:(作用域为子程序);标号名::(作用域为全局);@@(作用域为子程序)
案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ;--------------------------- ;程序名:identifier.asm ;功能:演示标识符中的 "@" 符号 ;作者:9unk ;编写时间:2023-1-2 ;---------------------------- include Irvine32.inc .code main proc mov al,0 mov bl,10 @@: cmp al,bl jz @F ;@F 跳转到该指令下面的第一个 @@ 标号处。 or al,1 sub bl,1 loop @B ;@B 跳转到该指令上面的最后一个 @@ 标号处。 @@: exit main endp end main
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 ;--------------------------- ;程序名:identifier.asm ;功能:演示标识符中的局部标号和全局标号 ;作者:9unk ;编写时间:2023-1-2 ;---------------------------- include Irvine32.inc .code main proc mov al,0 mov bl,10 jmp @@start ;jmp @@stop ;报错(找不到标号@@stop),因为不是全局标号。 call test1 exit main endp test1 PROC mov al,10 mov bl,10 @@start:: ;“标号::” 表示全局标号 cmp al,bl jz @F ;@F 跳转到该指令下面的第一个 @@ 标号处。 or al,1 sub bl,1 loop @@start @@: ;"标号:" 表示局部标号 @@stop: ret test1 ENDP end main
注释
单行注释:以分号字符开始
块注释:以 COMMENT 伪指令以及用户定义的符号开始,编译器忽略后面所有的文本行,直到另一个相同的用户定义符号出现。例如:
案例解析 例1:整数相减
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ;--------------------------- ;程序名:AddSub.asm ;功能:演示整数常量的定义 ;作者:9unk ;编写时间:2022-11-5 ;---------------------------- TITLE Add and Subtract INCLUDE Irvine32.inc .code main PROC MOV eax,10000h MOV eax,40000h MOV eax,20000h MOV DumpRegs exit main ENDP END main
伪指令 TITLE 将整行标为注释,该行可放任何东西
1 2 3 4 指令 exit 相当于 push 0 call ExitProcess
例2:不包含任何文件的 AddSub 版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ;--------------------------- ;程序名:AddSubAlt.asm ;功能:演示整数常量的定义 ;作者:9unk ;编写时间:2022-11-6 ;---------------------------- TITLE Add and Subtract .386 .model flat,stdcall .stack 4096 ExitProcess PROTO,dwExitCode:DWORD DumpRegs PROTO .code main PROC MOV eax,10000h MOV eax,40000h SUB eax,20000h call DumpRegs INVOKE ExitProcess,0 main ENDP END main
定义数据 内部数据类型 MASM 定义了多种内部数据类型,每种数据类型都描述了该类型的变量和表达方式的取值集合。数据类型的基本特征是以数据位数量的多少来衡量的:8,16,32,48,64,80位。其他特征(如有符号、指针、浮点等)主要是为了方便程序员记忆变量中存储的数据的类型。例如,声明一个 DWORD 变量逻辑上存储的是一个32位无符号整数,但事实上也可以存放一个32位的有符号整数、一个32位的浮点数、一个32位的指针。
数据定义部分基本都是重复知识点,这里就不写了。
为 AddSub 程序添加变量 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 ;----------------------------- ;程序名:AddSub2.asm ;功能:演示变量的定义 ;作者:9unk ;编写日期:2022-11-6 ;----------------------------- INCLUDE Irvine32.inc .data val1 DWORD 10000h val2 DWORD 40000h val3 DWORD 20000h finalVal DWORD ? .code main PROC MOV eax,val1 ADD eax,val2 SUB eax,val3 MOV finalVal,eax CALL DumpRegs exit main ENDP END main
后面的内容大都学过,这里不重复写了。如果忘记再回头看一下。
本章小结 三个整数相减 编写三个16位整数相减的程序,调用 DumpRegs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ;--------------------------------------------- ;程序名:lx3-1.asm ;功能:编写三个16位整数相减的程序,调用 DumpRegs ;作者:9unk ;编写时间:2022-11-6 ;--------------------------------------------- INCLUDE Irvine32.inc .code main PROC MOV ax,10 ADD ax,10 SUB ax,15 CALL DumpRegs exit main ENDP END main
数据定义 写一个程序,要求包含 3.4 节中列出的所有数据类型的定义,用合适的值初始化每个变量。
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 ;--------------------------------------------- ;程序名:lx3-2.asm ;功能:写一个程序,要求包含 3.4 节中列出的所有数据类型的定义,用合适的值初始化每个变量。 ;作者:9unk ;编写时间:2022-11-6 ;--------------------------------------------- ;.386 ;.model flat,stdcall ;option casemap:none INCLUDE Irvine32.inc .data ;BYTE、SBYTE、db val1 BYTE 'A',0,255 val2 SBYTE -128 val3 SBYTE +127 val4 BYTE 10h val5 db 255 greeting byte 'good',0 ;WORD、SWORD word1 WORD 65535 word2 SWORD -32768 val6 dw 65535 myList WORD 1,2,3,4,5 ;DWORD、SDWORD val7 DWORD 12345678h val8 SDWORD -2147483648 ;实数定义 rval1 REAL4 -2.1 rVAL2 REAL8 3.2E-260 ShortArray REAL4 20 DUP(0.0) .code main PROC MOV al,val1 MOV ah,val5 ADD AX,word1 ADC EAX,0 MOV EBX,val7 ADD EAX,EBX call DumpRegs exit main ENDP END main
整数符号常量 写一个程序,定义对应一周内每天的符号常量,创建一个数组变量并使用这些符号作为初始值。
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 ;--------------------------------------------- ;程序名:lx3-3.asm ;功能:写一个程序,定义对应一周内每天的符号常量,创建一个数组变量并使用这些符号作为初始值。 ;作者:9unk ;编写时间:2022-11-7 ;--------------------------------------------- .386 .model flat,stdcall option casemap:none INCLUDE kernel32.inc INCLUDE masm32.inc INCLUDELIB masm32.lib .data Other db 0 Sun EQU <星期日> Mon EQU <星期一> Tues TEXTEQU <星期二> Wed TEXTEQU <星期三> Thurs TEXTEQU <星期四> Fri EQU <星期五> Sat EQU <星期六> .code main PROC NOP INVOKE ExitProcess,0 main ENDP END main
文本符号常量 写一个程序,为几个字符串定义符号名。在变量定义中分别使用每个符号。
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 ;--------------------------------------------- ;程序名:lx3-4.asm ;功能:写一个程序,为几个字符串定义符号名。在变量定义中分别使用每个符号。 ;作者:9unk ;编写时间:2022-11-7 ;--------------------------------------------- ;INCLUDE Irvine32.inc .386 .model flat,stdcall option casemap:none INCLUDE kernel32.inc INCLUDE masm32.inc INCLUDELIB masm32.lib .data Sun DB '星期日',0 Mon DB '星期一',0 Tues DB '星期二',0 Wed DB '星期三',0 Thurs DB '星期四',0 Fri DB '星期五',0 Sat DB '星期六',0 .code main PROC INVOKE StdOut,offset Mon INVOKE ExitProcess,0 main ENDP END main
参考 getstdhandle MASM HelloWorld 高级控制台输入和输出功能