详细介绍:汇编传参和调用约定

基础知识

原码、反码、补码

原码、反码、补码是表示带符号整数的不同表示方法。

  • 原码:直接使用最高位表示数值的正负
    5: 00000101
    -5:10000101

  • 反码
    对原码的数值部分(符号位除外)取反表示负数。
    +5:00000101,正数反码与原码相同,不取反。
    -5:10000101,取反得到11111010

  • 补码
    正数的补码与原码相同。
    -5:10000101,先取反11111010,然后加1得到补码11111011

十六进制

位数术语说明常见数据类型
8位BYTE基本数据单位,1字节char
16位WORD2字节(通常是处理器的基本数据单位)short
32位DWORD4字节(双字)intlong
64位QWORD8字节(四字)long long
128位(Octa Word)16字节(较少见,通常用于SIMD)SIMD 寄存器
256位(YMM)32字节,通常用于SIMD(如AVX2)SIMD 寄存器
512位(ZMM)64字节,通常用于SIMD(如AVX - 512)SIMD 寄存器

x86架构

寄存器分类

通用寄存器32位

  • EAX:累加寄存器,用于算术运算、I/O操作。Windows代码中通常用于存放返回值。
  • EBX:基址寄存器
  • ECX:计数寄存器,用于循环寄存器,移位操作。
  • EDX:数据寄存器,存储乘法、除法结果
  • ESI:源索引寄存器,指向源操作数,尤其是字符串操作。
  • EDI:目的索引寄存器,指向目标操作数,尤其是字符串操作。
  • EBP:基指针寄存器
  • ESP:堆栈指针寄存器,指向栈顶。

al、ah、bl、bh、cl、ch、dl、dh

指针寄存器

  • EIP:指令指针寄存器,指向CPU正在执行的下一条指令的内存地址。在64位中,叫做RIP
    EIP存在于CPU中,没有汇编指令可以直接修改它。

  • VT技术可以修改EIP

标志寄存器

缩写全称中文名称功能说明
CFCarry Flag进位标志如果运算的结果的最高位产生了一个进位或者借位,那么值为1,否则为0
ZFZero Flag零标志表示运算结果是否为零
SFSign Flag符号标志表示运算结果的符号位(可辅助判断正负,结合结果最高位,0 正、1 负等场景)
OFOverflow Flag溢出标志表示加法或减法是否发生溢出(超出数据类型表示范围)
PFParity Flag偶校验标志用于检查运算结果二进制中 1 的个数是否为偶数
DFDirection Flag方向标志控制字符串操作方向(如增量/减量,决定指针移动方式)
AFAuxiliary Carry Flag辅助进位标志常用于 BCD(二进制编码的十进制)运算,辅助判断低 4 位进位情况
  • 溢出
    正+正=负,有溢出
    负+负=正,有溢出
    正+负,无溢出

浮点寄存器

  • ST-0到ST-7
  • FPU:用于执行浮点数运算

调试寄存器

寄存器用途说明
DR0 ~ DR3存储硬件断点地址,调试器可在此设断点,监视内存访问/指令执行
DR4, DR5已保留,现代处理器中作保留字段,不再使用
DR6调试状态寄存器,存储断点触发状态信息,用于判断哪个断点被触发
DR7调试控制寄存器,设置硬件断点条件(读/写/执行等)及权限(全局/本地断点)

FS和TEB寄存器

  • fs[0]:默认值是TEB的基地址

x64寄存器参数顺序

rdi:第 1 个参数
rsi:第 2 个参数
rdx:第 3 个参数
rcx:第 4 个参数
r8:第 5 个参数
r9:第 6 个参数

内存分布

指令

操作符

算术操作符

操作符功能说明示例代码
add加法add eax, ebx
sub减法sub eax, ecx
mul无符号乘法mul ecx
imul有符号乘法imul eax, ecx
div无符号除法div ecx
idiv有符号除法idiv ecx
inc自增(increase)inc eax
dec自减(decrease)dec eax
除法
div ax,r/m8    ;ax除以r/m8,al是商,ah是余数。
div eax,r/m32  ;eax是商,edx是余数。

位操作符

操作符功能示例说明
AND按位与
OR按位或
XOR按位异或
SHL逻辑左移shl eax,1所有位向左移动一位,空出位置填0,最左边丢弃
SHR逻辑右移
sal有符号左移
sar有符号右移
rol循环左移rol eax,1所有位向左移动一位,最左边移动到末尾
ror循环右移ror eax,1

数据传输指令

操作符功能描述示例代码
mov将数据从源传输到目的位置mov eax, ebx
push将数据压入栈push eax
pop从栈中弹出数据到目的位置pop eax
xchg交换两个操作数的值xchg eax, ebx
lea取地址lea eax, [ebx+4]

逻辑比较指令

操作符功能描述示例代码
cmp比较,减法cmp eax, ebx
test(按位与,不存结果,仅设置标志)test eax, eax
CPM eax,ebx // eax-ebx
//如果eax > ebx,通过JG跳转。
CF:(Carry Flag)
设置条件:发生借位,第一个操作数小于第二个。
清楚条件:未发生借位。
ZF:(Zero Flag)
设置条件:两个操作数相等(即结果为0)
清除条件:两个操作数不相等。
SF:(Sign Flag)
设置条件:结果为负(即最高有效位为1)
清除条件:结果为正或零
OF:(Overflow Flag)
设置条件:有符号比较时发生溢出(即正数减负数得到负数,或负数减正数得到正数)
清除条件:未发生溢出
PF:(Parity Flag)
设置条件:结果的最低字节中,1的位数是偶数。
清除条件:结果的最低字节中,1的位数是奇数。
AF:(Auxiliary Carry Flag)
设置条件:在低4位操作中发生借位。
清除条件:未发生借位。
test eax,eax //若eax为0,JZ跳转
test eax,0x01 //若eax最低为为1,JZ跳转

控制转移指令

操作符功能说明依赖标志位及条件示例代码
call调用子程序(执行完返回原位置)无(直接调用,自动压栈返回地址)call 0x00401000
ret返回调用点(配合 call)无(从栈弹出返回地址)ret
jmp无条件跳转(不依赖标志位)无(直接跳转)jmp 0x00401000
jbCF==1
jbeCF1 or ZF1
je/jz相等/零标志置位时跳转ZF= 1(结果为 0/相等)je 0x00401020
jne/jnz不等/零标志未置位时跳转ZF= 0(结果非 0/不等)jne 0x00401020
jle/jngZF==1 or SF!=OF
jg大于时跳转(有符号比较)SF= OF or ZF=0jg 0x00401020
jl小于时跳转(有符号比较)SF≠ OFjl 0x00401020
jge/jnl大于等于时跳转(有符号比较)SF= OF or ZF=1jge 0x00401020
jle小于等于时跳转(有符号比较)SF≠ OF or ZF=1jle 0x00401020
loop循环跳转(配合计数器)计数寄存器(ECX)减 1 后 ≠ 0loop 0x00401020
int调用中断(触发系统/硬件中断)无(主动触发中断)int 0x80
iret从中断返回(恢复状态)无(恢复 EFLAGS/CS/EIP 等)iret

栈操作指令

指令功能描述ESP 变化(32位环境)补充说明 & 场景
PUSH将寄存器、立即数、内存值压入栈顶(如 PUSH EAXPUSH 0x123ESP -= 4常用于保存临时数据、函数调用传参
POP从栈顶弹出数据,存入寄存器/内存(如 POP EBXESP += 4配合 PUSH 恢复数据,需注意栈平衡
PUSHAD按顺序压入通用寄存器:EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDIESP -= 32(8个寄存器×4字节)快速保存全部通用寄存器(调试/上下文切换)
POPAD按顺序弹出恢复通用寄存器:EDI, ESI, EBP, ESP(跳过), EBX, EDX, ECX, EAXESP += 32PUSHAD 配对,注意 ESP 会被跳过
PUSHFD将32位标志寄存器(EFLAGS)压入栈ESP -= 4保存标志位状态(如调试、异常处理)
POPFD从栈弹出值恢复32位EFLAGS寄存器ESP += 4PUSHFD 配对,恢复标志位
CALL 地址调用子程序:压入返回地址(EIP)→ 跳转到目标地址ESP -= 4(压入返回地址)函数调用核心指令,返回地址用于 RET
RET从栈弹出返回地址 → 跳转到该地址(可选带立即数平衡栈,如 RET 4ESP += 4(或更多)函数返回,RET N 用于跳过函数调用前压入的参数
ENTER设置栈帧:压入旧 EBP → 新 EBP = ESP → 分配局部变量空间(如 ENTER 8,0ESP -= 4 + 局部变量大小替代 MOV EBP, ESP + SUB ESP, N,较少用
LEAVE恢复栈帧:ESP = EBP → 弹出旧 EBPESP += 局部变量大小 + 4ENTER 配对,快速清理栈帧
INT imm8软中断:压入 EFLAGS、CS、EIP → 跳转到中断向量表ESP -= 12(32位环境)触发系统调用(如 INT 0x80 是Linux传统系统调用)

字符串操作指令

mov

mov eax,offset str_hello;    加载"hello"字符串的地址到EAX
mov [edi],eax           ;    把这个地址存入到EDI指向的位置

movs

movs是一组字符串操作指令,表示“移动字符串数据”。

movsb:移动一个字节
movsw:移动一个字
movsd:移动一个双字
movsq:(64位模式下使用)移动一个四字

repmovsb

rep movsb:批量复制字符串数据。

mov ecx,length     ;设置要复制的字节数
mov esi,source     ;源字符串地址
mov edi,destination;目标字符串地址
rep movsb          ;批量复制字符串

rep系列指令

rep:重复执行后续指令,直到ECX或CX寄存器的值为0
repe/repz:(Repeat While Equal/Repeat While Zero)两者等价在ZF为1,且ECX不为0时执行。
repne/repnz:(Repeat While Not Equal/Repeat While Not Zero)两者等价,当ZF不为0,且ECX不为0时执行。

cmp指令

mov esi,offset str_userinput ; 用户输入字符串
mov edi,offset str_password  ; 目标字符串
mov ecx,length               ; 设置比较的字节数
cld                          ; 清除方向标志
repe cmpsb                   ; 比较两段内存的字符串
je match                     ; 如果相等跳转

scas指令

扫描字符串中的特定字符

扫描方向由DF确定,cld清除DF从低地址扫描,std设置DF从高地址扫描。

mov edi,offset str_buffer ; EDI指向要扫描的字符串
mov al,'A'                ; 查找指定字符
mov ecx,length_of_string  ; ECX设置为字符串长度
repne scasb               ; 扫描直到找到‘A’或结束
jnz not_found             ; 未找到执行
xxxx                      ; 找到执行

stos指令

向目标地址填充数据,通常用于清空字符串或填充特定值。

mov edi,offset buffer ; 目标地址
mov al,0              ; 填充为0
mov ecx,size          ; 填充大小
cld                   ; 设置方向标志递增
req stosb             ; 填充字符串

loads指令

lodsb:加载一个字节到AL,并将ESI自增1。
lodsw:加载一个字到AX,并将ESI自增2.
lodsd:加载一个双字到EAX,并将ESI自增4.

堆栈中的字符串操作

push offset str_hello ;将"hello"的地址压栈。
call printf           ;调用函数
add  esp,4           ;平衡堆栈

浮点运算指令

FPU

FPU(浮点运算单元)

  • 定义:FPU是处理器中一个硬件模块,专门负责浮点运算。
  • 功能:处理浮点数的加减乘除,平方根,三角函数等复杂操作。
  • 实现:现在FPU集成到CPU内核中。

FPU组成

  • 寄存器堆栈:8个80位宽的浮点寄存器(ST0-ST7),以堆栈形式组织。
  • 通过push和pop操作

x87指令集

  • 定义:x87是专门位FPU涉及的一套指令集,早期专用于处理浮点运算。
  • 特点:
    • 操作对象是FPU的堆栈寄存器
    • 指令风格是Fxxx开头。
    • 支持扩展精度(80位),比SSE的单精度和双精度浮点数高。
加载与存储指令
指令功能描述
FLD从内存加载浮点数到 FPU 栈顶(支持单精度、双精度、长双精度)
FST将 FPU 栈顶浮点数复制到内存(栈顶数据保留)
FSTP将 FPU 栈顶浮点数存储并弹出(栈顶数据移除,栈指针 +1)
FLDENV从内存加载 FPU 环境(控制字、状态字、标记字等)
FSTENV将 FPU 环境(控制字、状态字等)保存到内存
FLDCW从内存加载 FPU 控制字(设置舍入模式、精度等)
FSTCW将 FPU 控制字保存到内存
算术运算指令
指令功能描述
FADD栈顶两数相加(FADD st(0), st(1) 或简写 FADD,结果存栈顶)
FSUB栈顶两数相减(FSUB st(0), st(1)st(0)-st(1),结果存栈顶)
FSUBR反向相减(FSUBR st(0), st(1)st(1)-st(0),结果存栈顶)
FMUL栈顶两数相乘(结果存栈顶)
FDIV栈顶两数相除(FDIV st(0), st(1)st(0)/st(1),结果存栈顶)
FDIVR反向相除(FDIVR st(0), st(1)st(1)/st(0),结果存栈顶)
FABS取栈顶数的绝对值
FNEG取栈顶数的相反数
FSQRT计算栈顶数的平方根
FSCALE栈顶数乘以 2 的(次顶数)次方(st(0) *= 2^st(1),然后弹出次顶数)
转换指令
指令功能描述
FIST将栈顶浮点数复制为整数存入内存(按当前 FPU 控制字舍入)
FISTP将栈顶浮点数转换为整数并存出,然后弹出栈顶(清理栈空间)
FCVT浮点数类型转换(如单精度→双精度,或反向)
FILD从内存加载整数到 FPU 栈顶(自动转换为浮点数)
FIST将栈顶浮点数转换为整数并存入内存(同 FIST,需区分操作数大小)
控制与比较指令
指令功能描述
FCOM比较栈顶两数(st(0) vs st(1)),设置 FPU 状态位(不修改栈)
FCOMI比较栈顶两数,结果写入 CPU 通用寄存器 EFLAGS(支持 JE/JL 等指令)
FUCOMFCOM,但处理非规范数(NaN、无穷大)
FUCOMIFCOMI,但处理非规范数(NaN、无穷大)
FTST比较栈顶数与 0(判断正负、是否为 0)
FBSTP将栈顶浮点数转换为 BCD 码(十进制)并存入内存,然后弹出栈
FINCSTP递增 FPU 栈指针(手动调整栈顶,风险高)
FDECSTP递减 FPU 栈指针(手动调整栈顶,风险高)
特殊指令
指令功能描述
FNINIT初始化 FPU(重置控制字、状态字等)
FNOP空操作(无实际功能,用于填充指令周期)
FWAIT等待 FPU 完成当前指令(旧版兼容,现代 CPU 多自动同步)
FXSAVE保存 SSE/FPU 状态到内存(支持 SIMD 扩展)
FXRSTOR从内存恢复 SSE/FPU 状态(支持 SIMD 扩展)

SSE

  • 定义:SSE是Intel在x86处理器上引用的一套SIMD(单指令多数据)扩展指令集。
  • 特点:
    • 采用xmm寄存器(每个128位宽)。
    • 支持并行处理多个单精度(32位)或双精度(64位)浮点数。
    • 指令风格是 xxxPs(处理单精度矢量)或xxxSD(处理双精度标量)。

数据扩展与传送指令

指令全称功能描述指令格式示例适用场景
movsxMove with Sign Extension将窄宽度有符号数扩展为宽宽度有符号数(高位填充符号位)movsx 存储位置, 源操作数有符号整数类型转换(如char→int
movzxMove with Zero Extension将窄宽度无符号数扩展为宽宽度数(高位填充0)movzx reg_dest, reg_src/mem_src无符号整数类型转换(如unsigned char→unsigned int
cbwConvert Byte to Word将8位寄存器al中的有符号数扩展为16位axal符号位扩展到ahcbw(无操作数,固定使用alaxint8_t→int16_t转换
cwdConvert Word to Doubleword将16位寄存器ax中的有符号数扩展为32位dx:axax符号位扩展到dxcwd(无操作数,固定使用axdx:axint16_t→int32_t转换
cdqConvert Doubleword to Quadword将32位寄存器eax中的有符号数扩展为64位edx:eaxeax符号位扩展到edxcdq(无操作数,固定使用eaxedx:eaxint32_t→int64_t转换
cqoConvert Quadword to Octword将64位寄存器rax中的有符号数扩展为128位rdx:raxrax符号位扩展到rdxcqo(无操作数,固定使用raxrdx:raxint64_t→int128_t转换

函数

函数特征代码

;函数头部
push ebp    ;保存上一函数的栈基指针。
mov ebp,esp ;设置当前栈帧基址。
sub esp,0x10;为局部变量分配空间
;函数退出
mov esp,ebp  ;
pop ebp      ;
ret          ;

函数参数和局部变量

push 3    ;第二个参数
push 2    ;第一个参数
call add  ;如果发现call上面有push,那些就是函数调用用到的参数。

当在内层函数看到ebp + xxx是函数的参数;ebp - xxx是函数的局部变量。
局部变量在栈上的位置(下图)
在这里插入图片描述

  • x64
lea     rdx, [rbp+input_flag]//参数
lea     rax, [rbp+var_90]    //参数
mov     rsi, rdx
mov     rdi, rax
call    function

调用约定

cdecl

又叫做c调用约定,在Windows下都是外平栈。

特点:

  • 参数传递:参数从右到左依次压栈
  • 栈清理:由调用者(Caller)清理栈(add esp,x)
  • 返回值:eax寄存器
  • 常见场景:c语言默认约定
push 3    ;第三个参数
push 2    ;
push 1    ;第一个参数
call my_function
add esp,0xC    ;调用者清理栈(3个参数 x 4个字节 = 12个字节)
my_function:
push ebp    ;保存旧的栈帧基址
mov ebp,esp ;建立新栈帧
sub esp,8   ;分配局部变量空间
mov dword ptr [ebp - 4],10    ;局部变量a = 10
mov dword ptr [ebp - 8],20    ;局部变量b = 20
; 访问参数
mov eax,[ebp + 8]    ;访问第一个参数
mov eax,[ebp + 12]   ;访问第二个参数
mov eax,[ebp + 16]   ;访问第三个参数
;尾部
mov esp,ebp    ;恢复栈指针
pop ebp        ;恢复旧的栈帧基址
ret            ;返回(无栈清理)

stdcall

叫做标准调用约定,windows下内平栈

特点:

  • 参数传递:参数从右到左压栈
  • 栈清理:由被调用者清理栈
  • 返回值:eax寄存器
  • 常见场景:Windows API
push 3    ;第三个参数
push 2    ;
push 1    ;第一个参数
call my_function
;无需清理栈
my_function:
push ebp    ;保存旧的栈帧基址
mov ebp,esp ;建立新栈帧
sub esp,8   ;分配局部变量空间
mov dword ptr [ebp - 4],10    ;局部变量a = 10
mov dword ptr [ebp - 8],20    ;局部变量b = 20
; 访问参数
mov eax,[ebp + 8]    ;访问第一个参数
mov eax,[ebp + 12]   ;访问第二个参数
mov eax,[ebp + 16]   ;访问第三个参数
;尾部
mov esp,ebp    ;恢复栈指针
pop ebp        ;恢复旧的栈帧基址
ret 0xC        ;被调用者清理栈

fastcall

windows下内平栈

特点:

  • 参数传递:前两个参数通过寄存器(ecx和edx),其余参数从右到左压栈
  • 栈清理:由被调用者清理栈(同stdcall)
  • 返回值:eax寄存器
  • 常见场景:性能敏感的函数
mov ecx,1        ;第一个参数,放入ecx
mov edx,2        ;第二个参数,放入edx
push 3           ;第三个参数(最右侧)
call my_function ;
my_function:
push ebp    ;保存旧的栈帧基址
mov ebp,esp ;建立新栈帧
sub esp,4   ;分配局部变量空间
; 访问参数
mov eax,ecx    ;访问第一个参数
mov eax,edx   ;访问第二个参数
mov eax,[ebp + 8]   ;访问第三个参数
;尾部
mov esp,ebp    ;恢复栈指针
pop ebp        ;恢复旧的栈帧基址
ret 4        ;被调用者清理栈

thiscall

特点:

  • 参数传递:this指针通过ecx传递,其余参数从右到左入栈
  • 栈清理:由调用者或者编译器确定
  • 返回值:eax寄存器
  • 常见场景:C++类成员函数
mov ecx,obj_ptr    ;this指针通过ecx传递,其余参数从右到左压栈
push 2             ;第二个参数
push 1             ;第一个参数
call obj.method    ;调用成员函数
add esp,8          ;调用者清栈
posted @ 2025-08-09 17:41  yfceshi  阅读(25)  评论(0)    收藏  举报