从内存加载Linux内核模块:init_module是否可行及替代方案
从内存加载Linux内核模块的解决方案
首先明确:init_module系统调用不支持直接加载用户态内存数组中的模块字节。虽然它的第一个参数是用户态指针,但内核内部会验证该内存区域是否关联到合法的文件映射(而非普通堆/栈内存),直接传入内存数组指针会触发错误(如EINVAL或ENOEXEC)。
可行的替代方案是通过匿名文件描述符中转,把内存中的模块字节写入一个匿名文件,再用内核提供的模块加载调用处理,具体步骤如下:
方案1:使用memfd_create + finit_module(推荐)
这是最接近内存加载的方案,不需要在磁盘上创建实体文件:
- 调用
memfd_create创建一个匿名的文件描述符,该文件仅存在于内存中 - 将内存中的模块字节数组写入这个文件描述符
- 调用
finit_module加载该文件对应的模块
示例代码片段:
#include <sys/memfd.h> #include <sys/syscall.h> #include <unistd.h> #include <fcntl.h> // 假设module_bytes是存储模块原始字节的数组,module_size是其长度 void *module_bytes; size_t module_size; // 创建匿名内存文件 int fd = memfd_create("kernel_module", MFD_CLOEXEC); if (fd == -1) { // 错误处理 } // 将内存中的模块字节写入fd if (write(fd, module_bytes, module_size) != module_size) { // 错误处理 close(fd); } // 加载模块,第二个参数是模块参数(空字符串表示无参数) if (syscall(SYS_finit_module, fd, "", 0) == -1) { // 错误处理 close(fd); } close(fd);
方案2:使用tmpfs文件中转
如果系统不支持memfd_create(Linux 3.17及以上才支持),可以用tmpfs(如/dev/shm)创建临时文件:
- 在
/dev/shm下创建一个临时文件 - 将内存中的模块字节写入该文件
- 打开该文件,调用
init_module或finit_module加载 - 加载完成后删除临时文件
示例代码片段:
#include <fcntl.h> #include <unistd.h> #include <sys/stat.h> // 假设module_bytes和module_size已定义 const char *tmp_path = "/dev/shm/tmp_module.ko"; // 创建并打开临时文件 int fd = open(tmp_path, O_RDWR | O_CREAT | O_EXCL, 0600); if (fd == -1) { // 错误处理 } // 写入模块字节 if (write(fd, module_bytes, module_size) != module_size) { // 错误处理 close(fd); unlink(tmp_path); } // 移动文件指针到开头 lseek(fd, 0, SEEK_SET); // 加载模块 if (syscall(SYS_finit_module, fd, "", 0) == -1) { // 错误处理 close(fd); unlink(tmp_path); } close(fd); unlink(tmp_path);
关键说明
finit_module是Linux 3.8及以上提供的系统调用,相比init_module更简洁,不需要手动传入模块长度- 无论哪种方案,加载模块都需要root权限,且内核必须允许加载自定义模块(即
CONFIG_MODULES=y,且未启用模块签名锁定)
内容的提问来源于stack exchange,提问作者Parvo
相关产品推荐
相关产品推荐

