Nem titok, hogy a Windows operációs rendszer egyes folyamatai „kritikusnak” minősülnek – ha valamelyiket leállítja, az operációs rendszer a halál kék képernyőjére kerülhet, és újraindulhat. Az operációs rendszernek ezt a tulajdonságát gyakran használják a vírusírók: ha kritikussá teszed a folyamatodat, akkor nem lehet csak úgy befejezni. A mai cikkben elmondom, hogyan hozhat létre ilyen örökkévaló folyamatokat, és hogyan lehet megfelelően megölni őket, ha szükséges, anélkül, hogy a Windows összeomlik.
Az a tény, hogy szinte lehetetlen megszervezni a folyamat megfelelő védelmét a leállástól, amikor a felhasználó a rendszergazda alatt dolgozik. Különféle módszerek léteznek, de ezek egyáltalán nem vezetnek valódi védelemhez. Használhat például kernel módú illesztőprogramokat, de a 64 bites operációs rendszerekben nem olyan egyszerű leküzdeni a Kernel Patch Protection mechanizmusát. Marad a sámánizmushoz folyamodni a tamburákkal, róluk később beszélünk.
Tartalom
Hogyan hozhat létre kritikus Windows-folyamatot
Nézzük meg, hogyan hozhat létre kritikus folyamatokat a Windows rendszerben, hogyan tudhatja meg, hogy egy folyamat kritikus, és hogyan lehet megölni anélkül, hogy a rendszert BSOD-ban hagyná. Szigorúan véve Ön és én nem egy folyamatot fogunk létrehozni, hanem egy kritikai folyamatot. Végül is a Windows folyamata olyan, mint egy tároló szálak számára, amelyekben maga a kód fut le.
A cikkben közölt összes kódot erősen ajánlott csak virtuális gépen futtatni, mivel egy kritikus folyamat leállítása rendszerszintű meghibásodást okoz, és a rendszer a CRITICAL_PROCESS_DIED kóddal a halál kék képernyőjére lép. és az esetleges adatvesztés. Ezeket a kísérleteket virtuálisan végeztemVirtualBox és használja a Windows 10 LTSB x64 rendszert gazdagépként.
Számos módon segíthetünk egy kritikus folyamat létrehozásában. Az összes módszer az NTAPI (Native Windows API) hívások manipulálásán alapul, amelyek a felhasználói módban végrehajtható legalacsonyabb szintűek. Ezeket a funkciókat az ntoskrnl.exe exportálja. Az ntdll.dll wrapperen keresztül megkapjuk a címüket, és felhívhatjuk őket.
RtlSetProcessIsCritical
Az RtlSetProcessIsCritical az első natív API-függvény, amely segít egy folyamat kritikusként való megjelölésében. A prototípusa így néz ki:
12345678 | NTSYSAPINTSTATUSSTDAPIVCALLTYPERtlsetprocessiscritical( IN BOOLEAN NewValue, OUT PBOOLEAN OldValue OPCIONÁLIS, IN BOOLEAN CheckFlag); |
A funkció működéséhez meg kell szerezni a SeDebugPrivilege jogosultságot a meghívása előtt. Ezt a "szokásos" WinAPI függvényeken keresztül lehet megtenni.
1234567891011121314151617181920212223 | BOOL setPrivilegs(LPCTSTR szPrivName){ TOKEN_PRIVILEGES tp = { 0 }; HANDLE hToken = 0; tp.PrivilegeCount = 1; tp.Privilégiumok[0].Attribútumok = SE_PRIVILEGE_ENABLED; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) std::cout |
A SE_DEBUG_NAME lekéréséhez a függvényhívás a következő lenne:
1 | Privilegák beállítása(SE_DEBUG_NAME); |
A beszerzés másik módja a natív RtlAdjustPrivilege függvény: első paraméterként át kell adnia neki a 20-as értéket (SE_DEBUG_NAME). Természetesen a függvény meghívása előtt a címét dinamikusan kell lekérni az ntdll.dll fájlból. A hívás így fog kinézni:
123 | PBOOLEAN pbEn; RtlAdjustPrivilege(20, TRUE, FALSE, pbEn); |
Tehát a kiváltság megkapta, kezdjük el a fő megvalósításátfunkcionalitás.
123456789 | typedef NTSTATUS(NTAPI *pRtlSetProcessIsCritical)(BOOLEAN bNewValue, BOOLEAN *pbOldValue, BOOLEAN CheckFlag); bool set_proc_critical(){ pRtlSetProcessIsCritical RtlSetProcessIsCritical = (pRtlSetProcessIsCritical)GetProcAddress(meghívja a loadlibraryt((“ntdll.dll”)), “RtlSetProcessIsCritical”); if (NT_SUCCESS(RtlSetProcessIsCritical(TRUE, 0, FALSE))) return TRUE; különben visszatér HAMIS;} |
Itt minden világos: az RtlSetProcessIsCritical függvényt a GetProcAddress/calls to loadlibrary-nek közvetlenül az ntdll.dll-ből történő dinamikus linkelésével kapjuk meg. A számunkra legérdekesebb kódsor ez
1 | if (NT_SUCCESS(RtlSetProcessIsCritical(TRUE, 0, FALSE))) return TRUE; |
Itt adjuk meg a TRUE értéket, hogy elmondjuk az RtlSetProcessIsCritical függvénynek, hogy a folyamat kritikus legyen. Ezzel egyidejűleg ellenőrizzük a visszatérési értékét, és ha sikerült, függvényünk TRUE-t ad vissza. Ha az RtlSetProcessIsCritical-t a FALSE paraméterrel hívja meg, a folyamat többé nem lesz kritikus.
Nézzünk egy kicsit mélyebbre, és értsük meg pontosan, hogyan működik az RtlSetProcessIsCritical függvény. A natív API-ból meghívja az NtSetInformationProcess függvényt, amely hozzáfér a folyamat PEB-éhez, és megváltoztatja benne a ThreadBreakOnTermination mezőt – ez felelős azért, hogy az operációs rendszer kritikusnak tekintse a folyamatunkat.
A folyamatkörnyezet blokkot (Process Environment Block, PEB) az operációs rendszer rendszerbetöltője tölti ki, a folyamat címterében található, és felhasználói módból módosítható. Sok mezőt tartalmaz – például innen tájékozódhat az aktuális modulról, a környezetről, a letöltött modulokról. A PEB struktúrát az fs:[30h] címen közvetlenül kapcsolatba lépve x86 rendszereknél ésgs:[60h] x64-hez.
Így áttérünk a második módszerre, amely megmutatja nekünk a kritikus folyamatok létrehozását.
NtSetInformationProcess
Az NtSetInformationProcess egy natív függvény, amely segít nekünk megváltoztatni a ThreadBreakOnTermination mező értékét a PEB-ben a szükségesre. Ennek a függvénynek a címét dinamikusan kell beszerezni az ntdll.dll fájlból, ahogy azt már megtettük az RtlSetProcessIsCritical esetében. Ez a módszer univerzális, mert ezzel a funkcióval biztosítjuk a kompatibilitást a Windows régebbi verzióival: Az RtlSetProcessIsCritical csak a Windows 8-ban jelent meg.
A megvalósítás a következőképpen fog kinézni.
123456789101112131415 | typedef NTSTATUS(WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); pNtSetInformationProcess NtSetInformationProcess = (pNtSetInformationProcess)GetProcAddress(meghívja a loadlibraryt((“ntdll.dll”)), “NtSetInformationProcess”); bool set_proc_critical(HANDLE hProc){ ULONG count = 1; if (NT_SUCCESS(NtSetInformationProcess(hProc, 0x1D, // ThreadBreakOnTermination a PROCESSINFOCLASS struktúrában & count, sizeof(ULONG)))) return TRUE; különben visszatér HAMIS;} |
Az NtSetInformationProcess függvényben átadjuk a folyamat fogantyúját, amit kritikussá teszünk, és a 0x1D számot, ami a PROCESSINFOCLASS struktúrában ThreadBreakOnTermination-t jelent.
A folyamat kritikusságának ellenőrzése
Most, ha megértjük, hogy az operációs rendszer hogyan határozza meg folyamataink állapotát, és pontosan tudjuk, hogy melyik WinAPI és NTAPI függvényeket használja, könnyen megállapíthatjuk, hogy bármelyik folyamat kritikus-e vagy sem. Szokás szerint több módszert is alkalmazunk egyszerre.
123456789101112 | BOOL ellenőrzési_kritikus(HANDLE hProc){ ULONG count = 0; if(NT_SUCCESS(NtQueryInformationProcess(hProc, 0x1D, // ThreadBreakOnTermination a PROCESSINFOCLASS struktúrában &count, sizeof(ULONG), NULL)) && count) return TRUE; különben visszatér HAMIS;} |
Itt az NtQueryInformationProcess függvénynek adjuk át a minket érdeklő folyamat fogantyúját és a PROCESSINFOCLASS struktúrában azt a mezőt, amelyet ellenőrizni kell. Az NtQueryInformationProcess függvény végrehajtása után ellenőrizzük a count változót, amely eggyel lesz egyenlő, ha a folyamat kritikus.
Az NtQueryInformationProcess függvényen kívül van egy speciálisan az igényeinknek kialakított funkció, az IsProcessCritical. A prototípusa így néz ki.
1 | BOOL IsProcessCritical(HANDLE hProcess, PBOOL Critical); |
Példa a függvény használatára:
1234567 | PBOOL teszt = HAMIS; if(IsProcessCritical(GetCurrentProcess(), teszt)){ if(teszt) std::cout |
Az IsProcessCritical függvény a tesztváltozót TRUE értékre módosítja, ha a folyamat kritikus, vagy FALSE-ra, ha nem.
Következtetések
Ebben a cikkben megpróbáltam elmondani, melyek a kritikus folyamatok, hogyan reagál az operációs rendszer ezek befejezésére. Azt is megtanultuk, hogyan távolítsuk el a kritikussági jelzőt a folyamatból, hogy fájdalommentesen leállítsuk azt, és hogyan ellenőrizhetjük programozottan, hogy kritikus-e. Remélem, hogy a kapott információkat nem fogja pusztító célokra felhasználni. Nos, kivéve a barátokkal kapcsolatos csínytevéseket!