汇编语言 基于x86处理器 第三章
第一个汇编语言程序:
Main PROC
Mov eax,5
Add eax,6
INVOKE ExitProcess,0
Main ENDP
加上变量: ;程序有代码段,数据段,还会有堆栈段
.data ;.data与.code为段(和PE文件格式有点关系)
sum DWORD 0 ;变量定义
.code
Main PROC
Mov eax,5
Add eax,6
Mov sum,eax
INVOKE ExitProcess,0
Main ENDP
算术优先级:
(), 一元+-, */, MOD, +- ;一元加减指 -5,+9此类
常数声名:
[ { + | - } ] digits [ radix ] ;radix指进制符号: h,q/o,d,b 默认下为十进制
比如:
26d
11010011b
实数常数声名:
Sign { + | - }
Exponent E [ { + | - } ] interger
比如:1.0 +3.0 -44.2E+05 26.E5
字符串常量声名:
‘ABC’ ‘X’ “4096” “This isn’t a test”
保留字≈关键字
标识符:
例如上文中的sum变量 或是(loop):
loop:
jmp loop
伪指令不作为指令执行,用于定义变量、宏和子程序等操作(下文伪指令 与 指令)
Cont DWORD 26
Mov eax, Cont
.386 ;表示这是一个32位程序
.model flat,stdcall ;选择程序的内存模式(flat),并确定了子程序的调用规范(stdcall)
;其原因是32位Windows服务要求使用stdcall规范
.stack 4096 ;为运行时堆栈保留了4096字节的存储空间,每个程序都必须有
Invoke ;比call好用,会帮你检查参数,参数用‘,’隔开
Invoke expression[ ,arguments ]
定义段:
.data .code .stack 100h
指令:
四个组成成分:标号(可选),指令助记符(必须),操作数(通常必须),注释(可选)
[ label: ] mnemonic [ operands ] [ ;comment ]
汇编器为每个标号分配一个数字地址。可以在一个标号后面定义多个数据项。在下面的例子中,array定义了第一个数字(1024)的位置,其他数字在内存中的位置紧随其后:
Array DWORD 1024, 2048
DWORD 4096, 8192
代码标号可以与指令在同一行上,也可以自己独立一行:
L1: mov ax,bx
L2:…………
注释:
单行注释: ;XXXX
块注释(使用COMMENT伪指令和一个用户定义的符号开始,到再次出现用户定义符号):
COMMENT !
This line is a comment.
This line is also a comment.
!
空指令:NOP
示例:
.386
.model flat,stdcall ;32位程序总是使用平面(flat)模式
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD ;声明了ExitProcess函数的原型
;它是一个标准的windows服务
.code
Main PROC
Mov eax, 5
Add eax, 6
INVOKE ExitProcess, 0
Main ENDP
END Main
注:使用RadASM编译并连接时,需加上 Includelib Kernel32.lib
否则会报错
Value1 BYTE ‘A’
Value2 BYTE 0
Value3 DB 255
Value4 SBYTE -128
Value5 DB +127
Value6 BYTE ? ;问号使得变量未初始化,这意味着在运行时分配数值到该变量
多初始值:
List BYTE 10,20,30,40
定义字符串:
;最常见的字符串类型是用一个空字节(值为0)作为结束标记,称为以空字节结束的字符串
Greeting1 BYTE “Good afternoon”, 0
Greeting2 BYTE “Good night”,0
Greeting3 BYTE “welcome to the Encryption Demo program ”
BYTE “created by Kip Irvine.”, 0dh,0ah
BYTE “If you wish to modify this program, please ”
BYTE “send me a copy.”,0dh,0ah,0
十六进制代码0dh和0ah也被称为CR/LF(回车换行符)或行结束字符。在编写标准输出时,它们将光标移动到当前行的下一行的左侧。
行连续字符(\)把两个源代码行连接成一条语句,他必须是一行的最后一个字符。下面的语句是等价的:
Greeting1 BYTE “welcome to the Encryption Demo program ”
Greeting1 \
BYTE “welcome to the Encryption Demo program ”
DUP操作符:
DUP操作符使用一个整数表达式作为计算器,为多个数据项分配存储空间。在为字符串或数组分配存储空间时,这个操作符非常有用,它可以使用初始化或非初始化数据。
BYTE 20 DUP(0) ;20个字节,值都为0
BYTE 20 DUP(?) ;20个字节,非初始化
BYTE 4 DUP(“STACK”) ;20个字节
WORD和SWORD----16位整数
Word1 WORD 65535 ;最大无符号数
Word2 SWORD -32768 ;最小有符号数
Word3 WORD ? ;未初始化,无符号
Word4 DW 65535
16位字数组:
通过列举元素或使用DUP操作符来创建字数组。下面的数组包含了一组数值:
myList DW 1,2,3,4,5
Array DW 5 DUP(?) ; 5个数值,未初始化
定义DWORD和SDWORD(双字32位)数据:
Val1 DD 12345678h
Val2 DD -2147483648
DWORD还可以用于声名一种变量,这种变量包含的是另一个变量的32位偏移量。如下所示,pVal包含的就是val3的偏移量:
pVal DWORD val3
32位双字数组:
myList DWORD 1,2,3,4,5
定义浮点类型:
REAL4 4字节单精度浮点变量 ; DD
REAL8 8字节双精度数值 ; DQ
REAL10 10字节扩展精度数值 ; DT
rVal1 REAL4 -1.2
rVal2 REAL8 3.2E-260
rVal3 REAL10 4.6E+4096
ShortArray REAL4 20 DUP(0.0)
变量加法程序:
.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD
.data
Firstval DD 20002000h
Secondval DD 11111111h
Thirdval DD 22222222h
Sum DD 0
.code
Main PROC
Mov eax,Firstval
Add eax,Secondval
Add eax,Thirdval
Mov Sum,eax
INVOKE ExitProcess, 0
Main ENDP
END Main
X86处理器在内存中按小端顺序(从低到高)存放和检索数据。最低有效字节存放在分配给该数据的第一个内存地址中,剩余字节存放在随后的连续内存位置中。
汇编器允许在程序中进行代码和数据的来回切换,但会使得程序变得难以阅读。
等号伪指令(符号常量):
Name = expression
通常,表达式是一个32位的整数值。当程序进行汇编,在汇编器的预处理阶段,所有出现的name都会被替换成expression。
使用等号伪指令来获得更好的编码风格
Esc_key = 27
Mov al, Esc_key ;好的风格
--------------------------
Mov al, 27 ;不好的风格
重定义:
COUNT = 5
Mov al, COUNT
COUNT = 10
Mov al, COUNT
COUNT = 100
Mov al, COUNT
当前地址计数器:
当前地址计数器表示为 $
selfPtr DWORD $
计算数组大小(必须紧跟其后):
List BYTE 10,20,30,40
ListSize = ( $ - list ) / 1
List WORD 1000h,2000h,3000h,4000h
ListSize = ( $ - list ) / 2
List DWORD 10000000h,20000000h,30000000h,40000000h
ListSize = ( $ - list ) / 4
EQU伪指令:
EQU伪指令把一个符号名称与一个整数表达式或任意文本连接起来
Name EQU expression
Name EQU symbol
Name EQU <text>
第一种格式中,expression必须是有效整数表达式(每个表达式的计算结果必须是一个整数)
第二种格式中,symbol是一个已存在的符号名称,已经用=或EQU定义过了。
第三种格式中,任何文本都可以出现在<…>内。
当汇编器在程序后面遇到Name时,他就用整数值或文本来代替符号。
PI EQU <3.1416>
PressKey EQU <”Press any key to continue...”,0>
与等号伪指令不同,在同一源代码文件中,用EQU定义的符号不能被重新定义。这个限制可以防止现有符号在无意中被赋予新值。
TEXTEQU伪指令:
TEXTEQU伪指令,类似于EQU,创建了文本宏(text macro)。
Name TEXTEQU <text>
Name TEXTEQU textmacro
Name TEXTEQU %constExpr
例如,变量prompt1使用了文本红continueMsg:
continueMsg TEXTEQU <”Do you wish to continue (Y/N)?”>
.data
Prompt1 BYTE continueMsg
文本宏可以相互构建,如下例:
rowSize = 5
count TEXTEQU %(rowSize * 2)
move TEXTEQU <mov>
setupAL TEXTEQU <move al, count>
因此,语句setupAL就会被会变为 mov al, 10
用TEXTEQU定义的符号随时可以被重新定义。
64位编程:
ExitProcess PROTO
.data
Sum DD 0
.code
Main PROC
Mov eax, 5
Add eax, 6
Mov sum, eax
Mov ecx, 0
Call ExitProcess
Main ENDP
END
与32位不同的是:
没有 .386
.model flat, stdcall
. stack 4096
64位程序中,使用PROTO关键字的语句不带参数:
32: ExitProcess PROTO, dwExitCode:DWORD
64: ExitProcess PROTO
64位MASM不支持INVOKE伪指令
END伪指令没有指定程序入口点,而32位程序则指定了
翻译 朗读 复制 正在查询,请稍候…… 重试 朗读 复制 复制 朗读 复制 via 谷歌翻译(国内) 译

浙公网安备 33010602011771号