// Підключимо бібліотеку Detours p>
VOID (* DynamicTrampoline) (VOID) = NULL;// Це буде функція-трамплін p>
VOID DynamicDetour (VOID)// Це - функція-перехоплювач p>
( p>
// У цьому прикладі вона нічого не робить,
просто викликає оригінальну функцію p>
return DynamicTrampoline (); p>
) p>
void main (void) p>
( p>
// Отримаємо адреса цільової функції p>
VOID (* DynamicTarget) (VOID) =
SomeFunction; p>
// Тут здійснюється перехоплення p>
DynamicTrampoline = (FUNCPTR) DetourFunction ((PBYTE) DynamicTarget,
(PBYTE) DynamicDetour); p>
//... p>
DetourRemoveTrampoline (DynamicTrampoline);
//А тут знімається p>
) p>
При перехоплення функція DetourFunction динамічно
створює трамплін і повертає його адресу. Як функції SomeFunction,
яка в даному прикладі повертає адресу цільової функції, можна використовувати
DetourFindFunction, яка намагається знайти потрібну функцію в потрібному модулі.
Спочатку вона намагається зробити це через LoadLibrary і GetProcAddress, а в разі
невдачі - використовує бібліотеку ImageHlp для пошуку налагоджувальних символів. p>
Макрос DETOUR_TRAMPOLINE і функція DetourFunction
включають в себе вбудований табличний дизассемблер, який визначає, яке
кількість байт з заголовка цільової функції має бути скопійовано в
функцію-трамплін (не менше 5 байт (розмір команди jmp), що складають ціле
число команд процесора). Якщо цільова функція займає менше 5 байт, то
перехоплення закінчується невдачею. p>
До переваг даного методу варто віднести простоту
і надійність. На відміну від методу з використанням розділу імпорту, не потрібно
враховувати всі можливі методи, якими може бути отриманий реальний адреса
функції. Недолік - не вдасться перехопити функцію з розміром менше 5 байт
або функцію з наступним заголовком: p>
push ecx; у функцію передається кількість
ітерацій циклу p>
begin_loop: p>
;... p>
; тут якийсь код p>
;... p>
loop begin_loop p>
ret p>
Вищенаведений приклад Galen Hunt, один з авторів
Detours, прокоментував так: «Існує безліч теоретичних
прикладів коду, де пролог функції менше 5 байт, необхідних для команди jmp.
Однак ніхто не повідомляв про реальних прикладах функції з такими проблемами ». P>
ПОПЕРЕДЖЕННЯ p>
На момент установки/зняття перехоплення
потрібно зупиняти всі інші потоки процесу, в якому відбувається
перехоплення (або упевнитися, що вони не можуть викликати перехоплюваних
функцію). p>
Існує інший спосіб реалізації даного методу.
Замість команди jmp в початок функції міститься команда INT 3, а управління
функції-перехоплювачі передається опосередковано в обробнику необроблених
винятків (її адреса заноситься до pExceptionInfo-> ContextRecord-> Eip і
оброблювач повертає EXCEPTION_CONTINUE_EXECUTION). Так як команда INT 3
займає 1 байт, то вищеописана ситуація в цьому випадку навіть теоретично
неможлива. p>
ПОПЕРЕДЖЕННЯ p>
Основним недоліком даного способу
є його вкрай мала швидкодія (обробка виключення в Windows
займає досить тривалий час). Крім того, наявність обробника
винятків в перехоплюваних процесі призведе до того, що даний метод
працювати не буде. Також даний спосіб не буде працювати під відладчиком. P>
Глобальний перехоплення
p>
Глобальний перехоплення може бути реалі?? ован різними
способами. Перший спосіб - застосування локального перехоплення до всіх програм
в системі (запущеним в момент перехоплення або пізніше). Другий спосіб - «злом
системи »- має на увазі підміну коду перехоплюваних функції безпосередньо в
DLL-файлі або його образ у пам'яті. P>
Глобальний перехоплення методом тотального локального
перехоплення
p>
Даний метод заснований на наступному: якщо можна
перехопити функцію з поточного процесу, то потрібно виконати код перехоплення під
всіх процесах в системі. Існує кілька методів змусити чужий процес
виконати код перехоплення. Найпростіший - внести цей код в DllMain деякої
бібліотеки, а потім впровадити її в чужій процес. Методів впровадження DLL також
існує кілька (див. Джеффрі Ріхтер). Найпростіший, що працює і в Win9X,
і в WinNT - впровадження DLL за допомогою пасток. Реалізується він так: у системі
встановлюється пастка (за допомогою функції SetWindowsHookEx) типу
WH_GETMESSAGE (ця пастка служить для перехоплення Windows-повідомлень). У цьому
випадку модуль, в якому знаходиться пастка, автоматично підключається до
потоку, вказаною в останньому аргумент SetWindowsHookEx (якщо вказано 0, то
здійснюється підключення до всіх потоків в системі). Однак підключення
відбувається не відразу, а перед тим, як в чергу повідомлень потоку буде надіслано
якесь повідомлення. Тому перехоплення здійснюється не відразу після запуску
додатки, а перед обробкою процесом перше повідомлення. Так що всі виклики
перехоплюваних функції до обробки процесом перші повідомлення
перехоплюватися не будуть. А в додатках без черги повідомлень (наприклад,
консольних) цей спосіб впровадження взагалі не працює. p>
Я написав приклад, який реалізує глобальний перехоплення
функції GetDriveTypeA з використанням впровадження DLL за допомогою пасток та
перехоплення з використанням секції імпорту. p>
Функція GetDriveTypeA з бібліотеки kernel32.dll
використовується програмами Windows для визначення типу диска (локальний, CD-ROM,
мережевий, віртуальний і т. д.). Вона має наступний прототип: p>
UINT GetDriveType (LPCTSTR
lpRootPathName); p>
lpRootPathName - шлях до диска (А:, В: і т.д.) p>
GetDriveTypeA повертає одне з наступних значень: p>
# define
DRIVE_UNKNOWN 0 p>
# define
DRIVE_NO_ROOT_DIR 1 p>
# define
DRIVE_REMOVABLE 2 p>
# define
DRIVE_FIXED 3 p>
# define
DRIVE_REMOTE 4 p>
# define
DRIVE_CDROM 5 p>
# define DRIVE_RAMDISK 6 p>
Перехоплення цієї функції дозволяє «обманювати» програми
Windows, перевизначений значення, що повертається цією функцією, для будь-якого диска. P>
Програма DriveType2 складається з двох модулів:
DriveType2.exe і DT2lib.dll. P>
DriveType2.exe реалізує інтерфейс, а вся робота
виконується в DT2lib.dll. p>
Проект DT2lib складається з трьох основних файлів: p>
APIHook.cpp - це фото написаний Джеффрі Ріхтером (за
винятком деяких виправлень, зроблених мною. Про них я розповім нижче). У
цей файл описаний клас CAPIHook, який реалізує перехоплення заданої API-функції у
всіх модулях поточного процесу. Тут же автоматично перехоплюються функції
LoadLibraryA, LoadLibraryW, LoadLibraryExA, LoadLibraryExW і GetProcAddress. P>
Toolhelp.h - це фото також написаний Джеффрі Ріхтером.
У ньому описаний клас CToolhelp, який реалізує звернення до системних
toolhelp-функцій. В даному випадку він використовується класом CAPIHook для
перерахування всіх модулів, підключених до процесу. p>
DT2Lib.cpp - в цьому файлі я реалізував перехоплення
функції GetDriveTypeA з використанням класу CAPIHook, а також установку
пастки типу WH_GETMESSAGE, що забезпечує підключення даного модуля
(DT2lib.dll) до всіх потоків в системі. P>
Як же відбувається перехоплення? p>
Відразу ж після запуску DriveType2.exe викликається
функція DT2_HookAllApps з DT2lib.dll, яка встановлює пастку. p>
BOOL WINAPI DT2_HookAllApps (BOOL fInstall, DWORD dwThreadId) p>
( p>
BOOL fOk; p>
if (fInstall) p>
( p>
chASSERT (g_hhook == NULL);// 2
рази перехоплювати ні до чого p>
g_hhook =
SetWindowsHookEx (WH_GETMESSAGE, GetMsgProc, p>
ModuleFromAddress (DT2_HookAllApps),
dwThreadId);// Встановимо пастку p>
fOk = (g_hhook! = NULL); p>
) p>
else p>
( p>
chASSERT (g_hhook! = NULL);// Знімати-то нічого p>
fOk = UnhookWindowsHookEx (g_hhook);//
Знімемо пастку p>
g_hhook = NULL; p>
) p>
return (fOk); p>
) p>
Функція пастки GetMsgProc нічого не робить, а просто
викликає наступну функцію пастки (можливо, не тільки наша програма
встановила пастку, і це, як мінімум потрібно перевірити). Перед тим, як
помістити в чергу, пов'язану з деяким потоком, якесь повідомлення,
система повинна викликати будь-яких пастки типу WH_GETMESSAGE (зазвичай
такі пастки використовуються для моніторингу або зміни деяких повідомлень,
однак ми нічого подібного не робимо - нам потрібно просто підключитися до всіх
потоків у системі). Система не може просто викликати нашу функцію пастки - вона
і одержувач повідомлення знаходяться в різних процесах, а значить, і в різних
адресних просторах. І вихід з цієї ситуації один - система просто
підключає модуль (а це обов'язково має бути DLL-модуль), в якому знаходиться
пастка, до того процесу, якому надсилається повідомлення (що нам власне і
потрібно). p>
Після підключення DLL до потоку відбувається
ініціалізація всіх змінних (кожен процес, до якого підключається DLL,
має копії всіх глобальних і статичних змінних, що описані в ній). З
глобальних змінних у нас є 6 примірників класу CAPIHook (для
GetDriveTypeA в DT2Lib.cpp і для LoadLibraryA, LoadLibraryW, LoadLibraryExA,
LoadLibraryExW і GetProcAddress - в APIHook.cpp). Таким чином, при підключенні
DLL відбувається шестиразовий виклик конструктора класу CAPIHook,
перехоплює перераховані вище функції в поточному (тобто в тому, до якого
тільки що відбулося підключення) процесі. p>
При завершенні процесу впроваджена DLL відключається.
При цьому відбувається виклик деструктора CAPIHook для всіх екземплярів класу. P>
Ця функція тепер буде викликатися кожного разу,
коли з даного модуля буде відбуватися звернення до GetDriveTypeA. p>
int WINAPI Hook_GetDriveTypeA (PCSTR lpRootPathName) p>
( p>
// Виклик оригінальну функцію p>
int Result =
((PGetDriveTypeA) (PROC) g_GetDriveTypeA) (lpRootPathName); p>
if (Result>
DRIVE_NO_ROOT_DIR) p>
( p>
int Drive =
toupper (* lpRootPathName); p>
if (Drive> = 'A' & &
Drive <= 'Z') p>
( p>
Drive -= 'A';// Індекс у масиві Drives p>
// Якщо цей диск перевизначений, повернемо
значення з масиву p>
if (Drives [Drive] <0xFF) p>
Result = Drives [Drive]; p>
) p>
) p>
return Result; p>
) p>
Функція Hook_GetDriveTypeA спочатку викликає
оригінальну GetDriveTypeA. Потім, якщо повертається значення більше
DRIVE_NO_ROOT_DIR (тобто функції був переданий коректний аргумент, і вона
виконати без помилок), то перевіряється, перевизначений чи диск, тип якого
запитується. Інформація про значення перехоплюваних функцій у даному випадку
зберігається у реалізованому мною масиві BYTE Drives [26], що дозволяє
реалізувати перехоплення 26 дисків, від A: до Z:. У цьому масиві зберігаються значення,
що повертаються функцією GetDriveTypeA для кожного з дисків. Отже, якщо значення
елемента масиву, відповідного аргументу GetDriveTypeA одно 0xFF, то значення
повертається без змін, в іншому випадку повертається значення з
масиву. Запис значень в цей масив реалізується в DriveType2.cpp. P>
РАДА p>
Якщо ви хочете, щоб ця програма
повноцінно працювала в WinNT, слід також перехопити функцію GetDriveTypeW. p>
Ще одна реалізація даного методу описано в статті
«Перехват API-функцій в Windows NT/2000/XP», автор Тихомиров В. А.,
публікувалася в RSDN Magazine # 1 (будьте обережні, там та ж помилка, що і у
Джеффрі Ріхтера). P>
ПРИМІТКА p>
У цього методу є ще один
істотний недолік: деякі комерційні програми (наприклад,
Популярний файловий менеджер Total Commander, упакований ASPack) використовують
різні системи захисту (ASProtect, VBox і т. д.), шифрувальні таблицю імпорту
захищається програми. З такими програмами цей метод не працює. P>
Глобальний перехоплення може бути реалізований і за допомогою
Detours (тільки в WinNT). А так як методів впровадження DLL відомо кілька,
то різних варіантів реалізації глобального перехоплення можна запропонувати
досить багато. p>
Глобальний перехоплення методом підміни коду в DLL
p>
Даний метод можна реалізувати двома способами:
безпосередній редагуванням коду DLL, в якій розташована цільова функція, або
підміною цієї DLL іншого, що експортує той же набір функцій. Другий спосіб
відомий під назвою «Підміна з використанням обгорток (wrappers )». p>
Перший спосіб дозволяє реалізовувати тільки
порівняно невеликі за розміром функції-перехоплювачі, так як код необхідно
впроваджувати у вільні ділянки DLL - в основному в міжсекційних простір.
Інший недолік - код необхідно писати на асемблері. Загальна ідеологія роботи
цього методу та ж, що і в Detours. У код цільової функції впроваджується команда
jmp до функції-перехоплювачі. Байти, скопійовані «з-під» jmp'а, переміщаються в
перехоплювач (так як перехоплювач все одно пишеться на асемблері, в цьому випадку
його простіше відразу поєднати з функцією-трампліном). Ось приклад реалізації цього
методу. p>
У каталозі DriveType0 знаходиться файл kernel32.dll, в
якому я зробив наступні виправлення (за допомогою hiew32.exe): p>
За адресою 4E02 - локальна адреса .0 BFF74E02 (це кінець
функції GetDriveTypeA) я помістив команду jmp .0 BFF71080 - на перше-ліпше
вільне місце (у виконуваних файлах завжди багато вільного місця - звичайно в
кінцях секцій). p>
За адресою .0 BFF71080 (глобальний адреса 1080) я
помістив наступний код: p>
. BFF71080:
3C03 cmp al, 003; Повертаємо DRIVE_FIXED? P>
. BFF71082:
750E jne .0 BFF71092 p>
. BFF71084:
B402 mov ah, 002; Так. P>
. BFF71086: CD16 int 016;/Перевіримо стан ScrollLock p>
. BFF71088:
2410 and al, 010; p>
. BFF7108A:
7404 je .0 BFF71090; Світлодіод горить? P>
. BFF7108C:
B005 mov al, 005; Так. Повертаємо DRIVE_CDROM p>
. BFF7108E:
EB02 jmps .0 BFF71092; На повернення p>
. BFF71090:
B003 mov al, 003 p>
. BFF71092:
5F pop edi;/Повернення з GetDriveTypeA p>
. BFF71093: 5E pop esi; (шматок коду, скопійований p>
. BFF71094: 5B pop ebx; з .0 BFF74E02 -. BFF74E06) p>
. BFF71095:
C9 leave; p>
. BFF71096:
C20400 retn 00004; p>
Таким чином, коли світлодіод ScrollLock не горить,
функція GetDriveTypeA працює як звичайно, а якщо горить - то для всіх
Windows-додатків всі локальні диски (у мене це С: і D:) перетворюються на
CD-ROMи. P>
ПРИМІТКА p>
Щоб все це запрацювало, необхідно
замінити файл C: WindowsSystemkernel32.dll на файл DriveType0kernel32.dll.
Зробити це можна, лише завантаживши комп'ютер в режимі MS-DOS, тому що
kernel32.dll - одна з системних бібліотек Windows. Даний приклад реалізований
для Windows 98. Оскільки системні бібліотеки змінюються залежно від
версії Windows (і навіть від номера білду), то в інших операційних системах
цей приклад працювати не буде (його потрібно реалізовувати для кожної версії
kernel32.dll заново). p>
Цей спосіб перехоплення - один з найпотужніших. Однак у
комерційних продуктах його використовувати не вдасться, тому що він, очевидно,
порушує практично будь-яке ліцензійну угоду. p>
Інший спосіб реалізації цього методу - використання
обгорток (wrappers). Суть його в створенні власної DLL з тим же набором
експортованих функцій, що і оригінальна. Як приклад можу навести
наступний варіант реалізації вищенаведеного прикладу: p>
Системну бібліотеку Kernel32.dll перейменовуємо в
kernel31.dll:). p>
Створюємо бібліотеку з ім'ям Kernel32.dll, в якій
реалізована одна функція - GetDriveTypeA (це буде функція-перехоплювач), а всі
інші функції переадресуємо до kernel31.dll (благо компілятор Visual C + +
підтримує переадресацію функцій DLL). p>
Отриману бібліотеку поміщаємо в системний каталог. p>
При цьому функція-перехоплювач може викликати
оригінальну функцію з kernel31.dll. p>
Основним недоліком даного способу є те, що
він не годиться для DLL, що експортують змінні. p>
Глобальний перехоплення методом підміни коду DLL в пам'яті
(тільки Win9X)
p>
Ідея даного методу полягає в наступному: у Win9X
системні DLL завантажуються в загальну для всіх процесів область пам'яті (у третьому
гігабайт). Тому якщо б вдалося провести реалізацію Detours під Win9X, то
зміни торкнулися би всіх процесів (тобто стався б глобальний
перехоплення). Ситуація ускладнюється тим, що запис в область системних DLL в Win9X
можлива або з режиму ядра, або недокументовані засобами. Крім того, в
час запису необхідно зупинити всі потоки, які можуть викликати цільову
функцію. Це можна зробити за допомогою SuspendThread. Однак ця функція вимагає
як аргумент handle потоку, а використовувані для перерахування потоків
функції Thread32First/Thread32Next повертають ThreadID. Функція OpenThread,
яка дозволяє отримати handle з ThreadID, реалізована тільки починаючи з
Windows ME. З цієї причини в загальному вигляді документовані коштами з
режиму користувача даний метод в Win9X нереалізуем. Однак є інший
спосіб. Обидві позначені проблеми (запис в область системних DLL і зупинка
всіх призначених для користувача потоків у системі) можуть бути вирішені, якщо для перехоплення
використовувати драйвер. Драйвер працює в режимі ядра, тому з нього можна
проводити запис з будь-яким доступним адресами. А якщо на момент
установки/зняття перехоплення підняти IRQL (рівень запиту на переривання) до
DISPATCH_LEVEL, то перервати потік зможе тільки процедура обробки апаратного
переривання (звідки викликати для користувача системні функції не можна). Крім
того, застосування драйвера дозволяє вирішити ще одну проблему.
Функція-перехоплювач і функція-трамплін повинні розташовуватися в області пам'яті,
загальною для всіх процесів (у старших 2 гігабайти). Звичайно, можна було б
створити файл, що відображається в пам'ять (MMF - вони в Win9X розміщуються в третьому
гігабайті), і помістити код цих функцій туди, або розмістити їх в окремій
DLL з ImageBase в третьому гігабайті, але простіше реалізувати їх безпосередньо в
драйвері (драйвери в Win9X розміщуються в четвертому гігабайті - у розділі коду та
даних режиму ядра). p>
Розглянемо приклад, який реалізує описаний метод. Для
здійснення перехоплення і розміщення функції-трампліну і функції-перехоплювача я
написав WDM-драйвер (з використанням Visual C + + 6.0, Windows 2000 DDK і
Compuware DriverStudio 2.7), а також програму для взаємодії з ним.
Програма і драйвер розташовані в каталозі DriveType1 (там же - інструкції
встановлення). p>
Приклад DriveType1 складається з двох частин - драйвера
DTDrv.sys і інсталяційного скрипта DTDrv.inf, а також програми DriveType.exe. p>
DriveType.exe компілюється з одного модуля
DriveType.cpp, в якому реалізовані користувальницький інтерфейс і інтерфейс з
драйвером. Інтерфейс із драйвером реалізується через функції CreateFile (відкриття
драйвера), DeviceIoControl (операції введення-виведення) і CloseHandle (закриття
драйвера). Реалізовано чотири команди, які викликаються через DeviceIoControl --
перехоплення функції GetDriveTypeA, зняття перехоплення, установка повертається
значення функцією перехоплення для кожного з дисків A: .. Z:, читання поточного
стану перехоплення. p>
Ну а вся робота з перехоплення робиться в драйвері, за
винятком того, що адреса функції GetDriveTypeA визначається також у
DriveType.cpp та надсилається як параметр в команді перехоплення. Після
отримання цієї адреси функція DTDRV_IOCTL_HOOK_Handler (з модуля
DTDrvDevice.cpp) реалізує перехоплення розглянутим вище способом. Функція
DTDRV_IOCTL_UNHOOK_Handler знімає перехоплення, функція
DTDRV_IOCTL_SETDRIVE_Handler встановлює значення, що повертається
перехоплювачем, функція DTDRV_IOCTL_GETSTATE_Handler повертає значення
перехоплення і прапор перехоплення. p>
Основні змінні, використовувані DriveType.cpp: p>
unsigned
char IsHook = false;// Прапор перехоплення p>
unsigned
char Drives [26];// Значення перехоплення p>
PUCHAR
GDT;// Адреса GetDriveTypeA p>
У Drives [26] зберігаються значення, що повертаються функцією
MGetDriveType для дисків A: .. Z: (= 0xFF, якщо інформація про диску не
перевизначено). p>
Отже, функція-трамплін NewGetDriveType буде виглядати
наступним чином: p>
__declspec (naked) unsigned int NewGetDriveType (LPSTR Path) p>
( p>
_asm p>
( p>
nop// Тут будуть перші 5 байт з функції
GetDriveTypeA p>
nop// (в Win98 3 команди асемблера) p>
nop p>
nop p>
nop p>
jmp $// А тут - перехід до GetDriveTypeA +
5 p>
) p>
) p>
Спочатку ця функція «порожня», тому що весь її код
пишеться під час перехоплення функцією DTDRV_IOCTL_HOOK_Handler, яка, якщо
оперувати термінами Detours, реалізує динамічний перехоплення. p>
ПРИМІТКА p>
Код цієї функції від початку може бути
будь-яким, але він повинен займати принаймні 10 байт (щоб вмістилися 5 байт
із заголовка GetDriveTypeA і 5 байт - команда jmp). p>
Власне функція-перехоплювач MGetDriveType
реалізована в моєму прикладі так: p>
unsigned int
MGetDriveType (LPSTR Path)// Це --
функція-перехоплювач p>
( p>
unsigned int res =
NewGetDriveType (Path);// виклик старій GetDriveTypeA p>
unsigned char Letter = * Path; p>
if (Letter> = 'a' & &
Letter <= 'z') Letter-= 'a' - 'A';// Заголовні p>
if (Letter> = 'A' & &
Letter <= 'Z') p>
( p>
unsigned char NewRes =
Drives [Letter - 'A']; p>
if (NewRes <0xFF) res = NewRes;// Якщо диск перепризначений, повернемо
значення з таблиці p>
) p>
return res; p>
) p>
Спочатку викликається функція-трамплін NewGetDriveType,
яка фактично виконує код оригінальній GetDriveTypeA (спочатку
виконуються перші 5 байт - це 3 команди асемблера, потім - в?? е інше).
Після цього визначається буква диска. Перетворення літери у верхній регістр
здійснюється вручну. Далі, якщо цей диск перехоплений, повертається
значення з масиву Drives, в іншому випадку - те, що повернула
NewGetDriveType. P>
Перехоплення реалізований у функції DTDRV_IOCTL_HOOK_Handler
наступним чином: p>
NTSTATUS DTDrvDev:: DTDRV_IOCTL_HOOK_Handler (KIrp I) p>
( p>
NTSTATUS status =
STATUS_SUCCESS; p>
I. Information () = 0; p>
if (IsHook) p>
return status; p>
# pragma pack (push, 1)// Включимо вирівнювання по межі байти p>
struct p>
( p>
UCHAR Byte0; p>
ULONG Byte1_4; p>
) Patch = (0xE9);// Код інструкції jmp p>
# pragma pack (pop)// Повернемо вирівнювання за замовчуванням p>
KIRQL oldirql; p>
KeRaiseIrql (DISPATCH_LEVEL,
& oldirql);// Піднімемо IRQL до DISPATCH_LEVEL p>
GDT =
(PUCHAR) * (PULONG) I. IoctlBuffer ();// GDT = Адреса GetDriveTypeA p>
RtlCopyMemory (NewGetDriveType,
GDT, 5);// Заголовок NewGetDriveType p>
Patch.Byte1_4 = (ULONG) GDT --
(ULONG) NewGetDriveType - 5; p>
RtlCopyMemory ((PVOID) ((ULONG) NewGetDriveType
+ 5), & Patch, 5);// jmp GetDriveTypeA + 5 p>
Patch.Byte1_4 =
(ULONG) MGetDriveType - (ULONG) GDT - 5; p>
RtlCopyMemory (GDT, & Patch,
5);// jmp MGetDriveType p>
IsHook = true; p>
KeLowerIrql (oldirql);// Повернемо IRQL назад p>
return status; p>
) p>
Якщо функція GetDriveTypeA ще не перехоплена
(IsHook = false), то: p>
Визначається адреса функції GetDriveTypeA (він
надсилається як параметр); p>
За адресою NewGetDriveType копіюються 5 байт з початку
GetDriveTypeA; p>
За ними вставляється байт 0xE9 (код команди jmp) і
зсув до точки GetDriveTypeA + 5; p>
За адресою GetDriveTypeA вставляється 0xE9 і зсув до
точки MGetDriveType; p>
Прапор перехоплення IsHook встановлюється в true. p>
Функція зняття перехоплення повертає все на свої місця: p>
NTSTATUS DTDrvDev:: DTDRV_IOCTL_UNHOOK_Handler (KIrp I) p>
( p>
NTSTATUS status =
STATUS_SUCCESS; p>
I. Information () = 0; p>
if (! IsHook) return status; p>
KIRQL oldirql; p>
KeRaiseIrql (DISPATCH_LEVEL,
& oldirql);// Піднімемо IRQL до DISPATCH_LEVEL p>
RtlCopyMemory (GDT,
NewGetDriveType, 5);// Повернемо заголовок GetDriveTypeA на місце p>
IsHook = false; p>
KeLowerIrql (oldirql);// Повернемо IRQL назад p>
return status; p>
) p>
Даний метод - компроміс між гнучкістю (перехоплення
через таблицю імпорту не вимагає написання драйвера), і потужністю (за потужністю він
практично не поступається підміну коду в DLL), проте він реалізується лише в
Win9X. p>
Висновок
h2>
Отже, перехоплення API-викликів - річ, хоча й досить
складна, але все-таки реалізована (причому різними способами). Методи
перехоплення різні і часто не переносите з однієї версії Windows в іншу. p>
Microsoft прагне зберігати сумісність
програмного забезпечення з старими версіями Windows, але виходить це далеко
не завжди, і аспекти програмування, настільки наближені до низкорівневому
системного програмування, дуже сильно розрізняються для різних версій
Windows. Тому часто доводиться жертвувати ефективністю на шкоду
універсальності - і навпаки. p>
Список літератури h2>
Для підготовки даної роботи були використані
матеріали з сайту http://www.rsdn.ru/
p>