Interested Article - Перехват (программирование)
- 2021-04-15
- 1
Перехват ( англ. hooking ) — технология, позволяющая изменить стандартное поведение тех или иных компонентов информационной системы.
Назначение технологии перехвата
Очень часто в системном программировании возникает задача изменения стандартного поведения системных функций. Например довольно интересным применением данной технологии является переопределение у GUI приложений Windows ( сабклассинг ). Это нужно, если программист хочет организовать собственную обработку какого-либо оконного сообщения и только потом передать стандартной оконной процедуре. После сабклассинга цикл обработки сообщений будет выглядеть так:
- До сабклассинга : [Сообщение Windows] -> [Окно (оконная процедура)]
- После : [Сообщение Windows] -> [Наша оконная процедура] -> [Окно (оконная процедура)]
Например, в уроках Iczelion’а описан пример того, как сабклассинг может использоваться для организации контроля ввода в элементы управления. Технологии перехвата нужны не только в этом случае, но и, например, для предварительной обработки результатов системных функций поиска файлов FindFirst и FindNext, EnumProcess, которая перечисляет процессы в Windows и т. д. Причем в этих целях такие технологии применяют как антивирусные средства , так и различного рода вирусы , руткиты и прочие виды вредоносного программного обеспечения.
Очень часто перехват бывает важен для организации отладки программ и является одной из основных технологий, применяемых в отладчиках. В данном случае эта технология позволяет одной программе контролировать выполнение другой. Для этих целей предусмотрен системный вызов , который позволяет подключаться к процессам, отслеживать значения регистров у контекста отлаживаемого процесса и в том числе контролировать другие системные вызовы. Он является основой для реализации такой возможности отладчиков как точки останова . Данный системный вызов хорошо документирован и присутствует во всех главных *nix системах: Linux , FreeBSD , Solaris . Чаще всего используется совместно с системным вызовом fork , который и вызывает , указывая в параметрах вызова, что запускаемый процесс — дочерний. Microsoft Windows также предоставляет для похожих целей т. н. .
Виды перехвата системных функций
Основными методами перехвата являются:
- Подмена адреса настоящей функции ( модификация IAT таблиц , модификация / IDT таблиц)
- Непосредственное изменение функции (сплайсинг, перехват в режиме ядра с модификацией тела функции)
- Непосредственная подмена всего компонента приложения/системы (например библиотеки с целевой функцией)
Методы можно также разделить по критерию режима выполнения:
- Пользовательские ( ring3 ) методы: модификация IAT таблиц, сплайсинг. Их особенность в том, что невозможно что-либо изменить в поведении ядра операционной системы и его расширений.
- Режима ядра: модификация SSDT/IDT таблиц, перехват в режиме ядра с модификацией тела функции. Позволяет модифицировать структуры данных и код любой части операционной системы и приложений.
Сплайсинг
Сплайсинг ( англиц. от «сращивание; склеивание») — метод перехвата API функций путём изменения кода целевой функции. Обычно изменяются первые 5 байт функции. Вместо них вставляется переход на функцию, которую определяет программист. Чтобы обеспечить корректность выполнения операции, приложение, которое перехватывает функцию, обязано дать возможность выполниться коду, который был изменён в результате сплайсинга. Для этого приложение сохраняет заменяемый участок памяти у себя, а после отработки функции перехвата восстанавливает изменённый участок функции и дает полностью выполниться настоящей функции.
Hot-patch point
Все функции стандартных DLL Windows поддерживают hot-patch point. При использовании этой технологии перед началом функции располагаются пять неиспользуемых однобайтовых операций
nop
, сама же функция начинается с двухбайтовой инструкции
mov edi, edi
. Места, занимаемого пятью
nop
, достаточно, чтобы разместить команду перехода на функцию-перехватчик. Два байта, занимаемых
mov edi, edi
, предоставляют достаточно места для команды перехода на код, размещенный на месте пяти
nop
. При этом, так как инструкция
mov edi, edi
не выполняет никаких осмысленных действий, её затирание никак не влияет на работоспособность исходной функции. Таким образом программист освобождается от необходимости где-либо сохранять исходное значение изменённого им кода
.
Сферы применения сплайсинга и методы обнаружения
Он применяется:
- В ПО, которому необходимо осуществлять функции мониторинга системы
- Механизмом хуков в Windows
- Различного рода вредоносными программами. Это основная технология сокрытия для руткитов пользовательского уровня
Основной метод обнаружения факта сплайсинга — это сравнение машинного кода функции, проверяемой на сплайсинг, и кода системной функции, полученного в заведомо чистой системе. Также в обнаружении сплайсинга функции может помочь контроль адресов перехода.
Сравнение с другими технологиями
- Изменение IAT таблиц процесса . Данная технология не позволяет изменить поведение самой системной функции, а лишь дает возможность «обмануть» выбранное приложение, заставив его использовать вашу функцию. IAT таблица — таблица адресов функций, импортируемых процессом. Технология носит лишь локальный характер, хотя может быть применена сразу к группе приложений. Может быть довольно быстро обнаружена из-за необходимости загрузки DLL в адресное пространство целевого процесса. Сплайсинг же не требует DLL и внедрения в чужой процесс, обладает возможностью глобального захвата функции. У сплайсинга есть ещё одно преимущество: не все системные функции импортируются процессом через IAT. Например, функция может быть загружена вызовом GetProcAddress. Использование же непосредственной модификации кода функции снимает подобное ограничение.
- Перехват в режиме ядра . Позволяет перехватывать любые функции, в том числе и экспортируемые ядром. Наиболее труден для обнаружения в случае успеха, так как позволяет фальсифицировать любые данные, предоставляемые операционной системой. Требует написания специального компонента для взаимодействия с ядром драйвера. Может привести к BSOD при неправильном программировании в режиме ядра. Может быть обнаружен на фазе загрузки драйвера в ядро или при проверке активных драйверов, а также при проверке ядра на изменения . Более трудный в программировании метод, чем сплайсинг, но более гибкий, так как позволяет перехватить функции самого ядра, а не только WinAPI функции, которые служат лишь посредником между ядром и программой, которая что-либо запрашивает у операционной системы.
- Замена самой библиотеки с функцией . Весьма радикальное решение проблемы, обладающее рядом существенных недостатков:
- Требует замены файла на диске, что может быть запрещено и пресечено самой системой. Например, замену системных файлов Windows не позволит выполнить защита файлов Windows (WFP) , хотя её можно и отключить. Такое действие может быть также обнаружено при статическом анализе системы ревизорами.
- Требуется полная эмуляция всех возможностей заменяемой DLL или иного компонента, что весьма трудоемко даже в случае открытости и осложняется необходимостью дизассемблирования в случае закрытости целевой программы.
Все это показывает, что это весьма нерациональный способ решения проблемы изменения поведения программы в случае возможности применения первых двух подходов или сплайсинга.
Перехват в режиме ядра
Он основан на модификации структур данных ядра и функций. Главными мишенями воздействия являются таблицы
- IDT Таблица диспетчеризации прерываний. Довольно важным для перехвата является прерывание, обрабатывающее обращение к таблице служб SSDT (0x2E) .
- (System Service Descriptor Table) Таблица диспетчеризации системных сервисов. Обращаясь к ней, система по номеру запрошенного сервиса может получить адрес соответствующего сервиса ядра и вызвать его. А таблица SSPT содержит общий размер параметров, передаваемых системному сервису.
- psActiveprocess Структура ядра, хранящая список процессов в системе.
- IRP Таблица драйвера, которая хранит указатели на функции обработки IRP-пакетов.
- Структура ядра, хранящая большое количество информации о процессе, включая, например, PID (идентификатор процесса).
Такого рода руткиты называются DKOM-руткитами, то есть руткитами, основанными на непосредственной модификации объектов ядра. В руткитах для систем Windows Server 2003 и XP эта технология была модернизирована, так как в этих ОС появилась защита от записи некоторых областей памяти ядра . Windows Vista и 7 получили дополнительную защиту ядра , однако все эти технологии были преодолены руткитописателями . В то же время перехват системных функций в режиме ядра — основа проактивных систем защиты и гипервизоров .
Иные формы перехвата
Можно выделить и другие формы перехвата:
- Перехват сетевых соединений и пакетов.
- Перехват паролей. Например, при помощи шпионажа за клавиатурным вводом при помощи кейлоггера .
- Перехват обращений браузера к сайтам при помощи HTTP Proxy или расширений браузера. Позволяет проанализировать и/или подменить данные, которыми обмениваются браузер и сервер.
Здесь описана лишь часть применений данной технологии.
Примеры программ, использующих перехват
- Гипервизор XEN
- Утилиты Марка Руссиновича FileMon , RegMon , Process Explorer
- Руткит
Примеры кода
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);
См. также
Примечания
- К примеру: невозможно получить доступ к процессу Kaspersky Internet Security штатными средствами Windows API, так как соответствующие функции перехвачены антивирусом.
- Страница из man Linux Ubuntu: от 21 января 2010 на Wayback Machine и русскоязычная версия: от 1 ноября 2014 на Wayback Machine
- Официальное описание: от 30 мая 2014 на Wayback Machine , а также примеры использования: от 4 марта 2010 на Wayback Machine
- . Дата обращения: 24 июля 2010. Архивировано из 23 декабря 2009 года.
- (англ.) . Дата обращения: 11 января 2017. 13 января 2017 года.
- Методы доступа и модификации IAT таблицы довольно подробно описаны у Хоглунд Г., Батлер Дж. — Руткиты: внедрение в ядро Windows. Гл 4 Древнее искусство захвата
- Методы внедрения DLL в чужой процесс описаны довольно подробно у Дж. Рихтера Кристофера Назара Windows via C/C++. Программирование на языке Visual C++. Некоторые методы внедрения впервые документировал сам Дж. Рихтер
- Например, один из первых руткит-детектеров от 25 июля 2010 на Wayback Machine
- ↑ Г. Хоглунд Дж. Батлер Руткиты внедрение в ядро Windows. Глава 4 Древнее искусство захвата
- . Дата обращения: 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 .
Ссылки
Для улучшения этой статьи
желательно
:
|
- 2021-04-15
- 1