dynup/kpatch

GitHub: dynup/kpatch

kpatch 是一个(已废弃的)Linux 动态内核补丁工具,允许在不重启系统或进程的情况下为运行中的内核应用更新。

Stars: 1689 | Forks: 340

# kpatch:动态内核补丁 kpatch 是一个 Linux 动态内核补丁基础设施,允许您为正在运行的内核打补丁,而无需重启或重新启动任何进程。它使系统管理员能够立即为内核应用关键的安全补丁,而无需等待长时间运行的任务完成、等待用户注销或等待计划的重启窗口。它在不牺牲安全性或稳定性的前提下,更好地控制了系统的正常运行时间。 **警告:请谨慎使用!可能会发生内核崩溃、自发重启和数据丢失!** 以下是 kpatch 实际运行的视频: [![kpatch 视频](https://img.youtube.com/vi/juyQ5TsJRTA/0.jpg)](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, 内核, 动态补丁, 安全渗透, 客户端加密, 热更新, 系统运维