Logical bug in GMER

Messing a little bit recently with a gmer’s code I discovered logical bug which can cause abnormal behavior of an random applications.
Our object of interest will be the newest gmer’s driver on day 22.07.2010.
FileVersion : 1, 0, 15, 4809 built by: WinDDK

[+]Localization of a problem
If some file can’t be deleted in the usual way, gmer will try to close all opened handlers related with this file and after it delete file.
In my opinion implementation of this procedure has not been thought out correctly.
Let’s take a look at this routine:

.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

Where the problem is?
Let’s take a look on issue where the gmer searches for a opened handles associated with interesting for us file to close them.
Gmer’s author decided here to use delivered FILE_OBJECT with SYSTEM_HANDLE_INFORMATION structure (NtQuerySystemInformation) and rightly, but …
Verification starts with comparison file path length of file passed as argument and this one included in a FILE_OBJECT.

.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

If a lengths are identical we go to a another test. Before I will go further I feel big need to niggle one piece of code and to mention here about good programming practices and code optimization. Exactly here is the piece of code:

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

Above fragment of code is inside the loop so during each iteration when object is a file, is called totally without any sense function wcslen which calculates length of filePath passed to sub_1B488 as argument!!! That multiple calculation of the same value leads to nothing else like loss of code productivity and in this case productivity will be decreased proportion to filePath length. Honestly, I highly don’t recommend such a constructions ;).

Go ahead:

.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

Above we can see code resposibles of comparison two strings representing paths to files.
But, but,but !!! It’s not comparison of file paths which contain volume letter!!!.
Paths look here in the following way:
„folderfile.ext”

Only that kind of comparison leads to situation where handle related with file which path is the same as this one passed by us as argument, but located on totally different volume will be also closed!!!.

[+]Example
For tests purpose I created an small application which opens a handle to a file without delete privilege ( without FILE_SHARE_DELETE ) to force gmer to use above explained procedure. Code of this application you can find below:
cpp]
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"<

This entry was posted in Analiza, RE and tagged , , , , . Bookmark the permalink.

Comments are closed.