Перехват (программирование)
Перехват (англ. hooking) — технология, позволяющая изменить стандартное поведение тех или иных компонентов информационной системы. Назначение технологии перехватаОчень часто в системном программировании возникает задача изменения стандартного поведения системных функций. Например довольно интересным применением данной технологии является переопределение оконной процедуры у GUI приложений Windows (сабклассинг). Это нужно, если программист хочет организовать собственную обработку какого-либо оконного сообщения и только потом передать стандартной оконной процедуре. После сабклассинга цикл обработки сообщений будет выглядеть так:
Например, в уроках Iczelion’а[1] описан пример того, как сабклассинг может использоваться для организации контроля ввода в элементы управления. Технологии перехвата нужны не только в этом случае, но и, например, для предварительной обработки результатов системных функций поиска файлов FindFirst и FindNext, EnumProcess, которая перечисляет процессы в Windows и т. д. Причем в этих целях такие технологии применяют как антивирусные средства[2], так и различного рода вирусы, руткиты и прочие виды вредоносного программного обеспечения. Очень часто перехват бывает важен для организации отладки программ и является одной из основных технологий, применяемых в отладчиках. В данном случае эта технология позволяет одной программе контролировать выполнение другой. Для этих целей предусмотрен системный вызов ptrace, который позволяет подключаться к процессам, отслеживать значения регистров у контекста отлаживаемого процесса и в том числе контролировать другие системные вызовы. Он является основой для реализации такой возможности отладчиков как точки останова. Данный системный вызов хорошо документирован и присутствует во всех главных *nix системах: Linux, FreeBSD, Solaris.[3] Чаще всего используется совместно с системным вызовом fork, который и вызывает ptrace, указывая в параметрах вызова, что запускаемый процесс — дочерний. Microsoft Windows также предоставляет для похожих целей т. н. Debug API[4]. Виды перехвата системных функцийОсновными методами перехвата являются:
Методы можно также разделить по критерию режима выполнения:
СплайсингСплайсинг (англиц. от splicing «сращивание; склеивание») — метод перехвата API функций путём изменения кода целевой функции. Обычно изменяются первые 5 байт функции. Вместо них вставляется переход на функцию, которую определяет программист. Чтобы обеспечить корректность выполнения операции, приложение, которое перехватывает функцию, обязано дать возможность выполниться коду, который был изменён в результате сплайсинга. Для этого приложение сохраняет заменяемый участок памяти у себя, а после отработки функции перехвата восстанавливает изменённый участок функции и дает полностью выполниться настоящей функции.[5] Hot-patch pointВсе функции стандартных DLL Windows поддерживают hot-patch point. При использовании этой технологии перед началом функции располагаются пять неиспользуемых однобайтовых операций nop, сама же функция начинается с двухбайтовой инструкции Сферы применения сплайсинга и методы обнаруженияОн применяется:
Основной метод обнаружения факта сплайсинга — это сравнение машинного кода функции, проверяемой на сплайсинг, и кода системной функции, полученного в заведомо чистой системе. Также в обнаружении сплайсинга функции может помочь контроль адресов перехода. Сравнение с другими технологиями
Все это показывает, что это весьма нерациональный способ решения проблемы изменения поведения программы в случае возможности применения первых двух подходов или сплайсинга. Перехват в режиме ядраОн основан на модификации структур данных ядра и функций. Главными мишенями воздействия являются таблицы
Такого рода руткиты называются 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);
}
}
}
}
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);
См. такжеПримечания
Литература
Ссылки
|
Portal di Ensiklopedia Dunia