Перехват (программирование) (Hyjy]fgm (hjkijgbbnjkfguny))
Эта статья нуждается в переработке. |
Перехват (англ. hooking) — технология, позволяющая изменить стандартное поведение тех или иных компонентов информационной системы.
Назначение технологии перехвата
[править | править код]Очень часто в системном программировании возникает задача изменения стандартного поведения системных функций. Например довольно интересным применением данной технологии является переопределение оконной процедуры у GUI приложений Windows (сабклассинг). Это нужно, если программист хочет организовать собственную обработку какого-либо оконного сообщения и только потом передать стандартной оконной процедуре. После сабклассинга цикл обработки сообщений будет выглядеть так:
- До сабклассинга: [Сообщение Windows]->[Окно (оконная процедура)]
- После: [Сообщение Windows]->[Наша оконная процедура]->[Окно (оконная процедура)]
Например, в уроках Iczelion’а[1] описан пример того, как сабклассинг может использоваться для организации контроля ввода в элементы управления. Технологии перехвата нужны не только в этом случае, но и, например, для предварительной обработки результатов системных функций поиска файлов FindFirst и FindNext, EnumProcess, которая перечисляет процессы в Windows и т. д. Причем в этих целях такие технологии применяют как антивирусные средства[2], так и различного рода вирусы, руткиты и прочие виды вредоносного программного обеспечения.
Очень часто перехват бывает важен для организации отладки программ и является одной из основных технологий, применяемых в отладчиках. В данном случае эта технология позволяет одной программе контролировать выполнение другой. Для этих целей предусмотрен системный вызов ptrace, который позволяет подключаться к процессам, отслеживать значения регистров у контекста отлаживаемого процесса и в том числе контролировать другие системные вызовы. Он является основой для реализации такой возможности отладчиков как точки останова. Данный системный вызов хорошо документирован и присутствует во всех главных *nix системах: Linux, FreeBSD, Solaris.[3] Чаще всего используется совместно с системным вызовом fork, который и вызывает ptrace, указывая в параметрах вызова, что запускаемый процесс — дочерний. Microsoft Windows также предоставляет для похожих целей т. н. Debug API[4].
Виды перехвата системных функций
[править | править код]Основными методами перехвата являются:
- Подмена адреса настоящей функции (модификация IAT таблиц, модификация SSDT/IDT таблиц)
- Непосредственное изменение функции (сплайсинг, перехват в режиме ядра с модификацией тела функции)
- Непосредственная подмена всего компонента приложения/системы (например библиотеки с целевой функцией)
Методы можно также разделить по критерию режима выполнения:
- Пользовательские (ring3) методы: модификация IAT таблиц, сплайсинг. Их особенность в том, что невозможно что-либо изменить в поведении ядра операционной системы и его расширений.
- Режима ядра: модификация SSDT/IDT таблиц, перехват в режиме ядра с модификацией тела функции. Позволяет модифицировать структуры данных и код любой части операционной системы и приложений.
Сплайсинг
[править | править код]Сплайсинг (англиц. от splicing «сращивание; склеивание») — метод перехвата API функций путём изменения кода целевой функции. Обычно изменяются первые 5 байт функции. Вместо них вставляется переход на функцию, которую определяет программист. Чтобы обеспечить корректность выполнения операции, приложение, которое перехватывает функцию, обязано дать возможность выполниться коду, который был изменён в результате сплайсинга. Для этого приложение сохраняет заменяемый участок памяти у себя, а после отработки функции перехвата восстанавливает изменённый участок функции и дает полностью выполниться настоящей функции.[5]
Hot-patch point
[править | править код]Все функции стандартных DLL Windows поддерживают hot-patch point. При использовании этой технологии перед началом функции располагаются пять неиспользуемых однобайтовых операций nop, сама же функция начинается с двухбайтовой инструкции mov edi, edi
. Места, занимаемого пятью nop, достаточно, чтобы разместить команду перехода на функцию-перехватчик. Два байта, занимаемых mov edi, edi
, предоставляют достаточно места для команды перехода на код, размещенный на месте пяти nop. При этом, так как инструкция mov edi, edi
не выполняет никаких осмысленных действий, её затирание никак не влияет на работоспособность исходной функции. Таким образом программист освобождается от необходимости где-либо сохранять исходное значение изменённого им кода[6].
Сферы применения сплайсинга и методы обнаружения
[править | править код]Он применяется:
- В ПО, которому необходимо осуществлять функции мониторинга системы
- Механизмом хуков в Windows
- Различного рода вредоносными программами. Это основная технология сокрытия для руткитов пользовательского уровня
Основной метод обнаружения факта сплайсинга — это сравнение машинного кода функции, проверяемой на сплайсинг, и кода системной функции, полученного в заведомо чистой системе. Также в обнаружении сплайсинга функции может помочь контроль адресов перехода.
Сравнение с другими технологиями
[править | править код]- Изменение IAT таблиц процесса[7]. Данная технология не позволяет изменить поведение самой системной функции, а лишь дает возможность «обмануть» выбранное приложение, заставив его использовать вашу функцию. IAT таблица — таблица адресов функций, импортируемых процессом. Технология носит лишь локальный характер, хотя может быть применена сразу к группе приложений. Может быть довольно быстро обнаружена из-за необходимости загрузки DLL[8] в адресное пространство целевого процесса. Сплайсинг же не требует DLL и внедрения в чужой процесс, обладает возможностью глобального захвата функции. У сплайсинга есть ещё одно преимущество: не все системные функции импортируются процессом через IAT. Например, функция может быть загружена вызовом GetProcAddress. Использование же непосредственной модификации кода функции снимает подобное ограничение.
- Перехват в режиме ядра. Позволяет перехватывать любые функции, в том числе и экспортируемые ядром. Наиболее труден для обнаружения в случае успеха, так как позволяет фальсифицировать любые данные, предоставляемые операционной системой. Требует написания специального компонента для взаимодействия с ядром драйвера. Может привести к BSOD при неправильном программировании в режиме ядра. Может быть обнаружен на фазе загрузки драйвера в ядро или при проверке активных драйверов, а также при проверке ядра на изменения[9]. Более трудный в программировании метод, чем сплайсинг, но более гибкий, так как позволяет перехватить функции самого ядра, а не только WinAPI функции, которые служат лишь посредником между ядром и программой, которая что-либо запрашивает у операционной системы.
- Замена самой библиотеки с функцией. Весьма радикальное решение проблемы, обладающее рядом существенных недостатков:
- Требует замены файла на диске, что может быть запрещено и пресечено самой системой. Например, замену системных файлов Windows не позволит выполнить защита файлов Windows (WFP), хотя её можно и отключить. Такое действие может быть также обнаружено при статическом анализе системы ревизорами.
- Требуется полная эмуляция всех возможностей заменяемой DLL или иного компонента, что весьма трудоемко даже в случае открытости и осложняется необходимостью дизассемблирования в случае закрытости целевой программы.
Все это показывает, что это весьма нерациональный способ решения проблемы изменения поведения программы в случае возможности применения первых двух подходов или сплайсинга.
Перехват в режиме ядра
[править | править код]Он основан на модификации структур данных ядра и функций. Главными мишенями воздействия являются таблицы
- IDT Таблица диспетчеризации прерываний. Довольно важным для перехвата является прерывание, обрабатывающее обращение к таблице служб SSDT (0x2E)[10].
- SSDT (System Service Descriptor Table) Таблица диспетчеризации системных сервисов. Обращаясь к ней, система по номеру запрошенного сервиса может получить адрес соответствующего сервиса ядра и вызвать его. А таблица SSPT содержит общий размер параметров, передаваемых системному сервису.
- psActiveprocess Структура ядра, хранящая список процессов в системе.
- IRP Таблица драйвера, которая хранит указатели на функции обработки IRP-пакетов.
- EPROCESS Структура ядра, хранящая большое количество информации о процессе, включая, например, PID (идентификатор процесса).
Такого рода руткиты называются DKOM-руткитами, то есть руткитами, основанными на непосредственной модификации объектов ядра. В руткитах для систем Windows Server 2003 и XP эта технология была модернизирована, так как в этих ОС появилась защита от записи некоторых областей памяти ядра[10]. Windows Vista и 7 получили дополнительную защиту ядра PatchGuard, однако все эти технологии были преодолены руткитописателями[11]. В то же время перехват системных функций в режиме ядра — основа проактивных систем защиты и гипервизоров.
Иные формы перехвата
[править | править код]Можно выделить и другие формы перехвата:
- Перехват сетевых соединений и пакетов.[12]
- Перехват паролей. Например, при помощи шпионажа за клавиатурным вводом при помощи кейлоггера.
- Перехват обращений браузера к сайтам при помощи 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);
}
}
}
}
Этот пример показывает, как используются хуки для контроля сетевого трафика в ядре 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 Архивная копия от 21 января 2010 на Wayback Machine и русскоязычная версия: Русский перевод на OpenNET Архивная копия от 1 ноября 2014 на Wayback Machine
- ↑ Официальное описание: The Debugging Application Programming Interface Архивная копия от 30 мая 2014 на Wayback Machine, а также примеры использования: Win32 API. Урок 28. Win32 Debug API I Архивная копия от 4 марта 2010 на Wayback Machine
- ↑ Цикл статей по перехвату WindowsAPI функций от Ms Rem . Дата обращения: 24 июля 2010. Архивировано из оригинала 23 декабря 2009 года.
- ↑ Why do Windows functions all begin with a pointless MOV EDI, EDI instruction? – The Old New Thing (англ.). Дата обращения: 11 января 2017. Архивировано 13 января 2017 года.
- ↑ Методы доступа и модификации IAT таблицы довольно подробно описаны у Хоглунд Г., Батлер Дж. — Руткиты: внедрение в ядро Windows. Гл 4 Древнее искусство захвата
- ↑ Методы внедрения DLL в чужой процесс описаны довольно подробно у Дж. Рихтера Кристофера Назара Windows via C/C++. Программирование на языке Visual C++. Некоторые методы внедрения впервые документировал сам Дж. Рихтер
- ↑ Например, один из первых руткит-детектеров KLISTNER Архивная копия от 25 июля 2010 на Wayback Machine
- ↑ 1 2 Г. Хоглунд Дж. Батлер Руткиты внедрение в ядро Windows. Глава 4 Древнее искусство захвата
- ↑ убийство часового [[Крис Касперски|КРИС КАСПЕРСКИ]], АКА МЫЩЪХ Спецвыпуск: Хакер, номер #072, стр. 072-072-5 . Дата обращения: 26 июля 2010. Архивировано 4 декабря 2010 года.
- ↑ Например, этим занимаются снифферы. Одной из бесплатных реализаций захвата сетевых пакетов является сетевой драйвер уровня NDIS WinPCAP
Литература
[править | править код]- Джеффри Рихтер. Программирование на языке Visual C++ = 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
Для улучшения этой статьи желательно:
|