32位汇编
调用约定 cdecl, stdcall, fastcall Link to heading
cdecl、stdcall 和 fastcall 是三种常见的函数调用约定,主要在参数传递、栈清理和寄存器使用方面有所不同。以下是它们的详细区别:
1. cdecl (C Declaration) Link to heading
- 参数传递:参数从右到左压栈。
- 栈清理:由调用者清理栈。
- 寄存器使用:通常不使用寄存器传递参数。
- 返回值:通过
eax返回。 - 可变参数:支持可变参数函数(如
printf)。
示例:
void __cdecl func(int a, int b);
调用时:
push b
push a
call func
add esp, 8 ; 调用者清理栈
2. stdcall (Standard Call) Link to heading
- 参数传递:参数从右到左压栈。
- 栈清理:由被调用者清理栈。
- 寄存器使用:通常不使用寄存器传递参数。
- 返回值:通过
eax返回。 - 可变参数:不支持可变参数函数。
示例:
void __stdcall func(int a, int b);
调用时:
push b
push a
call func
; 被调用者清理栈
3. fastcall Link to heading
- 参数传递:前两个参数通过
ecx和edx传递,其余从右到左压栈。 - 栈清理:由被调用者清理栈。
- 寄存器使用:使用
ecx和edx传递前两个参数。 - 返回值:通过
eax返回。 - 可变参数:不支持可变参数函数。
示例:
void __fastcall func(int a, int b, int c);
调用时:
mov ecx, a
mov edx, b
push c
call func
; 被调用者清理栈
总结对比 Link to heading
| 特性 | cdecl | stdcall | fastcall |
|---|---|---|---|
| 参数传递 | 从右到左压栈 | 从右到左压栈 | 前两个通过 ecx、edx,其余压栈 |
| 栈清理 | 调用者清理 | 被调用者清理 | 被调用者清理 |
| 寄存器使用 | 不使用 | 不使用 | 使用 ecx、edx |
| 返回值 | eax | eax | eax |
| 可变参数 | 支持 | 不支持 | 不支持 |
适用场景 Link to heading
- cdecl:适用于需要可变参数的函数,如
printf。 - stdcall:常用于 Windows API,简化调用者代码。
- fastcall:适用于性能要求高的场景,减少栈操作。
EFLAGS 寄存器 Link to heading
EFLAGS 是 x86 架构中的一个 32 位寄存器,用于存储处理器的状态和控制信息。它包含多个标志位,每个标志位反映了 CPU 操作的特定状态或控制某些操作的行为。EFLAGS 寄存器在程序执行、调试和系统控制中起着关键作用。
EFLAGS 的主要标志位
Link to heading
EFLAGS 寄存器包含多个标志位,以下是一些重要的标志位及其作用:
1. 状态标志(Status Flags) Link to heading
这些标志反映算术和逻辑操作的结果。
- CF (Carry Flag):进位标志。表示无符号数运算的进位或借位。
- PF (Parity Flag):奇偶标志。表示结果中 1 的个数是否为偶数。
- AF (Auxiliary Carry Flag):辅助进位标志。用于 BCD 运算,表示低 4 位的进位或借位。
- ZF (Zero Flag):零标志。表示操作结果是否为零。
- SF (Sign Flag):符号标志。表示操作结果的符号(最高位)。
- OF (Overflow Flag):溢出标志。表示有符号数运算是否溢出。
2. 控制标志(Control Flags) Link to heading
这些标志控制处理器的行为。
- DF (Direction Flag):方向标志。控制字符串操作的方向(
std设置 DF=1,从高到低;cld清除 DF=0,从低到高)。 - IF (Interrupt Enable Flag):中断允许标志。控制是否响应可屏蔽硬件中断。
- TF (Trap Flag):陷阱标志。用于单步调试,设置后每条指令执行后触发调试异常。
3. 系统标志(System Flags) Link to heading
这些标志用于操作系统和系统管理。
- IOPL (I/O Privilege Level):I/O 特权级别。决定当前程序执行 I/O 指令的权限。
- NT (Nested Task):嵌套任务标志。表示当前任务是否嵌套在另一个任务中。
- RF (Resume Flag):恢复标志。用于调试异常后恢复执行。
- VM (Virtual 8086 Mode):虚拟 8086 模式标志。表示处理器是否运行在虚拟 8086 模式。
- AC (Alignment Check):对齐检查标志。启用时检查内存访问是否对齐。
- VIF (Virtual Interrupt Flag):虚拟中断标志。虚拟模式下的中断允许标志。
- VIP (Virtual Interrupt Pending):虚拟中断挂起标志。虚拟模式下是否有挂起的中断。
- ID (Identification Flag):标识标志。表示 CPU 是否支持
CPUID指令。
EFLAGS 的作用
Link to heading
- 状态反馈:通过状态标志,程序可以判断算术和逻辑操作的结果(如是否为零、是否有进位等)。
- 流程控制:通过条件跳转指令(如
jz、jnz、jc等),程序可以根据标志位改变执行流程。 - 系统管理:通过控制标志和系统标志,操作系统可以管理中断、调试和任务切换等。
示例 Link to heading
以下是一个简单的汇编代码示例,展示如何通过 EFLAGS 判断操作结果:
mov eax, 10
sub eax, 20 ; eax = -10
; 此时 EFLAGS 的状态:
; CF = 1(借位)
; ZF = 0(结果不为零)
; SF = 1(结果为负)
; OF = 0(无溢出)
总结 Link to heading
EFLAGS 寄存器是 x86 架构中用于存储处理器状态和控制信息的关键组件。通过状态标志、控制标志和系统标志,EFLAGS 在程序执行、调试和系统管理中发挥着重要作用。理解 EFLAGS 的标志位及其作用,对于编写和调试汇编代码至关重要。
EAX 寄存器 Link to heading
eax 经常作为函数的返回值,如果遇到
mov eax, 0
或者
xor eax, eax
这种情况,通常是将 eax 寄存器清零,以便作为函数的返回值。
跳转指令 Link to heading
[!info] 跳转指令通常与比较指令一起使用,用于根据比较结果执行不同的操作。
jmp 指令 Link to heading
jmp 指令用于无条件跳转到指定地址,是汇编语言中最基本的跳转指令。jmp 指令的语法如下:
jmp <目标地址>
jne 指令 Link to heading
jne 是根据 Zero Flag (ZF) 的状态决定是否跳转。如果 ZF 为 0(即上一条指令的结果不为零或不相等),则跳转到指定地址;否则,继续执行下一条指令。
jne <目标地址>
je 指令 Link to heading
je 是根据 Zero Flag (ZF) 的状态决定是否跳转。如果 ZF 为 1(即上一条指令的结果为零或相等),则跳转到指定地址;否则,继续执行下一条指令。
je <目标地址>
ret 指令 Link to heading
ret 指令用于从函数或子程序返回到调用者,并可带有可选的字节数参数,形式为 ret n,其中 n 是一个立即数。
ret <n>
作用 Link to heading
无参数
ret:- 从栈顶弹出返回地址,并跳转到该地址继续执行。
- 通常用于无参数函数的返回。
带参数
ret n:- 除了弹出返回地址,还会将栈指针
esp增加n字节,用于清理调用者压入栈的参数。 - 常用于调用约定要求调用者清理栈的情况(如
stdcall)。
- 除了弹出返回地址,还会将栈指针
具体来说:
ret:- 弹出返回地址到
eip。 esp增加 4 字节(32 位地址)。
- 弹出返回地址到
ret n:- 弹出返回地址到
eip。 esp增加n + 4字节(4 字节用于地址,n字节用于参数)。
- 弹出返回地址到
ret:适用于调用者不清理栈的情况(如cdecl调用约定)。ret n:适用于被调用者清理栈的情况(如stdcall调用约定)。
比较指令 Link to heading
cmp 指令 Link to heading
cmp 指令用于比较两个操作数的大小,实际上是通过减法来实现的,但不保存结果,只更新标志寄存器。cmp 指令的语法如下:
cmp dest, src
cmp会将 dest - src 的结果保存在标志寄存器中,但不保存结果到目标操作数。通常,cmp 指令与条件跳转指令(如 je、jne)一起使用,用于比较两个值并根据结果执行不同的操作。
根据dest - src的结果,cmp 指令会更新以下标志位:
| 标志位 | 名称 | 说明 |
|---|---|---|
| ZF | 零标志 | 如果结果为 0,则 ZF = 1;否则 ZF = 0。 |
| SF | 符号标志 | 如果结果为负数,则 SF = 1;否则 SF = 0。 |
| CF | 进位标志 | 如果操作导致借位(无符号数下溢),则 CF = 1;否则 CF = 0。 |
| OF | 溢出标志 | 如果结果溢出(有符号数溢出),则 OF = 1;否则 OF = 0。 |
| PF | 奇偶标志 | 如果结果的最低字节中有偶数个 1,则 PF = 1;否则 PF = 0。 |
| AF | 辅助进位标志 | 如果结果的低 4 位向高 4 位有进位或借位,则 AF = 1;否则 AF = 0。 |
CMP常见的配合跳转指令 Link to heading
| 指令 | 说明 | 跳转条件 |
|---|---|---|
| JE | 等于时跳转 | ZF = 1 |
| JNE | 不等于时跳转 | ZF = 0 |
| JG | 大于时跳转(有符号数) | ZF = 0 且 SF = OF |
| JGE | 大于或等于时跳转(有符号数) | SF = OF |
| JL | 小于时跳转(有符号数) | SF ≠ OF |
| JLE | 小于或等于时跳转(有符号数) | ZF = 1 或 SF ≠ OF |
| JA | 大于时跳转(无符号数) | CF = 0 且 ZF = 0 |
| JAE | 大于或等于时跳转(无符号数) | CF = 0 |
| JB | 小于时跳转(无符号数) | CF = 1 |
| JBE | 小于或等于时跳转(无符号数) | CF = 1 或 ZF = 1 |
条件设置指令 Link to heading
setne 指令 Link to heading
setne 指令用于根据 ZF 标志位的状态设置一个字节寄存器的值。如果 ZF 为 0(即上一条指令的结果不为零或不相等),则将目标寄存器的值设置为 1;否则,设置为 0。
setne al
- AL: 是 8 位寄存器 AX 的低 8 位部分。
因此,SETNE AL 的意思是:
如果 零标志(ZF) 为 0(即前一条比较或算术指令的结果不相等),则将 AL 设置为 1。
如果 零标志(ZF) 为 1(即前一条比较或算术指令的结果相等),则将 AL 设置为 0。