- Перехват (программирование)
-
Перехват (англ. hooking) — технология, позволяющая изменить стандартное поведение тех или иных компонентов информационной системы.
Содержание
Назначение технологии перехвата
Очень часто в системном программировании возникает задача изменения стандартного поведения системных функций. Например довольно интересным применением данной технологии является переопределение оконной процедуры у GUI приложений Windows (сабклассинг). Это нужно, если программист хочет организовать собственную обработку какого-либо оконного сообщения и только потом передать стандартной оконной процедуре. После сабклассинга цикл обработки сообщений будет выглядеть так:
- ДО САБКЛАССИНГА:
Сообщение Windows->Окно (оконная процедура)
- ПОСЛЕ:
Сообщение Windows->Наша оконная процедура->Окно (оконная процедура)
Например, в Уроках Iczelion’а[1] описан пример того, как сабклассинг может использоваться для организации контроля ввода в элементы управления. Технологии перехвата нужны не только в этом случае, но и, например, для предварительной обработки результатов системных функций поиска файлов FindFirst и FindNext, EnumProcess, которая перечисляет процессы в Windows и т. д. Причем в этих целях такие технологии применяют как антивирусные средства[2], так и различного рода вирусы, руткиты и прочие виды вредоносного программного обеспечения.
Очень часто перехват бывает важен для организации отладки программ и является одной из основных технологий, применяемых в отладчиках. В данном случае эта технология позволяет одной программе контролировать выполнение другой. Для этих целей предусмотрен системный вызов ptrace, который позволяет подключаться к процессам, отслеживать значения регистров у контекста отлаживаемого процесса и в том числе контролировать другие системные вызовы. Он является основой для реализации такой возможности отладчиков как точки останова. Данный системный вызов хорошо документирован и присутствует во всех главных *Nix системах: Linux, FreeBSD, Solaris.[3] Чаще всего используется совместно с системным вызовом fork, который и вызывает ptrace, указывая в параметрах вызова, что запускаемый процесс — дочерний. Microsoft Windows также предоставляет для схожих целей т. н. DebugAPI[4].
Виды перехвата системных функций
Основными методами перехвата являются:
- Подмена адреса настоящей функции (модификация IAT таблиц, модификация SSDT/IDT таблиц)
- Непосредственное изменение функции (сплайсинг, перехват в режиме ядра с модификацией тела функции)
- Непосредственная подмена всего компонента приложения/системы (например библиотеки с целевой функцией)
Методы можно также разделить по критерию режима выполнения:
- Пользовательские (ring3) методы: модификация IAT таблиц, сплайсинг. Их особенность в том, что невозможно что-либо изменить в поведении ядра операционной системы и его расширений.
- Режима ядра: модификация SSDT/IDT таблиц, перехват в режиме ядра с модификацией тела функции. Позволяет модифицировать структуры данных и код любой части операционной системы и приложений.
Сплайсинг
Сплайсинг — метод перехвата API функций путем изменения кода целевой функции. Обычно изменяются первые 5 байт функции. Вместо них вставляется переход на функцию, которую определяет программист. Чтобы обеспечить корректность выполнения операции, приложение, которое перехватывает функцию, обязано дать возможность выполниться коду, который был изменен в результате сплайсинга. Для этого приложение сохраняет заменяемый участок памяти у себя, а после отработки функции перехвата восстанавливает измененный участок функции и дает полностью выполниться настоящей функции.[5]
Особенности технологии
Для того чтобы программа могла использовать данную технологию, она должна иметь встроенный дизассемблерный движок и специальный дизассемблер длин, который позволит находить нужную функцию и корректно её изменять. Эта технология крайне платформенно-зависима, а потому требует тщательного контроля и проверки системы на соответствие версий, а также проверки самой функции на соответствие целевой. Системные функции могут меняться при выходе патчей и обновлений Windows (особенно Service Pack для Windows), а также в результате модификаций со стороны других приложений. Ошибки при работе с данной технологией могут приводить к BSOD. В то же время эта технология позволяет осуществлять глобальный перехват API функций, влияя таким образом на все процессы в системе. Начиная с Windows XP SP2 для поддержки "горячего патча" Microsoft изменила стандартный пролог функций с трех байт до пяти (добавила mov edi, edi в стандартный пролог push ebp; mov ebp, esp), что позволяет не проводить анализ длин. Длины в пять байт хватает для замены пролога на опкоды дальнего перехода, а известный пролог позволяет корректно передать управление подменяемой функции.
Сферы применения сплайсинга и методы обнаружения
Он применяется:
- В ПО, которому необходимо осуществлять функции мониторинга системы
- Механизмом хуков в Windows
- Различного рода вредоносными программами. Это основная технология сокрытия для руткитов пользовательского уровня
Основной метод обнаружения факта сплайсинга — это сравнение машинного кода функции, проверяемой на сплайсинг, и кода системной функции, полученного в заведомо чистой системе. Также в обнаружении сплайсинга функции может помочь контроль адресов перехода.
Сравнение с другими технологиями
- Изменение IAT таблиц процесса[6]. Данная технология не позволяет изменить поведение самой системной функции, а лишь дает возможность «обмануть» выбранное приложение, заставив его использовать вашу функцию. IAT таблица — таблица адресов функций, импортируемых процессом. Технология носит лишь локальный характер, хотя может быть применена сразу к группе приложений. Может быть довольно быстро обнаружена из-за необходимости загрузки DLL[7] в адресное пространство целевого процесса. Сплайсинг же не требует DLL и внедрения в чужой процесс, обладает возможностью глобального захвата функции. У сплайсинга есть ещё одно преимущество: не все системные функции импортируются процессом через IAT. Например, функция может быть загружена вызовом GetProcAddress. Использование же непосредственной модификации кода функции снимает подобное ограничение.
- Перехват в режиме ядра. Позволяет перехватывать любые функции, в том числе и экспортируемые ядром. Наиболее труден для обнаружения в случае успеха, так как позволяет фальсифицировать любые данные, предоставляемые операционной системой. Требует написания специального компонента для взаимодействия с ядром драйвера. Может привести к BSOD при неправильном программировании в режиме ядра. Может быть обнаружен на фазе загрузки драйвера в ядро или при проверке активных драйверов, а также при проверке ядра на изменения[8]. Более трудный в программировании метод, чем сплайсинг, но более гибкий, так как позволяет перехватить функции самого ядра, а не только WinAPI функции, которые служат лишь посредником между ядром и программой, которая что-либо запрашивает у операционной системы.
- Замена самой библиотеки с функцией. Весьма радикальное решение проблемы, обладающее рядом существенных недостатков:
- Требует замены файла на диске, что может быть запрещено и пресечено самой системой. Например, замену системных файлов Windows не позволит выполнить защита файлов Windows (WFP), хотя её можно и отключить. Такое действие может быть также обнаружено при статическом анализе системы ревизорами.
- Требуется полная эмуляция всех возможностей заменяемой DLL или иного компонента, что весьма трудоемко даже в случае открытости и осложняется необходимостью дизассемблирования в случае закрытости целевой программы.
Все это показывает, что это весьма нерациональный способ решения проблемы изменения поведения программы в случае возможности применения первых двух подходов или сплайсинга.
Перехват в режиме ядра
Он основан на модификации структур данных ядра и функций. Главными мишенями воздействия являются таблицы
- IDT Таблица диспетчеризации прерываний. Довольно важным для перехвата является прерывание, обрабатывающее обращение к таблице служб SSDT (0x2E)[9].
- SSDT (System Service Dispatch Table) Таблица диспетчеризации системных сервисов. Обращаясь к ней, система по номеру запрещенного сервиса может получить адрес соответствующего сервиса ядра и вызвать его. А таблица SSPT содержит общий размер параметров, передаваемых системному сервису.
- psActiveprocess Структура ядра, хранящая список процессов в системе.
- IRP Таблица драйвера, которая хранит указатели на функции обработки IRP-пакетов.
- EPROCESS Структура ядра, хранящая большое количество информации о процессе, включая, например, PID (идентификатор процесса).
Такого рода руткиты называются DKOM-руткитами, то есть руткитами, основанными на непосредственной модификации объектов ядра. В руткитах для систем Windows Server 2003 и XP эта технология была модернизирована, так как в этих ОС появилась защита от записи некоторых областей памяти ядра[9]. Windows Vista и 7 получили дополнительную защиту ядра PatchGuard, однако все эти технологии были преодолены руткитописателями[10]. В то же время перехват системных функций в режиме ядра — основа проактивных систем защиты и гипервизоров.
Иные формы перехвата
Можно выделить и другие формы перехвата:
- Перехват сетевых соединений и пакетов.[11]
- Перехват паролей. Например, при помощи шпионажа за клавиатурным вводом при помощи кейлоггера.
- Перехват обращений браузера к сайтам при помощи HTTP Proxy или расширений браузера. Позволяет проанализировать и/или подменить данные, которыми обмениваются браузер и сервер.
Здесь описана лишь часть применений данной технологии.
Примеры программ, использующих перехват
- Гипервизор XEN
- Утилиты Марка Руссиновича Filemon, Regmon, Process Explorer
- Руткит Rustock.C
Примеры кода
using System; using System.Collections; using System.Diagnostics; using System.Runtime.InteropServices; namespace Hooks { public class KeyHook { #region Member variables protected static int hook; protected static LowLevelKeyboardDelegate dele; protected static readonly object Lock = new object(); protected static bool isRegistered = false; #endregion #region Dll Imports [DllImport("user32")] private static extern Int32 SetWindowsHookEx(Int32 idHook, LowLevelKeyboardDelegate lpfn, Int32 hmod, Int32 dwThreadId); [DllImport("user32")] private static extern Int32 CallNextHookEx(Int32 hHook, Int32 nCode, Int32 wParam, KBDLLHOOKSTRUCT lParam); [DllImport("user32")] private static extern Int32 UnhookWindowsHookEx(Int32 hHook); #endregion #region Type Definitions & Constants protected delegate Int32 LowLevelKeyboardDelegate(Int32 nCode, Int32 wParam, ref KBDLLHOOKSTRUCT lParam); private const Int32 HC_ACTION = 0; private const Int32 WM_KEYDOWN = 0x0100; private const Int32 WM_KEYUP = 0x0101; private const Int32 WH_KEYBOARD_LL = 13; #endregion [StructLayout(LayoutKind.Sequential)] public struct KBDLLHOOKSTRUCT { public int vkCode; public int scanCode; public int flags; public int time; public int dwExtraInfo; } static private Int32 LowLevelKeyboardHandler(Int32 nCode, Int32 wParam, ref KBDLLHOOKSTRUCT lParam) { if (nCode == HC_ACTION) { if (wParam == WM_KEYDOWN) System.Console.Out.WriteLine("Key Down: " + lParam.vkCode); else if (wParam == WM_KEYUP) System.Console.Out.WriteLine("Key Up: " + lParam.vkCode); } return CallNextHookEx(0, nCode, wParam, lParam); } public static bool RegisterHook() { lock(Lock) { if(isRegistered) return true; dele = new LowLevelKeyboardDelegate(LowLevelKeyboardHandler); hook = SetWindowsHookEx( WH_KEYBOARD_LL, dele, Marshal.GetHINSTANCE( System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0] ).ToInt32(),0 ); if(hook != 0) return isRegistered = true; else { dele= null; return false; } } } public static bool UnregisterHook() { lock(Lock) { return isRegistered = (UnhookWindowsHookEx(hook) != 0); } } } }
Netfilter hookЭтот пример показывает, как используются хуки для контроля сетевого трафика в ядре Linux при помощи Netfilter.
#include <linux/module.h> #include <linux/kernel.h> #include <linux/skbuff.h> #include <linux/ip.h> #include <linux/tcp.h> #include <linux/in.h> #include <linux/netfilter.h> #include <linux/netfilter_ipv4.h> /* Port we want to drop packets on */ static const uint16_t port = 25; /* This is the hook function itself */ static unsigned int hook_func(unsigned int hooknum, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct iphdr *iph = ip_hdr(*pskb); struct tcphdr *tcph, tcpbuf; if (iph->protocol != IPPROTO_TCP) return NF_ACCEPT; tcph = skb_header_pointer(*pskb, ip_hdrlen(*pskb), sizeof(*tcph), &tcpbuf); if (tcph == NULL) return NF_ACCEPT; return (tcph->dest == port) ? NF_DROP : NF_ACCEPT; } /* Used to register our hook function */ static struct nf_hook_ops nfho = { .hook = hook_func, .hooknum = NF_IP_PRE_ROUTING, .pf = NFPROTO_IPV4, .priority = NF_IP_PRI_FIRST, }; static __init int my_init(void) { return nf_register_hook(&nfho); } static __exit void my_exit(void) { nf_unregister_hook(&nfho); } module_init(my_init); module_exit(my_exit);
См. также
Примечания
- ↑ Уроки Iczelion’а. Win32 API. Урок 20. Сабклассинг окна
- ↑ К примеру: невозможно получить доступ к процессу Kaspersky Internet Security штатными средствами Windows API, так как соответствующие функции перехвачены антивирусом.
- ↑ Страница из man Linux Ubuntu: man страница о вызове ptrace и русскоязычная версия: Русский перевод на OpenNET
- ↑ Официальное описание: The Debugging Application Programming Interface, а также примеры использования: Win32 API. Урок 28. Win32 Debug API I
- ↑ Цикл статей по перехвату WindowsAPI функций от Ms Rem
- ↑ Методы доступа и модификации IAT таблицы довольно подробно описаны у Хоглунд Г., Батлер Дж. — Руткиты: внедрение в ядро Windows. Гл 4 Древнее искусство захвата
- ↑ Методы внедрения DLL в чужой процесс описаны довольно подробно у Дж. Рихтера Кристофера Назара Windows via C/C++. Программирование на языке Visual C++. Некоторые методы внедрения впервые документировал сам Дж. Рихтер
- ↑ Например, один из первых руткит-детектеров KLISTNER
- ↑ 1 2 Г. Хоглунд Дж. Батлер Руткиты внедрение в ядро Windows. Глава 4 Древнее искусство захвата
- ↑ убийство часового КРИС КАСПЕРСКИ, АКА МЫЩЪХ Спецвыпуск: Хакер, номер #072, стр. 072-072-5
- ↑ Например, этим занимаются сниферы. Одной из бесплатных реализаций захвата сетевых пакетов является сетевой драйвер уровня NDIS WinPCAP
Литература
- Джефри Рихтер Windows via C/C++. Программирование на языке Visual C++ = en:Windows via C/C++. — СПб.: Питер, 2010. — С. 689-728. — ISBN 978-5-7502-0367-3
- Хоглунд Г., Батлер Дж. Руткиты: внедрение в ядро Windows = Rootkits.Subverting the Windows kernel. — СПб.: Питер, 2010. — С. 36-58,77-129. — ISBN 978-5-469-01409-6
Ссылки
- Цикл статей по перехвату функций от Ms Rem
- Слежение за вызовами Native API
- Туториалы Iczelion’а о Win32 API
- Убийство часового КРИС КАСПЕРСКИ, АКА МЫЩЪХ Спецвыпуск: Хакер, номер #072, стр. 072-072-5
- Сплайсинг WinAPI с использованием Си и MASM
Для улучшения этой статьи желательно?: - Проставив сноски, внести более точные указания на источники.
Категории:- Исследование программ
- Информационная безопасность
- Системное программирование
Wikimedia Foundation. 2010.