【汇编逆向系列】六、函数调用包含多个参数之多个整型-参数压栈顺序,rcx,rdx,r8,r9寄存器
从本章节开始,进入到函数有多个参数的情况,前面几个章节中介绍了整型和浮点型使用了不同的寄存器在进行函数传参,ECX是整型的第一个参数的寄存器,那么多个参数的情况下函数如何传参,下面展开介绍参数为整型时候的几种情况:
1. 两个参数
1.1 汇编代码
1.1.1 debug编译
two_int_params:0000000000000170: 89 54 24 10 mov dword ptr [rsp+10h],edx0000000000000174: 89 4C 24 08 mov dword ptr [rsp+8],ecx0000000000000178: 57 push rdi0000000000000179: 8B 44 24 10 mov eax,dword ptr [rsp+10h]000000000000017D: 0F AF 44 24 18 imul eax,dword ptr [rsp+18h]0000000000000182: 8B 4C 24 18 mov ecx,dword ptr [rsp+18h]0000000000000186: 8B 54 24 10 mov edx,dword ptr [rsp+10h]000000000000018A: 03 D1 add edx,ecx000000000000018C: 8B CA mov ecx,edx000000000000018E: 03 C1 add eax,ecx0000000000000190: 5F pop rdi0000000000000191: C3 ret0000000000000192: CC int 3
1.2.1 release编译
two_int_params:0000000000000000: 8D 42 01 lea eax,[rdx+1]0000000000000003: 0F AF C1 imul eax,ecx0000000000000006: 03 C2 add eax,edx0000000000000008: C3 ret
1.2. 汇编分析
1.2.1 函数参数
Microsoft x64 调用约定参数传递的整数、指针参数,前四个参数依次使用:
RCX
(第1个)、RDX
(第2个)、R8
(第3个)、R9
(第4个)- 超过 4 个的参数从右向左压入栈(如第5个参数在
[RSP+0x20]
)
在本例子中rcx和rdx分别保存第一个和第二个参数
1.2.2 imul指令
有符号乘法指令,有2中操作数格式
1.2.2.1 单操作数格式
IMUL reg/mem
:被乘数隐含在累加器(AL/AX/EAX/RAX),结果存于扩展寄存器对
语法格式为:
IMUL src
操作流程:
(AL/AX/EAX/RAX) × src → AX/DX:AX/EDX:EAX/RDX:RAX
将(AL/AX/EAX/RAX)与src相乘存放到 (AL/AX/EAX/RAX)寄存器
标志位影响:若高半部分非符号扩展,则CF=OF=1
RAX寄存器详见上一节对RAX寄存器的介绍, OF为有符号的溢出的标值寄存器
示例:
MOV AL, 3 ; AL=03H(+3)
MOV BL, 6 ; BL=06H(+6)
IMUL BL ; AX=0012H(+18)
结果:AX=0012H → 高半部分AH=00H是AL=12H(正数)的符号扩展(补0)。
标志位:CF=OF=0(无需关注高半部)
MOV AL, 48 ; AL=30H(+48)
MOV BL, 4 ; BL=04H(+4)
IMUL BL ; AX=00C0H(192)
结果:AX=00C0H → AH=00H 不是 AL=C0H(C0H作为有符号数为-64)的符号扩展(应为FFH)。
标志位:CF=OF=1(高半部AH=00H含有效数据,实际结果192超8位有符号范围-128~127
MOV AL, -4 ; AL=FCH(-4)
MOV BL, 4 ; BL=04H(+4)
IMUL BL ; AX=FFF0H(-16)
结果:AX=FFF0H → 高半部分AH=FFH是AL=F0H(负数)的符号扩展(补1)。
标志位:CF=OF=0
总结OF和CF的用法:
💡 关键设计:
单操作数乘法结果需存于双倍位宽的寄存器(如16位乘→32位结果)。当CF=OF=1
时,表明低半部分无法完整表示结果,必须联合高半部分使用
1.2.2.2 双操作数格式
双操作数的语法格式为:
IMUL dest, src
目标寄存器dest
与源操作数src
相乘,结果直接存入dest
(按dest
大小截断)
dest × src → dest
若有效位被截断(如乘积超出dest
位数),则CF=OF=1
示例:
mov ax, -16 ; AX = FFF0h (-16)
mov bx, 2 ; BX = 0002h
imul bx, ax ; BX = AX × BX = FFE0h (-32) → 未溢出,OF=0[2,6](@ref)
mov ax, 32000 ; AX = 7D00h (32000)
imul ax, 2 ; AX = FA00h (-1536) → 实际应为64000(超16位范围),截断导致OF=1[6](@ref)
1.2.2.3 三操作数格式
语法格式为:
IMUL dest, src1, imm
源操作数src1
与立即数imm
相乘,结果存入目标寄存器dest:
src1 × imm → dest
同双操作数,截断时CF=OF=1
示例:
imul ebx, eax, 4 ; EBX = EAX × 4(例:EAX=-16 → EBX=FFFFFFC0h (-64))[4,6](@ref)
imul bx, word1, -16 ; BX = [word1] × (-16)(若word1=4 → BX=FFC0h (-64))[6](@ref)
imul ebx, dword1, -2000000000 ; 乘积超32位 → 截断,OF=1[6](@ref)
3. 汇编转化
1.3.1 debug编译
0000000000000170: mov dword ptr [rsp+10h], edx ; 保存第二个参数到栈 [rsp+10h]
0000000000000174: mov dword ptr [rsp+8], ecx ; 保存第一个参数到栈 [rsp+8]
0000000000000178: push rdi ; 保存 rdi(被调用者保存寄存器)
0000000000000179: mov eax, dword ptr [rsp+10h] ; 加载第一个参数到 eax
000000000000017D: imul eax, dword ptr [rsp+18h] ; eax = eax * 第二个参数(从栈加载)
0000000000000182: mov ecx, dword ptr [rsp+18h] ; 加载第二个参数到 ecx
0000000000000186: mov edx, dword ptr [rsp+10h] ; 加载第一个参数到 edx
000000000000018A: add edx, ecx ; edx = edx + ecx(两参数相加)
000000000000018C: mov ecx, edx ; ecx = 相加结果
000000000000018E: add eax, ecx ; eax = 乘积结果 + 相加结果
0000000000000190: pop rdi ; 恢复 rdi
0000000000000191: ret ; 返回(结果在 eax)
0000000000000192: int 3 ; 断点指令(调试用)
1.3.2 release编译
0000000000000000: lea eax, [rdx+1] ; eax = rdx + 1(第二个参数加1)
0000000000000003: imul eax, ecx ; eax = eax * ecx(乘以第一个参数)
0000000000000006: add eax, edx ; eax = eax + edx(再加第二个参数)
0000000000000008: ret ; 返回
相较于debug编译优化了中间所有的单步操作(因为debug需要能够断电调试)
1.3.3 c语言转化
int two_int_params(int param1, int param2) {return (param2 + 1) * param1 + param2;
}
2. 大量整型参数
2.1 汇编代码
2.1.1 debug编译
many_int_params:0000000000000960: 44 89 4C 24 20 mov dword ptr [rsp+20h],r9d0000000000000965: 44 89 44 24 18 mov dword ptr [rsp+18h],r8d000000000000096A: 89 54 24 10 mov dword ptr [rsp+10h],edx000000000000096E: 89 4C 24 08 mov dword ptr [rsp+8],ecx0000000000000972: 57 push rdi0000000000000973: 8B 44 24 18 mov eax,dword ptr [rsp+18h]0000000000000977: 8B 4C 24 10 mov ecx,dword ptr [rsp+10h]000000000000097B: 03 C8 add ecx,eax000000000000097D: 8B C1 mov eax,ecx000000000000097F: 03 44 24 20 add eax,dword ptr [rsp+20h]0000000000000983: 03 44 24 28 add eax,dword ptr [rsp+28h]0000000000000987: 03 44 24 30 add eax,dword ptr [rsp+30h]000000000000098B: 03 44 24 38 add eax,dword ptr [rsp+38h]000000000000098F: 03 44 24 40 add eax,dword ptr [rsp+40h]0000000000000993: 03 44 24 48 add eax,dword ptr [rsp+48h]0000000000000997: 03 44 24 50 add eax,dword ptr [rsp+50h]000000000000099B: 03 44 24 58 add eax,dword ptr [rsp+58h]000000000000099F: 5F pop rdi00000000000009A0: C3 ret00000000000009A1: CC int 300000000000009A2: CC int 300000000000009A3: CC int 3
2.1.2 release编译
many_int_params:0000000000000000: 8D 04 11 lea eax,[rcx+rdx]0000000000000003: 41 03 C0 add eax,r8d0000000000000006: 41 03 C1 add eax,r9d0000000000000009: 03 44 24 28 add eax,dword ptr [rsp+28h]000000000000000D: 03 44 24 30 add eax,dword ptr [rsp+30h]0000000000000011: 03 44 24 38 add eax,dword ptr [rsp+38h]0000000000000015: 03 44 24 40 add eax,dword ptr [rsp+40h]0000000000000019: 03 44 24 48 add eax,dword ptr [rsp+48h]000000000000001D: 03 44 24 50 add eax,dword ptr [rsp+50h]0000000000000021: C3 ret
2.2 汇编分析
为了更加方便理解,下面将使用这段汇编的原始C代码进行反向逆推,直接给出C源码:
// 大量参数函数(超过寄存器数量,测试栈传递)
int many_int_params(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, int p9, int p10) {// 这个函数会强制使用栈传递参数(在x64上,前4个整数参数通过寄存器传递)return p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9 + p10;
}
下面会以这个C函数举例
2.2.1 大量参数
接1.2.1章节当参数参数超过4个的时候,如何用栈空间传递参数
2.2.1.1 函数调用规定
根据 x64 快速调用约定(MSVC 默认规则)
- 寄存器传递:前 4 个整型参数通过寄存器传递:
p1
→RCX
p2
→RDX
p3
→R8
p4
→R9
- 栈传递:第 5–10 个参数通过栈传递,从右向左压栈(即
p10
先入栈,p5
最后入栈)。 - 影子空间(Shadow Space):调用者需预留 32 字节(0x20) 栈空间,供被调函数保存寄存器参数(即使未使用)
- 栈对齐:调用时
RSP
必须 16 字节对齐(地址能被 16 整除)
2.2.1.2 调用者(caller)的栈操作
假设在 main
函数中调用 many_int_params(1,2,...,10)
,调用者需完成以下步骤:
- 预留栈顶空间
sub rsp, 58h ; RSP -= 0x58 (88 字节)
空间构成:
影子空间:32 字节(0x20
)
额外参数空间:6 参数 × 8 字节 = 48 字节(0x30
)
总计:0x20 + 0x30 = 0x50
→ 额外预留 8 字节满足 16 字节对齐(0x58
对齐到 0x60
),由于call操作会额外的将rsp-=8, 为了满足在call指令之后栈对齐,这里要再预留8字节空间
-
压入第 5–10 个参数(从右向左)
mov qword ptr [rsp+50h], 10 ; p10 → [RSP+0x50]
mov qword ptr [rsp+48h], 9 ; p9 → [RSP+0x48]
...
mov qword ptr [rsp+20h], 5 ; p5 → [RSP+0x20]
栈偏移逻辑:
参数 p5
位于影子空间上方(RSP+0x20
)
后续参数地址递增 8 字节(p6
→RSP+0x28
, ..., p12
→RSP+0x50
)
-
设置寄存器参数
mov rcx, 1 ; p1 → RCX
mov rdx, 2 ; p2 → RDX
mov r8, 3 ; p3 → R8
mov r9, 4 ; p4 → R9
- 执行call指令
call many_int_params ; 1. 返回地址压栈(RSP -= 8); 2. 跳转到函数入口
SP 变化:CALL
隐含 push RIP
操作 → RSP -= 8
。
2.2.1.3 被调用函数(callee)的栈帧变化
; 1. 保存寄存器参数到栈(覆盖影子空间)
mov [rsp+20h], r9d ; p4 → [rsp+20h]
mov [rsp+18h], r8d ; p3 → [rsp+18h]
mov [rsp+10h], edx ; p2 → [rsp+10h]
mov [rsp+8], ecx ; p1 → [rsp+8]; 2. 保存非易失寄存器 (RSP -= 8)
push rdi ; RSP 下移 8 字节; 3. 计算求和(从栈加载参数)
mov eax, [rsp+18h] ; 加载 p1,由于push rdi会使rsp下移8字节,p1地址从[rsp+8]变成了[rsp+10h]
mov ecx, [rsp+10h] ; 加载 p2
add ecx, eax ; p2 + p1
...
add eax, [rsp+58h] ; 累加 p10; 4. 恢复栈帧
pop rdi ; RSP += 8 (恢复 RDI)
ret ; RSP += 8 (弹出返回地址)
这里要注意的是:
由于call函数会使rsp-=8, 原来影子空间的地址会从[rsp]-[rsp+20h]变成[rsp+8]-[rsp+28h],原来p5从[rsp+20]的位置变成[rsp+28],后面参数依次下推。push rdi之后,再次rsp-=8, 原来的参数的地址会再度变化p1-p10从[rsp+8]-[rsp+58h]变化到[rsp+10h]-[rsp+60h]
相关文章:

【汇编逆向系列】六、函数调用包含多个参数之多个整型-参数压栈顺序,rcx,rdx,r8,r9寄存器
从本章节开始,进入到函数有多个参数的情况,前面几个章节中介绍了整型和浮点型使用了不同的寄存器在进行函数传参,ECX是整型的第一个参数的寄存器,那么多个参数的情况下函数如何传参,下面展开介绍参数为整型时候的几种情…...
C#最佳实践:为何优先使用as或is而非强制转换
C#最佳实践:为何优先使用as或is而非强制转换 在 C# 的编程世界里,类型转换是我们经常会遇到的操作。就像在现实生活中,我们可能需要把不同形状的物品重新整理归类一样,在代码里,我们也常常需要将一个数据类型转换为另…...

PLC入门【4】基本指令2(SET RST)
04 基本指令2 PLC编程第四课基本指令(2) 1、运用上接课所学的基本指令完成个简单的实例编程。 2、学习SET--置位指令 3、RST--复位指令 打开软件(FX-TRN-BEG-C),从 文件 - 主画面,“B: 让我们学习基本的”- “B-3.控制优先程序”。 点击“梯形图编辑”…...

react更新页面数据,操作页面,双向数据绑定
// 路由不是组件的直接跳转use client,useEffect,useRouter,需3个结合, use client表示客户端 use client; import { Button,Card, Space,Tag,Table,message,Input } from antd; import { useEffect,useState } from react; impor…...
PostgreSQL 与 SQL 基础:为 Fast API 打下数据基础
在构建任何动态、数据驱动的Web API时,一个稳定高效的数据存储方案是不可或缺的。对于使用Python FastAPI的开发者来说,深入理解关系型数据库的工作原理、掌握SQL这门与数据库“对话”的语言,以及学会如何在Python中操作数据库,是…...

Python异步编程:深入理解协程的原理与实践指南
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 持续学习,不断…...

Ray框架:分布式AI训练与调参实践
Ray框架:分布式AI训练与调参实践 系统化学习人工智能网站(收藏):https://www.captainbed.cn/flu 文章目录 Ray框架:分布式AI训练与调参实践摘要引言框架架构解析1. 核心组件设计2. 关键技术实现2.1 动态资源调度2.2 …...

基于小程序老人监护管理系统源码数据库文档
摘 要 近年来,随着我国人口老龄化问题日益严重,独居和居住养老机构的的老年人数量越来越多。而随着老年人数量的逐步增长,随之而来的是日益突出的老年人问题,尤其是老年人的健康问题,尤其是老年人产生健康问题后&…...
el-amap-bezier-curve运用及线弧度设置
文章目录 简介示例线弧度属性主要弧度相关属性其他相关样式属性完整示例链接简介 el-amap-bezier-curve 是 Vue-Amap 组件库中的一个组件,用于在 高德地图 上绘制贝塞尔曲线。 基本用法属性path定义曲线的路径,可以是多个弧线段的组合。stroke-weight线条的宽度。stroke…...
深入浅出JavaScript中的ArrayBuffer:二进制数据的“瑞士军刀”
深入浅出JavaScript中的ArrayBuffer:二进制数据的“瑞士军刀” 在JavaScript中,我们经常需要处理文本、数组、对象等数据类型。但当我们需要处理文件上传、图像处理、网络通信等场景时,单纯依赖字符串或数组就显得力不从心了。这时ÿ…...

理想汽车5月交付40856辆,同比增长16.7%
6月1日,理想汽车官方宣布,5月交付新车40856辆,同比增长16.7%。截至2025年5月31日,理想汽车历史累计交付量为1301531辆。 官方表示,理想L系列智能焕新版在5月正式发布,全系产品力有显著的提升,每…...
Linux中INADDR_ANY详解
在Linux网络编程中,INADDR_ANY 是一个特殊的IPv4地址常量(定义在 <netinet/in.h> 头文件中),用于表示绑定到所有可用网络接口的地址。它是服务器程序中的常见用法,允许套接字监听所有本地IP地址上的连接请求。 关…...

运行vue项目报错 errors and 0 warnings potentially fixable with the `--fix` option.
报错 找到package.json文件 找到这个修改成 "lint": "eslint --fix --ext .js,.vue src" 为elsint有配置结尾换行符,最后运行:npm run lint --fix...
java+webstock
maven依赖 <dependency><groupId>org.java-websocket</groupId><artifactId>Java-WebSocket</artifactId><version>1.3.5</version></dependency><dependency><groupId>org.apache.tomcat.websocket</groupId&…...
STL 2迭代器
文章目录 1.迭代器2.输入迭代器3.输出迭代器1.插入迭代器 4.前向迭代器5.双向迭代器6.随机访问迭代器7.不同容器返回的迭代器类型1.输入 / 输出迭代器2.前向迭代器3.双向迭代器4.随机访问迭代器5.特殊迭代器适配器6.为什么 unordered_set 只提供前向迭代器? 1.迭代器…...
CppCon 2015 学习:Simple, Extensible Pattern Matching in C++14
什么是 Pattern Matching(模式匹配) ❝ 模式匹配就是一种“描述式”的写法,不需要你手动判断、提取数据,而是直接描述你希望的数据结构是什么样子,系统自动判断并提取。❞ 你给的定义拆解: ✴ Instead of …...

智警杯备赛--excel模块
数据透视与图表制作 创建步骤 创建 1.在Excel的插入或者数据标签页下找到数据透视表的按钮 2.将数据放进“请选择单元格区域“中,点击确定 这是最终结果,但是由于环境启不了,这里用的是自己的excel,真实的环境中的excel根据实训…...
uniapp获取当前位置和经纬度信息
1.1. 获取当前位置和经纬度信息(需要配置高的SDK) 调用uni-app官方API中的uni.chooseLocation(),即打开地图选择位置。 <button click"getAddress">获取定位</button> const getAddress () > {uni.chooseLocatio…...

【多线程初阶】单例模式 指令重排序问题
文章目录 1.单例模式1)饿汉模式2)懒汉模式①.单线程版本②.多线程版本 2.分析单例模式里的线程安全问题1)饿汉模式2)懒汉模式懒汉模式是如何出现线程安全问题的 3.解决问题进一步优化加锁导致的执行效率优化预防内存可见性问题 4.解决指令重排序问题 1.单例模式 单例模式确保某…...

基于Python的气象数据分析及可视化研究
目录 一.🦁前言二.🦁开源代码与组件使用情况说明三.🦁核心功能1. ✅算法设计2. ✅PyEcharts库3. ✅Flask框架4. ✅爬虫5. ✅部署项目 四.🦁演示效果1. 管理员模块1.1 用户管理 2. 用户模块2.1 登录系统2.2 查看实时数据2.3 查看天…...

Pandas 可视化集成:数据科学家的高效绘图指南
为什么选择 Pandas 进行数据可视化? 在数据科学和分析领域,可视化是理解数据、发现模式和传达见解的关键步骤。Python 生态系统提供了多种可视化工具,如 Matplotlib、Seaborn、Plotly 等,但 Pandas 内置的可视化功能因其与数据结…...

新版NANO下载烧录过程
一、序言 搭建 Jetson 系列产品烧录系统的环境需要在电脑主机上安装 Ubuntu 系统。此处使用 18.04 LTS。 二、环境搭建 1、安装库 $ sudo apt-get install qemu-user-static$ sudo apt-get install python 搭建环境的过程需要这个应用库来将某些 NVIDIA 软件组件安装到 Je…...
Vue 实例的数据对象详解
Vue 实例的数据对象详解 在 Vue 中,数据对象是响应式系统的核心,也是组件状态的载体。理解数据对象的原理和使用方式是成为 Vue 专家的关键一步。我将从多个维度深入剖析 Vue 实例的数据对象。 一、数据对象的定义方式 1. Options API 中的定义 在 Options API 中,使用 …...

Axure Rp 11 安装、汉化、授权
Axure Rp 11 安装、汉化、授权 1、前言2、汉化2.1、汉化文件下载2.2、windows汉化流程2.3、 macOs汉化流程 3、授权 1、前言 Axure Rp 11官方下载链接:https://www.axure.com/downloadthanks 2、汉化 2.1、汉化文件下载 链接: https://pan.baidu.com/s/18Clf…...

中科院1区顶刊|IF14+:多组学MR联合单细胞时空分析,锁定心血管代谢疾病的免疫治疗新靶点
中科院1区顶刊|IF14:多组学MR联合单细胞时空分析,锁定心血管代谢疾病的免疫治疗新靶点 当下,免疫与代谢性疾病的关联研究已成为生命科学领域的前沿热点。随着研究的深入,我们愈发清晰地认识到免疫系统与代谢系统之间存在着极为复…...
GB/T 43887-2024 核级柔性石墨板材检测
核级柔性石墨板材是指以可膨胀石墨为原料、未经改性和增强、用于核工业的核级柔性石墨板材。 GB/T 43887-2024核级柔性石墨板材检测检测指标: 测试项目 测试标准 外观 GB/T 43887 尺寸偏差 GB/T 43887 化学成分 GB/T 43887 密度偏差 GB/T 43887 拉伸强度…...
ffmpeg(三):处理原始数据命令
FFmpeg 可以直接处理原始音频和视频数据(Raw PCM、YUV 等),常见场景包括: 将原始 YUV 图像编码为 H.264 视频将 PCM 音频编码为 AAC 或 MP3对原始音视频数据进行封装(如封装为 MP4、TS) 处理原始 YUV 视频…...
标注工具核心架构分析——主窗口的图像显示
🏗️ 标注工具核心架构分析 📋 系统概述 主要有两个核心类,采用经典的 Scene-View 架构模式: 🎯 核心类结构 1. AnnotationScene (QGraphicsScene子类) 主要负责标注场景的管理和交互 🔧 关键函数&…...

作为点的对象CenterNet论文阅读
摘要 检测器将图像中的物体表示为轴对齐的边界框。大多数成功的目标检测方法都会枚举几乎完整的潜在目标位置列表,并对每一个位置进行分类。这种做法既浪费又低效,并且需要额外的后处理。在本文中,我们采取了不同的方法。我们将物体建模为单…...
基于Java项目的Karate API测试
Karate 实现了可以只编写Feature 文件进行测试,但是对于熟悉Java语言的开发或是测试人员,可以通过编程方式集成 Karate 丰富的自动化和数据断言功能。 本篇快速介绍在Java Maven项目中编写和运行测试的示例。 创建Maven项目 最简单的创建项目的方式就是创建一个目录,里面…...