Перехоплення api-функцій в windows nt

Перехоплення api-функцій в windows nt

Системні програмісти, які працювали під MS DOS, прекрасно пам'ятають технологію перехоплення системних переривань, яка дозволяла брати під контроль практично всі процеси, що проходили в улюбленій операційній системі.

Що таке «перехоплення API-функцій»

Розглянемо два методи перехоплення API функцій:

Метод 1. Перехоплення API безпосередній записом у код системної функції.

Прийом полягає в тому, щоб в початок перехоплюваних функції записати команду jmp ваша_функція_двойнік або еквівалентну їй. Затирають байти бажано десь зберегти. Після виклику виправленою функції додатком управління буде передано вашої функції. Вона повинна коректно обробити стек, тобто витягти передані їй параметри і провести необхідні вам дії. Потім, якщо ви збираєтеся викликати оригінальну функцію, необхідно відновити затерті байти на початку оригінальній функції. Викликати її, передавши їй всі необхідні параметри. Після повернення з оригінальної функції, необхідно знову в початок коду функції записати команду переходу на вашу функцію. Повернути управління викликала програмі.

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

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

Розберемо приклад програми (у вигляді DLL-файлу), перехоплює функцію MessageBoxA методом 1.

Для роботи нам будуть потрібні наступні заголовки:

Впровадження коду в чужій процес в Windows NT

Тепер залишилося показати, як вищеописані DLL можна впровадити в процес, обраний в якості жертви експерименту. (Не зайве нагадати, що для нашого прикладу процес-жертва повинен мати вікна зі стандартними повідомленнями MessageBox).

2. За допомогою хуков.

впровадження 1

Розглянемо найбільш ефективний, на наш погляд, спосіб впровадження - перший. Він полягає в записі короткого ділянки машинного коду в пам'ять процесу, який повинен приєднати DLL до цього процесу, запустити її код, після чого Dll зможе виконувати будь-які дії від імені даного процесу. В принципі можна і не приєднувати DLL, а реалізувати потрібні дії у впихається машинному коді, але це буде занадто трудомістким завданням, оскільки всі зміщення для даних втратять сенс, і ви не зможете коректно звернутися до них, не налаштувавши відповідним чином зміщення (морока :( ).

При реалізації даного методу необхідно вказати компілятору вирівнювати структури побайтовой. Інакше структура з машинним кодом буде містити абсолютно не той код, що був запланований.

Загальна схема впровадження:

  • Відкрити процес (OpenProcess).
  • Виділити в ньому пам'ять (VirtualAllocEx - доступно тільки для WinNT).
  • Записати впроваджуваний код в цю пам'ять (WriteProcessMemory).
  • Виконати його (CreateRemoteThread).

Впроваджуваний машинний код повинен (за нашим сценарієм) здійснити такі дії:

  • call LoadLibrary - викликати функцію LoadLibrary з kernel32.dll для завантаження приєднуваної бібліотеки (однієї з розбиралися вище).
  • Call ExitThread - викликати функцію ExitThread з kernel32.dll для коректного завершення даного потоку.

Нижче наведено приклад процедури, яка впроваджує dll з заданим ім'ям в процес із заданим PID (ідентифікатором процесу) (їх можна спостерігати в закладці «процеси» диспетчера задач або отримати за допомогою стандартних API-функцій).

впровадження 2

Другий спосіб впровадження виконуваного коду (через хукі) найбільш простий у використанні. Він заснований на технології хуков, а саме: якщо встановити хук на потік чужого процесу, то, як тільки потік отримає повідомлення, що відповідає заданому типу хука, система автоматично підключить DLL c хуком до даного процесу. Недоліком даного способу в тому, що не можна впровадити DLL в процес, який не має черги повідомлень. Дана DLL буде приєднана до чужого процесу лише до тих пір, поки запущена програма, яка б хук. Як тільки ви завершите цю програму, dll автоматично буде відключена. Перший спосіб позбавлений таких недоліків.

З іншого боку, перший спосіб буде працювати лише в WinNT, через використання функції VirtualAllocEx, яка резервує пам'ять в заданому (відмінному від того, в якому відбувається виклик цієї функції) процесі. Теоретично, дану проблему можна обійти, якщо писати код в деяку частину відображення exe-файлу чужого процесу, наприклад в заголовок DOS, який після завантаження не використовується. Але ОС не завжди дозволяє писати в цю область пам'яті, навіть якщо спробувати змінити дозволу за допомогою VirtualProtextEx.

Є ще й третій спосіб впровадження, але він найбільш небезпечний, оскільки може привести до краху системи. За допомогою даного методу ОС сама впроваджує зазначену dll в усі без винятку процеси операційної системи, навіть захищені. Для реалізації необхідно прописати в реєстрі шляхом Hkey_local_machine \ software \ microsoft \ windowsnt \ currentversion \ windows в ключі AppInit_DLLs повний шлях до своєї dll.

Як налагоджувати такі викрутаси

Більшість програмістів для налагодження своїх програм використовують вбудовані отладчики компіляторів. Вони прості у використанні і задовольняють більшості вимог, що пред'являються при налагодженні. Але якщо деякий програмний код буде впроваджений і виконаний в рамках іншого, стороннього процесу вбудований відладчик використовувати дуже важко. Для цих цілей зручно застосувати системний відладчик SoftIce, який вантажиться раніше операційної системи, працює в нульовому кільці і тому має доступ до будь-яких об'єктів ОС. Обговоримо, як налагоджувати впроваджений код, який виконує перехоплення API функцій всередині стороннього процесу.

Всі види налагодження умовно можна розділити на 3 групи:

  • налагодження коду завантажувача (на асемблері), який, будучи впровадженим в чужій процес, виповнюється як окремий потік і приєднує Dll від імені процесу.
  • налагодження функцій, що виконуються при старті даної Dll. Зазвичай це функції, які виконують підміну коду всередині тіла API-функції для передачі управління функції-двійника.
  • налагодження функцій-двійників, які отримують управління при виклику перехопленої API-функції.

Налагодження коду завантажувача

Отже, є 2 процесу:

  • Процес, який впроваджує код. Позначимо його П1.
  • Процес, в який впроваджують код. Позначимо його П2.

Налагодження функцій, що виконуються при старті DLL

У прикладі це функція InterceptFunctions бібліотеки intercpt.dll, яка викликається з DllMain при приєднанні бібліотеки до процесу і виконує перехоплення функцій.

Для початку необхідно відкомпілювати цю бібліотеку з налагоджування, яку в подальшому SoftIce буде використовувати для виведення інструкцій на мові С. В MS Visual C це робиться так: Project-> Settings-> C / C ++ список Debug Info - там необхідно вибрати тип символьної інформації - Program database for Edit and Continue, а так само Project-> Settings-> Link список Category -> debug, встановити галочку в поле Debug info і вибрати формат налагоджувальної інформації, наприклад Microsoft Format.

Альтернатива - можна просто встановити тип конфігурації проекту, при цьому всі параметри для отримання налагоджувальної інформації будуть встановлені автоматично. Це робиться так. Build-> Set Active Configuration -> Win32 Debug.

Тепер можна приступати до використання SoftIce. Для початку потрібно завантажити в відладчик символьну інформацію з даної Dll, при цьому сама dll ще завантажена не буде. Символьна інформація знадобиться згодом, для установки точок зупину і подання кодів на мові високого рівня. Це робиться за допомогою утиліти Symbol Loader з комплекту SoftIce.

Спочатку необхідно відкрити модуль dll (пункт File-> Open Module).

Потім необхідно завантажити його в відладчик (Module -> Load). При успішному виконанні всіх цих операцій на екрані Symbol Loader має бути щось на зразок цього:

Тепер приступимо до головного. Необхідно поставити крапку зупинки на функцію InterceptFunctions з dll, при цьому сама Dll поки що не приєднана до процесу! Запускаємо SoftIce. Створюємо точку зупину по символьному імені:

Налагодження функцій - двійників, які отримують управління при виклику перехоплених API функцій

Для початку необхідно завантажити символьну інформацію про Dll перехоплення.

В даному прикладі це intercpt.dll.

В утиліті Symbol Loader вибираємо File-> Open Module, потім, в меню Module-> Load, завантажуємо символьну інформацію в відладчик. Dll поки що не приєднана до жодного процесу.

Далі, знаючи імена функцій-двійників ставимо точку зупину на ім'я функції. Наприклад, функція-двійник Intercept_MessageBoxA, яка буде викликатися щоразу, коли відбудеться виклик функції MessageBoxA з програми. Поставимо крапку зупинки на неї - у вікні відладчика набираємо: bpx Intercept_MessageBoxA.

Тепер можна приєднати intercpt.dll до якого-небудь процесу.

Коли цей процес викличе перехоплену функцію MessageboxA, управління буде передано на функцію Intercpt_MessageBoxA і спрацює точка зупину.

тестування

Щоб випробувати все вищесказане у справі, спочатку підшукайте на своєму комп'ютері який-небудь додаток, що має в своєму складі вікна повідомлень типу MessageBox. Піддослідна додаток написано нами самими, воно називається MESS.EXE і виводить один за одним три вікна повідомлень, колаж з яких показано на малюнку:

Потім, відкомпілюйте приклади впроваджуваних DLL, описаних вище. Результат компіляції ми назвали у себе METOD1.DLL і METOD2.DLL.

Відкомпілюйте приклад процедури впровадження цих DLL в код зовнішнього процесу. Для працездатності цієї процедури до неї потрібно додати код головного модуля програми, щось на кшталт:

При запуску цієї програми (назвемо її ATTACH .EXE) в якості параметрів треба буде вказати ідентифікатор процесу, в який ми впроваджуємо свій код, і ім'я DLL, яку слід причепити до зовнішнього процесу.

Скопіюйте всі три отриманих модуля METOD1.DLL, METOD2.DLL, ATTACH .EXE в один каталог (наприклад, C: \ TEST \). Тепер можна приступати до тестування.

Запустіть програму-жертву (в нашому випадку це MESS.EXE). Відкрийте Диспетчер завдань, знайдіть в ньому запущений процес (mess.exe):

і визначте його PID (в нашому випадку PID mess.exe дорівнює тисячу сімдесят шість).

Тепер з командного рядка запустіть програму впровадження коду першої DLL:

В результаті при спробі викликати вікно MessageBox в програмі MESS.EXE ви будете отримувати одне і те ж зображення:

Перехоплення функції API стався!

висновок

"Не такий страшний чорт, як програми MicroSoft ..." Тим не менш, якщо Новомосковсктель вдумливо пропустив через себе викладений матеріал, то побачив, що, як завжди, все геніальне - просто. І навіть така річ, як перехоплення API в Windows NT, не вимагає надскладного програмного коду і може бути реалізована за першим бажанням.