8086汇编(16位汇编)学习笔记09.宏汇编-C/C++基础-断点社区-专业的老牌游戏安全技术交流社区 - BpSend.net
宏汇编在文件中是当做关键字的,但是在bug中运行时并没有这些指令,这些关键词被称为伪指令,cpu并不认识他们,需要经过编译器转化成 cpu认识的代码,但是他多我们写代码帮助又很大
表达式
表达式中的求值是在程序链接时完成的,所以表达式中的各值必须是在汇编或链接期就能确定,也就是 说不能将寄存器或者变量运⽤于表达式。
算术表达式
运算符 | 意义 | 例⼦ |
---|---|---|
+ | 加 | 65 + 32 |
- | 减 | size val - 54 |
* | 乘 | 23h * 65h |
/ | 除 | 98 / 45 |
mod | 取模 | 99 mod 65 |
mov ax, 54+65
mov ax, 65-48
mov ax, 65*78
mov ax, type byte * 78
mov ax, 78 mod 5
mov ax, 96 / 5
mov ax, size g_buf / type g_buf;mov ax,bx+7 ;这个是错的,因为编译器无法得到结果,不知道bx的值
逻辑运算符
逻辑运算即位运算,逻辑运算符与对应的指令助记符单词是相同的,当它们出现在操作码部分时是指 令,出现在操作数时是逻辑运算符
运算符 | 意义 |
---|---|
and | 位与 |
or | 位或 |
not | 按位取反 |
xor | 异或 |
mov ax, 5566h and 6655h
mov ax, 7788h or 8877h
mov ax, not 5566h
mov ax, 5566h xor 7788h
关系运算符
关系运算符的结果,如果结果为真,则所有位都置为1,即FFFF;否则所有位都置为0,即 .
运算符 | 英⽂ | 意义 |
---|---|---|
EQ | equal | 等于 == |
NE | not equal | 不等于 != |
GT | greater than | ⼤于 > |
LT | less than | ⼩于 < |
GE | greater than or equal | ⼤于等于 >= |
LE | less than or equal | ⼩于等于 <= |
mov bx,88 eq 88 ;bx= 0FFFFh 因为结果为turemov bx,88 ne 88 ;bx= 0000h 因为结果为flasemov bx,88 ge 88 ;bx= 0FFFFh 因为结果为ture
标号
匿名标号 @@
- @@是匿名标号
- @b 向上查找最近的@@, b是back
- @f 向下查找最近的@@ ,f是front
一般我们用 jmp 跳转你需要有标号,而且标号名必须唯一 ,但是如果 标号名是 @@ ,那么就可以重复,跳转用 @b 或 @f , @@ 的 跳转距离在段内是 没有限制的
调整偏移量指令 ORG
格式 ORG 偏移值
此指令后的下⼀个变量或指令从 偏移值 开始存放,即把一个变量或者指令放在指定的偏移内,可以提前,也可以延后
;这是栈段
stack segment stackdb 512 dup(0)
stack ends;这是数据段
data segmentg_buf db "hello world"org 20 g_buf1 db "hello world" ;表示 g_buf1 会从段偏移20的地方开始存放org 0 g_buf2 db "111111" ;表示 g_buf2 会从段偏移0的地方开始存放,回覆盖 g_buf的数据data ends;这里是代码
CODE segmentSTART:assume ds:datamov ax, datamov ds, axorg 20hmov ax,4C00H ; 带参数返回的退出 ;此处代码从20H 处开始存放,后面指令依次后推int 21h ;使用21号中断CODE endsend START
当前地址指令$
- $伪指令代表当前指令或变量的地址
- 常⽤于计算缓冲区⻓度和获取当前ip值
- 可与 ORG 配合使⽤
data_seg segmentg_buf dw 10h dup(0)g_len dw $ - offset g_buf ;获取上⾯的g_buf的⻓度org $ + 10h ;下⼀个变量从当前地址+10h的偏移开始存放g_w dw 65hdata_seg endsCODE segmentSTART:org 100hmov ax, 5566h and 6655h ;此指令从代码段偏移100h开始存放mov ax, $ ;获取 本条指令的地址mov ax, $+4;获取 本条指令的地址+4mov ax, 4c00hint 21hCODE ends
结构体 struc
格式
结构体名 struc
;这⾥定义结构体成员
结构体名 ends
结构体使⽤<>来初始化
结构体可以通过变量名和寄存器来访问成员
结构体可以在定义时候初始化,也可以在后面初始化
结构作为局部变量无法赋初值
;定义结构体
Point strucm_wX dw 0m_wY dw 0
Point ends;关键字 struc,可以在定义时候赋初值
TagStu strucm_byte db 0m_w dw 5656hm_ary db 8 dup(0)m_sz db "hello"m_pt Point <> ;结构体里面嵌套结构体
TagStu ends;堆栈
stack_seg segment stackdb 512 dup(0)
stack_seg ends;数据段
data_seg segmentg_stu TagStu <66h, 7777h, "testtest", "66", <11h, 22h>> ;初始化结构体g_ary Point 12 dup(<11h, 22h>) ;定义结构体数组
data_seg ends;代码
code_seg segment;结构体传参,直接传对象,它会将结构提对象入栈,每次入栈2字节(推荐用指针)
Foo0 PROC far c stu:TagStulea bx, stumov stu.m_byte, 99h ;修改结构体成员的值mov stu.m_w, 6666h ;修改结构体成员的值ret
Foo0 ENDP;结构体传参,传对象指针,通过指针访问必须借助寄存器,不能直接用指针
Foo1 PROC far c pStu:ptr TagStu;mov ax, [pStu]m_w ;指针,不能直接点使用,必须通过寄存器mov bx, pStu ;将结构体指针赋值给bx;通过结构体指针访问结构体成员assume bx:ptr TagStu ;将bx解释为 TagStu 结构体指针,类似强转mov ax, [bx].m_w ;通过结构体指针访问成员 assume bx:nothing ;将bx 的 解释方式转回默认 ret
Foo1 ENDP;结构体作为局部变量,局部变量无法赋初值
foo PROC far c local @stu:TagStu ;变量类型直接是结构体类型lea bx, @stu ;获取变量的偏移地址mov al, @stu.m_byte ;访问变量成员mov ax, @stu.m_w ;访问变量成员ret
foo ENDPSTART:assume ds:data_segmov ax, data_segmov ds, axmov ax, g_stu.m_pt.m_wX ;访问结构体内 结构体的的成员invoke Foo1, offset g_stu ;结构体指针作为函数参数 invoke Foo0, g_stu ;结构体对象作为函数参数invoke foo ;结构体作为局部变量mov al, g_stu.m_byte ;访问结构体成员 mov ax, g_stu.m_w ;访问结构体成员lea bx, g_stu.m_ary ;获取结构体成员的偏移地址mov byte ptr [bx], 'a' ;修改结构体成员的值mov ax, 4c00h;int 21h
code_seg ends
end START
宏
equ语句
作用:用来定义宏
不可以重命名
可⽤于常量和表达式
可⽤于字符串
可⽤于指令名,给指令取别名
可⽤于类型,给类型取别名
可⽤于操作数
;是无参宏,没有参数;堆栈
stack_seg segment stackdb 512 dup(0)
stack_seg endsPI equ 314 ;常量
SZNAME equ "hello world" ;字符串
MYMOV equ mov ;指令名
CHAR equ db ;类型
INT16 equ dw ;类型;PI equ 6677h ;不允许再次定义;数据段
data_seg segmentg_sz CHAR SZNAMEg_w INT16 PIg_w2 INT16 44h
data_seg ends;代码
code_seg segmentSTART:assume ds:data_segmov ax, data_segmov ds, axMYMOV ax, g_wmov ax, 4c00h;int 21h
code_seg ends
end START
=语句
可以被修改
只能⽤于常数
COUNT2 = 100h ;后跟数值
COUNT2 = 200h ;可以再次赋值;szTest = "hello world" ;错误,不能用于字符串,只能用于立即数(常量)mov ax, COUNT2;可以用来提高可读性MYFUNC:
nX = 2
nY = 4push bpmov bp, spsub sp, 10mov [bp-nX], axmov [bp-nY], axmov sp, bppop bpret
macro语句
格式
宏名 macro [参数1][,参数2]...
宏体
endm
宏会在使⽤的地⽅展开
宏可以带参数
字符串拼接使⽤&
;堆栈
stack_seg segment stackdb 512 dup(0)
stack_seg endsCHAR equ db
INT16 equ dw;内存到内存赋值宏
movm macro val1, val2push val1pop val2
endm;字符串拼接
shift macro d, opt, countpush cxmov cl, countsa&d opt, clpop cx
endm;数据段
data_seg segmentg_w INT16 55Hg_w2 INT16 44h
data_seg ends;代码
code_seg segmentSTART:assume ds:data_segmov ax, data_segmov ds, ax;内存到内存赋值movm g_w, g_w2movm g_w2, g_wshift r, ax, 2shift r, ax, 5shift l, bx, 4shift l, dx, 2mov ax, 4c00h;int 21h
code_seg endsend START
多文件编译
1.源文件
- 源文件后缀名为asm
- 每个源文件末尾需要有end
2.头文件
-
汇编头文件后缀名为inc
-
头文件包含 include xxx.inc
-
头文件防重复包含
ifndef SECOND_1 SECOND_1 equ 1Func1 proto far stdcall arg1 : word, arg2 : wordextern g_dw : word endif
3.函数使用
函数在源文件定义,在头文件中声明即可。
4.全局变量
全局变量在定义文件中必须使用public指明此变量为全局public变量名
全局变量在使用文件中必须使用extern 指明此变量来自外部文件 extern 变量:类型
ifndef HEADER_INC ;防止重复包含
HEADER_INC equ 1extern g_w:word ;声明明 g_w 是来自外部的全局变量endif
public g_w ;指名 g_w 是全局变量 ,不然默认本文件内才能使用(文件作用域)data_seg segmentg_w dw 0
data_seg endsend
5.编译
ml /c xx.asm yy.asm
link xx.obj yy.obj
或
ml *.asm
伪指定实现分支,循环
分支
格式
格式1:
.IF condition ;以英文“句号”开头 ;条件"condition"成立时所执行的指令序列
指令序列
.ENDIF
格式2:
.IF condition ;条件"condition"不成立时所执行的指令序列
指令序列1
.ELSE
指令序列2
.ENDIF
格式3:
.IF condition1
指令序列1
.ELSEIF condition2 ;条件"condition2"成立时所执行的指令序列
指令序列2
.ENDIF
其中:条件表达式“condition”的书写方式与C语言中条件表达式的书写方式相似,也可用括号来组成复杂的条件表达式。
条件表达式中可用的操作符有:==(等于)、!=(不等)、>(大于)、>=(大于等于)、<(小于)、<=(小于等于)、&(位操作与)、!(逻辑非)、&&(逻辑与)、||(逻辑或)等。
;堆栈
stack_seg segment stackdb 512 dup(0)
stack_seg ends;数据段
data_seg segmentdata_seg ends;代码
code_seg segmentSTART:assume ds:data_segmov ax, data_segmov ds, axmov g_w, 8.if ax >= bxxor ax, ax.elseif ax <= bxxor bx, bx.else xor cx, cx.endifmov ax, 4c00h;int 21h
code_seg endsend START
1、WHILE型循环伪指令
.WHILE condition ;条件"condition”成立时所执行的指令序列
循环体的指令序列
.ENDW
其中:.ENDW与前面的.WHILE相匹配,它标志着其循环体到此结束。
如果条件表达式“condition”在循环开始时,就为“假”(false),那么,该循环体一次也不会被执行。
2、REPEAT型循环伪指令
.REPEAT
循环体的指令序列
.UNTIL condition .REPEAT
循环体的指令序列
.UNTILCXZ [condition]
REPEAT型循环在执行完循环体后,才判定逻辑表达式condition的值。若该表达式的值为真,则终止该循环,并将执行伪指令.UNTIL[CXZ]后面的指令,否则,将向上跳转到伪指令.REPEAT之后的指令,为继续执行其循环体作准备
循环终止 break 和继续 continue
(1)、终止循环伪指令
.BREAK
.BREAK .IF condition
该伪指令用来终止包含它的最内层循环。前者是无条件终止循环,后者是仅当逻辑表达式condition为真时,才终止循环。
.WHILE 1 .REPEAT
…
.BREAK .IF condition
…
…
.BREAK .IF condition
…
ENDW .UNTIL 0
对于以上二个循环,如果没有指令来终止循环的话,它们都将进入死循环状态,但如果在该层循环体内,存在伪指令“.BREAK .IF condition”的话,那么,当逻辑表达式condition为真时,该循环就会被终止了。
(2)、循环继续伪指令
.CONTINUE
.CONTINUE .IF condition