32位汇编

调用约定 cdecl, stdcall, fastcall Link to heading

cdeclstdcallfastcall 是三种常见的函数调用约定,主要在参数传递、栈清理和寄存器使用方面有所不同。以下是它们的详细区别:

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

  • 参数传递:前两个参数通过 ecxedx 传递,其余从右到左压栈。
  • 栈清理:由被调用者清理栈。
  • 寄存器使用:使用 ecxedx 传递前两个参数。
  • 返回值:通过 eax 返回。
  • 可变参数:不支持可变参数函数。

示例

void __fastcall func(int a, int b, int c);

调用时:

mov ecx, a
mov edx, b
push c
call func
; 被调用者清理栈

总结对比 Link to heading

特性cdeclstdcallfastcall
参数传递从右到左压栈从右到左压栈前两个通过 ecxedx,其余压栈
栈清理调用者清理被调用者清理被调用者清理
寄存器使用不使用不使用使用 ecxedx
返回值eaxeaxeax
可变参数支持不支持不支持

适用场景 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

  • 状态反馈:通过状态标志,程序可以判断算术和逻辑操作的结果(如是否为零、是否有进位等)。
  • 流程控制:通过条件跳转指令(如 jzjnzjc 等),程序可以根据标志位改变执行流程。
  • 系统管理:通过控制标志和系统标志,操作系统可以管理中断、调试和任务切换等。

示例 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

  1. 无参数 ret

    • 从栈顶弹出返回地址,并跳转到该地址继续执行。
    • 通常用于无参数函数的返回。
  2. 带参数 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 指令与条件跳转指令(如 jejne)一起使用,用于比较两个值并根据结果执行不同的操作。

根据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。