博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
动态替换内核函数
阅读量:2455 次
发布时间:2019-05-10

本文共 8952 字,大约阅读时间需要 29 分钟。

做这个东西源自一次面试,当时面试官仰天斜视45度,自以为很牛逼的说出了这个想法,我此奥,看了后很不爽,回来埋头一个月,做出第一个版本。已经于开源于github。

需求:在某些情况下,系统需要升级,但是不能重启,此时需要动态替换内核函数,进行动态升级。

本功能是基于2.6.38版本进行开发。 patch:git@github.com:cooboos/kxchg.git

解决思想很简单,就是利用trap指令进行函数替换(目前只支持x86架构)。下面就代码进行分析:

+struct kxchg 
+{
+ struct list_head list; //链表
+
+ const char *symbol_name;//被替换的函数名
+ unsigned long addr; //被替换的函数地址
+
+ const char *symbol_name_rep;//替换函数的函数名
+ unsigned long addr_rep; //替换函数的函数地址
+
+ int ori_ins; //被替换的原始指令的第一个字节(目前只做了x86架构的支持,int3指令为)
+};
+/*

--- kernel/kxchg.c	(revision 0) 
+++ kernel/kxchg.c (revision 0)
@@ -0,0 +1,183 @@
+/*
+ *
, 2012
+ *
+ * Released under the terms of the GNU GPL v2.0
+ *
+ * version 1.0 for Linux-2.6.38
+ */
+/*
+ * trap int3 instruction
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+ +unsigned long kallsyms_lookup_name(const char *name); +extern void arch_trap_kxchg(struct kxchg *p); +extern void arch_saveins(struct kxchg *p); +extern int kxchg_notifier_process(struct notifier_block *self, unsigned long val, void *data); +struct kxchg_list kxchg_head; +struct kxchg_list kxchg_old; + +static int kxchg_addr(struct kxchg *p) +{ + if (p && p->symbol_name && p->symbol_name_rep) { + + p->addr = kallsyms_lookup_name(p->symbol_name); + if(!p->addr) + return 1; + + p->addr_rep = kallsyms_lookup_name(p->symbol_name_rep); + if(!p->addr_rep) + return 1; + } + + return 0; +} + +int get_kxchg(struct kxchg *kxchg) +{ + int found = 0; + struct list_head *plist, *tmp; + struct kxchg *pkxchg = NULL; + + //Fixme: rcu read lock + spin_lock(&kxchg_head.lock); + list_for_each_safe(plist, tmp, &kxchg_head.list) { + pkxchg = list_entry(plist, struct kxchg, list); + if(pkxchg->addr == kxchg->addr) { + /* + * this symbol already resitered + * one symbole is only registered once + */ + found = 1; + list_del(&pkxchg->list); + } + } + spin_unlock(&kxchg_head.lock); + //Fixme:rcu read unlock + + if (found) { + + //Fixme: rcu write lock + spin_lock(&kxchg_old.lock); + list_add(&pkxchg->list, &kxchg_old.list); + spin_unlock(&kxchg_old.lock); + //Fixme: rcu write unlock + } + + return 0; +} +int kxchg_register(struct kxchg *ptr) //注册kxchg, +{ + int ret; + /* + * address the original symbol + * and the replace symbol + */ + ret = kxchg_addr(ptr); // 获取被替换函数和替换函数的地址。 + if(ret) { + + printk("kxchg get addr error!\n"); + return 1; + } + + printk("symbol [%s] address [%lx] new replace symbol_add[%lx]\n",ptr->symbol_name, ptr->addr, ptr->addr_rep); + + ret = get_kxchg(ptr); + if(ret){ + printk("old [%s] also exist\n",ptr->symbol_name); + return 1; + } + + //Fixme: rcu write lock + spin_lock(&kxchg_head.lock); + list_add(&ptr->list, &kxchg_head.list); //将本次注册的替换函数加入链表 + spin_unlock(&kxchg_head.lock); + //Fixme: rcu write unlock + + /* + *save the instruction the original symbol address pointed to + */ + printk("arch save ins...\n"); + arch_saveins(ptr); //保存被替换指令的第一个字节 + /* + *replace the instruction by 0xcc,x86 for example; + */ + printk("arch trap kxchg...\n"); + arch_trap_kxchg(ptr); //将被替换指令的第一个字节替换为trap指令 + //sync_core(); + + return 0; +} +int kxchg_unregister(struct kxchg *kxchg) //unregister kxchg +{ + int found = 0; + struct list_head *pos, *n; + struct kxchg *pos_k; + + spin_lock(&kxchg_head.lock); + list_for_each_safe(pos, n, &kxchg_head.list){ + pos_k = list_entry(pos, struct kxchg, list); + if(pos_k && !memcmp(pos_k->symbol_name, kxchg->symbol_name, 128)) { + + memcpy((void *)kxchg->addr_rep, (void *)kxchg->ori_ins, 1); + list_del(pos); + found = 1; + break; + } + } + spin_lock(&kxchg_head.lock); + + + if(!found) { + spin_lock(&kxchg_old.lock); + list_for_each_safe(pos, n, &kxchg_old.list){ + pos_k = list_entry(pos, struct kxchg, list); + if(pos_k && !memcmp(pos_k->symbol_name, kxchg->symbol_name, 128)) { + + memcpy((void *)kxchg->addr_rep, (void *)kxchg->ori_ins, 1); + list_del(pos); + break; + } + } + spin_unlock(&kxchg_old.lock); + } + + + return 0; +} +struct notifier_block kxchg_notifier_block = { //注册事件通知函数 + .notifier_call = kxchg_notifier_process, + .priority = 0x7fffffff +}; + +static int __init init_kxchg(void) +{ + INIT_LIST_HEAD(&kxchg_head.list); + spin_lock_init(&kxchg_head.lock); + + INIT_LIST_HEAD(&kxchg_old.list); + spin_lock_init(&kxchg_old.lock); + + register_die_notifier(&kxchg_notifier_block); + return 0; +} + +module_init(init_kxchg); + +EXPORT_SYMBOL_GPL(kxchg_register); +EXPORT_SYMBOL_GPL(kxchg_unregister); + Index: arch/x86/kernel/kxchg.c =================================================================== --- arch/x86/kernel/kxchg.c (revision 0) +++ arch/x86/kernel/kxchg.c (revision 0) @@ -0,0 +1,98 @@ +/* + * wangsongbo
, 2012 + * + * Released under the terms of the GNU GPL v2.0 + * + * version 1.0 for Linux-2.6.38 + */ + +#include
+#include
+#include
+#include
+#include
+#include
+ +extern struct kxchg_list kxchg_head; +int kxchg_notifier_process(struct notifier_block *self, unsigned long val, void *data)//事件通知函数 +{ + struct list_head *pos; + struct kxchg *kxchg; + struct die_args *args = (struct die_args *)data; + int ret = NOTIFY_DONE; + + printk("kxchg_notifier_process...\n"); + + if (args->regs && user_mode_vm(args->regs)) + return ret; + + + switch(val){ + case DIE_INT3: //响应trap事件通知 + /* + * the size of cc instruction is 1 + * ip--; + */ + printk("DIE_INT3...\n"); + preempt_disable(); + + args->regs->ip = args->regs->ip - 1; + spin_lock(&kxchg_head.lock); + printk("list for each kxchg_head ...\n");//查看该trap指令是否是被注册的kxchg指令 + list_for_each(pos, &kxchg_head.list){ + kxchg = list_entry(pos, struct kxchg, list); + if(kxchg->addr == args->regs->ip){ + /* + * single step + */ + printk("[%s][%d] regs ip [%lx]\n",__FUNCTION__,__LINE__,args->regs->ip); + args->regs->flags |= TF_MASK; + args->regs->ip = kxchg->addr_rep; //将当前的ip指向替换的函数地址,进入替换函数 + + spin_unlock(&kxchg_head.lock); + return NOTIFY_STOP; + } + } + spin_unlock(&kxchg_head.lock); + /* + * if the int3 instruction is not from kxchg_register, may be from other debuger + * restore the original ip + */ + args->regs->ip = args->regs->ip + 1; + printk("no search regs ip [%lx]\n", args->regs->ip); + preempt_enable_no_resched(); + return NOTIFY_DONE; + case DIE_DEBUG: + args->regs->flags &= ~TF_MASK; //响应debug指令 + printk("DIE_DEBUG in\n"); + break; + default: + printk("no val[%ld] process\n",val); + goto nokxchg; + } + return NOTIFY_STOP; +nokxchg: + return 0; +} + +void arch_trap_kxchg(struct kxchg *p) +{ + memcpy((void *)(p->addr), ((unsigned char []){INT3_INSTRUCTION}), 1); + //text_poke(p->addr, ((unsigned char []){BREAKPOINT_INSTRUCTION}), 1); + //text_poke((unsigned char [])(p->addr), ((unsigned char []){INT3_INSTRUCTION}), 1); + + printk("[%s][%d] p addr [%lx]\n",__FILE__, __LINE__, p->addr); + return; +} + +void arch_saveins(struct kxchg *p) +{ + printk("[%s][%d] p addr [%lx]\n",__FILE__, __LINE__, p->addr); + memcpy((void *)&p->ori_ins, (void *)&p->addr, 1); + return; +} +void arch_restore_ins(struct kxchg *p) +{ + memcpy(&(p->addr), (void *)&p->ori_ins, 1); + return; +} Index: arch/x86/kernel/traps.c =================================================================== --- arch/x86/kernel/traps.c (revision 6603) +++ arch/x86/kernel/traps.c (working copy) @@ -461,6 +461,14 @@   == NOTIFY_STOP)   return;  #endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */ + +#define CONFIG_KXCHG +#ifdef CONFIG_KXCHG + if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP) //单独注册int3通知函数 + == NOTIFY_STOP) + return; +#endif +  #ifdef CONFIG_KPROBES   if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP)   == NOTIFY_STOP)
将此patch到内核,编译,此时版本已经支持动态替换,对需要被替换的函数进行注册,下面举一个实例.

如果需要替换内核中的函数xxxx_kkkk, 替换函数函数名为xxxx_kkkk_test, 需定义xxxx_kkkk_test,该函数的参数,类型需要与xxxx_kkkk一致。以下:

+#include 
+#include
+#include
+#include
+#include
+#include
+#include
+ + +static struct kxchg test; +extern int kxchg_register(struct kxchg *ptr); +extern int kxchg_unregister(struct kxchg *kxchg); + +int netif_receiver_skb_test(struct sk_buff *skb) // +{ + printk("netif_receive_skb replace ... ok"); + kfree_skb(skb); + return 0; +} + +int __init test_init(void)//加入需要替换 netif_receiver_skb,这个例子不太合适,如果需要测试,请换一个函数, +{ + memset(&test, 0x00, sizeof(test)); + + test.symbol_name=" netif_receiver_skb"; + test.symbol_name_rep=" netif_receiver_skb_test"; + + kxchg_register(&test); + return 0; +} + +void __exit test_exit(void) +{ + + kxchg_unregister(&test); + return; +} + +module_init(test_init); +module_exit(test_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("cooboos"); + +

目前这个版本只实现了基本功能,还没进行多种情况下的测试。

转载地址:http://nijhb.baihongyu.com/

你可能感兴趣的文章
使用Pygame模块使用Python构建游戏框架
查看>>
如何使用PostgreSQL简化Python代码
查看>>
软件博览会上的致辞_本地制造商博览会上有4个著名的开源项目
查看>>
pygame游戏角色旋转_使用Pygame移动游戏角色
查看>>
为什么Python和Pygame是入门程序员的最佳选择
查看>>
上海微钉科技面试题_钉住面试的7个技巧
查看>>
linux有桌面有的没桌面_Linux桌面的政治
查看>>
库蒂尼奥_尼奥基入门
查看>>
强化学习入门论文_强化学习入门
查看>>
kubernetes入门_Kubernetes入门
查看>>
aalto 交互_向芬兰的Aalto Fablab付款
查看>>
迈向更大包容性
查看>>
linux 邮件开源工具_5个适用于Linux的开源个人理财工具
查看>>
机器学习入门python_使用Python进行机器学习入门
查看>>
kde调整分辨率_7个很酷的KDE调整将改变您的生活
查看>>
卫星重访周期_通过开放式冒险重访巨大的洞穴
查看>>
Raspberry Pi Zero W修复了网络遗漏
查看>>
公众号精选评论点赞_十大和编辑精选:三月评论
查看>>
python中flask_为什么以及如何在Python Flask中处理异常
查看>>
适合初学者的开源c需要项目_您的开源项目需要总裁吗?
查看>>