我可以使用模块replaceLinux内核函数吗?

我正在进行一些我夏季研究的内核工作。 我们希望在特定的RTT计算中对TCP进行修改。 我想要做的是将tcp_input.c中的一个函数的分辨率replace为由dynamic加载的内核模块提供的函数。 我认为这会提高我们开发和分发修改的速度。

我感兴趣的函数被声明为静态的,但是我已经用非静态函数重新编译了内核,并由EXPORT_SYMBOL导出。 这意味着该function现在可以被内核的其他模块/部分访问。 我已经通过“cat / proc / kallsyms”validation了这一点。

现在我想能够加载一个模块,可以重写从最初的符号地址到我的dynamic加载函数。 同样,当模块被卸载时,它将恢复原来的地址。 这是一个可行的方法吗? 你们都有build议如何更好地实施?

谢谢!

Linux内核中的模块重写function相同

编辑:
这是我最终的方法。
鉴于以下function(我想覆盖,而不是导出):

static void internal_function(void) { // do something interesting return; } 

像这样修改:

 static void internal_function_original(void) { // do something interesting return; } static void (*internal_function)(void) = &internal_function_original; EXPORT_SYMBOL(internal_function); 

这重新定义了预期的函数标识符,而不是指向原始实现的函数指针(可以用类似的方式调用)。 EXPORT_SYMBOL()使地址全局可访问,所以我们可以从模块(或其他内核位置)修改它。

现在你可以用下面的forms编写一个内核模块:

 static void (*original_function_reference)(void); extern void (*internal_function)(void); static void new_function_implementation(void) { // do something new and interesting // return } int init_module(void) { original_function_reference = internal_function; internal_function = &new_function_implementation; return 0; } void cleanup_module(void) { internal_function = original_function_reference; } 

这个模块用一个dynamic加载的版本取代原来的实现。 卸载后,原始参考(和实现)被恢复。 在我的具体情况中,我为TCP中的RTT提供了一个新的估计器。 通过使用一个模块,我可以进行小小的调整并重新开始testing,而无需重新编译和重新启动内核。

Solutions Collecting From Web of "我可以使用模块replaceLinux内核函数吗?"

我不确定这是否会奏效 – 我相信内部调用您要替换的函数的符号解析将在您的模块加载完成之前完成。

相反,您可以通过重命名现有函数来更改代码,然后使用函数的原始名称创建全局函数指针。 初始化指向内部函数地址的函数指针,这样现有的代码将不会被修改。 导出全局函数指针的符号,然后你的模块可以通过在模块加载和卸载时的赋值来改变它的值。

你可以尝试使用ksplice – 你甚至不需要把它变成非静态的。

我曾经做过一个劫持模块的概念证明,它插入了它自己的函数来取代内核函数。 我恰好碰巧新的内核测试架构使用了一个非常类似的系统。

我通过用指向我的自定义函数的跳转覆盖了前几个字节的代码,在内核中注入了我自己的函数。 只要真正的函数被调用,它就跳转到我的函数中,在它完成之后,它被称为原始函数。

 #include <linux/module.h> #include <linux/kernel.h> #define CODESIZE 12 static unsigned char original_code[CODESIZE]; static unsigned char jump_code[CODESIZE] = "\x48\xb8\x00\x00\x00\x00\x00\x00\x00\x00" /* movq $0, %rax */ "\xff\xe0" /* jump *%rax */ ; /* FILL THIS IN YOURSELF */ int (*real_printk)( char * fmt, ... ) = (int (*)(char *,...) )0xffffffff805e5f6e; int hijack_start(void); void hijack_stop(void); void intercept_init(void); void intercept_start(void); void intercept_stop(void); int fake_printk(char *, ... ); int hijack_start() { real_printk(KERN_INFO "I can haz hijack?\n" ); intercept_init(); intercept_start(); return 0; } void hijack_stop() { intercept_stop(); return; } void intercept_init() { *(long *)&jump_code[2] = (long)fake_printk; memcpy( original_code, real_printk, CODESIZE ); return; } void intercept_start() { memcpy( real_printk, jump_code, CODESIZE ); } void intercept_stop() { memcpy( real_printk, original_code, CODESIZE ); } int fake_printk( char *fmt, ... ) { int ret; intercept_stop(); ret = real_printk(KERN_INFO "Someone called printk\n"); intercept_start(); return ret; } module_init( hijack_start ); module_exit( hijack_stop ); 

我警告你,当你要试验这些事情的时候,注意内核恐慌和其他灾难性事件。 我建议你在虚拟环境中做这个。 这是我刚才写的一个概念验证代码,我不确定它仍然有效。

这是一个非常简单的原则,但非常有效。 当然,一个真正的解决方案会使用锁来确保没有人会在覆盖它的时候调用这个函数。

玩的开心!

我想你想要的是Kprobe 。

caf提到的另一种方法是在原始例程中添加一个钩子,并在模块中注册/取消注册钩子。