Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #77, МАЙ 2005 г.

Поиграем с туксом в прятки

Крис Касперски ака мыщъх

Хакер, номер #077, стр. 077-102-3


Маскировка модуля методом Solar'a Designer'a

int init_module()

{

/* подставь сюда регистр, в котором твое ядро держит адрес module info */

register struct module *mp asm("%ebx");

*(char*)mp->name=0; // затираем имя модуля

mp->size=0; // затираем размер

mp->refs=0; // затираем референсы

}

Неправильное определение адреса module info, скорее всего, уронит ядро системы или заблокирует просмотр списка модулей, что сразу же насторожит администратора (см. рис 2). Но у нас есть в запасе еще один вариант: просматриваем список установленных модулей, находим из них самый ненужный, выгружаем его из памяти, и загружаем свой — с таким же точно именем. Если нам повезет, администратор ничего не заметит.

[исключение процесса из списка задач]

Перечень всех процессоров хранится внутри ядра в виде двунаправленного списка task_struct, определение которого можно найти в файле linux/sched.h. next_task указывает на следующий процесс в списке, prev_task – на предыдущий. Физически task_struct содержится внутри PCB-блоков (Process Control Block), адрес которых известен каждому процессу. Переключение контекста осуществляется планировщиком (scheduler), который определяет, какой процесс будет выполняется следующим (см. рис 3). Если мы исключим наш процесс из списка, он автоматически исчезнет из списка процессов /proc, но больше никогда не получит управление, что в наши планы вообще-то не входит.

Просматривая список процессов, легко обнаружить, что в нем отсутствует процесс, PID которого равен нулю. А ведь такой процесс (точнее — псевдопроцесс) есть! Он создается операционной системой для подсчета загрузки ЦП и прочих служебных целей.

Допустим, нам необходимо скрыть процесс с идентификатором 1901. Исключаем его из двунаправленного списка, склеивая между собой поля next_task/prev_task двух соседних процессов. Подцепляем наш процесс к процессу с нулевым PID'ом, оформляя себя как материнский процесс (за это отвечает поле p_pptr) и... модифицируем код планировщика так, чтобы родитель процесса с нулевым PID'ом хотя бы эпизодически получал управление (см. рис 4). Если необходимо скрыть более одного процесса, их можно объединить в цепочку, используя поле p_pptr или любое другое реально незадействованное поле.

Исходный код планировщика содержится в файле /usr/src/linux/kernel/sched.c. Нужный нам фрагмент легко найти по ключевому слову "goodness" (имя функции, определяющей "значимость" процесса в глазах планировщика). В различных ядрах он выглядит по-разному. Например, моя версия реализована так:

Сердце планировщика

// начальное значение "веса"

c = -1000;

// ищем процесс с наибольшим "весом" в очереди исполняющихся процессов

while (p != &init_task)

{

// определяем "вес" процесса в глазах планировщика

// (т.е. степень его нужды в процессорном времени)

weight = goodness(prev, p);

// выбираем процесс сильнее всех нуждающихся в процессорном времени

Назад на стр. 077-102-2  Содержание  Вперед на стр. 077-102-4