Как я могу переопределить запись в таблице системных вызовов своей собственной функцией? - PullRequest
3 голосов
/ 19 января 2020

Я хочу обновить таблицу системных вызовов, чтобы использовать мою пользовательскую функцию открытия. Я написал следующий код:

#include <linux/module.h>
#include <linux/kallsyms.h>

MODULE_LICENSE("GPL");
char *sym_name = "sys_call_table";

typedef asmlinkage long (*sys_call_ptr_t)(const struct pt_regs *);
static sys_call_ptr_t *sys_call_table;
typedef asmlinkage long (*custom_open) (const char __user *filename, int flags, umode_t mode);

custom_open old_open;

static asmlinkage long my_open(const char __user *filename, int flags, umode_t mode)
{
    pr_info("%s\n",__func__);
        return old_open(filename, flags, mode);
}

static int __init hello_init(void)
{
    sys_call_table = (sys_call_ptr_t *)kallsyms_lookup_name(sym_name);
    old_open = (custom_open)sys_call_table[__NR_open];
    sys_call_table[__NR_open] = (sys_call_ptr_t)my_open;

    return 0;
}

static void __exit hello_exit(void)
{
    sys_call_table[__NR_open] = (sys_call_ptr_t)old_open;    
}

module_init(hello_init);
module_exit(hello_exit);

Когда я загружаю модуль, я получаю следующую ошибку:

[69736.192438] BUG: unable to handle page fault for address: ffffffff98e001d0
[69736.192441] #PF: supervisor write access in kernel mode
[69736.192442] #PF: error_code(0x0003) - permissions violation
[69736.192443] PGD 10460e067 P4D 10460e067 PUD 10460f063 PMD 80000001040000e1 
[69736.192461] Oops: 0003 [#1] SMP PTI
[69736.192463] CPU: 0 PID: 45249 Comm: insmod Tainted: G           OE     5.2.8 #6

Могу ли я обновить таблицу системных вызовов? Как я могу устранить такую ​​ошибку?

1 Ответ

6 голосов
/ 19 января 2020

Ты почти у цели. В процессорах Intel x86 управляющий регистр 1003 * *1001* имеет специальный бит (называемый битом защиты от записи), позволяющий контролировать, может ли ЦП выполнять запись на страницы только для чтения при работе на уровне привилегий 0 (код ядра делает это). запустить с уровнем привилегий 0).

Поскольку таблица системных вызовов находится внутри страницы, предназначенной только для чтения, и по умолчанию установлен бит «Защита от записи», запись в нее запрещена. Поэтому, если вы попытаетесь сделать:

sys_call_table[__NR_open] = (sys_call_ptr_t)my_open;

, вы взломаете sh все.

Чтобы правильно перехватить системный вызов, вам нужно будет отключить защиту от записи, установив «Запись». Защитите «бит» регистра CR0 до 0 перед тем, как перезаписать запись таблицы, и снова включите его после того, как вы закончите. Два макроса read_cr0() и write_cr0() предназначены именно для управления указанным регистром.

Вот правильный код:

// Temporarily disable write protection
write_cr0(read_cr0() & (~0x10000));

// Overwrite the syscall table entry
sys_call_table[__NR_open] = /* whatever */;

// Re-enable write protection
write_cr0(read_cr0() | 0x10000);

Маска 0x10000 используется выше, так как "Запись Защитный бит - это 17-й младший бит регистра.

Обратите внимание, что вышеуказанные шаги необходимо выполнить как для функций init, так и exit вашего модуля.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...