Перехоплення API функцій у Delphi за допомогою сплайсингу - все про IT та програмування

Written on 26 Січня 2009 . Posted in Delphi

У цій статті я розповім найдієвішу методику перехоплення API функцій - це сплайсинг. Сплайсинг це заміна коду функції. Звичайно, є інший метод перехоплення - це редагування таблиці імпорту програми, редагування таблиці експорту. Розповідатиму по порядку.

Коли ви пишете у своєму додатку так

Якщо трохи переробити цю формулу, то вона виглядатиме так Тепер при кожному виклику цільової функції завжди буде передаватися управління нашому обробнику. А як тепер викликати оригінальну функцію? При встановленні перехоплення треба зберігати перші 5 байт функції. Для виклику оригіналу треба відновлювати початок функції та викликати її, потім знову встановлювати перехоплення. Оголосимо структуру в якій зберігатимемо перші 5 байт функції: Поле Address фактично в цій структурі не потрібен (він просто нема до чого), поле потрібно тільки для того, щоб було зручніше знімати перехоплення. Назвемо цю структуру "міст" до старої функції.

Тепер напишемо функцію, яка встановлюватиме перехоплення:

Ми спочатку встановлюємо атрибути доступу до коду функції, щоб можна було його переписувати. Потім обчислюємо значення для стрибка. Спочатку зберігаємо початок функції запис, потім переписуємо початок функції. Насамкінець встановлюємо старі атрибути доступу.

Тепер напишемо функцію, яка зніматиме перехоплення:

Я думаю, що тут все зрозуміло - просто відновлюємо початок функції, що перехоплюється. Адреса перехоплюваної функції беремо зі структури покажчик, яку передається функції як єдиного параметра.

Тепер напишемо функцію, яка встановлюватиме перехоплення на ім'я функції.

Їдемо далі. Описані вище функції можуть перехоплювати функції лише у поточному процесі. А як нам перехоплювати функції інших процесах? Найбільш простий метод це засунути перехоплювач функції в DLL і код бібліотечної функції встановлювати перехоплення, якщо DLL завантажується в процес і знімати перехоплення, якщо вона вивантажується. Тут ще одна проблема: як змусити інший процес завантажити нашу DLL. Найпростіше рішення це створення віддалених потоків. Тепер все по порядку.

Віддалений потік створюється функцією CreateRemoteThread.

Функція потоку повинна мати такі атрибути

Функція має лише один параметр – це звичайний покажчик. Тепер проаналізуємо ситуацію ... Думаємо ... Ворушимо мізками ... Функція LoadLibraryA має такі ж атрибути. Вона приймає покажчик на перший символ імені файлу DLL (рядок має закінчуватися символом #0). Отже, функція LoadLibraryA повністю підходить для того, щоб вона могла виступати як функція потоку. Так як вона приймає покажчик на рядок у своєму процесі, нам треба буде записати в пам'ять чужого процесу наш рядок та ім'ям файлу DLL. Це робиться функцією WriteProcessMemory. Ось її опис

Таким чином, ми завантажили свою DLL у чужий процес. Взагалі впровадження свого коду в чужі процеси це зовсім інша історія і вимагає написання окремої статті. Наведений вище приклад це найпростіший спосіб впровадитися в чужий процес. Звичайному процесу не дозволяється зміна пам'яті системних процесів, таких як winlogon.exe, lsass.exe, smss.exe, csrss.exe та ін. Для цього потрібний привілей SeDebugPrivilege. У доданих до статті вихідниках є функція EnableDebugPrivilege, яка включає цей привілей для поточного процесу.

Йдемо далі. Тепер у нас ми навчилися завантажувати своюDLL у чужий процес. Але для належного ефекту нам треба перехоплювати DLL у всіх процесах системи. Але як це зробити? Можна просто перераховувати процеси через ToolHelp32 та завантажувати свою DLL у кожен знайдений процес. Але не прийнятно, оскільки у новостворених процесах функції не будуть перехоплені. Але можна щомиті перераховувати процеси, коротше це теж неприйнятно і дуже довга історія. Найпростіший метод це скористатися тим, що надає нам механізм хуків. Коли ми ставимо будь-який глобальний хук за допомогою функції SetWindowsHookEx, то DLL, в якій знаходиться функція обробник хука, завантажується у всі процеси, які отримують повідомлення від системи через функції GetMessage та PeekMessage. Каркас DLL з перехопленням функцій виглядатиме приблизно так

Щоб встановити перехоплення на API функції у всіх процесах (у всіх процесах GUI) досить просто завантажити нашу DLL. Достатньо написати такий код:

Також слід пам'ятати один запобіжний засіб при встановленні та знятті перехоплення на функції: треба зупинити всі інші потоки поточного процесу, так як під час установки перехоплення інший потік може викликати потрібну функцію і це призведе до непередбачуваних наслідків.