Hookowanie syscalli z win32k
czerwiec 28th, 2007Hi,
O ile hookowanie syscalli zawartych w kernelu Windowsa z rodziny NT, czyli ntoskrnl, nie sprawia żadnych problemów, o tyle w przypadku syscalli odpowiedzialnych za GUI jest trochę więcej kombinowania. Ale po kolei.
Każdy thread pod WinNT ma określony zestaw syscalli (KSERVICE_TABLE_DESCRIPTOR[]) z którego może korzystać. Na dobrą sprawę mamy dwa takie zestawy:
- KeServiceDescriptorTable - zawierający jedynie odniesienie do syscalli z ntoskrnl
- KeServiceDescriptorTableShadow - zawierający odniesienie za równo do syscalli z ntoskrnl, jak i win32k
Domyślnie każdy utworzony thread ma przypisany (Thread->Tcb.ServiceTable) pierwszy zestaw, czyli KeServiceDescriptorTable. Symbol tego zestawu jest wyexportowany, więc zlokalizowanie zestawu oraz tablicy syscalli nie jest żadnym problemem.
W przypadku kiedy thread wywoła jakiś syscall >= 0×1000, np NtUserFindWindowEx (poprzez np funkcję FindWindow), uruchamiana jest procedura PsConvertToGuiThread() (base\ntos\ps\psquery.c) która między innymi podmienia zestaw syscalli na KeServiceDescriptorTableShadow.
I tutaj zaczynają się “schody”. Mianowicie KeServiceDescriptorTableShadow nie jest exportowany przez kernel, czyli w legalny sposób nie można się do tego dobrać. Ale OK, tak w zasadzie wystarczy tylko adres tablicy syscalli (_W32pServiceTable) w win32k, bo to pointery w tej tablicy podmieniamy. Niestety, _W32pServiceTable również nie jest eksportowany.
Wyjść jest oczywiście kilka:
- Sprawdzić w symbolach gdzie jest KeServiceDescriptorTableShadow lub _W32pServiceTable i użyć tego adresu (jako stałego). Niestety co patch na kernel/win32k trzeba ten adres ponownie sprawdzać.
- Alexander Volynkin zaproponował przeszukanie wszystkich adresów występujących w funkcji KeAddSystemServiceTable, tak aby natrafić na adres który wskazuje na strukturę wypełnioną identycznymi danymi jak KeServiceDescriptorTable, ale jednak nie będącą pod tym samym adresem (pierwszy wpis w obu strukturach w końcu jest identyczny, bo opisuje tablicę syscalli z ntoskrnl). Jest to niezła metoda, chociaż trochę “hackish” (jak to mawia pewien mój pro znajomy ;>).
- Różni ludzie proponują aby poszukać identycznego wpisu jak pierwszy z KeServiceDescriptorTable gdzieś w okolicy (+- 256 bajtów) KeServiceDescriptorTable. Faktycznie KeServiceDescritorTableShadow jest umieszczany zazwyczaj przez kompilator w tych okolicach (w źródle kernela obie struktury są koło siebie), jednak to nie jest zbyt pewna metoda. Niemniej jednak póki co skuteczna.
- Można przeszukać DriverEntry win32k na odwołania do KeAddSystemServiceTable, w końcu ta funkcja jako parametr przyjmuje adres tablicy syscalli (push offset _W32pServiceTable). Ta metoda nie brzmi jednak zbyt pewnie.
- Można poczekać aż driver zostanie wywołany w kontekscie threadu z “pełnym” zestawem syscalli (np. użyć procedur powiadamiania o zniszczeniu wątku i poczekać aż się jakiś trafi, a one często się trafiają), po czym odczytać z Thread->Tcb.ServiceTable adres tabeli syscalli.
- Można przeszukać listę threadów i zobaczyć które mają inną zestaw inny niż KeServiceDescriptorTable.
- Ewentualnie można podmienić procedurę obsługującą SYSENTER i zobaczyć gdzie ona szuka/skacze. Ta metoda jednak nie jest najszczęśliwsza (nie spełnia zasady KISS ;>).
- etc.
Metod jest dużo, wszystkie jednak zmuszają do uciekania się do pewnych sztuczek. Cóż, czasem ważne żeby po prostu działały.
To niestety nie koniec “schodów”. Okazuje się że w przypadku Windowsa XP (nie wiem jak z nowszymi) win32k nie zawsze jest widoczny w pamięci wirtualnej. Owszem, jest zawsze w pamięci fizycznej, ale jeśli dany thread nie jest ustawiony jako GUI, to win32k nie jest zamapowany do pamięci wirtualnej, nawet w przypadku gdy thread działa kernel mode. To wymaga znowu kombinowania. Metod znowu jest kilka:
- Znaleźć win32k w pamięci fizycznej i zamapować go “ręcznie”. Szybkość nie jest mocną stroną tej metody.
- Postarać się aby podmiana syscalli następowała w momencie gdy driver działa w kontekscie threadu GUI (patrz poprzednia metoda z PsSetCreateProcessNotifyRoutine tudzież PsSetCreateThreadNotifyRoutine, lub można też część działającą w user mode poprosić o “dotknięcie” drivera po uprzednim zainicjowaniu GUI).
Osobiście skorzystałem z tej drugiej metody. Nyom. Po rozwiązaniu takich dwóch problemów możemy się cieszyć sliczymi hookami w win32k.
Dodam jeszcze na koniec że należy uważać przy korzystaniu ze źródeł ReactOS do określania parametrów przyjmowanych przez dany syscall. Okazuje się iż mimo że ReactOS ma być w 100% kompatybilny z Windows’em, to nie wszystkie rzeczy są zachowane. Przykładem może być NtUserFindWindowEx który w przypadku ReactOS pobiera 16 bajtów parametrów (4 x 4 bajty), a przypadku Windows XP 20 bajtów parametrów (5 x 4 bajty). Cóż ;>
Dobra, to tyle ;>
Zachęcam do komentowania tego oraz poprzednich postów ;>
G.C.
Recently
Archives
Categories
