【汇编逆向系列】三、函数调用包含单个参数之float类型-xmm0寄存器,sub,rep,stos,movss,mulss,addss指令

一、汇编代码

single_float_param:0000000000000060: F3 0F 11 44 24 08  movss       dword ptr [rsp+8],xmm00000000000000066: 57                 push        rdi0000000000000067: 48 83 EC 10        sub         rsp,10h000000000000006B: 48 8B FC           mov         rdi,rsp000000000000006E: B9 04 00 00 00     mov         ecx,40000000000000073: B8 CC CC CC CC     mov         eax,0CCCCCCCCh0000000000000078: F3 AB              rep stos    dword ptr [rdi]000000000000007A: F3 0F 10 44 24 20  movss       xmm0,dword ptr [rsp+20h]0000000000000080: F3 0F 59 05 00 00  mulss       xmm0,dword ptr [__real@3fc00000]00 000000000000000088: F3 0F 58 05 00 00  addss       xmm0,dword ptr [__real@3f000000]00 000000000000000090: 48 83 C4 10        add         rsp,10h0000000000000094: 5F                 pop         rdi0000000000000095: C3                 ret0000000000000096: CC                 int         3

二、汇编分析

1. xmm0寄存器

xmm0 是 Intel x86 架构中 ​​16 个 128 位 XMM 寄存器​​的首个寄存器,属于 SSE(Streaming SIMD Extensions)指令集的核心组件

在这里是在函数调用中传递浮点参数或返回值(如 Windows fastcall 约定),用于函数参数中的第一个浮点型参数

2. movss 指令

movss(Move Scalar Single-Precision Floating-Point)是 SSE 指令集中的关键指令,用于操作单精度浮点数(32 位)。其核心特性如下:

​数据传输​​:将单精度浮点数从源操作数(内存或 XMM 寄存器)复制到目标操作数(XMM 寄存器或内存)的低 32 位

​高位处理​​:目标操作数的高位(96 位)保持不变(若目标为 XMM 寄存器)或忽略(若目标为内存)

3. float类型的内存空间

了解浮点类型传参首先要知道一个浮点类型的数在内存空间内是如何表示的,Float 类型(单精度浮点数)在内存中的存储遵循 ​​IEEE 754 标准​​,占用 ​​4 字节(32 位)​​,其结构分为三个部分:​​符号位(Sign)、指数位(Exponent)和尾数位(Mantissa)​​。以下是详细解析:

部分​比特位​​作用​
​符号位(S)最高位(第 31 位)0 表示正数,1 表示负数。
​指数位(E)​第 30–23 位(8 位)存储​​科学计数法的指数值​​,采用​​偏移编码​​(实际指数 = 存储值 - 127)
​尾数位(M)第 22–0 位(23 位)存储​​规格化后的小数部分​​,隐含整数部分为 1(不直接存储)

数字转换成浮点类型存储

下面以13.625这个数为例说明如何拆解float类型

步骤 1:转换为二进制​
  • ​整数部分 13​:
    13 ÷ 2 = 6...1 → 6 ÷ 2 = 3...0 → 3 ÷ 2 = 1...1 → 1 ÷ 2 = 0...1
    结果为 1101(从最后一个余数向前读)。
  • ​小数部分 0.625​:
    0.625 × 2 = 1.25 → 取整 1,余 0.25
    0.25 × 2 = 0.5 → 取整 0,余 0.5
    0.5 × 2 = 1.0 → 取整 1,余 0
    结果为 .101
  • ​合并​​:13.625 = 1101.101₂。
​步骤 2:规格化(科学计数法)​
  • 移动小数点至首位为 11101.101 → 1.101101 × 2³(左移 3 位)。
  • ​隐含整数 1​:尾数仅存储小数点后的部分 101101(补足 23 位:10110100000000000000000)。
​步骤 3:计算指数位​
  • 实际指数 3 → ​​存储值 = 3 + 127 = 130​​(偏移值 127 是 IEEE 754 固定规则)。
  • 130 的二进制:10000010₂。
​步骤 4:组合三部分​
  • ​符号位​​:0(正数)。
  • ​指数位​​:10000010
  • ​尾数位​​:10110100000000000000000
  • ​完整二进制​​:
    0 10000010 10110100000000000000000 → 十六进制为 0x415A0000

浮点类型转化成数字

下面以0x3FC00000 为例,将二进制转换成浮点数字

步骤一:​​拆分十六进制值
  • 十六进制:3FC00000 → 二进制:0011 1111 1100 0000 0000 0000 0000 0000
  • 按 IEEE 754 标准划分:
  • ​符号位 S​​(1位):0(正数)
  • ​指数位 E​​(8位):01111111(十进制 127)
  • ​尾数位 M​​(23位):10000000000000000000000
步骤二:计算指数偏移值

     实际指数 = E - 127 = 127 - 127 = 0

步骤三:计算尾数值

    尾数隐含整数部分 1(IEEE 754 规范),因此实际尾数为:

 1 + M = 1 + 0.10000000000000000000000₂

 M的二进制小数部分10000000000000000000000转化为10进制,套入公式:

   

  示例:

示例​​(单精度尾数 100000000000011110000000):第 1 位:1×2−1=0.5
第 13–16 位:1×(2−13+2−14+2−15+2−16)≈0.0002289
总和:1+0.5+0.0002289=1.5002289

    二进制小数 0.1₂ = 十进制 0.5(因 1×2⁻¹ = 0.5

       最终尾数 = 1 + 0.5 = 1.5

    步骤四:组合结果

    • 浮点数值 = (-1)^S × 尾数 × 2^{实际指数}
      1 × 1.5 × 2^0
      = ​​1.5​​。

    4. sub指令

    减法指令, sub         rsp,10h 指在rsp栈顶指针上分配16字节的内存空间,栈布局将会进行如下变化

    执行 sub rsp,10h 后:
    +-----------------+ <-- RSP
    | 16字节栈空间    |  // 初始化为 0xCC
    +-----------------+
    | 保存的 RDI      |  // push rdi
    +-----------------+ <-- 原始 RSP
    | 浮点参数        |  // [rsp+8] → [新RSP+20h]
    +-----------------+

    这里有个小细节:

    为什么分配16字节?

    在Debug模式下,编译器可能会分配额外的栈空间,即使这些空间并不完全被使用。这通常是为了:

    1. ​调试方便​​:分配的空间可以用于存储临时变量或用于调试检查(例如用0xCC填充以检测未初始化内存的使用)。
    2. ​栈对齐​​:x86-64架构要求栈在函数调用时保持16字节对齐(特别是在调用某些需要对齐的指令如SSE指令之前)。虽然在这个函数中,分配16字节之前栈已经是16字节对齐的(因为进入函数时RSP是16字节对齐的,然后push rdi(8字节)使RSP变为8字节对齐,再减去16字节(16的倍数)后,RSP又变为16字节对齐),但分配16字节可以确保后续操作满足对齐要求。
    3. ​填充初始化​​:在Debug模式下,编译器可能会分配额外的空间并用特定值(如0xCC)填充,以便在调试时更容易识别未初始化的内存访问。

    为什么用0xCC填充?

    • 0xCC是调试模式下常用的填充值,它有两个作用:
      1. 在调试器中,当程序执行到0xCC时,会触发断点(因为0xCC是int 3指令的机器码)。
      2. 在内存中填充0xCC,如果程序错误地执行了这些区域,会立即触发断点,便于发现错误。
      3. 另外,0xCC在内存中显示为连续的“烫”字(在GBK编码中),便于肉眼识别未初始化内存。

     5. rep指令

    重复前缀,作用​​:根据 ECX(32位)或 RCX(64位)寄存器的值重复执行后续指令,每次执行后计数器减1,直到计数器为

    6. stos指令

    存储字符串指令,将 EAX(32位)或 RAX(64位)寄存器的值存储到 [EDI] 或 [RDI] 指向的内存地址,并根据方向标志(DF)更新指针

    7. 重复操作

      000000000000006B: 48 8B FC           mov         rdi,rsp000000000000006E: B9 04 00 00 00     mov         ecx,40000000000000073: B8 CC CC CC CC     mov         eax,0CCCCCCCCh0000000000000078: F3 AB              rep stos    dword ptr [rdi]

    这几行代码的核心就是rep stos指令, 首先将rsp栈顶指针的地址给到rdi寄存器,给ecx寄存器赋值为4, eax寄存器赋值为0xCCCCCCCC

    req指令为将 stos dword ptr [rdi] 指令重复操作 ecx次,即4次

    stos指令为将eax的数据存储到rdi的地址,即栈顶指针,就是前面sub指令分配的16字节内存内

    8. mulss,addss指令

    故名思意,mulss即将两个数相乘,放在第一个数的地址

    addss则为相加

    9. 内存释放

    在函数结尾处的 add rsp, 10h 指令与函数开头的 sub rsp, 10h 严格对应, 调整栈顶指针的位置。

    三、汇编转化

    single_float_param:0000000000000060: F3 0F 11 44 24 08  movss   dword ptr [rsp+8],xmm0  ; 保存浮点参数到栈0000000000000066: 57                 push    rdi                     ; 保存 RDI0000000000000067: 48 83 EC 10        sub     rsp,10h                 ; 分配 16 字节栈空间000000000000006B: 48 8B FC           mov     rdi,rsp                 ; RDI = 栈顶指针000000000000006E: B9 04 00 00 00     mov     ecx,4                   ; 循环计数 = 40000000000000073: B8 CC CC CC CC     mov     eax,0CCCCCCCCh           ; 填充值 = 0xCC0000000000000078: F3 AB              rep stos dword ptr [rdi]        ; 用 0xCC 填充栈空间000000000000007A: F3 0F 10 44 24 20  movss   xmm0,dword ptr [rsp+20h] ; 加载参数到 XMM00000000000000080: F3 0F 59 05 00 00  mulss   xmm0,dword ptr [__real@3fc00000] ; 乘以 1.5f0000000000000088: F3 0F 58 05 00 00  addss   xmm0,dword ptr [__real@3f000000] ; 加上 0.5f0000000000000090: 48 83 C4 10        add     rsp,10h                 ; 释放栈空间0000000000000094: 5F                 pop     rdi                     ; 恢复 RDI0000000000000095: C3                 ret                             ; 返回结果

    其中很大部分的操作都是debug调试添加的,release都会优化掉,优化后即:

    Release 模式优化版本
    mulss xmm0, [__real@3fc00000]
    addss xmm0, [__real@3f000000]
    ret

    转化为C语言:

    // 函数声明
    float single_float_param(float param);// 实际实现
    float single_float_param(float param) {// Debug 模式专用:分配并初始化栈空间volatile unsigned char stack_buf[16]; for (int i = 0; i < sizeof(stack_buf); i++) {stack_buf[i] = 0xCC; // 调试填充值}// 实际计算:1.5 * param + 0.5float result = param * 1.5f + 0.5f;return result;
    }

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/pingmian/83930.shtml

    如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    深入了解UDP套接字:构建高效网络通信

    个人主页&#xff1a;chian-ocean 文章专栏-NET 深入了解UDP套接字&#xff1a;构建高效网络通信 个人主页&#xff1a;chian-ocean文章专栏-NET 前言&#xff1a;UDPUDP 特点&#xff1a;UDP的应用 套接字地址IP地址&#xff08;Internet Protocol Address&#xff09;IP地址…

    C++课设:实现简易文件加密工具(凯撒密码、异或加密、Base64编码)

    名人说&#xff1a;路漫漫其修远兮&#xff0c;吾将上下而求索。—— 屈原《离骚》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 专栏介绍&#xff1a;《编程项目实战》 目录 一、初识文件加密&#xff1a;为什么需要…

    Qt/C++学习系列之Excel使用记录

    Qt/C学习系列之Excel使用记录 前言The process was ended forcefully.解决方式断点查语句问题 总结 前言 在项目中解析条目达50多条&#xff0c;并且都需要将对应的结果进行显示。为了将结果显示的更加清晰&#xff0c;考虑采用QTableWidget进行表格设置&#xff0c;而在使用过…

    Mac软件卸载指南,简单易懂!

    刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…

    并发编程实战(生产者消费者模型)

    在并发编程中使用生产者和消费者模式能够解决绝大多数的并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序整体处理数据的速度。 生产者和消费者模式&#xff1a; 在线程的世界中生产者就是产生数据的线程&#xff0c;而消费者则是消费数据的线程。在多线程开…

    力扣hot100---152.乘积最大子数组

    给你一个整数数组 nums &#xff0c;请你找出数组中乘积最大的非空连续子数组&#xff08;该子数组中至少包含一个数字&#xff09;&#xff0c;并返回该子数组所对应的乘积。 测试用例的答案是一个 32-位 整数。 示例 1: 输入: nums [2,3,-2,4] 输出:6解释: 子数组 [2,3] 有最…

    什么是DevOps智能平台的核心功能?

    在数字化转型的浪潮中&#xff0c;DevOps智能平台已成为企业提升研发效能、加速产品迭代的核心工具。然而&#xff0c;许多人对“DevOps智能平台”的理解仍停留在“自动化工具链”的表层概念。今天&#xff0c;我们从一个真实场景切入&#xff1a;假设你是某互联网公司的技术负…

    柯尼卡美能达Konica Minolta bizhub 205i打印机信息

    基本参数 产品类型&#xff1a;激光数码复合机颜色类型&#xff1a;黑白涵盖功能&#xff1a;复印、打印、扫描最大原稿尺寸&#xff1a;A3内存容量&#xff1a;256MB供纸容量&#xff1a;标配 350 页&#xff0c;最大 1350 页介质重量&#xff1a;标准纸盒 64-157g/㎡&#xf…

    虚拟机与宿主机应用通信配置指南

    1. 选择虚拟机网络模式 桥接模式 (Bridged) 客户机获得独立局域网IP&#xff0c;与宿主机同网段。 客户机可直接访问宿主机IP&#xff08;如 192.168.1.x&#xff09;。 Host-Only 模式 仅宿主机与客户机之间通信&#xff0c;宿主机通常有一个虚拟网卡&#xff08;如 192.16…

    网络库libhv介绍

    libhv是一个类似于libevent、libev、libuv的跨平台网络库&#xff0c;提供了更易用的接口和更丰富的协议&#xff0c;用来开发TCP/UDP/SSL/HTTP/WebSocket/MQTT 客户端/服务端。源码地址&#xff1a;https://github.com/ithewei/libhv&#xff0c;最新发布版本为v1.3.3&#xf…

    施耐德特价型号伺服电机VIA0703D31A1022、常见故障

    ⚙️ ‌一、启动类故障‌ ‌电机无法启动‌ ‌可能原因‌&#xff1a;电源未接通、制动器未释放、接线错误或控制器故障。‌解决措施‌&#xff1a; 检查电源线路及断路器状态&#xff1b;验证制动器是否打开&#xff08;带制动器型号&#xff09;&#xff1b;核对电机与控制器…

    【Redis从入门到精通实战文章汇总】

    &#x1f4da;博客主页&#xff1a;代码探秘者 ✨专栏&#xff1a;文章正在持续更新ing… ✅C语言/C&#xff1a;C&#xff08;详细版&#xff09; 数据结构&#xff09; 十大排序算法 ✅Java基础&#xff1a;JavaSE基础 面向对象大合集 JavaSE进阶 Java版数据结构JDK新特性…

    MCP 技术完全指南:微软开源项目助力 AI 开发标准化学习

    引言 在人工智能快速发展的今天&#xff0c;如何让 AI 模型与客户端应用程序之间建立标准化的交互机制&#xff0c;已成为开发者们亟待解决的关键问题。微软近期开源的 mcp-for-beginners 项目&#xff0c;为我们提供了一个系统性学习 Model Context Protocol (MCP) 的绝佳机会…

    SQL进阶之旅 Day 20:锁与并发控制技巧

    【JDK21深度解密 Day 20】锁与并发控制技巧 文章简述 在高并发的数据库环境中&#xff0c;锁与并发控制是保障数据一致性和系统稳定性的核心机制。本文作为“SQL进阶之旅”系列的第20天&#xff0c;深入探讨SQL中的锁机制、事务隔离级别以及并发控制策略。文章从理论基础入手…

    Qt(part 2)1、Qwindow(菜单栏,工具栏,状态栏),铆接部件,核心部件 ,2、添加资源文件 3、对话框

    1、Qwindow tips&#xff1a;1&#xff0c;首先为什么创建出的对象基本都是指针形式&#xff0c;个人觉得是对象树的原因&#xff08;自动释放内存&#xff09;&#xff0c;指针来访问成员函数->的形式。2&#xff0c;菜单栏只能一个的&#xff0c;放窗口基本Set&#xff0c…

    一款“短小精悍的”手机录屏软件

    这个时代&#xff0c;手机自带录屏功能已经不是什么稀奇的事情了&#xff0c;但是手机自带的录屏功能不都是完美的&#xff0c;无法静音录屏、、不能修改画质、不能剪辑、不能自定义水印......emmm.....貌似除了录屏就什么都不会 今天分享的这款软件——ADV屏幕录制汉化版&…

    力扣HOT100之二分查找:153. 寻找旋转排序数组中的最小值

    这道题是上一道题&#xff1a;33. 搜索旋转排序数组的前置题&#xff0c;有点没看懂力扣为什么要这样安排题目顺序&#xff0c;应该把这道题按排在前面才对啊。。。这道题的思路已经在上一道题的思路中说过了&#xff0c;这里就直接复制粘贴上一篇博客中的内容了。 我们阅读完题…

    libiec61850 mms协议异步模式

    之前项目中使用到libiec61850库&#xff0c;都是服务端开发。这次新的需求要接收服务端的遥测数据&#xff0c;这就涉及到客户端开发了。 客户端开发没搞过啊&#xff0c;挑战不少&#xff0c;但是人不就是通过战胜困难才成长的嘛。通过查看libiec61850的客户端API发现&#xf…

    【 知你所想 】基于ernie-x1-turbo推理模型实现趣味猜心游戏

    &#x1f31f; 项目特点 &#x1f916; 智能AI&#xff1a;基于文心一言大模型&#xff0c;具有强大的推理能力&#x1f3af; 实时思考&#xff1a;展示AI的思考过程&#xff0c;让你了解AI是如何推理的&#x1f3ae; 互动性强&#xff1a;通过简单的"是/否"问答&…

    Excel 模拟分析之单变量求解简单应用

    正向求解 利用公式根据贷款总额、还款期限、贷款利率&#xff0c;求每月还款金额 反向求解 根据每月还款能力&#xff0c;求最大能承受贷款金额 参数&#xff1a; 目标单元格&#xff1a;求的值所在的单元格 目标值&#xff1a;想要达到的预期值 可变单元格&#xff1a;变…