Ответ 1
Минимальный пример runnable
Протестировано на QEMU + Buildroot VM и узле Ubuntu 16.04 с помощью этого простого модуля параметров параметров.
Мы используем init_module
и remove_module
системные вызовы Linux.
glibc, похоже, не предоставляет для них оболочку C, поэтому мы просто создаем свой собственный syscall
.
#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(mod, len, opts) syscall(__NR_init_module, mod, len, opts)
int main(int argc, char **argv) {
const char *params;
int fd;
size_t image_size;
struct stat st;
void *image;
if (argc < 2) {
puts("Usage ./prog mymodule.ko [args]");
return EXIT_FAILURE;
}
if (argc < 3) {
params = "";
} else {
params = argv[2];
}
fd = open(argv[1], O_RDONLY);
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;
}
#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;
}
Интерпретация источника Busybox
Busybox предоставляет insmod
, и поскольку он предназначен для минимализма, мы можем попытаться определить, как это делается оттуда.
В версии 1.24.2 точка входа находится в modutils/insmod.c
функции 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, и просто сохраняет его в противном случае.
Я не понимаю, почему этот бизнес зацикливается, потому что мы не можем даже полагаться на него, потому что
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
говорит:
Системный вызов init_module() не поддерживается glibc. В заголовках glibc объявления не декларируются, но, благодаря причуде истории, glibc экспортирует ABI для этот системный вызов. Поэтому, чтобы использовать этот системный вызов, достаточно вручную объявить интерфейс в коде; альтернативно, вы можете ссылаться системный вызов с использованием syscall (2).
BusyBox мудро следует этому совету и использует syscall
, который предоставляет glibc, и который предлагает C API для системных вызовов.