Windows 8

Periodic, de regulă, în cea de-a doua zi de miercuri a lunii, puteți auzi poveștile despre faptul că Windows, după următoarea actualizare, se oprește de încărcare, arătând ecranul albastru al morții. În majoritatea cazurilor, cauza acestei situații este fie un rootkit, fie un software de sistem specific, care manipulează în mod frivol sistemele interne de operare. Ei dau vina, desigur, oricum o actualizare, pentru că "totul a lucrat înainte de el".







Windows 8

De la început, nu a fost poziționat ca un mecanism de protecție împotriva rootkit-urilor, deoarece rootkit-urile lucrează în kernel cu aceleași privilegii și, prin urmare, PatchGuard poate fi făcut inofensiv. Acesta este mai mult un filtru care elimină dezvoltatorii leneși de rootkit-uri.

Ce protejează PatchGuard?

Cel mai popular loc pentru modificarea kernel-ului a fost tabelul de apeluri sistem. Prin modificarea indicatorilor la funcțiile de apel sistem, a fost ușor să interceptezi, să filtrezi, să loghezi etc. Și acest patch a fost popular atât pentru rootkit, cât și pentru software antivirus. Alte obiecte interesante pentru patch sunt tabelele descriptorilor (GDT, IDT). Prin modificarea tabelului descriptiv global, puteți schimba atributele segmentelor, creând spate pentru cod și întrerupe tabela descriptorilor de întreruperi ... întrerupe! Persoanele avansate împart direct funcțiile kernelului.

În consecință, prima versiune a PatchGuard a protejat:
  • Tabele de apeluri de sistem (SST),
  • tabel descriptiv global (GDT),
  • tabel descriptor de întreruperi (IDT),
  • imaginea nucleului,
  • stive nucleare.
Odată cu dezvoltarea NT, multe componente de kernel erau procesate, inclusiv PatchGuard. În momentul de față, este deja dificil să enumerați tot ce este protejat cu ajutorul lui:
  • o multitudine de imagini de sistem, nu numai imaginea nucleului (nt, HAL, WerLiveKernelApi, tm, CIFS, pshed, kdcom, bootvid, ci, MSRPC, NDIS, ntfs, TCPIP fltmgr),
  • structurile de date nucleare critice (de exemplu, o listă de procese);
  • un set de MSR (de exemplu, registrul specific modelului IA32_LSTAR),
  • KdpStub este o procedură de depanare care primește control după o excepție.

Cum de a proteja PatchGuard

Este demn de remarcat faptul că PatchGuard utilizează în mod activ noua implementare de gestionare a excepțiilor introdusă în versiunile x64 ale Windows. Este folosit atât pentru obturarea PatchGuard în sine, cât și pentru verificarea integrității imaginilor protejate.

În versiunile anterioare de Windows, dispozitivul de tratare a excepțiilor a folosit structurile de date direct pe stivă, ceea ce a permis chiar să ocoliți cookie-urile de stivă atunci când exploitați vulnerabilități. Schimbarea principală constă în stocarea unei tabele speciale în interiorul imaginii executabile cu intrări pentru fiecare din funcțiile sale individuale.

typedef struct _IMAGE_RUNTIME_FUNCTION_ENTRY // Porniți funcția uint32_t EndAddress; // Sfârșitul uniunii // Afișează datele utilizate pentru a extinde stiva și uint32_t UnwindData;

// tratarea excepțiilor>;>
_IMAGE_RUNTIME_FUNCTION_ENTRY, * _PIMAGE_RUNTIME_FUNCTION_ENTRY;

La pornirea sistemului de operare, PatchGuard creează de la 1 la 4 contexte - structuri de date, în care sunt stocate copii ale funcțiilor utilizate de acesta, sumele de control ale structurilor protejate și cheile de criptare ale contextului în sine. Aceste contexte sunt stocate într-o piscină non-puncturabilă într-o formă criptată. Vom vorbi despre verificarea contextelor un pic mai târziu.

Contextele PatchGuard sunt inițializate în faza 1 a boot-ului OS. Funcția direct implicată în crearea contextului nu are un simbol public (îl vom numi KiInitializePatchGuardContext), dar poate fi găsit în interiorul funcției KiFilterFiberContext. Am găsit două locuri în care poate fi creat contextul PatchGuard:

-(apel) -> Faza1InitializareDiscard - (apel) -> KeInitAmd64SpecificState
-(excepție) -> KiFilterFiberContext
. -(apel) -> Faza1InitializareDiscard - (apel) -> sub_14071815C
-(apel) -> ExpLicenseWatchInitWorker - (apel) -> KiFilterFiberContext


Prima opțiune creează întotdeauna cel puțin un context, în timp ce al doilea este doar în 4% din cazuri. De asemenea, prima opțiune este demnă de remarcat prin faptul că aceasta numește implicit funcția KiFilterFiberContext, și anume prin "aruncarea" excepției.

Funcția Pseudocode KeInitAmd64SpecificState

__int64 KeInitAmd64SpecificState () În modul sigur, programul PatchGuard nu funcționează
dacă (! InitSafeBootMode) // În absența unui debugger, diviziunea va provoca o excepție (depășire prin divizare cu -1),
// a cărui ieșire este doar KiFilterFiberContext result =






(v0 / ((KdPitchDebugger | KdDebuggerNotPresent)! = 0. -1.17));> rezultatul retur;>

Funcția sub_14071815C nu are, evident, caracter public, deoarece este asociată cu verificarea licenței de sistem.

Pseudocodul ExpLicenseWatchInitWorker


VOID ExpLicenseWatchInitWorker ()

NTSTATUS (* KiFilterFiberContext) (PVOID pFilterparam); BOOLEAN ForgetAboutPG;
// KiServiceTablesLocked == KiFilterParam KiFilterParam = KiInitialPcr.Prcb.HalReserved [1];
KiInitialPcr.Prcb.HalReserved [1] = NULL; KiFilterFiberContext = KiInitialPcr.Prcb.HalReserved [0];
KiInitialPcr.Prcb.HalReserved [0] = NULL; ForgetAboutPG = (InitSafeBootMode! = 0)
| | (KUSER_SHARED_DATA.KdDebuggerEnabled >> 1);
// 96% din cazuri dacă (__rdtsc ()% 100> 3) ForgetAboutPG | = 1;
dacă (! ForgetAboutPG KiFilterFiberContext (KiFilterParam)! = 1)
KeBugCheckEx (SYSTEM_LICENSE_VIOLATION, 0x42424242, 0xC000026A, 0, 0);>

Următorul este pseudocodul funcției KiFilterFiberContext, care selectează o modalitate de a testa un anumit context și sună funcția de a crea contextul în sine.

Funcția Pseudocode KiFilterFiberContext

BOOLEAN KiFilterFiberContext (PVOID pKiFilterParam)
// Selectați DPC în care va fi efectuat testul DWORD64 dwRand2 = __rdtsc ()% 10;
// 50 - 50, că va fi creat al doilea context DWORD64 dwMethod1 = __rdtsc ()% 6;
// Selectați metoda de rulare a testului AntiDebug (); Rezultat = KiInitializePatchGuardContext (dwDpcIdx,
dwMethod1, (dwRand2 <6) + 1, pKiFilterParam, TRUE);
dacă (dwRand2 <6) face în timp ce ((dwMethod1! = 0) (dwMethod1 == dwMethod2));
Rezultat = KiInitializePatchGuardContext (dwDpcIdx2, dwMethod2, 2, pKiFilterParam, FALSE);
> AntiDebug (); retur rezultatul;>

Funcția care creează contextul PatchGuard este obfuscată într-o asemenea măsură încât mijloacele automate nu se pot confrunta cu aceasta, iar cercetătorii devin brusc neinteresanți să o inverseze. În statică, aceasta este o mizerie completă, linii de 10 K + decompilate "în fruntea" codului (decompilația efectivă în IDA Pro durează aproximativ 40 de minute).

Windows 8

Totul spune despre utilizarea extensivă a macrocomenzilor:
  • chiar și cea mai simplă operațiune, cum ar fi luarea unui număr aleatoriu, este împărțită peste 50 de linii de cod de asamblare;
  • toate ciclurile sunt desfășurate;
  • se introduce o mulțime de coduri "moarte";
  • Se utilizează o referință indirectă la variabile și funcții externe.
Dinamica este, de asemenea, destul de dificilă. Iată câteva exemple despre ceea ce este împrăștiat prin cod.
cli xor eax, eax cmp octet ptr cs: KdDebuggerNotPresent,
al jnz scurt loc_140F3CFBD jmp scurt loc_140F3CFBB sti

Ce face trucul anti-depanare 1?

Când debuggerul este conectat, acesta intră într-un ciclu neîntrerupt fără sfârșit.

cli sidt fword ptr [rbp + 320h] lidt fword ptr [rbp + 228h] mov dr7,
r13 lidt fword ptr [rbp + 320h] sti

Ce face trucul anti-depanare 2?

Încarcă o tabelă descriptor de întrerupere temporară invalidă. Dacă monitorizăm accesul la registrele de depanare, va apărea o excepție de depanare, care în aceste condiții va duce la o eroare triplu urmată de o repornire.

Secvențe de apeluri către funcții recursive în funcție de funcția DPC


ExpTimerDpcRoutine -> KiCustomAccessRoutine0 -> KiCustomRecurseRoutine0 ... KiCustomRecurseRoutineN
IopTimerDispatch -> KiCustomAccessRoutine1 -> KiCustomRecurseRutine1 ... KiCustomRecurseRoutineN
IopIrpStackProfilerTimer -> KiCustomAccessRoutine2 -> KiCustomRecurseRutine2 ... KiCustomRecurseRoutineN
PopThermalZoneDpc -> KiCustomAccessRoutine3 -> KiCustomRecurseRutine3 ... KiCustomRecurseRoutineN
CmpEnableLazyFlushDpcRoutine -> KiCustomAccessRoutine4 -> KiCustomRecurseRoutine4 ... KiCustomRecurseRoutineN
CmpLazyFlushDpcRoutine -> KiCustomAccessRoutine5 -> KiCustomRecurseRutine5 ... KiCustomRecurseRoutineN
KiBalanceSetManagerDeferredRoutine -> KiCustomAccessRoutine6 -> KiCustomRecurseRutine6 ... KiCustomRecurseRoutineN
ExpTimeRefreshDpcRoutine -> KiCustomAccessRoutine7 -> KiCustomRecurseRoutine7 ... KiCustomRecurseRoutineN
ExpTimeZoneDpcRoutine -> KiCustomAccessRoutine8 -> KiCustomRecurseRoutine8 ... KiCustomRecurseRoutineN
ExpCenturyDpcRoutine -> KiCustomAccessRoutine9 -> KiCustomRecurseRoutine9 ... KiCustomRecurseRoutineN

Verificarea contextului constă în două etape: verificarea mai întâi a structurii contextului în sine, care are loc la nivelul DPC, apoi este planificată o lucrare care verifică structurile protejate din fluxul de sistem. Dacă cecul a avut succes, vechiul context este șters și în schimb este creat un nou, care va fi lansat după un interval aleatoriu de timp. Dacă verificarea eșuează, PatchGuard șterge toate pistele, inclusiv zero la stivă, și afișează un ecran albastru cu codul de eroare 0x109: CRITICAL_STRUCTURE_CORRUPTION.

Gifka cu contextul în care se auto-descifrează în prima etapă a verificării:

Cum să câștigi

Windows 10

Când ne-am uitat la KiFilterFiberContext din previzualizarea tehnică Windows 10, am observat o ușoară modificare. Toate metodele vechi de planificare au rămas la fel. Cu toate acestea, a apărut unul nou, care până acum cu siguranță returnează STATUS_HV_FEATURE_UNAVAILABLE. După o mică săpătură, am descoperit funcția KiSwInterruptDispatch, în interiorul căreia există în mod clar o decriptare și un apel de verificare a contextului. Evident, va fi posibil să verificați contextele din cererea Hyper-V hypervisor. De la un hypervisor, în anumite condiții, va avea loc o întrerupere sintetică, procesorul căruia va verifica integritatea kernel-ului.

Povestea continuă

În articol, am încercat să nu specificăm numele unor funcții specifice, nu pentru că ne pare rău pentru ei. Este simplu: numele funcțiilor utilizate pentru a decoda și verifica contextele sunt modificate intenționat de dezvoltatorii PatchGuard și diferă în diferite versiuni ale sistemului de operare.

Iată un exemplu de eșec al numelui funcției de a face ceea ce face cu adevărat. Aceasta este aceeași funcție, o copie a căreia este folosită pentru a decripta contextul.

Windows 8

Un lucru este bun - toate aceste funcții sunt aproape, astfel încât puteți începe cu funcția KiFilterFiberContext. Evident, toate se află în același fișier sursă. Totuși, testarea integrității kernel-ului nu este limitată la un PatchGuard. În diferite părți ale kernelului sunt inserate macro-uri care efectuează verificarea anumitor structuri. Fiecare astfel de loc trebuie căutat manual. exemplu:

--> Phase1InitializareDiscard -> CcInitializeCacheManager -> CcInitializeBcbProfiler


Cu o probabilitate de 50%, această funcție calculează suma de control pentru o funcție de nucleu arbitrar și intenționează să o verifice la fiecare 2 minute în DPC cu funcția CcBcbProfiler.

Deci, noroc în căutarea ta! PatchGuard este interesant tocmai pentru că este distractiv pentru a inversa;)







Articole similare

Trimiteți-le prietenilor: