80386汇编-汇编语言基础
9unk Lv5

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
  • WriteConsole 和 WriteFile 的区别:
    前者支持是宽字符的函数,其中中文可使用 unicode 和 ANSI 编码。

  • ExitProcess

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

1

以字母开头的十六进制常量前面必须加上一个 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 库

整数表达式

整数表达式是包含整数值和算数运算符的数学表达式。
2

实数常量

有两种类型的实数变量:十进制实数和十六进制实数。

进制实数常量:由符号、整数部分、小数点、表示小数的整数和指数部分组成。如:

1
2
3
4
2.
+3.0
-44.2E+05
26.E5

实数常量应该至少有一个数字和一个小数点,如果没有小数点,那它就是一个整数常量。

十六进制实数:以一个十六进制数表示一个实数,遵循 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

10

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

11

注释

  • 单行注释:以分号字符开始
  • 块注释:以 COMMENT 伪指令以及用户定义的符号开始,编译器忽略后面所有的文本行,直到另一个相同的用户定义符号出现。例如:
1
2
3
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位的指针。
3

数据定义部分基本都是重复知识点,这里就不写了。

为 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
高级控制台输入和输出功能

  • 本文标题:80386汇编-汇编语言基础
  • 本文作者:9unk
  • 创建时间:2022-11-03 23:53:00
  • 本文链接:https://9unkk.github.io/2022/11/03/80386-hui-bian-hui-bian-yu-yan-ji-chu/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!