Nu este un secret pentru nimeni faptul că unele dintre procesele sistemului de operare Windows sunt considerate „critice” - dacă opriți unul dintre ele, sistemul de operare poate cădea în ecranul albastru al morții și poate intra într-o repornire. Această proprietate a sistemului de operare este adesea folosită de scriitorii de viruși: dacă vă faceți procesul critic, atunci nu va fi posibil să îl finalizați chiar așa. În articolul de astăzi, vă voi spune cum să creați astfel de procese eterne și cum să le omorâți în mod corespunzător, dacă este necesar, și fără a bloca Windows.
Cert este că este aproape imposibil să se organizeze o bună protecție a procesului de la terminare atunci când utilizatorul lucrează sub administrator. Există diverse metode, dar nu duc deloc la o protecție reală. De exemplu, puteți folosi drivere pentru modul kernel, dar în sistemele de operare pe 64 de biți nu este atât de ușor să depășiți mecanismul Kernel Patch Protection. Ramane sa apelam la samanism cu tamburine, despre ele vom vorbi mai tarziu.
Conţinut
Cum să creați un proces critic Windows
Să vedem cum să creați un proces critic în Windows, cum să știți că un proces este critic și cum să îl omorâți fără a lăsa sistemul în BSOD. Strict vorbind, tu și cu mine nu vom crea un proces, ci un flux critic. La urma urmei, un proces în Windows este ceva ca un container pentru fire în care este executat codul în sine.
Tot codul pe care îl voi furniza în articol este insistent recomandat să fie executat doar într-o mașină virtuală, deoarece încheierea unui proces critic va cauza o eșec la nivelul întregului sistem și sistemul va intra pe ecranul albastru al morții cu codul CRITICAL_PROCESS_DIED și posibilă pierdere de date. Toate aceste experimente le-am efectuat în virtualVirtualBox și utilizați Windows 10 LTSB x64 ca gazdă.
Există mai multe moduri de a ne ajuta să creăm un proces critic. Toate metodele se bazează pe manipularea apelurilor NTAPI (Native Windows API), cele mai „low-level” care pot fi efectuate în modul utilizator. Aceste funcții sunt exportate de ntoskrnl.exe. Prin wrapper-ul ntdll.dll, vom putea obține adresele lor și le vom apela.
RtlSetProcessIsCritical
Prima funcție API nativă care ne va ajuta să marchem un proces ca fiind critic este RtlSetProcessIsCritical. Prototipul său arată astfel:
12345678 | NTSYSAPINTSTATUSSTDAPIVCALLTYPERtlsetprocessiscritical( ÎN BOOLEAN NewValue, OUT PBOOLEAN OldValue OPȚIONAL, ÎN BOOLEAN CheckFlag); |
Pentru ca această funcție să funcționeze, privilegiul SeDebugPrivilege trebuie obținut înainte de a o apela. Acest lucru se poate face prin funcțiile WinAPI „obișnuite”.
1234567891011121314151617181920212223 | BOOL setPrivileges(LPCTSTR szPrivName){ TOKEN_PRIVILEGES tp = { 0 }; HANDLE hToken = 0; tp.PrivilegieCount = 1; tp.Privileges[0].Atribute = SE_PRIVILEGE_ENABLED; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) std::cout |
Apelul de funcție pentru a obține SE_DEBUG_NAME ar fi:
1 | setPrivileges(SE_DEBUG_NAME); |
O altă modalitate de a-l obține este funcția nativă RtlAdjustPrivilege: trebuie să îi transmiteți valoarea 20 (SE_DEBUG_NAME) ca prim parametru. Desigur, înainte de a apela această funcție, adresa acesteia ar trebui să fie obținută dinamic de la ntdll.dll. Apelul va arăta astfel:
123 | PBOOLEAN pbEn; RtlAdjustPrivilege(20, TRUE, FALSE, pbEn); |
Deci, privilegiul a fost primit, să începem implementarea celui principalfuncţionalitate.
123456789 | typedef NTSTATUS(NTAPI *pRtlSetProcessIsCritical)(BOOLEAN bNewValue, BOOLEAN *pbOldValue, BOOLEAN CheckFlag); bool set_proc_critical(){ pRtlSetProcessIsCritical RtlSetProcessIsCritical = (pRtlSetProcessIsCritical)GetProcAddress(apeluri la loadlibrary((“ntdll.dll”)), “RtlSetProcessIsCritical”); dacă (NT_SUCCESS(RtlSetProcessIsCritical(TRUE, 0, FALSE))) returnează TRUE; altfel returnează FALSE;} |
Totul este clar aici: obținem funcția RtlSetProcessIsCritical prin conectarea dinamică folosind GetProcAddress/apeluri la loadlibrary direct din ntdll.dll. Cea mai interesantă linie de cod pentru noi este aceasta
1 | dacă (NT_SUCCESS(RtlSetProcessIsCritical(TRUE, 0, FALSE))) returnează TRUE; |
Aici trecem TRUE pentru a spune funcției RtlSetProcessIsCritical să facă procesul critic. În același timp, verificăm valoarea returnată a acesteia și, dacă reușește, funcția noastră returnează TRUE. Dacă apelați RtlSetProcessIsCritical cu parametrul FALSE, procesul nu va mai fi critic.
Să săpăm puțin mai adânc și să înțelegem exact cum funcționează funcția RtlSetProcessIsCritical. Apelează funcția NtSetInformationProcess din API-ul nativ, care accesează PEB-ul procesului și modifică câmpul ThreadBreakOnTermination din acesta - este responsabil ca sistemul de operare să considere procesul nostru ca fiind critic.
Blocul mediu de proces (Process Environment Block, PEB) este completat de bootloader-ul sistemului de operare, se află în spațiul de adrese al procesului și poate fi modificat din modul utilizator. Conține multe câmpuri — de exemplu, de aici puteți afla informații despre modulul curent, despre mediu, despre modulele descărcate. Structura PEB poate fi obținută contactând-o direct la fs:[30h] pentru sisteme x86 șigs:[60h] pentru x64.
Astfel, trecem la a doua metodă, care ne va arăta crearea proceselor critice.
NtSetInformationProcess
NtSetInformationProcess este o funcție nativă care ne va ajuta să schimbăm valoarea câmpului ThreadBreakOnTermination din PEB cu cea de care avem nevoie. Adresa acestei funcții trebuie obținută dinamic din ntdll.dll așa cum am făcut deja cu RtlSetProcessIsCritical. Această metodă este universală deoarece prin utilizarea acestei funcții asigurăm compatibilitatea cu versiunile mai vechi de Windows: RtlSetProcessIsCritical a apărut doar în Windows 8.
Implementarea va arăta după cum urmează.
123456789101112131415 | typedef NTSTATUS(WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); pNtSetInformationProcess NtSetInformationProcess = (pNtSetInformationProcess)GetProcAddress(apeluri la loadlibrary((“ntdll.dll”)), “NtSetInformationProcess”); bool set_proc_critical(HANDLE hProc){ ULONG count = 1; dacă (NT_SUCCESS(NtSetInformationProcess(hProc, 0x1D, // ThreadBreakOnTermination în structura PROCESSINFOCLASS & count, sizeof(ULONG)))) returnează TRUE; altfel returnează FALSE;} |
În funcția NtSetInformationProcess, trecem handle-ul procesului, pe care îl vom face critic, și numărul 0x1D, care înseamnă ThreadBreakOnTermination în structura PROCESSINFOCLASS.
Verificarea criticității procesului
Acum, înțelegând modul în care sistemul de operare determină starea proceselor noastre și știind exact ce funcții WinAPI și NTAPI folosește, putem determina cu ușurință dacă orice proces este critic sau nu. Ca de obicei, vom folosi mai multe metode simultan.
123456789101112 | BOOL check_critical(HANDLE hProc){ ULONG count = 0; if(NT_SUCCESS(NtQueryInformationProcess(hProc, 0x1D, // ThreadBreakOnTermination în structura PROCESSINFOCLASS &count, sizeof(ULONG), NULL)) && count) returnează TRUE; altfel returnează FALSE;} |
Aici, mânerul procesului care ne interesează și câmpul din structura PROCESSINFOCLASS care trebuie verificat sunt transmise funcției NtQueryInformationProcess. După executarea funcției NtQueryInformationProcess, verificăm variabila count, care va deveni egală cu unu dacă procesul este critic.
Pe lângă funcția NtQueryInformationProcess, există o funcție specială concepută special pentru nevoile noastre, IsProcessCritical. Prototipul ei arată așa.
1 | BOOL IsProcessCritical(HANDLE hProcess, PBOOL Critical); |
Un exemplu de utilizare a acestei funcții:
1234567 | Testul PBOOL = FALSE; if(IsProcessCritical(GetCurrentProcess(), test)){ if(test) std::cout |
Funcția IsProcessCritical va schimba variabila de test în TRUE dacă procesul este critic sau FALSE dacă nu este.
Concluzii
În acest articol, am încercat să spun care sunt procesele critice, cum reacționează sistemul de operare la finalizarea lor. De asemenea, am învățat cum să eliminăm indicatorul de criticitate dintr-un proces pentru a-l opri fără durere și cum să verificăm programatic dacă este critic. Sper că nu veți folosi informațiile primite în scopuri distructive. Ei bine, cu excepția farselor la prieteni!