Як створити і завершити критичний процес Windows

kak sozdat i zavershit kritichnyjj process windows39 Як створити і завершити критичний процес Windows

Не для кого не секрет, що частина процесів операційної системи Windows вважаються «критичними» — якщо завершити один з них, операційна система може випасти в синій екран смерті і піти в перезавантаження. Цим властивістю операційної системи частенько користуються вірусописьменники: якщо зробити свій процес критичним, то його не вдасться завершити просто так. У сьогоднішній статті я розповім, як створювати такі вічні процеси і як їх правильно у разі необхідності прибивати, причому без падіння Windows.

Справа в тому, що організувати хороший захист процесу від завершення, коли користувач працює під адміном, практично неможливо. Існують різні способи, але на справжню захист вони зовсім не тягнуть. Наприклад, можна використовувати драйверів режиму ядра, але в 64-бітових операційних системах не так просто подолати механізм Kernel Patch Protection. Залишається вдаватися до шаманство з бубнами про них ми поговоримо далі.

Зміст

  • Як створити критичний процес Windows
  • RtlSetProcessIsCritical
  • NtSetInformationProcess
  • Перевірка критичності процесу
  • Висновки
  • Як створити критичний процес Windows

    Давайте подивимося, як створити критичний процес в Windows, як дізнатися, що процес є критичним, і як його прибити без догляду системи в BSOD. Строго кажучи, створювати ми з вами будемо не процес, а критичний потік. Адже процес в Вінді — це щось на зразок контейнера для потоків, в яких і виконується сам код.

    Весь код, який я наведу в статті, настійно рекомендую виконувати тільки у віртуальній машині, так як завершення критичного процесу викличе загальносистемний збій і догляд системи в синій екран смерті з кодом CRITICAL_PROCESS_DIED та можливою втратою даних. Всі ці експерименти я проводив у виртуалке VirtualBox і в якості хоста використовувати Windows 10 LTSB x64.

    Існує кілька способів, які допоможуть нам створити критичний процес. Всі методи засновані на маніпуляції викликами NTAPI (Native Windows API), самими «низькорівневими», які можна виконати в режимі користувача. Ці функції експортуються ntoskrnl.exe. Через обгортку ntdll.dll ми зможемо отримати їх адреси і викликати їх.

    RtlSetProcessIsCritical

    Перша функція Native API, яка допоможе нам позначити процес як критичний, — це RtlSetProcessIsCritical. Її прототип виглядає так:

    12345678 NTSYSAPINTSTATUSSTDAPIVCALLTYPERtlsetprocessiscritical( IN BOOLEAN NewValue, OUT PBOOLEAN OldValue OPTIONAL, IN BOOLEAN CheckFlag);

    Щоб ця функція спрацювала, перед її викликом потрібно буде отримати привілей SeDebugPrivilege. Це можна зробити через «звичайні» функції WinAPI.

    1234567891011121314151617181920212223 BOOL setPrivileges(LPCTSTR szPrivName){ TOKEN_PRIVILEGES tp = { 0 }; HANDLE hToken = 0; tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) std::cout << “OpenProcessToken failedn”; if (!LookupPrivilegeValue NULL, szPrivName, &tp.Privileges[0].Luid)) std::cout << “LookupPrivilegeValue failedn”; if (!AdjustTokenPrivileges(hToken, FALSE &tp, sizeof(tp), NULL, NULL) { std::cout << “AdjustTokenPrivileges failedn”; CloseHandle(hToken); return TRUE; } return FALSE;}

    Виклик функції для отримання SE_DEBUG_NAME буде таким:

    1 setPrivileges(SE_DEBUG_NAME);

    Інший спосіб отримати її — це нативна функція RtlAdjustPrivilege: потрібно передати їй в якості першого параметра значення 20 (SE_DEBUG_NAME). Зрозуміло, перед викликом цієї функції її адресу слід отримати з ntdll.dll динамічно. Виклик буде виглядати таким чином:

    123 PBOOLEAN pbEn; RtlAdjustPrivilege(20, TRUE, FALSE, pbEn);

    Отже, привілей отримана, приступаємо до реалізації основної функціональності.

    123456789 typedef NTSTATUS(NTAPI *pRtlSetProcessIsCritical)(BOOLEAN bNewValue, BOOLEAN *pbOldValue, BOOLEAN CheckFlag); bool set_proc_critical(){ pRtlSetProcessIsCritical RtlSetProcessIsCritical = (pRtlSetProcessIsCritical)GetProcAddress(дзвінки на loadlibrary((“ntdll.dll”)), “RtlSetProcessIsCritical”); if (NT_SUCCESS(RtlSetProcessIsCritical(TRUE, 0, FALSE))) return TRUE; else return FALSE;}

    Тут все зрозуміло: функцію RtlSetProcessIsCritical отримуємо динамічної линковкой засобами GetProcAddress/дзвінки на loadlibrary безпосередньо з ntdll.dll. Найцікавіша рядок коду для нас — це

    1 if (NT_SUCCESS(RtlSetProcessIsCritical(TRUE, 0, FALSE))) return TRUE;

    Тут ми передаємо значення TRUE, вказуючи функції RtlSetProcessIsCritical зробити процес критичним. Одночасно ми перевіряємо повертається їй значення, і в разі успіху наша функція повертає TRUE. Якщо викликати RtlSetProcessIsCritical з параметром FALSE, процес перестане бути критичним.

    Давайте копнемо трохи глибше і розберемося, як саме працює функція RtlSetProcessIsCritical. Вона викликає функцію NtSetInformationProcess з Native API, яка звертається до PEB процесу і змінює в ньому полі ThreadBreakOnTermination — він і відповідає за те, щоб операційна система вважала наш процес критичним.

    Блок оточення процесу (Process Environment Block, PEB) заповнюється завантажувачем операційної системи, знаходиться в адресному просторі процесу і може бути модифікований з режиму usermode. Він містить багато полів — наприклад, звідси можна дізнатися інформацію про поточний модуль, про оточення, про завантажених модулях. Отримати структуру PEB можна, звернувшись до неї безпосередньо за адресою fs:[30h] для x86-систем і gs:[60h] для x64.

    Таким чином, ми переходимо до другого методу, який продемонструє нам створення критичних процесів.

    NtSetInformationProcess

    NtSetInformationProcess — це нативна функція, яка допоможе нам змінити значення поля ThreadBreakOnTermination в PEB на потрібне нам. Адреса цієї функції необхідно отримати динамічно з ntdll.dll як ми вже робили з RtlSetProcessIsCritical. Цей метод універсальний, оскільки, використовуючи цю функцію, ми забезпечуємо сумісність з більш старими версіями Windows: RtlSetProcessIsCritical з’явилася тільки в Windows 8.

    Реалізація буде виглядати наступним чином.

    123456789101112131415 typedef NTSTATUS(WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); pNtSetInformationProcess NtSetInformationProcess = (pNtSetInformationProcess)GetProcAddress(дзвінки на loadlibrary((“ntdll.dll”)), “NtSetInformationProcess”); bool set_proc_critical(HANDLE hProc){ ULONG count = 1; if (NT_SUCCESS(NtSetInformationProcess(hProc, 0x1D, // ThreadBreakOnTermination в структурі PROCESSINFOCLASS &count, sizeof(ULONG)))) return TRUE; else return FALSE;}

    У функцію NtSetInformationProcess передаємо хендл процесу, який будемо робити критичним, і число 0x1D, яке означає ThreadBreakOnTermination в структурі PROCESSINFOCLASS.

    Перевірка критичності процесу

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

    123456789101112 BOOL check_critical(HANDLE hProc){ ULONG count = 0; if(NT_SUCCESS(NtQueryInformationProcess (hProc, 0x1D, // ThreadBreakOnTermination в структурі PROCESSINFOCLASS &count, sizeof(ULONG), NULL)) && count) return TRUE; else return FALSE;}

    Тут у функцію NtQueryInformationProcess передається хендл нас цікавить процесу і поле в структурі PROCESSINFOCLASS, який потрібно перевірити. Після виконання функції NtQueryInformationProcess перевіряємо змінну count, яка стане дорівнює одиниці, якщо процес критичний.

    Крім функції NtQueryInformationProcess, є спеціальна функція, призначена саме для наших потреб, — IsProcessCritical. Її прототип виглядає таким чином.

    1 BOOL IsProcessCritical(HANDLE hProcess, PBOOL Critical);

    Приклад використання цієї функції:

    1234567 PBOOL test = FALSE; if(IsProcessCritical(GetCurrentProcess(), test)){ if(test) std::cout << “Critical processn”; else std::cout << “NOT critical processn”;}

    Функція IsProcessCritical змінить змінну test на TRUE, якщо процес критичний, або залишить FALSE в тому випадку, якщо це не так.

    Висновки

    У цій статті я постарався розповісти, що таке критичні процеси, як ОС реагує на їх завершення. Також ми дізналися, як зняти прапор критичності з процесу, щоб його безболісно завершити, і як програмно перевірити, чи є критичним. Я сподіваюся, що ви не будете застосовувати отриману інформацію в деструктивних цілях. Ну, хіба що для жартів над друзями!

    Сподобалася стаття? Поділитися з друзями:
    Всезнайко - Корисні поради