Перехват (англ. hooking) — технология, позволяющая изменить стандартное поведение тех или иных компонентов информационной системы.
Очень часто в системном программировании возникает задача изменения стандартного поведения системных функций. Например довольно интересным применением данной технологии является переопределение оконной процедуры у GUI приложений Windows (сабклассинг). Это нужно, если программист хочет организовать собственную обработку какого-либо оконного сообщения и только потом передать стандартной оконной процедуре. После сабклассинга цикл обработки сообщений будет выглядеть так:
Сообщение Windows->Окно (оконная процедура)
Сообщение Windows->Наша оконная процедура->Окно (оконная процедура)
Например, в Уроках Iczelion’а[1] описан пример того, как сабклассинг может использоваться для организации контроля ввода в элементы управления. Технологии перехвата нужны не только в этом случае, но и, например, для предварительной обработки результатов системных функций поиска файлов FindFirst и FindNext, EnumProcess, которая перечисляет процессы в Windows и т. д. Причем в этих целях такие технологии применяют как антивирусные средства[2], так и различного рода вирусы, руткиты и прочие виды вредоносного программного обеспечения.
Очень часто перехват бывает важен для организации отладки программ и является одной из основных технологий, применяемых в отладчиках. В данном случае эта технология позволяет одной программе контролировать выполнение другой. Для этих целей предусмотрен системный вызов ptrace, который позволяет подключаться к процессам, отслеживать значения регистров у контекста отлаживаемого процесса и в том числе контролировать другие системные вызовы. Он является основой для реализации такой возможности отладчиков как точки останова. Данный системный вызов хорошо документирован и присутствует во всех главных *Nix системах: Linux, FreeBSD, Solaris.[3] Чаще всего используется совместно с системным вызовом fork, который и вызывает ptrace, указывая в параметрах вызова, что запускаемый процесс — дочерний. Microsoft Windows также предоставляет для похожих целей т. н. DebugAPI[4].
Основными методами перехвата являются:
Методы можно также разделить по критерию режима выполнения:
Сплайсинг (от англ. splice — «сращивать или склеивать концы чего-либо») — метод перехвата API функций путём изменения кода целевой функции. Обычно изменяются первые 5 байт функции. Вместо них вставляется переход на функцию, которую определяет программист. Чтобы обеспечить корректность выполнения операции, приложение, которое перехватывает функцию, обязано дать возможность выполниться коду, который был изменён в результате сплайсинга. Для этого приложение сохраняет заменяемый участок памяти у себя, а после отработки функции перехвата восстанавливает изменённый участок функции и дает полностью выполниться настоящей функции.[5]
Все функции стандартных dll Windows поддерживают hot-patch point. При использовании этой технологии перед началом функции располагаются пять неиспользуемых однобайтовых операций nop, сама же функция начинается с двубайтовой инструкции mov edi, edi. Места занимаемого пятью nop достаточно чтобы разместить команду перехода на функцию-перехватчик. Два байта занимаемых mov edi, edi предоставляют достаточно места для команды перехода на код размещенный на месте пяти nop. При этом, так как инструкция mov edi, edi не выполняет никаких осмысленных действий, её затирание никак не влияет на работоспособность исходной функции. Таким образом, программист освобождается от необходимости где-либо сохранять исходное значение изменённого им кода[6].
Он применяется:
Основной метод обнаружения факта сплайсинга — это сравнение машинного кода функции, проверяемой на сплайсинг, и кода системной функции, полученного в заведомо чистой системе. Также в обнаружении сплайсинга функции может помочь контроль адресов перехода.
Все это показывает, что это весьма нерациональный способ решения проблемы изменения поведения программы в случае возможности применения первых двух подходов или сплайсинга.
Он основан на модификации структур данных ядра и функций. Главными мишенями воздействия являются таблицы
Такого рода руткиты называются DKOM-руткитами, то есть руткитами, основанными на непосредственной модификации объектов ядра. В руткитах для систем Windows Server 2003 и XP эта технология была модернизирована, так как в этих ОС появилась защита от записи некоторых областей памяти ядра[10]. Windows Vista и 7 получили дополнительную защиту ядра PatchGuard, однако все эти технологии были преодолены руткитописателями[11]. В то же время перехват системных функций в режиме ядра — основа проактивных систем защиты и гипервизоров.
Можно выделить и другие формы перехвата:
Здесь описана лишь часть применений данной технологии.
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);
Для улучшения этой статьи желательно: |
Данная страница на сайте WikiSort.ru содержит текст со страницы сайта "Википедия".
Если Вы хотите её отредактировать, то можете сделать это на странице редактирования в Википедии.
Если сделанные Вами правки не будут кем-нибудь удалены, то через несколько дней они появятся на сайте WikiSort.ru .