Linux Kernel Modules#3: адресное пространство / Kernel / UNIX
.. Linux Kernel Modules#3: адресное пространство //dev0id
Если до этого момента для понимания написанного почти ничего знать не нужно было, то теперь необходимы хотя бы базовые знания архитектуры операционной системы.
В предыдущей статье, в образовательных целях, специально была допущена ошибка. Тем, кто ее нашел, следует пропустить следующий, а может два, абзаца.
Давайте проанализируем наш модуль, а точнее его работу в ядре. Так как модуль меняет оригинальныю функцию 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):
Рассмотрим подробнее. Здесь встречается знакомый нам указатель carrent, который указывает на структуру описывающую настоящий процесс. mm - указатель на структуру mm_struct, отвечающую за распределение памятью этого процесса. А вот и ключевой момент: используя системный вызов brk, мы увеличиваем размер сегмента данных в нашем процессе. Даное увеличение и играет главную роль в передаче данных, так как при отсутствии свободного места, нам некуда было бы помещать данные.