Windows 64编程中寄存器的使用

2023-11-17

下面是摘自 MSDN 的文章,在 Win64 下的 registers 用途

Register
Status
Use
RAX Volatile Return value register
RCX Volatile First integer argument
RDX Volatile Second integer argument
R8 Volatile Third integer argument
R9 Volatile Fourth integer argument
R10:R11 Volatile Must be preserved as needed by caller; used in syscall/sysret instructions
R12:R15 Nonvolatile Must be preserved by callee
RDI Nonvolatile Must be preserved by callee
RSI Nonvolatile Must be preserved by callee
RBX Nonvolatile Must be preserved by callee
RBP Nonvolatile May be used as a frame pointer; must be preserved by callee
RSP Nonvolatile Stack pointer
XMM0 Volatile First FP argument
XMM1 Volatile Second FP argument
XMM2 Volatile Third FP argument
XMM3 Volatile Fourth FP argument
XMM4:XMM5 Volatile Must be preserved as needed by caller
XMM6:XMM15 Nonvolatile Must be preserved as needed by callee.

 

1. 传递参数

在 Win64 里使用下面寄存器来传递参数:

  • rcx - 第 1 个参数
  • rdx - 第 2 个参数
  • r8 - 第 3 个参数
  • r9 - 第 4 个参数

其它多出来的参数通过 stack 传递。

使用下面寄存器来传递浮数数:

  • xmm0 - 第 1 个参数
  • xmm1 - 第 2 个参数
  • xmm2 - 第 3 个参数
  • xmm3 - 第 4 个参数

下面的代码:

void EditTextFile(HWND hEdit, LPCTSTR szFileName)
{
        HANDLE hFile;
        DWORD dwFileSize;
        DWORD dwFileSizeHigh;
        LPTSTR lpFileText;
        LPTSTR lpFileTextW;
        WORD wSignature;
        DWORD dwReadSize;

        hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

        ... ...

}

CreateFile() 的参数有 7 个,那么看看 VC 是怎样安排参数传递:

void EditTextFile(HWND hEdit, LPCTSTR szFileName)
{
000000013F791570 40 56                push        rsi 
000000013F791572 41 54                push        r12 
000000013F791574 41 55                push        r13 
000000013F791576 48 83 EC 50          sub         rsp,50h 
000000013F79157A 48 8B C2             mov         rax,rdx 
        HANDLE hFile;
        DWORD dwFileSize;
        DWORD dwFileSizeHigh;
        LPTSTR lpFileText;
        LPTSTR lpFileTextW;
        WORD wSignature;
        DWORD dwReadSize;

        hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
000000013F79157D 45 33 ED             xor         r13d,r13d 
000000013F791580 4C 8B E1             mov         r12,rcx 
000000013F791583 4C 89 6C 24 30       mov         qword ptr [rsp+30h],r13       // 第 7 个参数 
000000013F791588 45 8D 45 01          lea         r8d,[r13+1]                   // 第 3 个参数
000000013F79158C 45 33 C9             xor         r9d,r9d                       // 第 4 个参数
000000013F79158F BA 00 00 00 80       mov         edx,80000000h                 // 第 2 个参数
000000013F791594 48 8B C8             mov         rcx,rax                       // 第 1 个参数 
000000013F791597 C7 44 24 28 80 00 00 00 mov      dword ptr [rsp+28h],80h       // 第 6 个参数
000000013F79159F C7 44 24 20 03 00 00 00 mov      dword ptr [rsp+20h>],3         // 第 5 个参数
000000013F7915A7 FF 15 2B 0B 00 00    call        qword ptr [__imp_CreateFileW (13F7920D8h)] 
000000013F7915AD 48 8B F0             mov         rsi,rax 

        ... ...

上面已经对 7 个参数的传递进行了标注,前 4 个参数通过 rcx,rdx,r8 以及 r9 寄存器传递,后 3 个参数确实通过 stack 传递。

可是,事情并没有这么简单:

在 Win64 下,会为每个参数保留一份用来传递的 stack 空间,以便回写 caller 的 stack

在上面的例子中:

  • [rsp+20h] - 第 5 个参数
  • [rsp+28h] - 第 6 个参数
  • [rsp+30h] - 第 7 个参数

实际上已经为前面 4 个参数保留了 stack 空间,分别是:

  • [rsp] - 第 1 个参数(使用 rcx 代替)
  • [rsp+08h] - 第 2 个参数(使用 rdx 代替)
  • [rsp+10h] - 第 3 个参数(使用 r8 代替)
  • [rsp+18h] - 第 4 个参数(使用 r9 代替)

虽然是使用了 registers 来传递参数,然而还是保留了 stack 空间。接下着就是 [rsp+20h], [rsp+28h] 以及 [rsp+30h] 对应的 4,5,6 个参数

 

2. 回写 caller stack

VC 使用了下面编译参数来实现回写 caller stack

/homeparams

当使用了这个编译选项或者在 Debug 版下,它强制将 registers 里的值写回 stack 中

正如下面的代码:

CreateFileWImplementation:
0000000076EC2A30 48 89 5C 24 08       mov         qword ptr [rsp+8],rbx             // 回写 caller stack
0000000076EC2A35 48 89 6C 24 10       mov         qword ptr [rsp+10h],rbp           // 回写 caller stack
0000000076EC2A3A 48 89 74 24 18       mov         qword ptr [rsp+18h],rsi           // 回写 caller stack
0000000076EC2A3F 57                   push        rdi 
0000000076EC2A40 48 83 EC 50          sub         rsp,50h 
0000000076EC2A44 8B DA                mov         ebx,edx 
0000000076EC2A46 48 8B F9             mov         rdi,rcx 
0000000076EC2A49 48 8B D1             mov         rdx,rcx 
0000000076EC2A4C 48 8D 4C 24 40       lea         rcx,[rsp+40h] 
0000000076EC2A51 49 8B F1             mov         rsi,r9 
0000000076EC2A54 41 8B E8             mov         ebp,r8d 
0000000076EC2A57 FF 15 33 A1 08 00    call        qword ptr [__imp_RtlInitUnicodeStringEx (76F4CB90h)] 

上面所显示的是 CreateFile() 在 kernel32.dll 模块里的实现代码,上面对回写机制进行了标注,回写的 stack 正好是 caller 调用时未参数所保留的 stack 空间,上面的代码并不是那么直观。

下面我演示一下使用 /homeparams 选项来编译代码。

上面的 EditTextFile() 函数结果如下:

void EditTextFile(HWND hEdit, LPCTSTR szFileName)
{
000000013F6A15C0 48 89 54 24 10       mov         qword ptr [rsp+10h],rdx          // 第 2 个参数回写
000000013F6A15C5 48 89 4C 24 08       mov         qword ptr [rsp+8],rcx            // 第 1 个参数回写
000000013F6A15CA 56                   push        rsi 
000000013F6A15CB 41 54                push        r12 
000000013F6A15CD 48 83 EC 58          sub         rsp,58h 
 HANDLE hFile;
 DWORD dwFileSize;
 DWORD dwFileSizeHigh;
 LPTSTR lpFileText;
 LPTSTR lpFileTextW;
 WORD wSignature;
 DWORD dwReadSize;

 hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
000000013F6A15D1 48 8B 4C 24 78       mov         rcx,qword ptr [szFileName] 
000000013F6A15D6 45 33 E4             xor         r12d,r12d 
000000013F6A15D9 45 33 C9             xor         r9d,r9d 
000000013F6A15DC 4C 89 64 24 30       mov         qword ptr [rsp+30h],r12 
000000013F6A15E1 45 8D 44 24 01       lea         r8d,[r12+1] 
000000013F6A15E6 BA 00 00 00 80       mov         edx,80000000h 
000000013F6A15EB C7 44 24 28 80 00 00 00 mov         dword ptr [rsp+28h],80h 
000000013F6A15F3 C7 44 24 20 03 00 00 00 mov         dword ptr [rsp+20h],3 
000000013F6A15FB FF 15 D7 1A 00 00    call        qword ptr [__imp_CreateFileW (13F6A30D8h)]

第 1 个参数回写 [rsp+8] 处,第 2 个参数回写 [rsp+10h] 处。

注意这里的 stack 就是对应 caller 调用时的 stack,经过调用后 [rsp]返回地址值,因此,在 callee 里设置:

  • callee 写 [rsp+8] = caller 的 [rsp]
  • callee 写 [rsp+10h] = caller 的 [rsp+8]
  • callee 的 [rsp] = return address

上面很直观地显示了使用 /homeparams 选项时的效果,对比前一段没有使用 /homeparams 选项编译时的结果,很容易发现这个机制。

回写 caller stack 机制目的是为了 Debug 所需。

 

3. 由 callee 保存

在一个程序里应尽量使用 registers,在 x64 里有 16 个通用寄存器和 16 个 xmm 寄存器,可是一些 registers 在使用前必须保存原来值,以防丢失原来值。

因此,在 callee 使用它们时会将原值压入栈中保存,在 Win64 里,下面 registers 由 callee 负责保存:

  • rbx, rbp, rsi, rdi
  • r12 - r15
  • xmm6 - xmm15

每进入一个 callee,在使用它们之前都保存起来,返回 caller 之前,恢复原来值。因此这些寄存器的值是保持恒定的。

void EditTextFile(HWND hEdit, LPCTSTR szFileName)
{
000000013F9115C0 48 89 54 24 10       mov         qword ptr [rsp+10h],rdx 
000000013F9115C5 48 89 4C 24 08       mov         qword ptr [rsp+8],rcx 
000000013F9115CA 56                   push        rsi                     // 保存
000000013F9115CB 41 54                push        r12                     // 保存
000000013F9115CD 48 83 EC 58          sub         rsp,58h 
        HANDLE hFile;
        DWORD dwFileSize;

... ...


000000013F911708 48 83 C4 58          add         rsp,58h 
000000013F91170C 41 5C                pop         r12                    // 恢复
000000013F91170E 5E                   pop         rsi                    // 恢复
000000013F91170F C3                   ret

 

4. stack frame 结构

进入每个 callee 时,都会生成属于自己的 stack frame 结构,返回时会注销自己的 stack frame

  • rbp
  • rsp

由这两个 registers 来构造 stack frame 结构,rbp stack frame pointerrspstack pointer

可是,在 Win64 里,似乎不使用 stack frame 结构,VC 不会为每个函数创建 stack frame 结构

在 Win64 里,始终在使用动态使用 rsp 来维护 stack

void EditTextFile(HWND hEdit, LPCTSTR szFileName)
{
000000013F9115C0 48 89 54 24 10       mov         qword ptr [rsp+10h],rdx 
000000013F9115C5 48 89 4C 24 08       mov         qword ptr [rsp+8],rcx 
000000013F9115CA 56                   push        rsi                   
000000013F9115CB 41 54                push        r12                    
000000013F9115CD 48 83 EC 58          sub         rsp,58h             // 为 callee 分配 stack
        HANDLE hFile;
        DWORD dwFileSize;

... ...


000000013F911708 48 83 C4 58          add         rsp,58h            // 注销 callee stack 结构
000000013F91170C 41 5C                pop         r12                   
000000013F91170E 5E                   pop         rsi                  
000000013F91170F C3 

VC 不会生成 x86 下典型的 stack frame 结构,始终由 rsp 维护 stack,/Gd 编译选项在 Win64 下会被忽略,rbp 被保留起来

在 Win64 里,rdi 寄存器的角色变得很微妙,在某些场合下它充当了一部分 stack frame pointer 的角色。

 

5. r11 与 rcx 以及 r10

在 64 位模式下,在 sysret 指令返回时,将从 rcx 处得到返回地址,从 r11 处得到 rflags 值,因此在进入 system services routine(系统服务例程)前,或者在系统服务例程中的第1个任务是 rcx 与 r11 寄存器,以便 sysret 返回。

在 Win64 里,r10 寄存器充当保存 rcx 值的作用,如下:

NtCallbackReturn:
00000000770FFDA0 4C 8B D1             mov         r10,rcx 
00000000770FFDA3 B8 02 00 00 00       mov         eax,2 
00000000770FFDA8 0F 05                syscall 
00000000770FFDAA C3                   ret 

在进入 system call 之前,保存 rcx 的值。

 

原文:http://www.mouseos.com/win64/registers.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Windows 64编程中寄存器的使用 的相关文章

  • Linux c获取任意路径的硬盘使用情况

    没有什么好说的 其实就是获取硬盘的statfs信息结构 代码如下 include
  • 利用opencv检测移动的物体----mog2()

    检测移动的物体 opencv具有一个函数是mog2 参考链接 https www jb51 net article 143527 htm
  • ​TinyLlama-1.1B:从零开始训练一个精悍的语言模型

    PaperWeekly 原创 作者 StatNLP 单位 新加坡科技设计大学 TinyLlama 项目旨在在 3 万亿 tokens 上进行预训练 构建一个拥有 11 亿参数的 Llama 模型 经过精心优化 我们 仅 需 16 块 A10
  • java-性能&故障分析工具

    java 性能 故障分析工具 突然想起了 对HR小姐姐的承诺 你要是问我是不是为了福利 我是打死不会承认的 我们公司的技术栈以java居多 那就介绍一下之前自己写的一个性能 故障分析工具吧 也希望能对公司的java开发者有一点点帮助吧 居然
  • makefile是如何自动生成的

    GNU make允许将一个软件项目的代码分开放在不同的源文件里 有改动的时候可以只对改动的文件重新编译 然后重新连接 这种编译管理方法提高了生成目标的效率 make要调用一个makefile文件来实现 Makefile的编写是使用make的
  • 微信html5活动页面制作,完整的微信H5活动页面设计规范

    微信火了H5的专题页面设计也越来越多了 经常有朋友问H5专题设计按什么尺寸来做 有没有统一的规范 这里就给大家分享一套非常完整的关于微信H5活动页面设计的UI视觉设计规范教程 希望对那些刚刚从事微信H5界面设计的小伙伴们提供一些帮助 微信H
  • Caddy2学习笔记——Caddy2反向代理docker版本的DERP中继服务器

    一 个人环境概述 本人拥有一个国内云服务商的云主机和一个备案好的域名 通过caddy2来作为web服务器 我的云主机系统是Ubuntu 我的云主机是公网ip 地址为 43 126 100 78 我备案好的域名是 hotgirl com 后面
  • the “scope“ attribute for scoped slots have been deprecated and replaced by “slot-scope“ since 2.5

    在 VSCode 中运行 Vue 关于作用域插槽时的问题 报出警告 Module Warning from node modules vue vue loader v15 lib loaders templateLoader js Emit
  • (支付宝支付)Spring实现一个项目配置多个信息、付款给对应商户

    如何实现一个项目配置多个商户信息付款给对应商户 最近在对接支付宝支付时 遇到了一个问题 用户在付款时 需要直接付款到指定支付宝账户 这个需求也无可厚非 就像我们公司有四个分公司 分别在北京 上海等地 如果钱只能到总公司的账户上 那在财务结算
  • linux c do while循环,C语言do-while循环

    要执行程序或代码的一部分几次或多次 我们可以使用C语言的do while循环 在do和while之间给出的代码将被执行 直到条件 condition 成为true 在do while循环中 语句在条件之前给出 所以语句或代码将至少有一次执行
  • Wav2lip-GAN 环境配置

    首先使用 conda 创建新的虚拟环境 然后激活这个环境 conda create n myenv python 3 8 activate myenv 使用 git 克隆代码 或者直接下载源码压缩包解压 安装依赖 我使用的豆瓣源 git c
  • 热部署,未测试

    package agent import java lang instrument Instrumentation import java lang instrument UnmodifiableClassException import
  • 在子类的override方法中调用父类的父类的未被重写的方法

    今天做一个自定义控件 扩展TableLayoutPanel这个控件加一些自己的属性 重写OnPaintBackground这个虚方法 控件的继承关系是这样的 Control ScrollableControl Panel TableLayo
  • 5_spring-cloud-zuul-网关

    文章目录 网关 链路追踪 Zuul zuul 原理 负载均衡设置 配置指定服务的路由 重定向 忽略服务 禁止直接请求 前缀配置 暴露路由端点 网关 链路追踪 Zuul Sleuth https start aliyun com Zuul是N
  • 软件测试学习规划路线

    现如今互联网行业飞速发展 IT行业也是水涨船高 软件行业的未来发展也是越来越好 而软件测试在软件行业可谓是一个必不可少的职业 它不仅算得上一个长青工作 而且也是一个在需求持续增长的职业 如果你已经入行软件测试了 那就沉住气继续努力下去 保持
  • MyEclipse新建Maven webapp项目

    MyEclipse中创建新的Maven项目 webapp目录结构 过程如下 1 New gt Project gt Maven Project 2 Next 3 Next 选择 maven archetype webapp 创建一个weba
  • 编程教育案例

    1 Osmo Coding早教玩具 可以编代码的智能版乐高 https www playosmo com zh cn schools https digi tech qq com a 20160527 055566 htm https ww
  • BUUCTF Misc [GXYCTF2019]SXMgdGhpcyBiYXNlPw== & 间谍启示录 & Mysterious & [UTCTF2020]docx

    目录 GXYCTF2019 SXMgdGhpcyBiYXNlPw 间谍启示录 Mysterious UTCTF2020 docx GXYCTF2019 SXMgdGhpcyBiYXNlPw 下载文件 base64隐写 使用脚本 base64
  • linux下apt-get install软件出现,E: 未发现软件包

    在debian虚拟机中安装库时 总是提示E 未发现软件包 libXtst dev 搜索后发现可能是缺少软件源 打开 etc apt sources list 发现所有语句均被注释 搜索了很多 最后使用的Debian镜像使用帮助的使用样例 在
  • ps如何把人物扣下来

    做前端的难免会自己用上ps 我在网上看了很多关于ps如何把人物扣下来的 我给大家分享一下自己的一些见解 首先打开ps工具 里面放上一张图片 放进去图片之后什么东西都不用动 第二步 shift 键可以扩大选取 ail键可以减小选取 第三步 完

随机推荐

  • 区块链学习笔记(一)

    https zhuanlan zhihu com p 23243289 1 区块的数据结构 区块高度 每个区块的唯一ID 块高度为0的创世块 一段时间生成一个块 高度加1 头哈希 每个区块的唯一哈希值 根据父哈希 数据块哈希 随机数生成 父
  • 程序人生-Hello’s P2P[HITICS-大作业]

    计算机系统 大作业 题 目 程序人生 Hello s P2P 专 业 计算机科学与技术学院 学 号 1180301006 班 级 1803010 学 生 宋永玺 指 导 教 师 史先俊 计算机科学与技术学院 2019年12月 摘 要 一个简
  • Java 中j+=i 和 j=+i 的区别

    博主前些天发现了一个巨牛的人工智能学习网站 通俗易懂 风趣幽默 忍不住也分享一下给大家 点击跳转到网站 一 j i 意思就是 把i获取到的值与j相加 之后再把值赋给j 更新j的值 换句话说 j j i 写下代码更好的理解一下 Test pu
  • 静态绑定和动态绑定

    对于非虚成员函数 是静态绑定的 而虚函数都是动态绑定 如此才可实现多态性 这也是 语言和其它语言Java Python的一个显著区别 几个名词定义 静态类型 对象在声明时采用的类型 在编译期既已确定 动态类型 通常是指一个指针或引用目前所指
  • vscode按住ctrl+鼠标左键无法跟踪跳转方法名【带vscode编辑PHP的配置教程】

    今天刚装好vscode 发现vscode按住ctrl 鼠标左键无法跟踪跳转方法名 其实就是装一个插件就好了 vscode elm jump 常规的代码跳转定义 Vue CSS Peek 按ctrl可以跳转css定义 vue helper 变
  • ZooKeeper(六)权限管理机制

    一 ZooKeeper权限管理机制 1 1 权限管理ACL Access Control List ZooKeeper 的权限管理亦即ACL 控制功能 使用ACL来对Znode进行访问控制 ACL的实现和Unix文件访问许可非常相似 它使用
  • 【解决】CommandNotFoundError: Your shell has not been properly configured to use conda activate

    在linux系统中 安装了anaconda 配置了conda环境变量 也使用conda命令创建了新的环境my env 但在使用conda激活时 报错 报错问题 输入 conda activate my env 报错 CommandNotFo
  • 快速排序(C语言简单实现)

    快速排序 C语言简单实现 快速排序 Quick Sort 是冒泡排序的升级版 基本思想 通过一趟排序将待排记录分割成独立的两部分 其中一部分记录的关键字均比另一部分记录的关键字小 则可分别对这两部分记录继续进行排序 以达到整个序列有序的目的
  • [ Shell ] 通过 Shell 脚本导出 CDL 网表

    Python微信订餐小程序课程视频 https edu csdn net course detail 36074 Python实战量化交易理财系统 https edu csdn net course detail 35475 https b
  • 服务器要如何提高性能

    服务器要如何提高性能 一 将服务器虚拟化 如果同期拥有多个项目 增加额外服务器会显得浪费 成本费用也会大幅度上升 这时不妨通过技术将其划分成多个虚拟空间 而每个空间又可以使用不同操作系统 运行不同应用程序 使得符合项目要求 这种方式通常能增
  • 数字猜谜游戏

    数字猜谜游戏 1 数字猜谜之需求分析 2 在写程序时需要学习的知识 3 数字猜谜之编程思路 4 数字猜谜之程序代码 5 数字猜谜之用户演示 6 数字求和 1 100数字之间的偶数之和 包含1和100 1 100数字之间的奇数之和 包含1和1
  • nginx配置监听443端口,开启ssl协议,走 https 访问

    本文目录 前言 一 检查 linux 服务器上的 nginx 是否安装 ssl 模块 二 为 nginx 安装 ssl 模块 三 nginx 开启 443 端口监听 https配置 成功配好后的效果如下 遇到的问题一 证书无效 遇到的问题二
  • Unity UGUI的RawImage(原始图片)组件的介绍及使用

    Unity UGUI的RawImage 原始图片 组件的介绍及使用 1 什么是RawImage组件 RawImage是Unity UGUI中的一个组件 用于显示原始图片 与Image组件不同 RawImage可以直接显示原始图片的像素数据
  • 全面的数仓建设规范指南(纯干货建议收藏)

    本文将全面讲解数仓建设规范 从数据模型规范 到数仓公共规范 数仓各层规范 最后到数仓命名规范 包括表命名 指标字段命名规范等 目录 一 数据模型架构原则 数仓分层原则 主题域划分原则 数据模型设计原则 二 数仓公共开发规范 层次调用规范 数
  • 菜鸟求职记2

    9月14号 记得那天下午早早的就到了西电 等待alibaba的宣讲开始 然后就是占了个前排 听了一下午的宣讲 接下来要笔试了 可是人家只给收到笔试通知的菜鸟有座位 我们这等野鸟连笔试的机会也不给 后来听说最后还是给机会了 可是本菜鸟没有参加
  • 分布式注册中心-etcd

    etcd是什么 etcd 设计为提供高可用 强一致性的小型 kv 数据存储服务 etcd v2 和 v3 比较 v3使用 gRPC protobuf 取代 v2的http json 通信 提高通信效率 gRPC 只需要一条连接 http是每
  • C# 字符串(String)

    在 C 中 您可以使用字符数组来表示字符串 但是 更常见的做法是使用 string 关键字来声明一个字符串变量 string 关键字是 System String 类的别名 创建 String 对象 您可以使用以下方法之一来创建 strin
  • 学习总结HTML CSS JAVASCRIPT,对三剑客的一些理解

    网页设计一般用到三大技术 html css javascript HTML 相当于一个文档 里面是要给用户的信息 使用他提供的一些标签 来把你想传递给用户的信息进行合理编排 使信息更好的传递给用户 CSS 相当于把这个文档进行修饰 美化实现
  • 8.16 IO作业

    拷贝 int main int argc const char argv if argc 3 printf input file error n printf wsage a out srcfile dstfile n FILE fp if
  • Windows 64编程中寄存器的使用

    下面是摘自 MSDN 的文章 在 Win64 下的 registers 用途 Register Status Use RAX Volatile Return value register RCX Volatile First integer