news 2026/5/3 19:56:15

动态修改内核 / 模块代码

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
动态修改内核 / 模块代码

动态修改内核 / 模块代码的核心挑战:

  1. 代码段只读:内核 / 模块加载后,.text 段默认映射为只读(x86 受 CR0.WP 保护,ARM64 受页表权限保护);
  2. Cache 不一致:写入的指令先存在 D-cache,I-cache 仍保留旧指令,CPU 执行旧代码;
  3. 多核一致性:其他核可能仍在执行旧代码,需同步所有核的 Cache;
  4. 指令原子性:修改指令时需避免 CPU 取指到 “半写” 的指令(导致崩溃)。

核心模块解释

模块作用关键注意点
权限修改解除代码段只读限制x86 修改 CR0.WP 位,ARM64 修改页表权限;操作后必须恢复,否则内核崩溃
自旋锁保护避免多核并发修改加锁时需禁用中断(spin_lock_irqsave),防止死锁
Cache 同步保证 I/D 缓存一致flush_dcache_range+invalidate_icache_range是核心,ARM64 需额外加内存屏障
多核同步on_each_cpu向所有核发送 IPI,强制其他核失效本地 I-cache,避免执行旧代码
指令构造生成跳转指令不同架构指令格式不同,需按指令集规范构造(x86 jmp rel32,ARM64 b 偏移)

多核场景下的额外注意事项

如果你的场景是多核修改代码(如 SMP 系统),需额外处理:

  1. 核间中断(IPI):向所有核发送中断,强制其他核失效本地 I-cache,避免其他核仍执行旧指令。
    #include <linux/smp.h> // 向所有核发送 IPI,执行缓存失效 on_each_cpu((void (*)(void *))invalidate_icache_range, (void *)func_ptr, 1);
  2. 避免并发修改:使用自旋锁保护代码修改过程,防止多核同时修改同一地址:
    static DEFINE_SPINLOCK(code_mod_lock); // 修改代码前加锁 spin_lock(&code_mod_lock); modify_code_with_cache_sync(...); spin_unlock(&code_mod_lock);

不同架构的核心差异

架构Cache 操作核心差异
x86/x86_64硬件自动保证 I/D 缓存一致性,invalidate_icache_range基本是空操作,但修改代码仍需刷写 D-cache + 关闭写保护。
ARM64哈佛架构,I/D 缓存完全独立,必须显式执行flush_dcache_range + invalidate_icache_range + 内存屏障,否则必崩溃。
RISC-V类似 ARM64,需显式同步 I/D 缓存,指令为sfence.vma/fence.i

完整实现框架

以下是通用的内核模块模板,支持动态修改任意内核 / 模块函数,并保证跨架构、多核稳定。

#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/smp.h> #include <linux/spinlock.h> #include <asm/cacheflush.h> #include <asm/page.h> #include <asm/cpufeature.h> #include <asm/barrier.h> #include <asm/unistd.h> // 自旋锁:保护代码修改过程,避免多核并发修改 static DEFINE_SPINLOCK(code_mod_lock); // 保存原始指令的结构体(用于恢复) struct code_mod_ctx { void *target_addr; // 要修改的目标函数地址 unsigned char *orig_insns; // 保存的原始指令 size_t insn_len; // 指令长度(按缓存行对齐) }; // ===================== 架构相关工具函数 ===================== #if defined(CONFIG_X86_64) /** * x86_64 关闭/恢复写保护 */ static unsigned long cr0_save; static inline void disable_write_protect(void) { cr0_save = read_cr0(); write_cr0(cr0_save & ~X86_CR0_WP); // 关闭写保护位 } static inline void enable_write_protect(void) { write_cr0(cr0_save); // 恢复写保护 } #elif defined(CONFIG_ARM64) /** * ARM64 修改页表权限(代码段默认只读) */ static pgprot_t orig_prot; static inline void set_text_rw(void *addr) { unsigned long va = (unsigned long)addr; pte_t *pte = virt_to_kpte(va); orig_prot = pte_pgprot(*pte); set_pte(pte, pte_modify(*pte, pgprot_writecombine(orig_prot))); flush_tlb_kernel_range(va, va + PAGE_SIZE); } static inline void set_text_ro(void *addr) { unsigned long va = (unsigned long)addr; pte_t *pte = virt_to_kpte(va); set_pte(pte, pte_modify(*pte, orig_prot)); flush_tlb_kernel_range(va, va + PAGE_SIZE); } #endif // ===================== 核心:修改代码 + Cache 同步 ===================== /** * code_modify - 动态修改代码并保证 Cache/多核一致性 * @ctx: 上下文(目标地址、原始指令缓存、指令长度) * @new_insns: 新指令缓冲区 * 返回:0 成功,负数失败 */ static int code_modify(struct code_mod_ctx *ctx, const unsigned char *new_insns) { unsigned long flags; void *target = ctx->target_addr; size_t len = ctx->insn_len; if (!ctx || !new_insns || len == 0) return -EINVAL; // 1. 加自旋锁,避免多核并发修改 spin_lock_irqsave(&code_mod_lock, flags); // 2. 保存原始指令(用于恢复) memcpy(ctx->orig_insns, target, len); // 3. 修改代码段权限(只读 → 可写) #if defined(CONFIG_X86_64) disable_write_protect(); #elif defined(CONFIG_ARM64) set_text_rw(target); #endif // 4. 写入新指令(核心操作) memcpy(target, new_insns, len); // 5. 恢复代码段权限 #if defined(CONFIG_X86_64) enable_write_protect(); #elif defined(CONFIG_ARM64) set_text_ro(target); #endif // 6. 关键:同步 Cache(I/D 一致性) // - 刷写 D-cache:将新指令从 D-cache 写回内存 flush_dcache_range((unsigned long)target, (unsigned long)target + len); // - 失效 I-cache:让 CPU 重新从内存取指 invalidate_icache_range((unsigned long)target, (unsigned long)target + len); // 7. 多核同步:向所有核发送 IP I,强制失效其本地 I-cache on_each_cpu( (void (*)(void *))invalidate_icache_range, (void *)((unsigned long)target), 1 // 等待所有核执行完成 ); // 8. ARM64 额外:内存屏障,确保指令生效 #if defined(CONFIG_ARM64) dsb(ish); // 数据同步屏障:确保所有缓存操作完成 isb(); // 指令同步屏障:刷新 CPU 流水线 #endif // 释放锁 spin_unlock_irqrestore(&code_mod_lock, flags); pr_info("Code modified at %p (len: %zu), cache synced\n", target, len); return 0; } // ===================== 恢复原始代码 ===================== static int code_restore(struct code_mod_ctx *ctx) { unsigned long flags; void *target = ctx->target_addr; size_t len = ctx->insn_len; if (!ctx || !ctx->orig_insns) return -EINVAL; spin_lock_irqsave(&code_mod_lock, flags); // 修改权限 #if defined(CONFIG_X86_64) disable_write_protect(); #elif defined(CONFIG_ARM64) set_text_rw(target); #endif // 恢复原始指令 memcpy(target, ctx->orig_insns, len); // 恢复权限 #if defined(CONFIG_X86_64) enable_write_protect(); #elif defined(CONFIG_ARM64) set_text_ro(target); #endif // 同步 Cache flush_dcache_range((unsigned long)target, (unsigned long)target + len); invalidate_icache_range((unsigned long)target, (unsigned long)target + len); on_each_cpu((void (*)(void *))invalidate_icache_range, (void *)((unsigned long)target), 1); #if defined(CONFIG_ARM64) dsb(ish); isb(); #endif spin_unlock_irqrestore(&code_mod_lock, flags); pr_info("Code restored at %p\n", target); return 0; } // ===================== 测试用例 ===================== // 原始测试函数 static void test_func(void) { pr_info("Original test_func called\n"); } // 替换后的新函数 static void new_test_func(void) { pr_info("Modified new_test_func called (cache synced)\n"); } // 构造跳转指令(x86: jmp; ARM64: b) static void build_jmp_insn(unsigned char *insns, size_t len, void *from, void *to) { unsigned long from_addr = (unsigned long)from; unsigned long to_addr = (unsigned long)to; #if defined(CONFIG_X86_64) // x86_64: E9 (jmp rel32) + 4字节偏移(总长度 5 字节) if (len < 5) return; insns[0] = 0xE9; *(unsigned int *)(insns + 1) = (to_addr - (from_addr + 5)); #elif defined(CONFIG_ARM64) // ARM64: 14000000 (b) + 26位偏移(总长度 4 字节) if (len < 4) return; unsigned long offset = (to_addr - from_addr) >> 2; *(unsigned int *)insns = 0x14000000 | (offset & 0x03FFFFFF); #endif } // 模块上下文 static struct code_mod_ctx test_ctx; static unsigned char new_insns[L1_CACHE_BYTES] __aligned(L1_CACHE_BYTES); // 模块初始化 static int __init code_mod_init(void) { // 初始化上下文 test_ctx.target_addr = test_func; test_ctx.insn_len = L1_CACHE_BYTES; test_ctx.orig_insns = kzalloc(test_ctx.insn_len, GFP_KERNEL); if (!test_ctx.orig_insns) return -ENOMEM; // 构造跳转指令:test_func → new_test_func build_jmp_insn(new_insns, test_ctx.insn_len, test_func, new_test_func); // 动态修改代码 if (code_modify(&test_ctx, new_insns) != 0) { kfree(test_ctx.orig_insns); return -EFAULT; } // 调用修改后的函数,验证效果 pr_info("Calling modified test_func...\n"); test_func(); return 0; } // 模块退出 static void __exit code_mod_exit(void) { // 恢复原始代码 code_restore(&test_ctx); // 验证恢复效果 pr_info("Calling restored test_func...\n"); test_func(); // 释放内存 kfree(test_ctx.orig_insns); } module_init(code_mod_init); module_exit(code_mod_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Dynamic kernel/module code modification with cache sync");

核心模块解释

模块作用关键注意点
权限修改解除代码段只读限制x86 修改 CR0.WP 位,ARM64 修改页表权限;操作后必须恢复,否则内核崩溃
自旋锁保护避免多核并发修改加锁时需禁用中断(spin_lock_irqsave),防止死锁
Cache 同步保证 I/D 缓存一致flush_dcache_range+invalidate_icache_range是核心,ARM64 需额外加内存屏障
多核同步on_each_cpu向所有核发送 IPI,强制其他核失效本地 I-cache,避免执行旧代码
指令构造生成跳转指令不同架构指令格式不同,需按指令集规范构造(x86 jmp rel32,ARM64 b 偏移)

总结

  1. 修改内核代码段的核心 Cache 操作是:刷写 D-cache 到内存 + 失效 I-cache + 内存屏障(ARM64/RISC-V)
  2. x86 需临时关闭写保护,ARM64 需加dsb/isb屏障,多核场景需发送 IPI 同步所有核的 Cache。
  3. 操作完成后必须恢复原始代码和系统状态(如写保护、Cache 状态),避免内核稳定性问题。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/26 21:00:48

赛事直播系统高可用性测试指标制定

1.高可用性在赛事直播系统中的核心地位 赛事直播系统&#xff08;如体育赛事、电竞赛事&#xff09;是当今数字娱乐的核心场景&#xff0c;具有高并发、低延迟和实时性要求。2026年&#xff0c;随着5G普及和用户对无缝体验的期望提升&#xff0c;系统可用性成为关键竞争指标。…

作者头像 李华
网站建设 2026/4/27 9:20:13

启动一个three.js项目 不使用vue框架

新建文件夹&#xff1a; 选择&#xff1a;Vanilla (就是不用vue框架的意思) 报错&#xff1a;node版本&#xff1a; 展示&#xff1a; 代码中使用index.html引入three.js 删除main.js: 创建style.css: index.html引入&#xff1a; 安装three.js: 当前版本&#xff1a; 切…

作者头像 李华
网站建设 2026/5/3 19:16:53

P0973CA FEM100现场总线扩展

P0973CA FEM100 现场总线扩展模块简介&#xff1a; P0973CA FEM100 现场总线扩展模块是工业控制系统中用于扩展现场总线连接能力的重要组件&#xff0c;能够将控制器与更多现场设备可靠连接&#xff0c;实现系统的灵活扩展与集中管理。P0973CA FEM100 模块用于扩展工业现场总线…

作者头像 李华
网站建设 2026/5/1 15:30:12

leetcode 110. 平衡二叉树 简单

给定一个二叉树&#xff0c;判断它是否是 平衡二叉树。示例 1&#xff1a;输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;true示例 2&#xff1a;输入&#xff1a;root [1,2,2,3,3,null,null,4,4] 输出&#xff1a;false示例 3&#xff1a;输入&#xff1a;…

作者头像 李华
网站建设 2026/5/1 8:20:34

客流暴涨350%!华润万家华北调改店5连开,掀起“品质生活“潮

客流暴涨350%&#xff01;华润万家华北调改店5连开&#xff0c;掀起“品质生活“潮引言春节未至&#xff0c;华北的零售市场却早已火热起来。其中&#xff0c;华润万家就是最具热力的品牌之一。2月6日&#xff0c;华润万家天津湾凯德MALL店正式焕新亮相&#xff0c;这是华润万家…

作者头像 李华