最小的可运行示例
使用此简单的参数打印机模块,在QEMU
+ Buildroot VM和Ubuntu 16.04主机上进行了测试。
我们使用
init_module/
finit_module和
remove_module
Linux系统调用。
Linux内核为模块插入提供了两个系统调用:
init_module
finit_module
和:
man init_module
证明文件:
系统调用finit_module()类似于init_module(),但是从文件描述符fd中读取要加载的模块。当可以从内核模块在文件系统中的位置确定内核模块的真实性时,它很有用;在可能的情况下,可以避免使用经过密码签名的模块来确定模块的真实性的开销。param_values参数与init_module()相同。
finit是较新的版本,仅在v3.8中添加。更多理由:https :
//lwn.net/Articles/519010/
glibc似乎没有为他们提供C包装器,因此我们仅使用创建自己的
syscall。
insmod.c
#define _GNU_SOURCE#include <fcntl.h>#include <stdio.h>#include <sys/stat.h>#include <sys/syscall.h>#include <sys/types.h>#include <unistd.h>#include <stdlib.h>#define init_module(module_image, len, param_values) syscall(__NR_init_module, module_image, len, param_values)#define finit_module(fd, param_values, flags) syscall(__NR_finit_module, fd, param_values, flags)int main(int argc, char **argv) { const char *params; int fd, use_finit; size_t image_size; struct stat st; void *image; if (argc < 2) { puts("Usage ./prog mymodule.ko [args="" [use_finit=0]"); return EXIT_FAILURE; } if (argc < 3) { params = ""; } else { params = argv[2]; } if (argc < 4) { use_finit = 0; } else { use_finit = (argv[3][0] != '0'); } fd = open(argv[1], O_RDONLY); if (use_finit) { puts("finit"); if (finit_module(fd, params, 0) != 0) { perror("finit_module"); return EXIT_FAILURE; } close(fd); } else { puts("init"); fstat(fd, &st); image_size = st.st_size; image = malloc(image_size); read(fd, image, image_size); close(fd); if (init_module(image, image_size, params) != 0) { perror("init_module"); return EXIT_FAILURE; } free(image); } return EXIT_SUCCESS;}GitHub上游。
rmmod.c
#define _GNU_SOURCE#include <fcntl.h>#include <stdio.h>#include <sys/stat.h>#include <sys/syscall.h>#include <sys/types.h>#include <unistd.h>#include <stdlib.h>#define delete_module(name, flags) syscall(__NR_delete_module, name, flags)int main(int argc, char **argv) { if (argc != 2) { puts("Usage ./prog mymodule"); return EXIT_FAILURE; } if (delete_module(argv[1], O_NONBLOCK) != 0) { perror("delete_module"); return EXIT_FAILURE; } return EXIT_SUCCESS;}GitHub上游。
Busybox源码解释
Busybox提供了
insmod,并且由于它是为极简主义而设计的,因此我们可以尝试从那里推断出它是如何完成的。
在版本1.24.2中,入口点位于
modutils/insmod.cfunction上
insmod_main。
该
IF_FEATURE_2_4_MODULES是对老年人的Linux内核2.4模块可选支持,所以我们就可以忽略它。
这只是
modutils.c功能
bb_init_module。
bb_init_module尝试两件事:
mmap
该文件通过进入内存try_to_mmap_module
。
这总是设置
image_size为
.ko文件的大小,这是一个副作用。
- 如果失败,则使用
malloc
将文件保存到内存xmalloc_open_zipped_read_close
。
如果该文件是zip,则此函数可以选择先解压缩文件,否则仅对malloc进行解压缩。
我不明白为什么要执行此压缩业务,因为我们甚至不能依靠它,因为
try_to_mmap_module似乎无法解压缩。
终于来了电话:
init_module(image, image_size, options);
image放在内存中的可执行文件在哪里,而选项就像
""我们在调用时
insmod file.elf不带其他参数的情况一样。
init_module由以上提供:
#ifdef __UCLIBC__extern int init_module(void *module, unsigned long len, const char *options);extern int delete_module(const char *module, unsigned int flags);#else# include <sys/syscall.h># define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)# define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags)#endif
ulibc是嵌入式libc实现,它似乎提供
init_module。
如果它不存在,我认为是glibc,但是
man init_module说:
glibc不支持init_module()系统调用。glibc标头中未提供任何声明,但经过一段古怪的历史,glibc确实为此系统调用导出了一个ABI。因此,为了使用此系统调用,在代码中手动声明接口就足够了。或者,您可以使用syscall(2)调用系统调用。
明智地使用BusyBox的建议并使用
syscallglibc提供的建议和使用,并为系统调用提供C API。



