intel-x86
- 前言
- 汇编注意
- 变量概念
- 保留字
- 预定义说明内容
- 寄存器
- assume
- $当前地址计数器(有点指针的味道)
- EQU(类似define)
- INC | DEC
- MOV 操作字符串
- 64位MOV指令
- 4种基本I/O控制方式
- MOVZX | MOVSX
- LAHF | SAHF 保存eflags寄存器
- XCHG
- NEG
- ALIGN
- LENGTHOF运算符
- 硬件如何检测溢出?
- LABEL伪指令(类似替代下方附近的别名)
- LOOP
- PUSHFD | POPFD (操作eflags)
- PUSHAD | POPAD(操作通用寄存器)
- USES运算符
- Microsoft x64 调用规范
- TEST 指令
- NOT
- CMP
- STC | CLC (控制CF)
- STD | CLD(DF)
- STI | CLI(interrupt register)
- 条件跳转指令可以分为四个类型
- AND | OR 作用
- 字节宽度转换指令
- 位移动(bit shifting)
- ADC | SBB
- ENTER | LEAVE
- ADDR (配合INVOKE)
- 函数三部曲
- 操作字符串
前言
本篇基于X86架构,Intel格式。
AT&T格式语义上差不多,语法上特征%。
汇编注意
- 伪指令
- 保留字
- 标识符
- 模块:OEJ文件。
- 条件转移范围-128—127。
- BCD码的本质是十进制数 //二进制编码的十进制数。
- 被显示的字符串必须以‘$’结束。
- 以字母开头的十六进制数必须加个前置 0,以防汇编器将其解释为标识符。
- 掩码是一串二进制代码对目标字段进行位与运算,屏蔽当前的输入位。
- 列表文件(信息很多)
汇编器读取源文件,并生成目标文件,即对程序的机器语言翻译。或者,它也会生成列表文件。
列表文件 (listing file) 包括了程序源文件的副本,再加上行号、每条指令的数字地址、每条指令的机器代码字节(十六进制)以及符号表。
变量概念
内存单元的符号地址。
- 变量最重要的三个属性
段属性
偏移属性
类型属性
保留字
$
?
@B(预定义符号)
@F
等等查表说明书
预定义说明内容
.386 //使用386指令集
.model flat //使用flat平坦内存模式
option casemap:none //大小写敏感
寄存器
- 特别eflags(非所有)
PF 低8位有效 //奇偶标志位PF检测最低有效字节(低8位)中 1 的个数。
AF Bit3向Bit4有进位(借位)
-
间址寄存器
BX,BP,SI,DI -
控制标志位3个
TF(Trap Flag)
IF(Intcrrupt Flag)
DF(Direction Flag)
assume
assume 将段名和对应的段寄存器关联起来
$当前地址计数器(有点指针的味道)
最重要的符号之一被称为当前地址计数器(current location counter),表示为 $。
例如,下面的语句声明了一个变量 selfPtr,并将其初始化为该变量的偏移量:
selfPtr DWORD $
EQU(类似define)
INC | DEC
INC 和 DEC 指令不会影响进位标志位CF。
MOV 操作字符串
Di必须属于ES段
64位MOV指令
-
类一,MOV RAX 常量
操作常数时,多出来的高位清零。 -
类二,MOV RAX 内存操作数
8位或16位内存操作数送入 RAX 的低位,但目标寄存器的高位不受影响。
4种基本I/O控制方式
无条件传送
查询工作方式
中断方式
DMA方式
MOVZX | MOVSX
MOVZX无符号扩展传送
MOVSX有符号扩展传送
LAHF | SAHF 保存eflags寄存器
LAHF加载EFLAGS寄存器内容到AH
SAHF保存AH内容到EFLAGS寄存器
XCHG
XCHG交换数据(不能内存和内存交换)
NEG
NEG(非)指令通过把操作数转换为其二进制补码,将操作数的符号取反。
在非零操作数上应用 NEG 指令总是会将进位标志CF位置 1。
ALIGN
ALIGN伪指令:对齐一个变量
注:靠填充CC对齐。
LENGTHOF运算符
如果数组定义占据了多行,那么 LENGTHOF 只针对第一行定义的数据。
硬件如何检测溢出?
计算结果的最高有效位产生的进位(C即F位)与结果的最高位进行 异或操作,异或的结果存入溢岀标志位。
LABEL伪指令(类似替代下方附近的别名)
插入一个标号,并定义它的大小属性,但是不为这个标号分配存储空间。
LOOP
两步:
- ECX 减 1。
- 将 ECX 与 0 比较。
循环目标必须距离当前地址计数器 -128 到 +127 字节范围内。
PUSHFD | POPFD (操作eflags)
PUSHFD 指令把 32 位 EFLAGS 寄存器内容压入堆栈。
POPFD 指令则把栈顶单元内容弹出到 EFLAGS 寄存器。
PUSHAD | POPAD(操作通用寄存器)
PUSHAD 指令按照 EAX、ECX、EDX、EBX、ESP(执行 PUSHAD 之前的值)、EBP、ESI 和 EDI 的顺序,将所有 32 位通用寄存器压入堆栈。
POPAD 指令按照相反顺序将同样的寄存器弹出堆栈。
USES运算符
USES 运算符与 PROC 伪指令一起使用,让程序员列出在该过程中修改的所有寄存器名(即保护此寄存器原先的环境)。
USES 告诉汇编器做两件事情:
- 第一,在过程开始时生成 PUSH 指令,将寄存器保存到堆栈;
- 第二,在过程结束时生成 POP 指令,从堆栈恢复寄存器的值。
Microsoft x64 调用规范
Microsoft 在 64 位程序中使用统一模式来传递参数并调用过程,称为 Microsoft x64 调用规范。
程序员只有在调用 Windows API 的函数或用 C/C++ 编写的函数时,才会使用这个调用规范。该调用规范的一些基本特性如下所示:
-
CALL 指令将 RSP(堆栈指针)寄存器减 8,因为地址是 64 位的。
-
前四个参数依序存入 RCX、RDX、R8 和 R9 寄存器,并传递给过程。如果只有一个参数,则将其放入 RCX。如果还有第二个参数,则将其放入 RDX,以此类推。其他参数,按照从左到右的顺序压入堆栈。
-
调用者的责任还包括在运行时堆栈分配至少 32 字节的影子空间(shadow space),这样,被调用的过程就可以选择将寄存器参数保存在这个区域中。
-
在调用子程序时,堆栈指针(RSP)必须进行 16 字节边界对齐(16 的倍数)。CALL 指令把 8 字节的返回值压入堆栈,因此,除了已经减去的影子空间的 32 之外,调用程序还必须从堆栈指针中减去 8。
TEST 指令
TEST 指令是一种非破坏性的 AND 操作,并适当地设置 CPU 标志位。
NOT
NOT 指令不影响标志位。
CMP
CMP(比较)指令执行从目的操作数中减去源操作数的隐含减法操作,并且不修改任何操作数。
STC | CLC (控制CF)
stc CF进位标志位置 1
clc CF进位标志位清零
STD | CLD(DF)
STD 将方向标志位DF置1
CLD 将方向标志位DF清零
STI | CLI(interrupt register)
STI 允许中断发生
CLI 禁止中断发生
这两个指令只能在内核模式下执行,不可以在用户模式下执行
条件跳转指令可以分为四个类型
基于特定标志位的值跳转
基于两数是否相等,或是否等于(E)CX 的值跳转
基于无符号操作数的比较跳转
基于有符号操作数的比较跳转
AND | OR 作用
AND 屏蔽作用
OR 保留作用
字节宽度转换指令
CBW 字节转字指令
CWD 字转双字指令
CDQ 双子转四字
位移动(bit shifting)
逻辑左移 | 逻辑右移 |
---|---|
SHL逻辑左移 | SHR逻辑右移 |
逻辑移位 (logic shift),空出来的位用 0 填充
算术左移 | 算术右移 |
---|---|
SAL算术左移 | SAR算术右移 |
算术移位 (arithmetic shift),空出来的位用原数据的符号位填充
循环左移 | 循环右移 |
---|---|
ROL循环左移 | ROR循环右移 |
最高(低)位复制到进位标志位和最低(高)位
左移 | 右移 |
---|---|
RCL带进位的循环左移 | RCR带进位的循环右移 |
SHLD双精度左移 | SHRD双精度右移 |
ADC | SBB
ADC指令:带进位加法
SBB指令:带借位减法
ENTER | LEAVE
-
ENTER 指令为被调用过程自动创建堆栈帧。
把 EBP 入栈 (push ebp)
把 EBP 设置为堆栈帧的基址 (mov ebp, esp)
为局部变量保留空间 (sub esp, numbytes)
用法:ENTER numbytes, nestinglevel
这两个操作数都是立即数。Numbytes 总是向上舍入为 4 的倍数,以便 ESP 对齐双字边界。
Nestinglevel 确定了从主调过程堆栈帧复制到当前帧的堆栈帧指针的个数。 -
LEAVE 指令反转了之前的 ENTER 指令操作:恢复了过程被调用时 ESP 和 EBP 的值。
ADDR (配合INVOKE)
ADDR 运算符只能与 INVOKE 一起使用,
在使用 INVOKE 调用过程时,它可以传递指针参数。
函数三部曲
PROC 定义
PROTO 声明
INVOKE 调用
操作字符串
注:
[x]:B,W,D
movsx 传送字符串
cmpsx 比较字符串
scasx 扫描字符串
stosx 保存字符串
lodsx 从字符串加载到累加器
配合REP指令使用
REPNE SCASB 不相等则重复查找的字符串搜索指令,
在使用此指令之前bai要指du定:查找的基址DS,偏移指针DI,和查找长度CX以及目标字符AL(以ascii存放)
扫描DS:DI所指字符是否与AL中字符匹配,若不匹配,则零标志ZF=0,DI++,CX–直到找到匹配的字符或者找遍整个指定的区域(即CX为0为止)
若找到了与AL值相等的字符ascii码,改变ZF=1,退出查找
LODSB、LODSW 和 LODSD 指令分别从 ESI 指向的内存地址加载一个字节或一个字到 AL/AX/EAX。
ESI 按照方向标志位的状态递增或递减。