Comments 42
Для загрузки модуля еще можно использовать modprobe
https://habrahabr.ru/post/215577/
Вот мне стало интересно и я скачал исходники strace. Он работает через ptrace (это набор функций под каждую архитектуру в ядре) далее он обращается к ptrace_traceme, в тех же самых security_hook_heads
в некоторых случаях приходится править уже код ядра, и только потом пытаться отправить изменения в апстрим.
А как на самом деле работает этот механизм и на что влияет? Или все ограничивается ворнингом?
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/reboot.h>
MODULE_LICENSE("I hate GPL!");
int init(void)
{
int *i = kmalloc(sizeof(int), GFP_KERNEL); //exported with EXPORT_SYMBOL
kernel_restart(NULL); // exported with EXPORT_SYMBOL_GPL
return *i;
}
module_init(init);
И результат попытки сборки:
make -C /lib/modules/4.8.14-300.fc25.x86_64/build M=/home/work/ make[1]: Entering directory '/usr/src/kernels/4.8.14-300.fc25.x86_64' CC [M] /home/work/workspace/work/module/gpl_test.o Building modules, stage 2. MODPOST 1 modules FATAL: modpost: GPL-incompatible module gpl_test.ko uses GPL-only symbol 'kernel_restart' scripts/Makefile.modpost:91: recipe for target '__modpost' failed
Посмотреть на объявление kernel_restart можно здесь.
Хук будет вызываться перед системным вызовом я так понял. А возможен ли вызов хука после системного вызова, а не до?
Предназначение LSM несколько в другом — в возможности реализации дополнительных проверок прав доступа поверх стандартных. Более того, c некоторых пор LSM стал стековым интерфейсом, то есть приобрёл возможность подключать сразу несколько модулей безопасности (например SELinux, Yama). Это означает, что если ваш модуль работает после SELinux, а он решает не давать доступа на mkdir, то ваш обработчик не будет вызываться.
На мой взгляд, если нужен именно перехват всех системных вызовов, то можно было бы использовать Systemtap. Вот, кстати стандартный примерчик https://sourceware.org/systemtap/examples/process/syscalls_by_proc.stp
#! /usr/bin/env stap
global syscalls
probe nd_syscall.* {
syscalls[execname()]++
}
probe end {
printf ("%-10s %-s\n", "#SysCalls", "Process Name")
foreach (proc in syscalls-)
printf("%-10d %-s\n", syscalls[proc], proc)
}
syscall_test.stp:
#!/usr/bin/stp
probe nd_syscall.* {
#if (pid() == target()) {
printf("<syscall %s: %s(%s)\n", execname(), name, argstr)
#}
}
probe nd_syscall.*.return {
#if (pid() == target()) {
printf(">syscall ret %s: %s result: %s\n", execname(), name, retstr)
#}
}
Запускается так:
$ sudo stap ./syscall_test.stp
Вот пример вывода (ядро скомпилировано с поддержкой многоядерности, поэтому системные вызовы обрабатываются не последовательно):
<syscall konsole: write(95, "\0", 1) <syscall ksmserver: ioctl(28, 21531, 0x7fffab1c9ec4) >syscall ret konsole: write result: 1 >syscall ret ksmserver: ioctl result: 0 <syscall ksmserver: read(28, 0x5626d6785938, 40) >syscall ret ksmserver: read result: 40 <syscall konsole: lseek(94, -2147482516, SEEK_SET) <syscall ksmserver: poll(0x5626d66df110, 64, -1) >syscall ret konsole: lseek result: -22 (EINVAL) <syscall konsole: write(2, "HistoryFile::add.seek: Invalid argument\n", 40) >syscall ret konsole: write result: 40 <syscall konsole: lseek(93, 22447548, SEEK_SET) >syscall ret konsole: lseek result: 22447548 <syscall konsole: write(93, "l\004\0\200", 4) >syscall ret konsole: write result: 4
Любопытно, а virtualbox при установке своих модулей тоже так же перекомпилирует ядро?
Интересно, у всех ли всё получилось, и если не получилось, то почему?
Если что-то не так — будем разбираться, править.
всегда думал, что максимум CPU в подобных случаях поедает компилятор,
или измененный приоритет make из гнома наследуется и на него как дочернего?
После успешной сборки нужно установить всё то, что мы собрали. Это требует root-прав.
Установка заголовков:
sudo make headers_install
Это нужно только для сборки libc, при обычной сборке ядра/модулей этот шаг не нужен.
>Makefile:13: *** missing separator.
Ну ладно, выровнял через пробел и;
>arch/x86/Makefile:173: CONFIG_X86_X32 enabled but no binutils support
>Makefile:670: Cannot use CONFIG_CC_STACKPROTECTOR_REGULAR: -fstack-protector not supported by compiler
>make[1]: *** Нет правила для сборки цели «стол/test». Останов.
Класс, чё тут сказать.
Подписался. Продолжайте писать в таком жанре.
Как уже написали выше, LSM — это не про перехват системных вызовов, а про реализацию политик по работе с объектами ядра. Например, хук inode_mkdir вызывается не только в системном вызове mkdir. С недавнего времени можно писать программы-обработчики хуков LSM на BPF без необходимости писать модули, см. Security Auditing and Enforcement using eBPF, KP Singh
Если же вам хочется просто посмотреть на все системные вызовы (хотя и без возможности вернуть ошибку), то это еще проще, например, смотрим на тот же mkdir при помощи bpftrace:
# bpftrace -e 't:*:sys_enter_mkdir {printf("%s: %s\n", comm, str(uptr(args->pathname)));}'
Attaching 1 probe...
mkdir: /tmp/123
mkdir: /tmp/1234
...
Здесь мы подсосались к tracepoint sys_enter_mkdir — вход в системный вызов mkdir. В момент срабатывания мы печатаем comm
— имя процесса и args->pathname
— один из аргументов системного вызова, который мы читаем при помощи str
— вытянуть строку. Для чтения из pathname
мы делаем uptr
, так как строка лежит в пространстве пользователя. Hint: bpftrace -vl 't:*:*'
покажет все доступные вам tracepoints + их аргументы.
Перехват системных вызовов Linux с помощью LSM