Błąd logiczny w GMER

Przy okazji badań opisanych w ostatnim poście, odkryłem w sterowniku gmer’a pewien błąd logiczny mogący powodować nieprawidłowe działanie losowych aplikacji.
Żeby przybliżyć sobie kwestię, o której będę pisał polecam zajrzeć do punktu drugiego poprzedniego postu, a dokładnie do implementacji rozwiązanie w ring0.
Do rzeczy, naszym obiektem zainteresowań jest najnowszy sterownik gmer’a na dzień 22.07.2010:
FileVersion : 1, 0, 15, 4809 built by: WinDDK

[+]Lokalizacja problemu
Jeśli jakiś plik nie może zostać usunięty w standardowy sposób, gmer stara się zamknąć wszystkie otwarte handlery odwołujące się do tego pliku, a następnie go usunąć. Moim zdaniem, niestety implementacja tej procedury przez twórcę gmer’a nie została do końca dobrze przemyślana.
Rzućmy okiem na tą prockę:

.text:0001B488 ; int __stdcall sub_1B488(wchar_t *filePath, int a2)
.text:0001B488 sub_1B488       proc near               ; CODE XREF: sub_1CB32+299p
.text:0001B488
.text:0001B488 PFILE_OBJECT    = dword ptr -30h
.text:0001B488 var_2C          = dword ptr -2Ch
.text:0001B488 var_28          = dword ptr -28h
.text:0001B488 PSYSTEM_HANDLE_INFORMATION= dword ptr -24h
.text:0001B488 pEPROCESS       = dword ptr -20h
.text:0001B488 index           = dword ptr -1Ch
.text:0001B488 ms_exc          = CPPEH_RECORD ptr -18h
.text:0001B488 filePath        = dword ptr  8
.text:0001B488 a2              = dword ptr  0Ch
.text:0001B488
.text:0001B488                 push    20h
.text:0001B48A                 push    offset stru_1EED0
.text:0001B48F                 call    __SEH_prolog
.text:0001B494                 xor     esi, esi
.text:0001B496                 mov     [ebp+pEPROCESS], esi
.text:0001B499                 lea     eax, [ebp+var_28]
.text:0001B49C                 push    eax             ; int
.text:0001B49D                 push    10h             ; SystemHandleInformation
.text:0001B49F                 call    wrap_NtQuerySystemInformation
.text:0001B4A4                 mov     [ebp+PSYSTEM_HANDLE_INFORMATION], eax
.text:0001B4A7                 cmp     eax, esi
.text:0001B4A9                 jz      error
.text:0001B4AF                 mov     [ebp+ms_exc.disabled], esi
.text:0001B4B2                 mov     [ebp+index], esi
.text:0001B4B5                 mov     ebx, ds:NtClose
.text:0001B4BB
.text:0001B4BB loc_1B4BB:                              ; CODE XREF: sub_1B488+119j
.text:0001B4BB                 mov     ecx, [ebp+index]
.text:0001B4BE                 cmp     ecx, [eax]      ; eax = NumberOfHandles
.text:0001B4C0                 jnb     end_of_SYSTEM_HANDLE_INFORMATION
.text:0001B4C6                 shl     ecx, 4
.text:0001B4C9                 lea     edi, [ecx+eax+4]
.text:0001B4CD                 mov     [ebp+var_2C], edi
.text:0001B4D0                 mov     esi, [edi+8]
.text:0001B4D3                 mov     [ebp+PFILE_OBJECT], esi
.text:0001B4D6                 test    esi, esi
.text:0001B4D8                 jz      next_struct
.text:0001B4DE                 push    esi             ; VirtualAddress
.text:0001B4DF                 call    ds:MmIsAddressValid
.text:0001B4E5                 test    al, al
.text:0001B4E7                 jz      next_struct
.text:0001B4ED                 cmp     word ptr [esi], 5
.text:0001B4F1                 jnz     next_struct
.text:0001B4F7                 mov     eax, [esi+34h]
.text:0001B4FA                 test    eax, eax
.text:0001B4FC                 jz      next_struct
.text:0001B502                 push    eax             ; VirtualAddress
.text:0001B503                 call    ds:MmIsAddressValid
.text:0001B509                 test    al, al
.text:0001B50B                 jz      next_struct
.text:0001B511                 push    [ebp+filePath]  ; VirtualAddress
.text:0001B514                 call    ds:MmIsAddressValid
.text:0001B51A                 test    al, al
.text:0001B51C                 jz      short next_struct
.text:0001B51E                 push    [ebp+filePath]  ; wchar_t *
.text:0001B521                 call    ds:wcslen
.text:0001B527                 pop     ecx
.text:0001B528                 sub     eax, 6
.text:0001B52B                 mov     [ebp+a2], eax
.text:0001B52E                 movzx   ecx, word ptr [esi+30h] ; fileObject->FileName.Length
.text:0001B532                 shr     ecx, 1
.text:0001B534                 cmp     ecx, eax
.text:0001B536                 jnz     short next_struct
.text:0001B538                 push    eax             ; size_t
.text:0001B539                 mov     eax, [ebp+filePath]
.text:0001B53C                 add     eax, 0Ch
.text:0001B53F                 push    eax             ; wchar_t *
.text:0001B540                 push    dword ptr [esi+34h] ; fileObject->FileName.Buffer
.text:0001B543                 call    ds:_wcsnicmp
.text:0001B549                 add     esp, 0Ch
.text:0001B54C                 test    eax, eax
.text:0001B54E                 jnz     short next_struct
.text:0001B550                 lea     eax, [ebp+pEPROCESS]
.text:0001B553                 push    eax
.text:0001B554                 push    dword ptr [edi]
.text:0001B556                 call    ds:PsLookupProcessByProcessId
.text:0001B55C                 mov     status, eax
.text:0001B561                 test    eax, eax
.text:0001B563                 jnz     short next_struct
.text:0001B565                 mov     eax, [ebp+pEPROCESS]
.text:0001B568                 cmp     eax, current_process_EPROCESS
.text:0001B56E                 jz      short handleBelongsToCurrentProcess
.text:0001B570                 push    eax
.text:0001B571                 call    ds:KeAttachProcess
.text:0001B577                 movzx   eax, word ptr [edi+6]
.text:0001B57B                 push    eax             ; Handle
.text:0001B57C                 call    ebx ; NtClose
.text:0001B57E                 mov     status, eax
.text:0001B583                 call    ds:KeDetachProcess
.text:0001B589                 jmp     short loc_1B592
.text:0001B58B ; ---------------------------------------------------------------------------
.text:0001B58B
.text:0001B58B handleBelongsToCurrentProcess:          ; CODE XREF: sub_1B488+E6j
.text:0001B58B                 movzx   eax, word ptr [edi+6]
.text:0001B58F                 push    eax             ; Handle
.text:0001B590                 call    ebx ; NtClose
.text:0001B592
.text:0001B592 loc_1B592:                              ; CODE XREF: sub_1B488+101j
.text:0001B592                 mov     ecx, [ebp+pEPROCESS] ; Object
.text:0001B595                 call    ds:ObfDereferenceObject
.text:0001B59B
.text:0001B59B next_struct:                            ; CODE XREF: sub_1B488+50j
.text:0001B59B                                         ; sub_1B488+5Fj ...
.text:0001B59B                 inc     [ebp+index]
.text:0001B59E                 mov     eax, [ebp+PSYSTEM_HANDLE_INFORMATION]
.text:0001B5A1                 jmp     loc_1B4BB
.text:0001B5A6 ; ---------------------------------------------------------------------------
.text:0001B5A6
.text:0001B5A6 _end:                                   ; DATA XREF: .rdata:stru_1EED0o
.text:0001B5A6                 xor     eax, eax
.text:0001B5A8                 inc     eax
.text:0001B5A9                 retn

Gdzie tu jest problem?
Przyjrzyjmy się samej kwesti odnalezienia przez gmer’a uchwytu, który jest powiązany z plikiem do którego handlery chcemy pozamykać. Autor gmer’a postanowił tutaj skorzystać z dostarczonego w
strukturze SYSTEM_HANDLE_INFORMATION FILE_OBJECT’u i slusznie,no ale…

Sprawdzenie rozpoczyna się od zbadania długości scieżki podanej jako argument i tej zawartej w file object’e.

.text:0001B51E                 push    [ebp+filePath]  ; wchar_t *
.text:0001B521                 call    ds:wcslen
.text:0001B527                 pop     ecx
.text:0001B528                 sub     eax, 6
.text:0001B52B                 mov     [ebp+a2], eax
.text:0001B52E                 movzx   ecx, word ptr [esi+30h] ; fileObject->FileName.Length
.text:0001B532                 shr     ecx, 1
.text:0001B534                 cmp     ecx, eax
.text:0001B536                 jnz     short next_struct

Jeśli długości są identyczne przechodzimy do następnego testu. Zanim jednak przejde dalej odczuwam wielką potrzebe czepienia się jednej kwestii związaną z dobrymi praktykami jak i optymalizacją kodu.
Chodzi dokładnie o ten fragment:

.text:0001B51E                 push    [ebp+filePath]  ; wchar_t *
.text:0001B521                 call    ds:wcslen

Ten fragment znajduję się wewnątrz pętli i przy każdej iteracji, kiedy obiektem okazuje się plikiem, wywoływana jest zupełnie bez sensownie funkcja wcslen na zmiennej filePath, która została podana jako parametr do sub_1B488!!! Takie wielokrotne wyliczanie tej samej wartości wewnątrz pętli prowadzi do niczego innego niż zmniejszenia wydajności kodu a w tym przypadku wydajność będzie mala wręcz wprost proporcjonalnie do długości stringu. Także, nie polecam takich konstrukcji ;) (btw: jak zobaczyłem ten twór to przypomniał mi się post Malcom’a „Programowanie wymaga myślenia!” http://blog.malcom.pl/2009/09/09/programowanie-wymaga-myslenia/, polecam).

Kontynuujmy:

.text:0001B538                 push    eax             ; length
.text:0001B539                 mov     eax, [ebp+filePath]
.text:0001B53C                 add     eax, 0Ch
.text:0001B53F                 push    eax             ; wchar_t *
.text:0001B540                 push    dword ptr [esi+34h] ; fileObject->FileName.Buffer
.text:0001B543                 call    ds:_wcsnicmp

Tutaj dokonywane jest już porównanie stringów reprezentujących ścieżki do plików.
Ale, ale !!! Nie jest to porównanie zawierające litery partycji, na której dany plik się znajduje!!!
Ścieżki mają tu postać:
„\folder\file.ext”

Tylko i wyłącznie takie porównanie doprowadza do sytuacji, w której handler powiązany z plikiem o takiej samej scieżce jak scieżka podana jako parametr funkcji, lecz znajdujący się na innej partycji zostanie zamknięty!!!

[+]Przykład
Na potrzeby testów stworzyłem niewielką aplikację, która otwiera handler do pliku bez praw do usunięcia( bez FILE_SHARE_DELETE, w celu zmuszenia gmer’a do wywołania powyżej omawianej procki). Kod tej aplikacji jest następujący:

int main(int argc, char* argv[])
{

	HANDLE h = CreateFileA(argv[1],
				GENERIC_READ,
				FILE_SHARE_READ | FILE_SHARE_WRITE,
				0,
				OPEN_EXISTING,
				FILE_ATTRIBUTE_NORMAL,
				0);
	if(h == INVALID_HANDLE_VALUE)
	{
		cout<<"[+]INVALID_HANDLE_VALUE"<<endl;
		cout<<GetLastError();
		return 0;
	}
	cout<<"[+]File opened"<<endl;
	getchar();
}

oraz narzędzia Handle v3.42, do wylistowania wszystkich otwartych handlerów przez dany proces. Na dwóch konsolach odpaliłem swój kod skompilowany do pliku zlo.exe z następującymi parametrami:

Console_1:
zlo.exe C:\install.exe

oraz

Console_2:
zlo.exe E:\install.exe

Po odpaleniu aplikacji wylistowałem otwarte przez nie handler:

// Przed proba usuniecia C:\install.exe [+]
c:\Documents and Settings\virtual&amp;amp;gt;handle.exe -p zlo.exe

Handle v3.42
Copyright (C) 1997-2008 Mark Russinovich
Sysinternals - www.sysinternals.com

------------------------------------------------------------------------------
zlo.exe pid: 1928 SLAVE\virtual
    C: File  (RW-)   C:\Documents and Settings\virtual
  7E8: File  (RW-)
C:\WINDOWS\WinSxS\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375
  7F4: File  (RW-)   C:\install.exe
------------------------------------------------------------------------------
zlo.exe pid: 932 SLAVE\virtual
    C: File  (RW-)   C:\Documents and Settings\virtual
  7E8: File  (RW-)
C:\WINDOWS\WinSxS\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375
  7F4: File  (RW-)   E:\install.exe

Następnie przy pomocy gmer’a usunąłem plik C:\install.exe i ponownie wylistowałem handlery:

//Po usunieciu przez gmer'a C:\install.exe
c:\Documents and Settings\virtual&amp;amp;gt;handle.exe -p zlo.exe

Handle v3.42
Copyright (C) 1997-2008 Mark Russinovich
Sysinternals - www.sysinternals.com

------------------------------------------------------------------------------
zlo.exe pid: 1928 SLAVE\virtual
    C: File  (RW-)   C:\Documents and Settings\virtual
  7E8: File  (RW-)
C:\WINDOWS\WinSxS\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375
------------------------------------------------------------------------------
zlo.exe pid: 932 SLAVE\virtual
    C: File  (RW-)   C:\Documents and Settings\virtual
  7E8: File  (RW-)
C:\WINDOWS\WinSxS\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375

Efekt potwierdził moją teorie, tak jak widać uchwyt do E:\install.exe również został zamknięty.

[+]Rada
Dodanie pełnego badania ścieżek do plików m.in z wykorzystaniem np. IoQueryFileDosDeviceName.

No i chyba tyle;)

Ten wpis został opublikowany w kategorii Analiza, RE i oznaczony tagami , , , , . Dodaj zakładkę do bezpośredniego odnośnika.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

*

bXrK

Please type the text above:

Możesz użyć następujących tagów oraz atrybutów HTML-a: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>