x86汇编入门

x86汇编入门

前言

我们知道,CPU需要指令来执行对应的操作。比如在x86架构下,add指令就是加法操作,inc指令就是加一操作。不同的cpu架构(比如arm架构、mips架构、8051架构)拥有不同的指令集,x86汇编是比较常见的,所以我们用它来进行一个汇编的入门。

寄存器

寄存器是cpu里的东西,用来存储临时数据的。由于内存读取相较于cpu运算速度来说太慢(大约在百倍左右),所以cpu一般自带一级缓存、二级缓存,但是自带缓存还是不够快,于是就有了寄存器。寄存器只依靠名字区分,读取速度最快,被喻为零级缓存。
在x86架构下,cpu寄存器有以下几种

通用寄存器

数据寄存器

寄存器 名称 作用
EAX 累加寄存器 在乘法和除法指令中被自动使用,一般也用于函数返回值
EBX 基地址寄存器 存放内存寻址时的基地址
ECX 计数寄存器 在循环计数中被自动使用,一般在字符串和循环操作中常用
EDX 数据寄存器 在整数除法中被自动用来存放余数

指针变址寄存器

寄存器 名称 作用
ESP 栈指针寄存器 用于指向当前栈帧的顶部
EBP 基指针寄存器 指向当前栈帧的底部,通常用于在函数中访问局部变量和参数
ESI 源索引寄存器 常用于字符串或其它数据序列操作中的源地址
EDI 目的索引寄存器 常用于字符串或其它数据序列操作的目标地址

以上寄存器是32位的,cpu在使用数据寄存器时有时候不必使用寄存器的全部位数,所以寄存器可以只使用部分位数,所以有以下相似名称的寄存器
16位寄存器,使用32位寄存器的低16位,有AX、BX、CX、DX、SI、DI、BP、SP
8位寄存器,使用16位寄存器的高8位或低8位,高8位有AH、BH、CH、DH,低8位有AL、BL、CL、DL
除此之外,x64结构cpu的通用寄存器为RAX、RBX、RCX、RDX、RSI、RDI、RBP、RSP,32位寄存器使用64位寄存器的低32位

标志寄存器EFLAGS

标志寄存器用来存储指令的执行结果,比如结果是否为零、是否为负数,可以为下一条指令提供判断依据,比如说条件跳转指令、循环指令就可以通过EFLAGS对应标志位是否置为1来进行对应的操作。
在x86架构cpu中,标志寄存器一共有32位,其中一些位数被保留不作使用,下面介绍一些常用的标志,它们大致分为两类:状态标志和控制标志。

状态标志,用来记录程序运行结果的状态信息

标志位 名称 作用
CF 进位标志 当运算结果最高有效位进位(加法)或借位(减法)时,置为1
ZF 零标志 运算结果为0,置为1
SF 符号标志 运算结果最高位为1则置为1,无符号数最高位为1也会置为1
PF 奇偶标志 运算结果二进制中‘1’的个数为偶数,置为1
OF 溢出标志 运算结果有溢出,置为1
AF 辅助进位标志 主要用于CPU内部使用,一般不用关心

控制标志,用于处理器执行指令的方式

标志位 名称 作用
DF 方向标志 用于控制串操作指令中的地址变化方向,置为1地址正向增加,否则反向减少
IF 中断允许标志 控制CPU是否可响应外部可屏蔽中断(比如I/O设备完成操作),置为1为允许
TF 陷阱标志 通常用于程序调试,置为1后,CPU会进入单步执行方式

指令指针寄存器EIP

一般只用来存放要执行的下一条指令的地址

段寄存器

段寄存器用来存放内存中程序对应段的基地址。程序在运行时,会将自身的二进制代码还有变量数据加载到内存中,同时开辟堆空间与栈空间。在x86架构中,段寄存器共有6个,它们分别指向对应内存区域的起始位置,它们为CS(代码段)、DS(数据段)、SS(堆栈段)、ES(附加段)、FS(附加段)、GS(附加段),指向的内存区域如下:

段寄存器 指向区域 用途
CS 代码段 代码段用来存放程序运行的代码
DS 数据段 数据段用来存放程序自身的变量、常量
SS 堆栈段 堆栈段用来确定栈的内存地址,而不是堆的
ES、FS、GS 附加段 附加段可以被用来访问额外的数据,例如字符串操作或其它需要额外数据段的操作

常用指令

寻址方式

在清楚的了解各种指令之前,我们先来了解一下CPU的寻址方式。
cpu可以直接从寄存器读取、从cpu内部缓存读取、通过内存总线从内存读取,所以我们可以看到它有以下寻址方式:

寻址方式 介绍
直接寻址 在指令中直接给出操作数的内存地址,比如访问数据段中的常量
间接寻址 指令中给出一个地址,该地址指向操作数所在的内存地址,可以认为是C语言中通过指针取变量的值
立即寻址 操作数直接在指令中
寄存器寻址 操作数在cpu寄存器中
寄存器间接寻址 寄存器中为指向操作数内存的地址,类似于间接寻址
寄存器相对寻址 基址寄存器的地址加上指令中的偏移量就是操作数地址
基址变址寻址 基址寄存器加变址寄存器中的偏移量就是操作数地址
堆栈寻址 直接通过堆栈指针寄存器来访问

堆栈操作指令及堆栈段

我们再来复习一下堆栈段,想要理解汇编,对于汇编在堆栈段的操作上是必不可少的,因为程序主要就在这块内存区域入栈、出栈。
堆栈段是一块先进后出的内存区域,可以理解为数据结构中的栈。既然是栈,就有入栈和出栈。在高级语言进行函数调用时,会将返回地址、函数参数入栈,然后在栈中开辟一段空间,形成栈帧,更新基指针寄存器与栈指针寄存器的值,分别让它们重新指向当前栈帧的底部与顶部。
入栈、出栈指令如下:

指令 作用 用法
push 将操作数入栈 push 操作数
pop 将操作数出栈到对应地址 pop 内存地址/寄存器

数据传递指令

内存与内存之间不能直接进行数据传递

指令 作用 用法
mov 数据传输,相当于赋值 mov 目的操作数,源操作数
xchg 交换两个操作数的数据 xchg 源操作数,目的操作数
lea 载入有效地址到寄存器 lea 寄存器,地址

逻辑运算指令

指令 作用 用法
and 将两个数进行按位与操作的结果放在目的操作数 and 目的操作数,源操作数
or 将两个数进行按位或操作的结果放在目的操作数 or 目的操作数,源操作数
not 将操作数按位取反 not 操作数
xor 将两个数按位异或后的结果放在目的操作数 xor 目的操作数,源操作数

算术运算指令

指令 作用 用法
add 两操作数相加,结果存在目的操作数 add 目的操作数,源操作数
sub 两操作数相减,结果存在目的操作数 sub 目的操作数,源操作数
inc 加一指令 inc 操作数
dec 减一指令 dec 操作数
mul 将操作数与eax寄存器相乘 ,结果存在eax寄存器中 mul 操作数
div 将eax寄存器除于操作数,商存在eax,余数存在edx div 操作数

转移相关指令

指令 作用 用法
jmp 无条件转移到指定地方 jmp 地址
loop 循环控制,若ecx寄存器不为0,跳转,否则执行下一条 loop 地址
call 调用函数,会将下一条指令地址保存至堆栈 call 函数地址
ret 函数返回,与call配合使用 ret

条件转移指令
条件转移指令有多条,称为jcc指令集。它们用法与jmp差不多,但是只根据EFLAG标志寄存器中的值来决定是否跳转,以下列出了几个常见条件转移指令

指令 作用 检查符号位
jz 若为0则跳转 ZF=1
jnz 若不为0则跳转 ZF=0
js 若为负/符号位为1则跳转 SF=1
jns 若不为负则跳转 SF=0
je 若相等则跳转 ZF=1
jne 若不等则跳转 ZF=0

通常配合使用的指令

指令 作用 用法
test 将两操作数进行逻辑与运算 test 操作数1,操作数2
cmp 两操作数比较,目的操作数减去源操作数 cmp 目的操作数,源操作数

结语

以上只是大致介绍了一下x86汇编,想要深入下去还是要多看看实际程序的汇编代码是怎样的,高级语言对应语句的汇编又是怎样实现的。看得多了,自然就熟悉了。

posted @ 2025-04-07 20:16  上午微憩  阅读(79)  评论(0)    收藏  举报