Новости

Введення в перехоплення API для системи команд x86

  1. Короткий огляд
  2. Вступ
  3. Базовий перехоплення API
  4. трампліни
  5. Просунуті методи перехоплення
  6. Метод I: вставка Nop в початок
  7. Метод II: Push / Retn
  8. Метод III: Числа з плаваючою комою
  9. Метод IV: MMX / SSE
  10. Метод V: непрямий перехід
  11. Метод VI: інструкція Call
  12. інші методи
  13. Інші методи I: Hotpatching
  14. Інші методи II: Методи для класів C ++
  15. побудова трамплінів
  16. багатошарові перехоплення
  17. Запобігання рекурсії перехоплення
  18. демонстрація
  19. ПОСИЛАННЯ

У даній статті представлені способи перехоплення API для системи команд x86.

Автор: Jurriaan Bremer

Короткий огляд

В даній статті представлено кілька способів перехоплення API для системи команд x86.

У наступних параграфах ми введемо читача в перехоплення API: то, що ми можемо зробити з його допомогою, то, чому він корисний, і в його основні форми. Після представлення простих методів перехоплення API ми охопимо деякі менш відомі і використовуються (якщо взагалі використовуються) методи, які одного дня можуть стати в нагоді, а також деякі техніки, які варто мати на увазі при використанні будь-яких методів перехоплення.

На завершення ми познайомимо читача з кодом, який щодня використовується для аналізу тисяч зразків шкідливого ПЗ.

Вступ

Наступний фрагмент тексту - коротке пояснення перехоплення, взяте з Вікіпедії [1].

У програмуванні термін перехоплення (hooking) охоплює ряд методів, використовуваних для зміни або доповнення поведінки операційної системи, додатків або інших програмних компонентів шляхом перехоплення викликів функцій, повідомлень або подій, що передаються між програмними компонентами. Код, який обробляє подібні перехоплені виклики функцій, події або повідомлення, називається перехватчиком (hook).

Отже, ми встановили, що перехоплення дозволяє нам змінювати поведінку існуючих програм. Перед тим, як рушити далі, ми представимо короткий список прикладів використання перехоплення.

  • Профілювання: Наскільки швидко виконуються певні виклики функцій?
  • Моніторинг: Послали ми коректні параметри функції X?
  • ..?

Більш повний список прикладів використання перехоплення функцій можна знайти тут [1] [2].

Даний список повинен свідчити про корисність перехоплення функцій. Тепер настав час перейти до перехоплення функцій як такого.

Просимо читача помітити, що даний пост не охоплює точки зупину, перехоплення IAT і т. Д., Так як ці теми заслуговують окремого поста.

Базовий перехоплення API

Найбільш простий спосіб перехоплення - вставка інструкції переходу (jump). Як ви вже знаєте (а може і не знаєте), інструкції системи команд x86 мають змінну довжину (тобто, інструкція може мати довжину в байтах від 1 до 16). Інструкція безумовного переходу має довжину 5 байтів. Один байт представляє опкод, що залишилися чотири представляють відносне 32-бітове зсув. (Відзначимо, що існує також інструкція безумовного переходу, яка містить лише восьмібітного відносне зміщення, але в даному прикладі використовувати її ми не будемо).

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

Припустимо, що функція A розташована за адресою 0 × 401000, а функція B - за адресою 0 × 401 800. Далі ми визначимо необхідну відносне зміщення. Різниця між адресами даних функцій становить 0 × 800 байт, і ми хочемо перейти з функції A в функцію B, так що нам поки не потрібно турбуватися про негативні зсувах.

Далі слід хитрий момент. Припустимо, що ми вже записали нашу інструкцію переходу за адресою 0 × 401000 (функції A), і що дана інструкція виконується. CPU при цьому зробить наступне: спочатку він додасть довжину інструкції до Покажчику на Інструкцію [3] (або Програмного Лічильнику). Довжина інструкції переходу дорівнює п'яти байтам, як ми встановили раніше. Після цього до Покажчику на Інструкцію додається відносне зміщення. Іншими словами, CPU обчислює нове значення Маркера на Інструкцію наступним чином:

instruction_pointer = instruction_pointer + 5 + relative_offset;

Тому для обчислення відносного зміщення нам потрібно переписати формулу в наступному вигляді:

relative_offset = function_B - function_A - 5;

Ми віднімаємо 5, оскільки це довжина інструкції переходу, яку CPU додає при запуску даної інструкції, а function_A is віднімається з function_B, так як це відносний перехід. Різниця між адресами функцій дорівнює, як ми пам'ятаємо, 0 × 800 байтам. (Наприклад, якщо ми забудемо відняти function_A, то CPU перейде за адресою 0 × 401 800 + 0 × 401000 + 5, що, очевидно, небажано).

На мові асемблера перенаправлення з функції A в функцію B буде виглядати приблизно так.

До впровадження перехоплювача на початку функції можна бачити кілька вихідних інструкцій. Після впровадження вони перезаписувати інструкцією jmp. Перші три вихідні інструкції займають 6 байт разом узяті (т. Е. Можна бачити, що інструкція push ebx розташована за адресою 0 × 401 006). Наша інструкція переходу використовує тільки п'ять байтів, що залишає нам один додатковий байт. Ми перезаписали цей байт інструкцією nop (інструкція, що не робить нічого).

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

трампліни

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

Наступний фрагмент коду показує простий приклад використання трампліну для запуску вихідної функції з перехопленої функції. У ньому function_A_trampoline позначає трамплін до функції A (функції, яка була перехоплена).

// this is the hooked function void function_A (int value, int value2);
// this is the Trampoline with which we can call
// function_A without executing the hook void (* function_A_trampoline) (int value, int value2);
// this is the hooking function which is executed
// when function_A is called void function_B (int value, int value2) {
// alter arguments and execute the original function function_A_trampoline (value + 1, value2 + 2); }

У прикладі перехоплення, який ми щойно обговорили, ми перезаписали перші п'ять байт функції A. Щоб запустити вихідну функцію без перехоплювача, нам знадобиться запустити байти, які ми перезаписали при установці перехоплювача, а потім перейти за адресою, на кілька байт більшого, ніж адреса функції A (щоб пропустити код перехоплювача). Це в точності те, що відбувається в вищевказаному фрагменті коду, але в коді на мові C ви цього не бачите через відносно високою абстракції. У будь-якому випадку, картинки говорять більше, ніж тисяча слів, так що ... ось зображення, яке показує пристрій трампліну.

ось зображення, яке показує пристрій трампліну

На зображенні ви бачите наступний потік виконання: запускається функція A, виконується перехоплювач, передаючи тим самим управління функції B. Функція B виконує якісь дії, але на адресу 0 × 401 820 вона хоче виконати вихідну версію функції A (без перехоплювача), де і приходить на допомога трамплін. Про трамплін можна написати безліч слів, але одне зображення пояснює його повністю. Трамплін складається з двох частин: вихідних інструкцій і переходу на ту частину функції A, яка слідує за перехоплювачем. Якщо ви повернетеся до зображення з розділу Базовий перехоплення API, ви побачите, що перезаписані перехватчиком інструкції тепер розташовуються в трампліні. Перехід в трампліні обчислюється за вказаною раніше формулою, однак, в даному випадку адреси і зсуву трохи відрізняються, так що формула приймає наступний вигляд:

relative_offset = (function_A_trampoline + 6) - (function_A + 6) - 5;

Відзначимо, що ми переходимо з адреси 0 × 402 006 (function_A_trampoline + 6) на 0 × 401 006 (function_A + 6). Дані адреси можна перевірити по розглянутому раніше зображенню. Нічого особливого, крім того факту, що у нас вийшло від'ємне зміщення. Але це не завдасть нам проблем (CPU зробить всю брудну роботу по коректному поданням негативного відносного зсуву).

Ось загалом-то і все, що потрібно знати про базову перехопленні API, так що далі ми обговоримо деякі більш просунуті методи. В одному з наступних розділів, Побудова трамплінів, ми більш детально розглянемо процес створення трамплінів.

Просунуті методи перехоплення

Ми розглянули базовий метод перехоплення API і> трампліни. Однак, оскільки базовий метод перехоплення такий простий (всього лише вставка інструкції переходу), його дуже легко виявити. Тому ми також обговоримо кілька більш просунутих методів. Крім цього, ми також введемо читача в перехоплення методів класів C ++.

Виявлення перехоплення для прикладу, розглянутого в розділі «Базовий перехоплення API», можна здійснити так:

if (* function_A == 0xe9) {printf ( "Hook detected in function A. \ n"); }

Суть в тому, що 0xe9 - опкод інструкції безумовного переходу з 32-бітовим відносним зсувом. ПО, яке ми перехоплюємо, може виявити або не виявити подібний перехоплювач. У будь-якому випадку, далі ми обговоримо різні методи, які намагаються обійти подібні алгоритми виявлення. (Зауважимо, що програми на кшталт GMER [4] виявляють всі типи методів перехоплення, так як звіряють віртуальну пам'ять з фізичним чином на диску).

Метод I: вставка Nop в початок

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

По суті, замість запису за адресою функції A, наприклад, інструкції переходу, ми спочатку запишемо інструкцію nop (не робити нічого), за якою вже піде функція переходу. Застосовуючи цю техніку, майте на увазі, що інструкція переходу тепер буде розташовуватися за адресою 0 × 401 001 (function_A + 1), і це змінить відносне зміщення на одиницю.

Ось зображення, яка ілюструє цю техніку.

Оскільки перша інструкція функції A тепер - nop (а не jmp), щоб виявити перехоплювач, нам потрібно переписати метод виявлення так само:

unsigned char * addr = function_A; while (* addr == 0x90) addr ++; if (* addr == 0xe9) {
printf ( "Hook detected in function A. \ n"); }

По суті, він пропускає будь-яку кількість послідовних інструкцій nop, що мають опкод 0 × 90, і перевіряє присутність інструкції переходу після всього ланцюжка nop-ів.

Метод II: Push / Retn

Інструкція push кладе 32-бітове значення в стек, а retn виштовхує 32-бітову адресу з стека в Покажчик на Інструкцію (іншими словами, вона починає виконання інструкцій, розташованих за адресою, який лежить на вершині стека).

Цей метод займає 6 байт і виглядає наступним чином. Відзначимо, що інструкція push приймає абсолютний адресу, а не відносний.

Відзначимо, що інструкція push приймає абсолютний адресу, а не відносний

Виявлення даного методу може виглядати так, як показано нижче. Однак, врахуйте, що приміщення nop-інструкцій в початок або між інструкціями push і retn не дозволить даному коду виявити перехоплювач.

unsigned char * addr = function_A; if (* addr == 0x68 && addr [5] == 0xc3) {
printf ( "Hook detected in function A. \ n"); }

Як ви напевно вже здогадалися, 0 × 68 - опкод інструкції push, а 0xc3 - опкод інструкції retn.

Метод III: Числа з плаваючою комою

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

Даний приклад схожий на метод push / retn: ми поміщаємо в стек фіктивне значення, яке потім Перезаписуємо справжнім адресою, після чого виконуємо retn.

Примітна ж техніка тим, що замість зберігання адреси переходу у вигляді 32-бітного значення ми зберігаємо його у вигляді 64-бітного числа з плаваючою комою. Далі ми зчитуємо його інструкцією fld і перетворимо в 32-бітове значення інструкцією fistp.

Наступне зображення демонструє дану техніку. Перехоплювач має розмір 11 байт, що трохи більше, ніж для попередніх методів, але це не так страшно. Також зауважимо, що floating_point - покажчик на 64-бітове число з плаваючою комою, що дорівнює адресою нашої перехоплює функції (функції B).

Також зауважимо, що floating_point - покажчик на 64-бітове число з плаваючою комою, що дорівнює адресою нашої перехоплює функції (функції B)

Отримати потрібне значення з плаваючою точкою досить просто:

double floating_point_value = (double) function_B;

Функція B тут, як і в інших прикладах, - наша перехоплюють функція.

Виклик вихідної функції робиться через трамплін, як і в інших методах. (Трамплін в даному випадку повинен містити як мінімум інструкції, які знаходяться в перших одинадцяти байтах функції).

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

Метод IV: MMX / SSE

Дані техніки схожі на перехоплення з використанням чисел з плаваючою комою. Однак, замість використання чисел з плаваючою комою, тут ми будемо використовувати розширення MMX / SSE системи команд x86.

Обидві техніки використовують, як і метод з плаваючою комою, інструкції push / retn. Перший метод використовує MMX-інструкції, зокрема інструкцію movd. Вона, як і інструкція fistp, дозволяє прочитати значення з пам'яті та зберегти значення в стеку. Другий метод з SSE-інструкціями також використовує інструкцію movd. Єдина відмінність між цими двома методами в тому, що MMX-інструкції оперують 64-бітними регістрами, тоді як SSE оперують 128-бітними регістрами. (Хоча в нашому випадку це не має значення, оскільки інструкція movd дозволяє читати і записувати 32-бітові значення).

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

Метод V: непрямий перехід

Непрямий перехід - це перехід за адресою, який лежить в пам'яті за вказаною адресою. У розділі Базовий перехоплення API ми розглянули прямий відносний перехід. Непрямі переходи більше схожі на метод Push / Retn.

Непрямий перехід має довжину 6 байтів, і приклад перехоплення виглядає так:

Непрямий перехід має довжину 6 байтів, і приклад перехоплення виглядає так:

Зауважимо, що hook_func_ptr позначає адресу, за якою знаходиться адресу нашої перехоплює функції (т. Е. B).

Метод VI: інструкція Call

Тоді як всі попередні методи перехоплення здійснювали перехід прямо в перехоплює функцію (т. Е. Функцію B), для даного методу перехоплення потрібен додатковий крок. Це через те, що інструкція call переходить по заданому адресою після приміщення адреси повернення в стек (адреса повернення дорівнює поточному значенню покажчика на інструкцію, збільшеному на довжину інструкції call).

Оскільки в стеці тепер з'явився додатковий адресу повернення, спочатку нам потрібно виштовхнути цю адресу з стека, інакше стек буде пошкоджений (перехоплюють функція вважає з стека некоректні аргументи, т. К. Покажчик стека буде неправильним). Ми виштовхни цю адресу з стека шляхом додавання 4 до покажчика стека (коли адреса кладеться в стек, покажчик стека спочатку зменшується на 4, потім цю адресу записується туди, куди вказує оновлений покажчик стека). Після виштовхування адреси з стека ми переходимо на перехоплює функцію.

Дана техніка працює як для прямого, так і для непрямого варіанту інструкції call. Далі слід малюнок, що ілюструє розглянутий метод.

інші методи

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

Інші методи I: Hotpatching

Це метод, специфічний для програм, скомпільованих компілятором Microsoft Visual C ++ Compiler з включеним прапором Hotpatching (це справедливо для безлічі dll начебто user32.dll).

Якщо функція допускає так званий Hotpatch, вона вже певним чином підготовлена ​​для перехоплення. Першою інструкцією функції в цьому випадку буде mov edi, edi (два байта довжиною), а перед власне функцією розташовані 5 nop-інструкцій. Це дозволяє розмістити інструкцію близького переходу (ту, що має довжину два байта і приймає 8-бітове відносне зміщення) за адресою власне функції (перезаписавши інструкцію mov edi, edi) і звичайну інструкцію переходу з 32-бітовим відносним зсувом на місце nop-інструкцій.

Ось і все, що стосується даної техніки. Відзначимо, що замість хотпатчінга подібної функції можливо також виконати перехоплення одним з раніше розглянутих методів, помістивши перехоплювач за адресою function + 2, де два означає розмір інструкції, вставленої компілятором для хотпатчінга (В цьому випадку залишається можливість застосування Hotpatch, незважаючи на установку перехоплення одним з наших улюблених методів).

Ось зображення, що ілюструє хотпатчінг. Перехоплюваних функцією тут є MessageBoxA, а перехоплює - hook_MessageBoxA (т. Е. MessageBoxA = функція A, hook_MessageBoxA = функція B).

MessageBoxA = функція A, hook_MessageBoxA = функція B)

Інші методи II: Методи для класів C ++

Ця техніка відноситься до перехоплення методів класів C ++. Методи класів C ++ використовують так звану угоду виклику __thiscall [5] (принаймні в Windows).

Угода виклику __thiscall зберігає покажчик на інформацію про об'єкт (до якого можна звернутися в методах класу через змінну this) в регістрі ecx перед викликом методу класу. Іншими словами, якщо ми хочемо перехопити метод класу, необхідно особливу увагу.

У подальшому обговоренні ми будемо використовувати наступний фрагмент коду, який визначає функцію (function_A), яку ми хочемо перехопити.

class A {public: void function_A (int value, int value2, int value3)
{Printf ( "value:% d% d% d \ n", value, value2, value3); }};

Так як ми хочемо перехоплювати функції C ++ звичайними функціями C, нам потрібно, щоб першим параметром перехоплює функції був покажчик this. Приклад перехоплює функції і трамплін (який ми обговоримо пізніше) виглядають наступним чином. Відзначимо, що замість this використовується ім'я змінної self через те, що this - зарезервоване в C ++ ключове слово.

void (* function_A_trampoline) (void * self, int value, int value2, int value3);
void function_B (void * self, int value, int value2, int value3)
{Return function_A_trampoline (self, value + 1, value + 2, value + 3); }

Щоб мати можлівість перехоплюваті методи класів C ++ зі звичайний C-функцій, нам придется Изменить стек, оскількі нам нужно Вставити в него покажчик this. Наступний приклад представляет розмітку стека при віконанні Функції A (ліворуч) и розмітку, якові ми Хочемо мати при віконанні Функції B (перехоплює Функції). Відзначимо, що ecx містить значення покажчика this, а вершина стека - це адреса, за якою розташований return_address.

На щастя для нас, ми можемо вставити покажчик this всього двома інструкціями - досить лаконічно. Перший крок - обмін значень покажчика this (регістра ecx) і вершини стека (return_address). Після цього обміну на вершині стека виявиться покажчик this, а в регістрі ecx - return_address. Тепер ми можемо просто покласти значення регістра ecx на стек, і стек стане виглядати в точності так, як ми хотіли (див. Зображення).

Ось ассемблерного уявлення перехоплення методу класу C ++.

Ось ассемблерного уявлення перехоплення методу класу C ++

Ми розглянули частину, що стосується перехоплення. Однак, нам також потрібно адаптувати наш трамплін, оскільки трамплін приймає покажчик this як перший аргумент. Стек, який ми маємо і стек, до якого ми хочемо прийти, виглядають наступним чином. (Очевидно, що значення this після всіх маніпуляцій повинно знову опинитися в регістрі ecx).

(Очевидно, що значення this після всіх маніпуляцій повинно знову опинитися в регістрі ecx)

Ми виконуємо дії, в точності зворотні тим, які робили в ході перехоплення функції. Спочатку ми виштовхуємо з стека return_address, так що стек тепер вказує на this, а регістр ecx приймає значення return_address. Тепер ми обмінюємо значення на вершині стека з регістром ecx, після чого стік приймає бажаний вид, а в регістр ecx завантажується значення покажчика this. Наступне зображення ілюструє трамплін, хоча інструкції з функції A на ньому не показані (т. Е. Це зображення лише показує що особливого в трампліні до методу класу C ++, а не те, що ми раніше обговорювали в розділі трампліни ).

Це зображення лише показує що особливого в трампліні до методу класу C ++, а не те, що ми раніше обговорювали в розділі   трампліни   )

побудова трамплінів

В одному з попередніх розділів, Трампліни, ми обговорювали, як повинні будуватися трампліни. В даному розділі ми обговоримо деякі граничні випадки, які потрібно брати до уваги при побудові трамплінів.

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

Однак, остання інструкція в цих п'яти байтах може виявитися досить довгою, наприклад, розтягнутися від третього до шостого байта, що має місце в використаному раніше прикладі (далі ще раз приведено ілюструє його зображення)

Як ви можете бачити, третя інструкція використовує шостий байт, через це ми не можемо просто скопіювати перші п'ять байт, нам доведеться скопіювати інструкцію цілком. Щоб зробити це ми використовуємо так званий LDE (Length Disassembler Engine). LDE здатний обчислювати довжину інструкції (зазвичай шляхом махінацій з визначеною таблицею, що містить інформацію про кожного опкоде).

Оскільки LDE може обчислити для нас довжину інструкції, ми можемо просто продовжувати отримувати довжини інструкцій, поки не знайдемо досить інструкцій для покриття перших п'яти байт. На вищевказаному малюнку видно, що нам достатньо трьох інструкцій, щоб набрати довжину в 6 байт, достатню для трампліну.

Це була проста частина, оскільки інструкції, які ми знайшли, не містили розгалужень. Однак, будь-які інструкції переходу, виклику і повернення також вимагають особливої ​​уваги. Для початку, інструкції виклику і переходу повинні вказувати в трампліні на ту ж адресу, на який вони вказували в вихідної функції. Втім, цього нескладно домогтися за допомогою двох формул (однієї для отримання адреси інструкції виклику або переходу і інший для обчислення відносного зміщення для інструкції виклику або переходу, розміщеної в трампліні). Відзначимо також, що будь-які переходи, які мають 8-бітове відносне зміщення, повинні бути перетворені в інструкції переходу з 32-бітними відносними зсувами (навряд чи відносне зміщення між трампліном і вихідною функцією буде в межах, що задаються 8-бітовим зміщенням).

Нарешті, іноді буває, що для нашого методу перехоплення просто не вистачає місця. Це трапляється при наявності інструкції безумовного переходу або інструкції повернення. Таке відбувається, коли ми потрапляємо в кінець функції (т. Е. Нам потрібно переписати байти, що знаходяться вже після інструкції повернення). Після інструкції повернення, наприклад, може розташовуватися абсолютно інша функція, вміст якої переписувати ми не збиралися і в загальному випадку не можемо. Те ж справедливо і для ситуації, в якій потрібно переписати інструкцію безумовного переходу: Функція не буде продовжувати виконуватися після інструкції переходу, так що перезапис ділянки пам'яті після даної інструкції може призвести до невизначеним наслідків.

багатошарові перехоплення

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

Запобігання рекурсії перехоплення

І, наостанок, декілька міркувань на рахунок рекурсії перехоплення. Іноді буває, що в перехоплює функції (функції B в нашому прикладі) використовуються функції на кшталт логування в файл. Однак, що якщо така функція перехоплена тим же або іншим нашим перехватчиком? Це може привести до рекурсивному перехоплення, т. Е. Перехоплювач може почати циклічно перехоплювати себе, або утворюється цикл з кількох перехоплювачів. Це не те, що ми хочемо, та ще й дуже дратує.

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

Відзначимо також, що подібний лічильник перехоплень повинен бути специфічним для потоку. Для вирішення цього завдання, код, наведений в розділі «Демонстрація», зберігає лічильник в сегменті, на який вказує регістр fs [6] (даний розділ позначає Блок Інформації Потоку, т. Е. Специфічну для потоку інформацію).

демонстрація

Для демонстрації практичного застосування міститься в даній статті інформації ми представляємо читачеві Cuckoo Sandbox , Систему для автоматичного аналізу шкідливих програм. У відносно новому компоненті аналізатора Cuckoo ми застосували техніки з даного поста. Наприклад, хоча він використовує прямий 32-бітний перехід, він має досить потужним двигуном для побудови трамплінів і підтримує запобігання рекурсії перехоплення.

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

ПОСИЛАННЯ

  1. Hooking - Wikipedia
  2. API Hooking Revealed - CodeProject
  3. Instruction Pointer / Program Counter - Wikipedia
  4. Rootkit Detector - GMER
  5. Thiscall Calling Convention - Nynaeve
  6. Win32 Thread Information Block - Wikipedia

Профілювання: Наскільки швидко виконуються певні виклики функцій?
Моніторинг: Послали ми коректні параметри функції X?
Отже, якщо у нас є дві функції - A і B, як ми переспрямуємо виконання з функції A в функцію B?
Однак, що якщо ми хочемо виконати вихідну функцію A, не виконуючи перехоплювач?
Однак, що якщо така функція перехоплена тим же або іншим нашим перехватчиком?

Уважаемые партнеры, если Вас заинтересовала наша продукция, мы готовы с Вами сотрудничать. Вам необходимо заполнить эту форму и отправить нам. Наши менеджеры в оперативном режиме обработают Вашу заявку, свяжутся с Вами и ответят на все интересующее Вас вопросы.

Или позвоните нам по телефонам: (048) 823-25-64

Организация (обязательно) *

Адрес доставки

Объем

Как с вами связаться:

Имя

Телефон (обязательно) *

Мобильный телефон

Ваш E-Mail

Дополнительная информация: