Нестандартные сценарии использования модулей ядра

Часть 41. Выполнение системных вызовов

Comments

Серия контента:

Этот контент является частью # из серии # статей: Нестандартные сценарии использования модулей ядра

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Нестандартные сценарии использования модулей ядра

Следите за выходом новых статей этой серии.

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

Осуществление системных вызовов

В качестве первого варианта применения найденных символов ядра можно предложить использование разнообразных функций обработчиков системных вызовов Linux непосредственно из кода ядра. Для иллюстрации описываемых технических приёмов мы рассмотрим выполнение вызова printf() из кода модуля ядра, как самого наглядного и наиболее известного вызова. Полный код примера, приведенного в листинге 1, можно найти в архиве new_sys.tgz, расположенном в разделе "Материалы для скачивания".

Листинг 1. Вывод строки на терминал выполнением системного вызова.
#include <linux/module.h> 
#include <linux/kallsyms.h> 
#include <linux/uaccess.h> 

static unsigned long waddr = 0; 
static char buf[ 80 ]; 
static int len; 

int symb_fn( void* data, const char* sym, struct module* mod, unsigned long addr ) { 
   if( 0 == strcmp( (char*)data, sym ) ) { 
      waddr = addr; 
      return 1; 
   } 
   else return 0; 
} 

/* заимствуем из <linux/syscalls.h>, здесь очень важно - asmlinkage:  
asmlinkage long sys_write(unsigned int fd, const char __user *buf, 
                          size_t count); */ 
static asmlinkage long (*sys_write) ( 
   unsigned int fd, const char __user *buf, size_t count ); 

static int do_write( void ) { 
   int n; 
   mm_segment_t fs = get_fs(); 
   set_fs( get_ds() ); 
   sys_write = (void*)waddr; 
   n = sys_write( 1, buf, len ); 
   set_fs(fs); 
   return n; 
} 

static int __init wr_init( void ) { 
   int n = kallsyms_on_each_symbol( symb_fn, (void*)"sys_write" ); 
   if( n != 0 ) { 
      sprintf( buf, "адрес системного обработчика sys_write = %lx\n", waddr ); 
      len  = strlen( buf ) + 1; 
      printk( "+ [%d]: %s", len, buf ); 
      n = do_write(); 
      printk( "+ write return : %d\n", n ); 
   } 
   else 
      printk( "+ %d: symbol not found\n", n ); 
   return -EPERM; 
} 

module_init( wr_init ); 
MODULE_LICENSE( "GPL" ); 
MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );

Хотя в данном примере мы не выполняем вызов printf(), но, как говорилось в предыдущих статьях, библиотечный вызов printf() выполняется через библиотечный вызов sprintf(), который, в свою очередь, вызывает системный вызов write( 1, ... ). Поэтому мы фактически решили поставленную задачу, воспроизведя указанную последовательность вызовов. Выполним проверку созданного модуля.

$ sudo insmod mod_wrc.ko 
адрес системного обработчика sys_write = c04e12fc 
insmod: error inserting 'mod_wrc.ko': -1 Operation not permitted 
$ dmesg | tail -n30 | grep + 
+ [77]: адрес системного обработчика sys_write = c04e12fc 
+ write return : 77

Видно, что вывод строки происходит до вывода сообщения о завершении выполнения кода модуля на графический терминал (X11). С подобным поведением мы уже встречались при рассмотрении системных вызовов. Естественно, вызов write() был выполнен из функции инициализации модуля, и поэтому осуществляет вывод на управляющий терминал вызывающего процесса, а в данном случае — это процесс insmod. Чтобы окончательно убедиться, проверим адрес обработчика sys_write в файле /proc/kallsyms.

$ cat /proc/kallsyms | grep T | grep sys_write 
c04e12fc T sys_write 
c04e196b T sys_writev 
c05f99fc T fb_sys_write

В коде модуля нет ничего сложного, за исключением вопроса: откуда был взят точный прототип для собственной новой функции sys_write(), которой позже был присвоен адрес системного обработчика вызова sys_write? Вот определение, о котором идёт речь:

asmlinkage long sys_write( unsigned int fd, const char __user *buf, 
                           size_t count );

Очевидно, что определение для нашей функции было скопировано из заголовочного файла linux/syscalls.h (об этом есть явное упоминание в комментарии). Я рекомендую поступать так и в отношении всех других системных вызовов, так как в данном случае любая ошибка в определении прототипа функции приводит к немедленному краху всей системы. В частности, ключевым фактором для некоторых системных обработчиков и других неэкспортируемых функций ядра будет наличие или отсутствие определения: asmlinkage;. При его наличии параметры вызова будут поочерёдно (от первого до последнего) заноситься в регистры процессора, а при его отсутствии — помещаться в стек (от последнего к первому) вызывающим процессом, в соответствии с соглашениями о вызове, установленными в языке C.

Этот пример порождает ещё ряд интересных вопросов, как, например, сработает ли такой вызов только для вывода в графический терминал (X11) или и в текстовую консоль (<Ctrl><Alt><F2>)? Да, сработает, но проверку этого утверждения я оставляю читателю.

Другой вопрос состоит в том, зачем в качестве строки вывода использовалась русскоязычная строка, хотя это и не приветствуется в программировании ядра? Так как в полном тракте прохождения сообщений от ядра задействовано слишком много последовательных слоёв и компонентов (реализация printk(), демон журнала, файл журнала, терминальная система UNIX, терминал, визуализатор dmesg, и т.д.), то вывод такой строки может стать хорошей проверкой для согласованности работы всех этих компонентов. Предварительно следует проверить установки системы:

$ echo $LANG
ru_RU.UTF-8

Установим уровень диагностики ниже порога вывода (но только с терминала root, а не с помощью sudo), иначе запускать модуль из консоли просто бессмысленно, и повторим установку нашего модуля:

# cat /proc/sys/kernel/printk 
3	4	1	7 
# echo 8 > /proc/sys/kernel/printk # cat /proc/sys/kernel/printk 
8	4	1	7  
# sudo insmod mod_wrc.ko
...

Мы увидим в консоли достаточно странную картину:

  • вывод вызова write() (который выполняется модулем) выведет ожидаемую строку: "адрес …";
  • вывод команд dmesg и cat /var/log/messages (выполняемых в консоли!) выведет ту же строку: "адрес …";
  • тестовая программа hello_world выведет в консоли русскоязычную строку: "Привет мир!";
  • а вот диагностический вывод в консоль вызова printk() из ядра покажет строку странного содержания и длиной в 77 символов.

Получается, что реализация вызова printk() в ядре:

  1. выводит диагностику не через системный журнал, а параллельно демону журналирования;
  2. пытается интерпретировать поток UNICODE символов и преобразовывать их побайтно в ASCII;
  3. в результате символы UNICODE получают «необратимые» повреждения и строка выводится в таком странном виде.

В результате проведенных экспериментов мы можем заявить, что программисту модулей ядра, кроме инструментов программирования, присутствующих в ядре, доступен и набор всех системных вызовов POSIX API пространства пользователя. Естественно, толкование результатов некоторых из таких системных вызовов в контексте ядра может быть весьма двусмысленным, а иногда даже и бессмысленным. Но тем не менее, сами вызовы можно выполнять и использовать для решения отдельных задач!

Заключение

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


Ресурсы для скачивания


Похожие темы


Комментарии

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, Open source
ArticleID=856078
ArticleTitle=Нестандартные сценарии использования модулей ядра: Часть 41. Выполнение системных вызовов
publish-date=01242013