(Polish) Błąd logiczny w <= gmer.sys [1, 0, 15, 4809 built by: WinDDK]
Błąd logiczny w <= gmer.sys [1, 0, 15, 4809 built by: WinDDK]
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ę:
[asm]
.text:0001B488 ; int __stdcall sub_1B488(wchar_t *filePath, int a2)
.text:0001B488 sub_1B488 proc near ; CODE XREF: sub_1CB32+299 p
.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+119 j
.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+E6 j
.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+101 j
.text:0001B592 mov ecx, [ebp+pEPROCESS] ; Object
.text:0001B595 call ds:ObfDereferenceObject
.text:0001B59B
.text:0001B59B next_struct: ; CODE XREF: sub_1B488+50 j
.text:0001B59B ; sub_1B488+5F j …
.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_1EED0 o
.text:0001B5A6 xor eax, eax
.text:0001B5A8 inc eax
.text:0001B5A9 retn
[asm]
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.
[asm]
.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
[asm]
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:
[asm]
.text:0001B51E push [ebp+filePath] ; wchar_t *
.text:0001B521 call ds:wcslen
[asm]
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:
[asm]
.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-&amp;amp;gt;FileName.Buffer
.text:0001B543 call ds:_wcsnicmp
[/asm]
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&amp;amp;amp;lt;&amp;amp;amp;lt;&amp;amp;amp;quot;[+]INVALID_HANDLE_VALUE&amp;amp;amp;quot;&amp;amp;amp;lt;&amp;amp;amp;lt;endl;
cout&amp;amp;amp;lt;&amp;amp;amp;lt;hex&amp;amp;amp;lt;&amp;amp;amp;lt;GetLastError();
return 0;
}
cout&amp;amp;amp;lt;&amp;amp;amp;lt;&amp;amp;amp;quot;[+]File opened!&amp;amp;amp;quot;&amp;amp;amp;lt;&amp;amp;amp;lt;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;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;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;)
















































