dynup/kpatch
GitHub: dynup/kpatch
kpatch 是一个(已废弃的)Linux 动态内核补丁工具,允许在不重启系统或进程的情况下为运行中的内核应用更新。
Stars: 1689 | Forks: 340
# kpatch:动态内核补丁
kpatch 是一个 Linux 动态内核补丁基础设施,允许您为正在运行的内核打补丁,而无需重启或重新启动任何进程。它使系统管理员能够立即为内核应用关键的安全补丁,而无需等待长时间运行的任务完成、等待用户注销或等待计划的重启窗口。它在不牺牲安全性或稳定性的前提下,更好地控制了系统的正常运行时间。
**警告:请谨慎使用!可能会发生内核崩溃、自发重启和数据丢失!**
以下是 kpatch 实际运行的视频:
[](https://www.youtube.com/watch?v=juyQ5TsJRTA)
还有几个视频:
- https://www.youtube.com/watch?v=rN0sFjrJQfU
- https://www.youtube.com/watch?v=Mftc80KyjA4
# 目录
- [支持的架构](#supported-architectures)
- [安装](#installation)
- [快速入门](#quick-start)
- [补丁编写指南](#patch-author-guide)
- [工作原理](#how-it-works)
- [kpatch-build](#kpatch-build)
- [限制](#limitations)
- [常见问题](#frequently-asked-questions)
- [参与贡献](#get-involved)
- [许可证](#license)
## 支持的架构
- [x] x86-64
- [x] ppc64le
- [x] arm64
- [x] s390 [上游前提条件](doc/s390-upstream-prerequisites.md)
- [x] loongarch64
## 安装
请参阅 [INSTALL.md](doc/INSTALL.md)。
## 快速入门
首先,使用 diff、git 或 quilt 针对内核树制作一个源代码补丁。
作为一个简单的示例,我们来给 /proc/meminfo 打补丁,将 VmallocChunk 显示为全大写字母,以便我们看得更清楚:
```
$ cat meminfo-string.patch
Index: src/fs/proc/meminfo.c
===================================================================
--- src.orig/fs/proc/meminfo.c
+++ src/fs/proc/meminfo.c
@@ -95,7 +95,7 @@ static int meminfo_proc_show(struct seq_
"Committed_AS: %8lu kB\n"
"VmallocTotal: %8lu kB\n"
"VmallocUsed: %8lu kB\n"
- "VmallocChunk: %8lu kB\n"
+ "VMALLOCCHUNK: %8lu kB\n"
#ifdef CONFIG_MEMORY_FAILURE
"HardwareCorrupted: %5lu kB\n"
#endif
```
构建补丁模块:
```
$ kpatch-build meminfo-string.patch
Using cache at /home/jpoimboe/.kpatch/3.13.10-200.fc20.x86_64/src
Testing patch file
checking file fs/proc/meminfo.c
Building original kernel
Building patched kernel
Detecting changed objects
Rebuilding changed objects
Extracting new and modified ELF sections
meminfo.o: changed function: meminfo_proc_show
Building patch module: livepatch-meminfo-string.ko
SUCCESS
```
这会在当前目录下生成一个名为 `kpatch-meminfo-string.ko` 的补丁模块。现在将其应用到正在运行的内核:
```
$ sudo kpatch load kpatch-meminfo-string.ko
loading patch module: livepatch-meminfo-string.ko
```
完成!内核现已打好补丁。
```
$ grep -i chunk /proc/meminfo
VMALLOCCHUNK: 34359337092 kB
```
## 补丁编写指南
遗憾的是,热补丁并不总是像前面的示例那么简单,如果不小心,可能会产生一些重大的隐患。要了解有关如何正确创建热补丁的更多信息,请参阅[补丁编写指南](doc/patch-author-guide.md)。
## 工作原理
kpatch 的工作粒度是函数级别:用新函数替换旧函数。它包含三个主要组件:
- **kpatch-build**:一组工具,用于将源码 diff 补丁转换为补丁模块。它的工作原理是分别在带有和不带有源码补丁的情况下编译内核,比较二进制文件,并生成一个包含需要被替换的函数的新二进制版本的补丁模块。
- **补丁模块**:一个内核 livepatch 模块(.ko 文件),包含替换函数以及有关原始函数的元数据。加载后,它会向内核 livepatch 基础设施(CONFIG\_LIVEPATCH)注册,由后者执行补丁操作。
- **kpatch 实用程序:** 一个命令行工具,允许用户管理一系列补丁模块。可以配置一个或多个补丁模块在系统引导时加载,以便系统即使在重启到相同版本的内核后也能保持打补丁状态。
### kpatch-build
“kpatch-build”命令将源码级别的 diff 补丁文件转换为内核补丁模块。它的大部分工作由 kpatch-build 脚本执行,该脚本使用名为 `create-diff-object` 的实用程序来比较发生更改的目标文件。
kpatch-build 的主要步骤如下:
- 为内核构建未剥离的 vmlinux
- 为源码树打补丁
- 重新构建 vmlinux 并监视哪些目标文件被重新构建。这些就是“发生更改的目标文件”。
- 使用 `-ffunction-sections -fdata-sections` 重新编译每个更改过的目标文件,生成打过补丁且发生更改的目标文件
- 撤销对源码树的补丁
- 使用 `-ffunction-sections -fdata-sections` 重新编译每个更改过的目标文件,生成原始的且发生更改的目标文件
- 对于每个发生更改的目标文件,使用 `create-diff-object` 执行以下操作:
* 分析每个原始/补丁目标文件对的可打补丁性
* 向输出目标文件添加 `.kpatch.funcs` 和 `.rela.kpatch.funcs` 节。kpatch 核心模块利用它来确定需要使用 ftrace 重定向的函数列表。
* 向输出目标文件添加 `.kpatch.dynrelas` 和 `.rela.kpatch.dynrelas` 节。这将用于解析对未包含的本地符号和非导出的全局符号的引用。这些重定位将由 kpatch 核心模块解析。
* 生成包含新修改节的结果输出目标文件
- 将所有输出目标文件链接为一个累积目标文件
- 生成补丁模块
## 限制
- 注意:其中许多限制都可以通过创造性的解决方案来解决。有关更多详细信息,请参阅[补丁编写指南](doc/patch-author-guide.md)。
- 不支持修改初始化函数(标有 `__init`)的补丁。如果补丁尝试这样做,kpatch-build 将返回错误。
- 不直接支持修改静态分配数据的补丁。kpatch-build 会检测到这一点并返回错误。可以使用回调或影子变量来克服此限制,如[补丁编写指南](doc/patch-author-guide.md)中所述。
- 改变函数与动态分配数据交互方式的补丁可能是安全的,也可能不安全。kpatch-build 无法验证此类补丁的安全性。用户需要自行了解补丁的作用、新函数与动态分配数据的交互方式是否不同于旧函数,以及将此类补丁原子地应用到正在运行的内核是否安全。
- 不支持修改 vdso 中函数的补丁。这些函数在用户空间运行,ftrace 无法 hook 它们。
- 不支持修改缺少 `fentry` 调用的函数的补丁。这包括任何归档到 `lib.a` 库中以便后续链接的 `lib-y` 目标(例如,`lib/string.o`)。
- 目前,kpatch 与 ftrace 和 kprobes 的使用之间存在一些不兼容性。有关更多详细信息,请参阅常见问题部分。
## 常见问题
**问:这不就是一个病毒/rootkit 注入框架吗?**
kpatch 使用内核模块来替换代码。它需要 `CAP_SYS_MODULE` 权限。如果您已经拥有该权限,那么无论有没有 kpatch,您都已经具备了任意修改内核的能力。
**问:如何检测是否有人给内核打了补丁?**
如果当前应用了补丁,您可以在 `/sys/kernel/livepatch` 中看到它。此外,如果之前应用过补丁,则会设置 `TAINT_LIVEPATCH` 标志。要检测这些标志,请执行 `cat /proc/sys/kernel/tainted` 并检查是否对 `TAINT_LIVEPATCH` (32768) 的值进行了 OR 运算。
请注意,由于补丁模块是在 Linux 内核源码树之外构建的,因此也会设置 `TAINT_OOT_MODULE` 标志 (4096)。
如果您的补丁模块未签名,则还会设置 `TAINT_UNSIGNED_MODULE` 标志 (8192)。
**问:它会使我的系统不稳定吗?**
不会,只要谨慎创建补丁即可。请参阅上面的限制部分和[补丁编写指南](doc/patch-author-guide.md)。
**问:为什么不使用像 kexec 这样的替代方案呢?**
如果您想避免硬件重启,但不介意重启进程或使用 CRIU,那么 kexec 是一个不错的替代方案。
**问:如果应用程序无法处理重启,那它的设计就是错误的。**
这倒是个好问题…… [系统重启]
**问:支持哪些内核?**
kpatch 需要 gcc >= 4.8 和 Linux >= 4.0。
**问:可以移除补丁吗?**
可以。只需运行 `kpatch unload`,它将禁用并卸载补丁模块,并将函数恢复到其原始状态。
**问:可以应用多个补丁吗?**
可以,但是为了防止多个补丁模块之间出现任何意外的交互,建议补丁升级采用累积方式,以便每个补丁都是前一个补丁的超集。这可以通过在运行 `kpatch-build` 之前使用 `combinediff` 将新补丁与前一个补丁合并来实现。还建议使用 livepatch 原子的“替换”模式,这也是默认模式。
**问:为什么 kpatch-build 会检测到未被源码补丁触及的更改函数?**
这可能是由多种原因造成的,例如:
- 补丁更改了 inline 函数。
- 编译器决定 inline 一个已更改的函数,导致外部函数被重新编译。这在内部函数为 static 且仅被调用一次的情况下很常见。
- kpatch-build 在检测 `__LINE__` 宏的使用时存在 bug。
**问:支持为内核模块打补丁吗?**
- 支持。
**问:可以为树外模块打补丁吗?**
可以!但有几个要求,而且该功能仍处于起步阶段。
1. 您需要使用 `--oot-module` 标志来指定当前正在机器上运行的模块版本。
2. 必须传入 `--oot-module-src`,并附带一个目录,该目录包含与正在运行的模块相同版本的代码,并且所有内容都已设置好,随时可以使用 `make` 命令进行构建。例如,某些模块需要先运行 `autogen.sh` 和 `./configure` 并带有适当的标志,以匹配当前正在运行的模块。
3. 如果树外模块的 `Module.symvers` 文件未出现在所提供源码目录的根目录中,则需要在该目录中创建一个 symlink,指向其实际位置。
4. 通常,您还需要传入 `--target` 标志,以指定正确的 `make` target 名称。
5. 目前仅针对单个补丁包含单个树外模块进行了测试,不适用于依赖于单独构建的其他树外模块的情况。
***调用示例***
`kpatch-build --oot-module-src ~/test/ --target default --oot-module /lib/modules/$(uname -r)/extra/test.ko test.patch`
**问:支持一种新架构需要做什么?**
移植一种架构可以分为三个阶段进行:
1. 在内核中添加 `CONFIG_HAVE_LIVEPATCH` 支持。对于某些架构来说,这可能就像启用 `CONFIG_DYNAMIC_FTRACE_WITH REGS` 一样简单。有了这种支持,您就可以进行基本的热补丁操作,例如 samples/livepatch 中的那些。热补丁功能有限,必须格外小心以避免某些陷阱。
2. 添加 kpatch-build (create-diff-object) 支持。这使得构建补丁变得更容易,并且可以避免一些陷阱。例如,https://github.com/dynup/kpatch/pull/1203 添加了对 s390x 的支持。
3. 在内核中添加 `CONFIG_HAVE_RELIABLE_STACKTRACE` 和(如果需要的话)objtool 支持。这可以避免更多的陷阱并启用完整的热补丁功能。
## 许可证
kpatch 采用 GPLv2 许可证。
本程序是自由软件;您可以根据自由软件基金会发布的 GNU 通用公共许可证的条款重新分发或修改它;或者使用许可证的第二版,或(您自行选择的)任何更新的版本。
分发本程序是希望它能有用,但不提供任何保证;也不包括对适销性或特定用途适用性的任何默示保证。有关更多详细信息,请参阅 GNU 通用公共许可证。
您应该已经随本程序收到了一份 GNU 通用公共许可证的副本;如果没有,请写信给 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA。
标签:Cutter, Hakrawler, 内核, 动态补丁, 安全渗透, 客户端加密, 热更新, 系统运维