Могу ли я заменить функцию ядра Linux на модуль?

Я занимаюсь ядром для небольшого летнего исследования. Мы планируем внести изменения в TCP в конкретных расчетах RTT. Я бы хотел заменить разрешение одной из функций в tcp_input.c на функцию, предоставляемую динамически загружаемым модулем ядра. Я думаю, что это улучшит темпы разработки и распространения модификации.

Интересующая меня функция была объявлена ​​как статическая, однако я перекомпилировал ядро ​​с нестатической функцией и экспортировал ее с помощью EXPORT_SYMBOL. Это означает, что функция теперь доступна для других модулей / частей ядра. Я проверил это с помощью "cat / proc / kallsyms".

Теперь я хотел бы иметь возможность загрузить модуль, который может переписать адрес символа с исходного на мою динамически загружаемую функцию. Точно так же, когда модуль должен быть выгружен, он восстановит исходный адрес. Это осуществимый подход? У всех есть предложения, как это можно было бы лучше реализовать?

Спасибо!

То же, что и Переопределение функциональности модулями в ядре Linux

Изменить:
Это был мой возможный подход.
Учитывая следующую функцию (которую я хотел переопределить и не экспортируется):

static void internal_function(void) 
{
  // do something interesting
  return;
}

изменить так:

static void internal_function_original(void)
{
  // do something interesting
  return;
}

static void (*internal_function)(void) = &internal_function_original;
EXPORT_SYMBOL(internal_function);

Это переопределяет ожидаемый идентификатор функции вместо указателя функции (который может вызываться аналогичным образом), указывающего на исходную реализацию. EXPORT_SYMBOL () делает адрес глобально доступным, поэтому мы можем изменить его из модуля (или другого местоположения ядра).

Теперь вы можете написать модуль ядра в следующем виде:

static void (*original_function_reference)(void);
extern void (*internal_function)(void);

static void new_function_implementation(void)
{
  // do something new and interesting
  // return
}

int init_module(void)
{
  original_function_reference = internal_function;
  internal_function           = &new_function_implementation;
  return 0;
}

void cleanup_module(void)
{
  internal_function = original_function_reference;
}

Этот модуль заменяет исходную реализацию динамически загружаемой версией. При выгрузке исходная ссылка (и реализация) восстанавливаются. В моем конкретном случае я предоставил новую оценку RTT в TCP. Используя модуль, я могу вносить небольшие изменения и перезапускать тестирование без перекомпиляции и перезагрузки ядра.


person Willi Ballenthin    schedule 28.07.2009    source источник


Ответы (4)


Я не уверен, что это сработает - я считаю, что разрешение символов для внутренних вызовов функции, которую вы хотите заменить, уже будет выполнено к моменту загрузки вашего модуля.

Вместо этого вы можете изменить код, переименовав существующую функцию, а затем создав глобальный указатель на функцию с исходным именем функции. Инициализируйте указатель функции адресом внутренней функции, чтобы существующий код работал без изменений. Экспортируйте символ глобального указателя на функцию, тогда ваш модуль может просто изменить его значение путем присвоения во время загрузки и выгрузки модуля.

person caf    schedule 28.07.2009
comment
В итоге я пошел по маршруту, который вы предложили, добавив глобальный крючок. Это было легко реализовать и предоставило именно то, что мне было нужно. Спасибо за информацию о разрешении символов. Я не нашел источника, который бы окончательно объяснил, как и когда был осуществлен доступ к таблице символов (при каждом вызове функции или только при связывании). Это был полезный совет. - person Willi Ballenthin; 29.07.2009

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

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


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

#define CODESIZE 12

static unsigned char original_code[CODESIZE];
static unsigned char jump_code[CODESIZE] =
    "\x48\xb8\x00\x00\x00\x00\x00\x00\x00\x00" /* movq $0, %rax */
    "\xff\xe0"                                          /* jump *%rax */
        ;
/* FILL THIS IN YOURSELF */
int (*real_printk)( char * fmt, ... ) = (int (*)(char *,...) )0xffffffff805e5f6e;

int hijack_start(void);
void hijack_stop(void);
void intercept_init(void);
void intercept_start(void);
void intercept_stop(void);
int fake_printk(char *, ... );


int hijack_start()
{
    real_printk(KERN_INFO "I can haz hijack?\n" );
    intercept_init();
    intercept_start();

    return 0;
}

void hijack_stop()
{
    intercept_stop();
    return;
}

void intercept_init()
{
    *(long *)&jump_code[2] = (long)fake_printk;
    memcpy( original_code, real_printk, CODESIZE );

    return;
}

void intercept_start()
{
    memcpy( real_printk, jump_code, CODESIZE );
}

void intercept_stop()
{
    memcpy( real_printk, original_code, CODESIZE );
}

int fake_printk( char *fmt, ... )
{
    int ret;
    intercept_stop();
    ret = real_printk(KERN_INFO "Someone called printk\n");
    intercept_start();
    return ret;
}

module_init( hijack_start );
module_exit( hijack_stop );

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

Это действительно простой принцип, но очень эффективный. Конечно, реальное решение будет использовать блокировки, чтобы никто не вызвал функцию, пока вы ее перезаписываете.

Развлекайся!

person Kasper    schedule 07.08.2009

Вы можете попробовать использовать ksplice - вам даже не нужно делать его нестатичным.

person bdonlan    schedule 29.07.2009
comment
Это не бесплатно. Есть ли альтернатива FOSS? - person Suici Doga; 27.05.2016

Думаю, вам нужен Kprobe.

Другой способ, упомянутый caf, - это добавить ловушку к исходной подпрограмме и зарегистрировать / отменить регистрацию в модуле.

person Sam Liao    schedule 29.07.2009
comment
Kprobe выглядит интересным и полезным инструментом. Спасибо за ссылку. Хотя я выбрал другой путь, я думаю, что это могло быть эффективным подходом. - person Willi Ballenthin; 29.07.2009