Руткиты, и способы противодействия им. Принципы работы руткита
Скачать 465.04 Kb.
|
1 2 ОглавлениеВведение 2 1. Принципы работы руткита 5 1.1 Модификация исходного кода 5 1.2 Патчинг 6 2.1 Захват в режиме пользователя 8 2.1.1 Захват таблицы импорта 10 2.2 Механизмы работы руткита в режиме ядра (kernel mode) 12 2.2.1 Захват в режиме ядра 12 2.2.2 Модификация кода во время исполнения 16 2.2.3 Манипулирование аппаратурой 20 2.3 Клавиатурные шпионы 26 3. Методики обнаружения RootKit в системе 32 Заключение 36 Список использованной литературы 37 Введение Руткит (англ. rootkit, т.е. «набор root'а») – программа или набор программ для скрытия следов присутствия злоумышленника или вредоносной программы в системе. Этот набор, как правило, включает в себя разнообразные утилиты для «заметания следов» вторжения в систему, делает незаметными сканеры, кейлоггеры (клавиатурный шпион), троянские программы, замещающие основные утилиты операционной системы. Важно понимать, что руткит – это всего лишь технология. Сами по себе руткиты не являются чем-то плохим, они не всегда используются злодеями, плохие или хорошие намерения исходят от людей, их использующих. Существует великое множество легитимных коммерческих программ для удаленного управления и даже подслушивания, причем обнаружить некоторые из них невозможно. Во многих отношениях подобные программы можно назвать руткитами. Правоохранительные органы могут называть «руткитами» вполне легитимные программы-лазейки, устанавливаемые с разрешения суда. Большие корпорации также используют технологию руткитов для мониторинга и контроля парка своих компьютеров Во-первых, руткит – это не шпион. Шпионской программой (spyware) называется такая, которая записывает все, что делает за компьютером пользователь: что он вводит с клавиатуры (в том числе и пароли), какие приложения запускает и т. п. Во-вторых, руткит – это не вирус, хотя он и умеет прятаться от средств обнаружения (антивирусов), используя классические методы вирусов: модификацию системных таблиц, памяти и программной логики. В отличие от вирусов руткиты не размножаются. Другое отличие состоит в том, что вирус полностью автономен, руткит же подчиняется человеку, установившему его. Руткиты могут задействовать недокументированные функции и методы, но обычно их функционирование не строится на программных ошибках (таких, как возможность переполнения буфера). Одним из способов установки руткита является использование эксплойта. Эксплойты – это программы, эксплуатирующие уязвимости в программном обеспечении, они служат средством доставки постороннего кода на машину, имеющую определенную уязвимость, и запуска его на этой машине. Руткиты предоставляют две основные функции: удаленное управление и подслушивание программ. Удаленное управление может включать управление файлами, перезагрузкой и синим экраном смерти (Blue Screen of Death, BSOD), доступ к командной оболочке (cmd.exe или /bin/sh). Подслушивание означает выяснение действий других. Это может быть перехват пакетов, нажатий клавиш, чтение электронных сообщений. В системе Windows под RootKit принято считать программу, которая внедряется в систему и перехватывает системные функции, или производит замену системных библиотек. Перехват и модификация низкоуровневых API (Application Programming Interface) функций в первую очередь позволяет такой программе достаточно качественно маскировать свое присутствие в системе, защищая ее от обнаружения пользователем и антивирусным программным обеспечением. Кроме того, многие RootKit могут маскировать присутствие в системе любых описанных в его конфигурации процессов, папок и файлов на диске, ключей в реестре. Многие RootKit устанавливают в систему свои драйверы и сервисы (они естественно также являются «невидимыми»). 1. Принципы работы руткита Основной принцип работы руткита - это модификация, или подмена. Программы, в том числе сама операционная система, предназначены для обработки определенных данных и вывода определенных результатов. Руткит может подтасовывать либо данные, либо саму программу, чтобы она выводила не верные результаты, а те, которые нужны руткиту. Руткит находит и изменяет программы так, что они выполняют неверные действия. Существует множество мест, где можно внести изменения в программу. Причем программный код можно подменить и тогда, когда он не загружен в память, а лежит на диске как исполняемый файл или библиотека, и непосредственно в памяти. 1.1 Модификация исходного кода Некоторое программное обеспечение распространяется в исходных кодах. Обычно это свободно распространяемые программы. Вы можете скачать исходный код, слегка изменить его и выложить где-то в Интернете, выдав за оригинальную версию. Ничего не подозревающий пользователь загрузит измененную копию исходников и скомпилирует ее на своем компьютере. Чтобы собрать программу, достаточно средней пользовательской квалификации, но для того, чтобы разобраться в том, что делает программа, глядя на ее исходный код, нужно быть программистом. А рядовой пользователь вряд ли заметит, что в исходный код добавлена функция, отсылающая кое-какие данные на неизвестный этому пользователю (но хорошо известный злоумышленнику) почтовый ящик. Проблема модификации исходного кода особенно остро стоит в мире открытого программного обеспечения (open source). 1.2 Патчинг Патч (заплата) - это специальная программа, которая вносит изменения в основную программу, например, исправляет допущенные ошибки или вносит дополнительные функции. Теоретически, можно пропатчить любую программу, добавив в нее дополнительные функции или изменив уже существующие. Например, можно пропатчить IE (Internet Explorer) так, чтобы он постепенно загружал из Интернета остальные части руткита, как только пользователь подключится к Интернету. Даже если на компьютере установлен брандмауэр, он не запретит действия Internet Explorer, поскольку будет «думать», что эти файлы скачивает сам пользователь. Пользователь же ничего не заметит, поскольку остальные функции браузера будут работать как обычно: он сможет просматривать Web-страницы, заходить на FTP и т. п. В некоторых случаях, если программа пропатчена неправильно, пользователь может догадаться, что происходит, по заметному «торможению» браузера или по ошибкам при запуске и завершении работы. Также патчинг может использоваться для изменения существующих функций программы. Очень часто патчи используются для удаления защиты программы или же получения каких-либо дополнительных возможностей. Например, если при запуске программа проверяет, зарегистрирована она или нет, можно пропатчить ее так, чтобы функция, отвечающая за проверку, всегда возвращала «наверх» положительный результат. С патчингом можно бороться. Нужно установить на компьютер программу-ревизор. Ревизор вычислит CRC (контрольную сумму) всех файлов (точнее, всех системно-важных: если контролировать вообще все файлы, то системных ресурсов может не хватить) на жестком диске и запишет ее в свою базу данных. Даже если изменить один байт файла, изменится его контрольная сумма. Ревизор может постоянно находиться в памяти и контролировать содержимое системно-важных каталогов. Как только какая-то программа хочет модифицировать системный файл (как правило, это файлы из каталогов %WINDIR% и всех его подкаталогов, а также файлы каталогов Program Files на всех дисках), ревизор блокирует эту программу и спрашивает у вас, что делать дальше. Если устанавливается программа, полученная из надежного источника, можно разрешить установку, в противном случае нужно разобраться, откуда эта программа проникла в систему. Кроме режима монитора у современных ревизоров есть режим сканера. В этом режиме ревизор не находится постоянно в памяти, отнимая ресурсы, а запускается по вашему требованию и бьет тревогу, если CRC какого-либо системно-важного файла изменилась. Если это изменение не санкционировано, можно переустановить этот файл из резервной копии или дистрибутива, в противном случае ревизор внесет соответствующие изменения в свою базу данных, и больше не будет беспокоить. Важно, чтобы ревизор был установлен на исходно «чистую» систему. Если в системе уже были пропатченные файлы, ревизор будет считать, что с ними все в порядке. Тогда пользы от него будет немного. В Windows есть свой штатный ревизор – служба защиты файлов и каталогов, но вредоносная программа знает о нем и может отключить, если получит привилегии администратора. Поэтому рекомендуется дополнительно установить посторонний ревизор, который программа-патчер не сможет обнаружить и отключить. 2. Механизмы работы руткита 2.1 Захват в режиме пользователя Операционная система Windows поддерживает три подсистемы окружения, Win32, POSIX и OS/2. Каждая из них предоставляет свой хорошо документированный набор API-функций. Через эти API-функции все процессы взаимодействуют с операционной системой. Не являются исключением и такие программы, как диспетчер задач, проводник Windows и редактор реестра. Исходя из этого, API-функции являются превосходной целью для руткита. Например, какая-то программа получает список файлов каталога и выполняет какие-то операции над ними. Эта программа может быть запущена в режиме пользователя, как обычное приложение, или же как служба. Предположим, что она является обычным Win32-пpилoжeниeм, значит, она будет пользоваться услугами таких библиотек, как Kernel32.dll, User32.dll, Gui32.dll и Advapi.dll, которые в конечном итоге производят вызов функций ядра. В подсистеме Win32, чтобы получить список файлов каталога, нужно в первую очередь вызвать функцию FindFirstFile, которая экспортируется библиотекой Kernel32.dll. В случае успешного завершения эта функция возвращает описатель файла. Этот описатель используется в последующих вызовах функции FindNextFile для перечисления всех файлов и подкаталогов, содержащихся в данном каталоге. Функция FindNextFile тоже экспортируется библиотекой Kernel32.dll. Чтобы использовать эти функции, приложение загружает библиотеку Kemel32.dll в память во время исполнения и копирует адреса импортируемых функций в свою таблицу импорта (Import Address Table, I AT). Когда приложение вызывает функцию FindNextFile, происходит передача управления по адресу, который хранится в IAT. Дальше процесс продолжает выполняться уже в теле функции FindNextFile библиотеки Kernel32.dll. То же самое справедливо и для FindFirstFile. Функция FindNextFile из Kernel32.dll на самом деле вызывает аналогичную функцию (NtQueryDirectoryFile) из Ntdll.dll. Та, в свою очередь, заносит в регистр ЕАХ номер системной службы режима ядра, которая и выполняет нужные действия. Помимо регистра ЕАХ, Ntdll.dll использует еще и регистр EDX, в который помещается адрес буфера в режиме пользователя, хранящего параметры функции FindNextFile. Далее Ntdll.dll вызывает прерывание INT 2Е или использует инструкцию SYSENTER для перехода в режим ядра. Следующий рисунок иллюстрирует последовательность вызовов функций. Поскольку приложение загружает библиотеку Kernel32.dll в свое адресное пространство (между адресами 0x00010000 и 0x7FFE0000), то руткит может переписать любую функцию из Kernel32.dll или же изменить таблицу импорта приложения, если только он получит доступ к процессу. Это и называется захватом API-функций (API hooking). В данном примере руткит мог бы заменить код функции FindNextFile собственным кодом с целью скрыть какие-либо файлы либо просто изменить нормальный ход исполнения функции. Можно было бы также изменить таблицу импорта приложения, так чтобы вместо функций из Kernel32.dll она указывала на функции, находящиеся в теле руткита. Путем захвата API-функций можно скрыть процесс или сетевой порт, перенаправить запись файла в другой файл, предотвратить открытие описателя определенного процесса приложением и даже больше. 2.1.1 Захват таблицы импорта Одним из простейших способов захвата является захват таблицы импорта. Прежде чем какое-либо приложение сможет использовать функцию из какой-либо библиотеки, оно должно получить адрес этой функции. Как было отмечено ранее, большинство Win32-приложений для этого используют таблицу импорта. Для каждой библиотеки DLL, применяемой приложением, в исполняемом файле приложения на диске есть структура IMAGEIMPORTDESCRIPTOR. Эта структура содержит имя импортируемой библиотеки и два указателя на массивы указателей на структуры IMAGEIMPORTBYNAME, в которых содержатся имена импортируемых функций. Когда операционная система загружает приложение, она читает структуры IMAGEIMPORTDESCRIPTOR и загружает в память программы все упомянутые там библиотеки DLL. Как только библиотека загружена, операционная система заполняет один из массивов, ссылавшийся ранее на IMAGE IMPORTBY NAME, действительными адресами функций из DLL. Как только руткит окажется в адресном пространстве нужного вам приложения, он сможет проанализировать структуру вашего РЕ-файла непосредственно в памяти и заменить в таблице импорта адреса нужных ему функций адресами подложных функций. Таким образом, во время исполнения программы ваши функции будут вызываться вместо оригинальных. Рисунок 1 иллюстрирует ход выполнения программы, в которой посредством подмены адресов в таблице импорта произведен захват функции: Рис. 1 Такой захват – это достаточно мощная и в то же время простая техника. Конечно, она имеет свои недостатки. Захват такого типа довольно легко обнаружить. Хотя, с другой стороны, эта техника используется очень часто, и даже операционная система задействует ее в процессе, называемом продвижением DLL (DLL forwarding). Таким образом, очень сложно отличить захват, выполненный злоумышленником, от вполне легального и безобидного. Другая проблема связана со временем загрузки DLL в память процесса. Некоторые приложения выполняют отложенную загрузку DLL. В этом случае адреса функций не разрешаются до тех пор, пока не произойдет их первый вызов. Эта техника хороша тем, что сокращает потребление оперативной памяти приложением. Таким образом, на момент захвата многие функции могут не иметь адресов в таблице импорта. То же самое относится и к приложениям, которые выполняют динамическую загрузку DLL при помощи функций LoadLibrary и GetProcAddress. В этих программах захват IAT работать не будет. 2.2 Механизмы работы руткита в режиме ядра (kernel mode) 2.2.1 Захват в режиме ядра Захват в режиме пользователя полезен, но он легко обнаруживается и предотвращается. Более эффективное решение – установка захватчика памяти ядра. Работая в режиме ядра, руткит оказывается на одном уровне с программами защиты и обнаружения вторжений. Ядро расположено в верхней части виртуальных адресов оперативной памяти. В компьютерах архитектуры Intel х86 оно обычно располагается, начиная с адреса 0x80000000 и выше. Если же при загрузке был использован ключ / 3GB, который позволяет использовать программам 3 Гбайт виртуальной памяти, то ядро располагается, начиная с адреса 0хС0000000. Как правило, процессы не имеют доступа к памяти ядра. Исключением являются лишь процессы, имеющие привилегию отладки и взаимодействующие с ядром посредством специальных отладочных API-функций. При создании руткита обычно пишут драйвер устройства. Ядро является идеальным местом для установки захватчиков. Существует множество причин для этого, но главная это то, что захват в режиме ядра является глобальным и обнаружить его намного сложнее из-за того, что и руткит, и программы обеспечения безопасности находятся в нулевом кольце зашиты. Руткит может противодействовать обнаружению и даже блокировать защитное программное обеспечение. Захват таблицы дескрипторов системных служб Исполнительная система Windows работает в режиме ядра и предоставляет службы поддержки для всех подсистем окружения: Win32, POSIX и OS/2. Адреса этих системных служб перечислены в структуре ядра, называемой таблицей диспетчеризации системных служб (System Service Dispatch Table, SSDT). Имея номер системной службы, можно получить ее адрес из этой таблицы. Другая таблица, называемая таблицей параметров системных служб (System Service Parameter Table, SSPT), содержит общий размер передаваемых параметров для каждой службы. Таблица дескрипторов системных служб, KeServiceDescriptorTable, экспортируется ядром. В ней есть указатель на часть таблицы SSDT, содержащую информацию об основных системных службах, реализованных в библиотеке Ntoskrnl.exe и являющихся основной частью ядра. В таблице KeServiceDescriptorTable есть также указатель на SSPT. Таблица SSDT содержит адреса отдельных функций, экспортируемых ядром. Каждый адрес занимает 4 байта. Для того чтобы вызвать определенную функцию, диспетчер системных служб KiSystemService просто умножает идентификационный номер желаемой функции на четыре, получая при этом смещение адреса функции в таблице SSDT. Заметьте, что таблица KeServiceDescriptorTable хранит и количество служб. Это число используется для определения максимального смещения в таблицах SSDT и SSPT. Каждый элемент таблицы SSPT имеет размер один байт и задает размер данных в байтах, которые соответствующая функция из SSDT принимает в качестве параметров. Существует еще одна таблица, KeServiceDescriptorTableShadow, которая содержит адреса служб GDI и USER, реализованных в драйвере Win32k.sys ядра; Диспетчер системных служб включается инструкцией INT 2Е или SYSENTER. В этот момент процесс переключается в режим ядра. Приложение может вызвать диспетчер системных служб KiSystemService непосредственно или через одну из подсистем окружения. Если используется какая-либо подсистема окружения (такая, как Win32), тогда сначала вызывается одна из функций Ntdll.dll, которая в свою очередь помещает в регистр ЕАХ номер системной службы, а в регистр EDX – адрес параметров функции в адресном пространстве пользователя. Диспетчер системных служб сверяет количество параметров и копирует их из пользовательского стека в стек ядра. После этого происходит вызов одной из служб из таблицы SSDT в соответствии с номером, находящемся в регистре ЕАХ. Итак, если руткит будет загружен в систему как драйвер устройства, он сможет изменить таблицу SSDT так, что вместо Ntoskrnl.exe или Win32k.sys будет вызвана его функция. В результате, когда любое приложение пользовательского уровня попытается обратиться к ядру, на самом деле оно будет обращаться к руткиту. Таким образом, возможно возвращать приложениям отфильтрованную информацию о системе, полностью скрывая свое присутствие и информацию об используемых ресурсах. Отключение защиты памяти для SSDT Некоторые версии Windows выпускаются с включенной защитой от записи в некоторые участки памяти. В Windows ХР и Windows 2003 это стало нормой. Последние версии Windows делают таблицу SSDT доступной только для чтения, так как маловероятно, чтобы обычным программам могло бы понадобиться вносить в нее какие-либо изменения. Защита от записи может явиться вашего руткита серьезной проблемой, если нужно, чтобы руткит фильтровал данные, возвращаемые определенными системными вызовами. Если попробовать произвести запись в таблицу SSDT, к которой имеется доступ только для чтения, результатом окажется синий экран. Но этого можно избежать. Диапазон адресов памяти можно описать в виде списка дескрипторов памяти (Memory Descriptor List, MDL). Элемент списка MDL содержит стартовый адрес, идентификатор процесса-владельца, размер в байтах и флаги. Для изменения флагов в памяти прежде всего нужно извлечь нужную информацию из таблицы KeServiceDescriptorTable, экспортируемой ядром. Необходимо получить базовый адрес таблицы SSDT и количество служб, прежде чем вызывать MmCreateMdl. После этого нужно построить MDL из не выгруженной на диск области памяти. Чтобы производить запись в эту область, руткит устанавливает флаг MDL_ MAPPED_TO_SYSTEM_VA. Далее производится блокировка MDL-страниц памяти при помощи функции MmMapLockedPages. Теперь сюда можно произвоить запись, не опасаясь синего экрана. Пример скрытия процессов путем захвата SSDT. В операционной системе Windows используется функция ZwQuerySystemlnformation, которая предоставляет различную информацию о системе. Например, программа taskmgr.exe задействует эту функцию для получения списка процессов. Тип возвращаемой информации зависит от параметра SystemlnformationClass. Для получения списка процессов он устанавливается в 5, как описано в Microsoft Windows DDK. Подменив функцию ZwQuerySystemlnformation в SSDT, руткит захватывает исходную функцию и получает возможность фильтровать возвращаемую ею информацию. На рис. 3 показан формат расположения записей о процессах в буфере, возвращаемом функцией ZwQuerySystemlnformation. Рис. 3 Буфер содержит структуры _SYSTEM_PROCESSES и соответствующие им структуры _SYSTEM_THREADS. Наиболее важным элементом структуры _SYSTEM_PROCESSES является поле UNICODE_STRING, содержащее имя процесса. В структуре имеются также два поля типа LARGE_INTEGER, содержащие время, проведенное процессом в режимах ядра и пользователя. При скрытии какого-либо процесса, необходимо перераспределить время, потраченное этим процессом, на все оставшиеся процессы из списка, так чтобы все суммарное время соответствовало 100 % времени работы процессора. Для скрытия процессов нужно подменить в SSDT функцию ZwQuerySystemlnformation на свою, которая фильтрует процессы и добавляет отработанное время скрываемых процессов процессу простоя (Idle). В SSDT есть множество других полезных функций, которые также можно захватить. 1 2 |