Windows on AArch64 同样支持热补丁,但实现比 x86 上简单得多

The Old New Thing (Raymond Chen) 新闻

摘要

Raymond Chen 解释了 Windows on AArch64 如何比 x86 更简单地实现函数热补丁:利用 12 字节的补丁空间,通过 xip0 暂存寄存器构建三条指令的跳板(trampoline),同时指出函数入口通常有指针认证(pacibsp)指令进行保护。

<p>我过去曾指出,<a title="Why do Windows functions all begin with a pointless MOV EDI, EDI instruction?" href="https://devblogs.microsoft.com/oldnewthing/20110921-00/?p=9583"> x86-32</a> 和 <a title="Why don't Windows functions begin with a pointless MOV EDI,EDI instruction on x86-64?" href="https://devblogs.microsoft.com/oldnewthing/20221109-00/?p=107373"> x86-64</a> 版本的 Windows 都会小心地在每个函数开头留出一个补丁点。那么 AArch64(在 Windows 中称为 arm64)呢?</p> <p>Windows 在 AArch64 上同样会为函数插入补丁点,但由于指令集长度固定,这些补丁点简单得多。你不必担心在指令指针恰好位于字节序列中间时去打补丁,因为指令指针<i>从不</i>位于字节序列的中间。指令指针始终指向 4 的倍数的地址。</p> <p>因此,函数的第一条指令并没有什么特殊限制。所有指令都满足可原子更新的要求,不会出现指令指针停在某条指令中间的情况。</p> <p>每个函数前面都有 12 字节的补丁空间,这<a title="The AArch64 processor (aka arm64), part 15: Control transfer" href="https://devblogs.microsoft.com/oldnewthing/20220815-00/?p=106975">恰好足以容纳一个三条指令的跳板</a>:</p> <pre>; 用以下三条指令覆盖补丁空间 adrp xip0, PageStart(replacement) add xip0, xip0, PageOffset(replacement) br xip0 function_entry_point: ; 用一条指令覆盖函数入口点 br $-12 ; 跳转到补丁空间 </pre> <p><code>xip0</code> 寄存器是<a title="The AArch64 processor (aka arm64), part 1: Introduction" href="https://devblogs.microsoft.com/oldnewthing/20220726-00/?p=106898">两个过程内调用暂存寄存器之一</a>,按照约定,任何分支指令都可以破坏这个寄存器的内容。由于调用方本来就必须使用分支指令才能到达 <code>function_<wbr />entry_<wbr />point</code>,所以它不可能用 <code>xip0</code> 保存任何内容,因此我们可以在跳板中自由地破坏 <code>xip0</code>。</p> <p><b>额外补充</b>:函数入口点的第一条指令几乎可以肯定是 <code>pacibsp</code>,即<a title="The AArch64 processor (aka arm64), part 18: Return address protection" href="https://devblogs.microsoft.com/oldnewthing/20220819-00/?p=107020">对返回地址进行签名的指针认证指令</a>,用于增强代码对 ROP 攻击以及覆写返回地址类攻击的抵抗力。</p> <p>这篇文章 <a href="https://devblogs.microsoft.com/oldnewthing/20260930-00/?p=112744/">Windows on AArch64 同样支持热补丁,但实现比 x86 上简单得多</a> 最早发布于 <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>。</p>
查看原文
查看缓存全文

缓存时间: 2026/10/01 14:50

# Windows on AArch64 同样支持热修补,但比 x86 上简单得多 - The Old New Thing 来源: https://devblogs.microsoft.com/oldnewthing/20260930-00/?p=112744/ 我过去曾指出,Windows 的x86\-32 (https://devblogs.microsoft.com/oldnewthing/20110921-00/?p=9583)和x86\-64 (https://devblogs.microsoft.com/oldnewthing/20221109-00/?p=107373)版本都谨慎地在每个函数开头设置了修补点。那么 AArch64(在 Windows 中称为 arm64)呢? Windows 在 AArch64 上同样为函数插入了修补点,但由于采用定长指令集,情况要简单得多。你不必担心在修补指令时指令指针恰好处于字节序列的中间,因为指令指针*永远不会*处于字节序列的中间。指令指针始终位于 4 的倍数上。 因此,对函数的第一条指令没有特殊限制。所有指令都满足原子更新的条件,不存在指令指针落在指令中间的风险。 每个函数之前都有 12 字节的修补空间,恰好够放一条三指令的跳板 (https://devblogs.microsoft.com/oldnewthing/20220815-00/?p=106975): `` ; 用这三条指令覆盖修补空间 adrp xip0, PageStart(replacement) add xip0, xip0, PageOffset(replacement) br xip0 function_entry_point: ; 用一条指令覆盖函数入口点 br $-12 ; 跳转到修补空间 `` `xip0` 寄存器是两个过程内调用临时寄存器 (https://devblogs.microsoft.com/oldnewthing/20220726-00/?p=106898) 之一,约定是任何分支指令都可以覆写该寄存器。由于调用者本来就必须使用分支指令才能到达 `function_entry_point`,它不可能正在用 `xip0` 做任何事情,因此我们可以在跳板中自由覆写 `xip0`。 **附加说明**:函数入口点的第一条指令几乎可以确定是 `pacibsp`,即用于对返回地址进行签名的指针认证指令 (https://devblogs.microsoft.com/oldnewthing/20220819-00/?p=107020),目的是增强代码对 ROP 攻击以及覆写返回地址攻击的抵抗力。 ### 分类 ### 主题 ## 作者 Raymond Chen Raymond 参与 Windows 的演进已超过 30 年。2003 年,他开始了一个名为 The Old New Thing 的网站,其受欢迎程度远远超出他最狂野的想象,这一发展至今仍让他感到不安。这个网站催生了一本书,书名恰好也叫 The Old New Thing (Addison Wesley 2007)。他偶尔会出现在 Windows Dev Docs 的 Twitter 账号上,讲一些没有任何有用信息的故事。

相似文章

关于滥用Windows窗口类额外字节的兼容性说明

The Old New Thing (Raymond Chen)

Raymond Chen 讨论了一个历史性的 Windows 兼容性问题,其中一些 16 位程序滥用窗口类额外字节来存储私有数据,以及微软如何在保持向后兼容性的同时,对 32 位和 64 位程序堵住了这个漏洞。

Windows堆栈限制检查回顾,后续

The Old New Thing (Raymond Chen)

Raymond Chen跟进了他之前关于ARM64堆栈限制检查的文章,指出了堆栈探测函数中x15寄存器的非常规使用细节,并比较了多个架构的寄存器使用。

强制 ARM64X 可执行文件以特定架构运行

The Old New Thing (Raymond Chen)

本文介绍如何在 Windows 上强制 ARM64X 可执行文件以特定架构运行,使用 PROC_THREAD_ATTRIBUTE_MACHINE_TYPE 属性在需要插件兼容性时重新启动进程。

在64位Alpha AXP Windows NT上的Pinball

Lobsters Hottest

探讨了Windows内置Pinball游戏的历史、导致其无法在64位Windows(特别是Alpha AXP版本)上运行的碰撞检测Bug,以及近期模拟技术突破使得稀有的64位Alpha NT构建能够运行该游戏。