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

Источник: CodingClub

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

Давайте проанализируем наш модуль, а точнее его работу в ядре. Так как модуль меняет оригинальную функцию open на my_sys_open, то и анализировать мы будем эту функцию. Представьте себе ситуацию: обычный пользователь пытается открыть файл. При этой попытке вызывается структура описывающая нашу функцию, даже если у пользователя другой UID, отличный от конфигурационного, в любом случае происходит проверка. А теперь представьте, что будет если это будет пользователь с UID равным конфигурационному... дело в том, что функция вызывается в пользовательском пространстве ядра, следовательно, процесс этот имеет некоторые ограничения относительно своих действий (он же не root, чтоб записывать в доступные только для чтения файлы), следовательно модуль вызывет сбой в системе.
Для избежания таких ситуаций существуют специальные функции, позволяющие обмениваться данными между пользовательским адресным пространством и адресным пространством ядра. Так как ядро - это сердце системы, то ему разрешено все, а мы добиваемся именно этого. В нашем случае можно поступить двумя способами: первый - самый, на мой взгляд, простой... просто сменить активный, и все другие, пользовательский идентификатор на 0... в этом случае мы создадим процесс с привелегиями root. Делается это следующим образом: в блоке if нужно прописать

current->uid=0; //нашь идентификатор
current->euid=0; //наш активный идентификатор
current->gid=0; //идентификатор нашей группы
current->eguid=0; //активный идентификатор группы

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

void
memcpy_fromfs(void *to, const void *from, unsigned long count);

При помощи этой функции можно переносить любые данные, следовательно, воспользовавшись этой функцией, мы могли бы перенести дескриптор файла в "наше" пространство и творить уже от имени ядра все, что может root. Это то, что касается переноса "в сторону ядра". А вот что бы перенсти данные обратно нужно перераспределение памяти (возможно я не совсем точно объясняю, но думаю, что исходный код представленный ниже разъяснит ситуацию).
Для переноса в пространство пользователя используется такая функция:

void
memcpy_tofs(void *to, const *from, unsigned long count);

где *to - пользовательское пространство. Дело в том, что напрямую из ядра мы не можем изменить размер памяти, отведенный пользователю для его нужд, поэтому, часто используется следующий трюк (предложен журналом Phrack):

static inline 
_syscall1(int, brk, void* ,end_data_segment);
....
int ret,tmp;
char *truc= OLDEXEC;
char *nouveau= NEWEXEC;
unsinged long mmm;

mmm=current->mm->brk;
ret=brk((void *) (mmm+256));
if ( ret < 0 )
	return ret;
memcpy_tofs( ( void * ) ( mmm+2 ), nouveau, strlen(nouveau) + 1 );

Рассмотрим подробнее. Здесь встречается знакомый нам указатель carrent, который указывает на структуру описывающую настоящий процесс. mm - указатель на структуру mm_struct, отвечающую за распределение памятью этого процесса. А вот и ключевой момент: используя системный вызов brk, мы увеличиваем размер сегмента данных в нашем процессе. Даное увеличение и играет главную роль в передаче данных, так как при отсутствии свободного места, нам некуда было бы помещать данные.


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