[Bug 45632] Garena client v2.0.x crashes on startup (' ntdll.NtQueryVirtualMemory' needs to validate 'MemoryInformationLength' before writing to buffer)

wine-bugs at winehq.org wine-bugs at winehq.org
Mon Feb 18 06:49:42 CST 2019


https://bugs.winehq.org/show_bug.cgi?id=45632

Anastasius Focht <focht at gmx.net> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                URL|https://www.garena.co.id/gp |cdn.gxx.garenanow.com/gxx/p
                   |c                           |c/installer/Garena-v2.0.exe
             Status|UNCONFIRMED                 |NEW
                 CC|                            |focht at gmx.net
           Keywords|                            |download, obfuscation
            Summary|Cannot Start Garena Client  |Garena client v2.0.x
                   |                            |crashes on startup
                   |                            |('ntdll.NtQueryVirtualMemor
                   |                            |y' needs to validate
                   |                            |'MemoryInformationLength'
                   |                            |before writing to buffer)
          Component|-unknown                    |ntdll
     Ever confirmed|0                           |1

--- Comment #1 from Anastasius Focht <focht at gmx.net> ---
Hello folks,

confirming.

Relevant part of trace log:

--- snip ---
$ pwd
/home/focht/.wine/drive_c/Program Files (x86)/Garena/Garena

$ file *
2.0.1804.0420: directory
2.0.1902.0110: directory
games:         directory
Garena.exe:    PE32 executable (GUI) Intel 80386, for MS Windows

$ WINEDEBUG=+seh,+loaddll,+process,+relay wine ./Garena.exe >>log.txt 2>&1
...
0032:Starting process L"C:\\Program Files (x86)\\Garena\\Garena\\Garena.exe"
(entryproc=0x42842d) 
...
0032:Call KERNEL32.LoadLibraryW(00148a60 L"C:\\Program Files
(x86)\\Garena\\Garena\\2.0.1902.0110\\appshield.dll") ret=004035c6
...
0032:trace:loaddll:load_native_dll Loaded L"C:\\Program Files
(x86)\\Garena\\Garena\\2.0.1902.0110\\appshield.dll" at 0x10000000: native
0032:Call PE DLL (proc=0x7d4b2344,module=0x7d420000
L"shell32.dll",reason=PROCESS_ATTACH,res=(nil))
...
0032:Ret  PE DLL (proc=0x7d4b2344,module=0x7d420000
L"shell32.dll",reason=PROCESS_ATTACH,res=(nil)) retval=1
0032:Call TLS callback
(proc=0x10033ab0,module=0x10000000,reason=PROCESS_ATTACH,reserved=0)
0032:Ret  TLS callback
(proc=0x10033ab0,module=0x10000000,reason=PROCESS_ATTACH,reserved=0)
0032:Call PE DLL (proc=0x1001cbd9,module=0x10000000
L"appshield.dll",reason=PROCESS_ATTACH,res=(nil)) 
...
0032:Call
msvcr120._beginthreadex(00000000,00000000,00bc5340,001603c8,00000004,001603d8)
ret=00bc52ec
0032:Call
KERNEL32.CreateThread(00000000,00000000,00bc5340,001603c8,00000004,001603d8)
ret=7d1ee20f
0032:Ret  KERNEL32.CreateThread() retval=00000074 ret=7d1ee20f
0032:Ret  msvcr120._beginthreadex() retval=00000074 ret=00bc52ec
0032:Call KERNEL32.ResumeThread(00000074) ret=00bc532c
0032:Ret  KERNEL32.ResumeThread() retval=00000001 ret=00bc532c
...
0033:Call PE DLL (proc=0x7e6d1d7f,module=0x7e630000
L"user32.dll",reason=THREAD_ATTACH,res=(nil))
0032:Ret  msvcr120.memcpy() retval=0033f458 ret=00ba2bbd
0033:Ret  PE DLL (proc=0x7e6d1d7f,module=0x7e630000
L"user32.dll",reason=THREAD_ATTACH,res=(nil)) retval=1
0032:Call msvcr120.memcpy(0033f4b8,0033f458,0000000f) ret=00ba2bbd
0033:Call PE DLL (proc=0x7e179c65,module=0x7e160000
L"imm32.dll",reason=THREAD_ATTACH,res=(nil))
0032:Ret  msvcr120.memcpy() retval=0033f4b8 ret=00ba2bbd
0033:Ret  PE DLL (proc=0x7e179c65,module=0x7e160000
L"imm32.dll",reason=THREAD_ATTACH,res=(nil)) retval=1
0032:Call msvcr120.memcpy(0033f560,0033f4b8,0000000f) ret=00ba2bbd
0033:Call PE DLL (proc=0x7e48978c,module=0x7e440000
L"rpcrt4.dll",reason=THREAD_ATTACH,res=(nil))
0032:Ret  msvcr120.memcpy() retval=0033f560 ret=00ba2bbd
0033:Ret  PE DLL (proc=0x7e48978c,module=0x7e440000
L"rpcrt4.dll",reason=THREAD_ATTACH,res=(nil)) retval=1
0032:Call msvcr120.memcpy(0033f4d8,0033f560,0000000f) ret=00ba2bbd
0033:Call PE DLL (proc=0x7e93def1,module=0x7e860000
L"ole32.dll",reason=THREAD_ATTACH,res=(nil))
0032:Ret  msvcr120.memcpy() retval=0033f4d8 ret=00ba2bbd
0033:Ret  PE DLL (proc=0x7e93def1,module=0x7e860000
L"ole32.dll",reason=THREAD_ATTACH,res=(nil)) retval=1
0032:Call ntdll.RtlAllocateHeap(00110000,00000000,00000060) ret=00bad94e
0033:Call TLS callback
(proc=0x440830,module=0x400000,reason=THREAD_ATTACH,reserved=0)
0032:Ret  ntdll.RtlAllocateHeap() retval=00172590 ret=00bad94e
0033:Ret  TLS callback
(proc=0x440830,module=0x400000,reason=THREAD_ATTACH,reserved=0)
0032:Call msvcr120.??2 at YAPAXI@Z(00000020) ret=00bafa98
0033:Call TLS callback
(proc=0x10033ab0,module=0x10000000,reason=THREAD_ATTACH,reserved=0)
0032:Call ntdll.RtlAllocateHeap(00a70000,00000000,00000020) ret=7d1b8e60
0033:Ret  TLS callback
(proc=0x10033ab0,module=0x10000000,reason=THREAD_ATTACH,reserved=0)
0032:Ret  ntdll.RtlAllocateHeap() retval=00a86e70 ret=7d1b8e60
0033:Call PE DLL (proc=0x1001cbd9,module=0x10000000
L"appshield.dll",reason=THREAD_ATTACH,res=(nil))
0032:Ret  msvcr120.??2 at YAPAXI@Z() retval=00a86e70 ret=00bafa98
0032:Call KERNEL32.CreateEventA(00000000,00000001,00000000,00000000)
ret=00bafc6a
0033:Call ntdll.RtlAllocateHeap(00110000,00000008,000003bc) ret=1002a78c
0033:Ret  ntdll.RtlAllocateHeap() retval=00172610 ret=1002a78c
0033:Call
ntdll.NtQueryInformationThread(fffffffe,00000009,00ceea34,00000004,00000000)
ret=10015b2a
0032:Ret  KERNEL32.CreateEventA() retval=00000078 ret=00bafc6a
0032:Call msvcr120.memcpy(001725d8,0033f4d8,0000000f) ret=00ba2bbd
0032:Ret  msvcr120.memcpy() retval=001725d8 ret=00ba2bbd
0032:Call
msvcr120._beginthreadex(00000000,00000000,00bc5340,00172590,00000004,001725a0)
ret=00bc52ec
0032:Call
KERNEL32.CreateThread(00000000,00000000,00bc5340,00172590,00000004,001725a0)
ret=7d1ee20f
0033:Ret  ntdll.NtQueryInformationThread() retval=00000000 ret=10015b2a
0033:Call
ntdll.NtQueryVirtualMemory(ffffffff,00bc5340,00000000,00ceea38,00000004,00ceea30)
ret=10015be7
0033:Ret  ntdll.NtQueryVirtualMemory() retval=00000000 ret=10015be7
0033:trace:process:NtQueryInformationProcess
(0xffffffff,0x00000022,0xcee708,0x00000004,(nil))
0033:trace:seh:raise_exception code=c0000005 flags=0 addr=0x20 ip=00000020
tid=0033
0033:trace:seh:raise_exception  info[0]=00000008
0033:trace:seh:raise_exception  info[1]=00000020
0033:trace:seh:raise_exception  eax=00000000 ebx=00000000 ecx=34bd91fc
edx=00000000 esi=00000002 edi=f7db7000
0033:trace:seh:raise_exception  ebp=00001000 esp=00ceea50 cs=0023 ds=002b
es=002b fs=0063 gs=006b flags=00010246
0033:err:seh:raise_exception Exception frame is not in stack limits => unable
to dispatch exception.
0032:Ret  KERNEL32.CreateThread() retval=0000007c ret=7d1ee20f
0032:Ret  msvcr120._beginthreadex() retval=0000007c ret=00bc52ec 
...
--- snip ---

One of the threads crashes due to stack corruption.

Relevant part of app disassembly:

--- snip ---
10015AD0 | push    ebp                          
10015AD1 | mov     ebp, esp                     
10015AD3 | push    FFFFFFFF                     
10015AD5 | push    appshield.10035B78           
10015ADA | mov     eax, dword ptr fs:[0]        
10015AE0 | push    eax                          
10015AE1 | sub     esp, 38                      
10015AE4 | push    esi                          
10015AE5 | push    edi                          
10015AE6 | mov     eax, dword ptr ds:[10046618] 
10015AEB | xor     eax, ebp                     
10015AED | push    eax                          
10015AEE | lea     eax, dword ptr ss:[ebp-C]    
10015AF1 | mov     dword ptr fs:[0], eax        
10015AF7 | mov     edi, ecx                     
10015AF9 | cmp     byte ptr ds:[edi], 0         
10015AFC | je      appshield.10015C16           
10015B02 | cmp     byte ptr ds:[edi+1], 0       
10015B06 | je      appshield.10015C16           
10015B0C | call    dword ptr ds:[10037048] ; GetCurrentThread
10015B12 | push    0                            
10015B14 | push    4                            
10015B16 | lea     ecx, dword ptr ss:[ebp-14]   
10015B19 | mov     dword ptr ss:[ebp-14], 0     
10015B20 | push    ecx                          
10015B21 | push    9                            
10015B23 | push    eax                          
10015B24 | call    dword ptr ds:[10037188] ; NtQueryInformationThread
10015B2A | test    eax, eax                       
10015B2C | js      appshield.10015C16             
10015B32 | mov     esi, dword ptr ss:[ebp-14]     
10015B35 | test    esi, esi                       
10015B37 | je      appshield.10015C16             
10015B3D | mov     eax, dword ptr ds:[edi+4]      
10015B40 | mov     ecx, dword ptr ds:[edi+8]      
10015B43 | cmp     eax, ecx                       
10015B45 | je      appshield.10015BA4             
10015B47 | cmp     dword ptr ds:[eax], esi        
10015B49 | je      appshield.10015B54             
10015B4B | add     eax, 4                         
10015B4E | cmp     eax, ecx                       
10015B50 | jne     appshield.10015B47             
10015B52 | jmp     appshield.10015BA4             
10015B54 | lea     eax, dword ptr ds:[edi+10]     
10015B57 | mov     byte ptr ss:[ebp-20], 0        
10015B5B | lea     ecx, dword ptr ss:[ebp-24]     
10015B5E | mov     dword ptr ss:[ebp-24], eax     
10015B61 | call    appshield.100169B0             
10015B66 | mov     dword ptr ss:[ebp-4], 0        
10015B6D | call    dword ptr ds:[10037070]        
10015B73 | mov     dword ptr ss:[ebp-10], eax     
10015B76 | movzx   eax, byte ptr ds:[1004BC53]    
10015B7D | push    eax                            
10015B7E | lea     eax, dword ptr ss:[ebp-10]     
10015B81 | push    eax                            
10015B82 | push    ecx                            
10015B83 | lea     eax, dword ptr ss:[ebp-1C]     
10015B86 | push    eax                            
10015B87 | lea     ecx, dword ptr ds:[edi+18]     
10015B8A | call    appshield.10015E70             
10015B8F | mov     dword ptr ss:[ebp-4], FFFFFFFF 
10015B96 | cmp     byte ptr ss:[ebp-20], 0        
10015B9A | je      appshield.10015BA4             
10015B9C | mov     ecx, dword ptr ss:[ebp-24]     
10015B9F | call    appshield.1000C970             
10015BA4 | lea     eax, dword ptr ss:[ebp-40]     
10015BA7 | mov     dword ptr ss:[ebp-10], eax     
10015BAA | call    dword ptr ds:[10037074] ; GetCurrentProcess
10015BB0 | mov     ecx, dword ptr ss:[ebp-10]     
10015BB3 | xorps   xmm0, xmm0                     
10015BB6 | mov     dword ptr ss:[ebp-18], 0       
10015BBD | movdqu  xmmword ptr ds:[ecx], xmm0     
10015BC1 | mov     ecx, dword ptr ss:[ebp-10]     
10015BC4 | movq    qword ptr ds:[ecx+10], xmm0    
10015BC9 | mov     ecx, dword ptr ss:[ebp-10]     
10015BCC | mov     dword ptr ds:[ecx+18], 0       
10015BD3 | lea     ecx, dword ptr ss:[ebp-18]     
10015BD6 | push    ecx                     ; ReturnLength (out)
10015BD7 | push    4                       ; MemoryInformationLength = 4 (!)
10015BD9 | lea     ecx, dword ptr ss:[ebp-10]
10015BDC | push    ecx                     ; MemoryInformation buffer =
0x00CEEA38
10015BDD | push    0                       ; MemoryInformationClass
10015BDF | push    esi                     ; BaseAddress
10015BE0 | push    eax                     ; ProcessHandle
10015BE1 | call    dword ptr ds:[10037184] ; NtQueryVirtualMemory
10015BE7 | test    eax, eax                       
10015BE9 | js      appshield.10015C16             
10015BEB | test    dword ptr ss:[ebp-28], 1000000 
10015BF2 | jne     appshield.10015C07             
10015BF4 | xor     al, al                         
10015BF6 | mov     ecx, dword ptr ss:[ebp-C]      
10015BF9 | mov     dword ptr fs:[0], ecx          
10015C00 | pop     ecx                            
10015C01 | pop     edi                            
10015C02 | pop     esi                            
10015C03 | mov     esp, ebp                ; ebp was corrupted               
10015C05 | pop     ebp                     ; *boom*                 
10015C06 | ret                                    
--- snip ---

Don't ask me why the app hard-codes 'MemoryInformationLength' to 4, it doesn't
make sense.

Stack before 'NtQueryVirtualMemory' call:

--- snip ---
00CEEA30  00000000 ; res_len (out)
00CEEA34  00BC5340 ;
00CEEA38  00CEEA08 ; MemoryInformation buffer
00CEEA3C  00CEEA84 ; 
00CEEA40  10035B78 ; 
00CEEA44  FFFFFFFF                      
00CEEA48  00CEEA54 ; prolog: saved EBP
00CEEA4C  1000CBAC ; return to appshield.1000CBAC from appshield.10015AD0
00CEEA50  FFFFFFFE ; 
00CEEA54  00CEEA94 ; 
00CEEA58  1001CC6E ; return to appshield.1001CC6E from appshield.1000CB60
...
--- snip ---

Corrupted stack after 'NtQueryVirtualMemory' call:

--- snip ---
00CEEA30  0000001C ; res_len (out) -> sizeof(MEMORY_BASIC_INFORMATION) 
00CEEA34  00BC5340 ; 
00CEEA38  00BC5000 ; BaseAddress -> ipc.00BC5000
00CEEA3C  00BA0000 ; AllocationBase
00CEEA40  00000080 ; AllocationProtect
00CEEA44  00007000 ; RegionSize
00CEEA48  00001000 ; State -> corrupted prolog: saved EBP
00CEEA4C  00000020 ; Protect
00CEEA50  01000000 ; Type
00CEEA54  00CEEA94 ;
00CEEA58  1001CC6E ; return to appshield.1001CC6E from appshield.1000CB60
...
--- snip ---

Wine doesn't check if the caller supplied buffer size is sufficient to fit
MEMORY_BASIC_INFORMATION.
It should return STATUS_INFO_LENGTH_MISMATCH in that case but instead happily
writes to the caller supplied buffer.

Wine source:

https://source.winehq.org/git/wine.git/blob/f244c3b5ebfae0bcbe13b127c4b3abd356a22f7e:/dlls/ntdll/virtual.c#l2794

--- snip ---
2794 /***********************************************************************
2795  *             NtQueryVirtualMemory   (NTDLL.@)
2796  *             ZwQueryVirtualMemory   (NTDLL.@)
2797  */
2798 NTSTATUS WINAPI NtQueryVirtualMemory( HANDLE process, LPCVOID addr,
2799                                       MEMORY_INFORMATION_CLASS info_class,
PVOID buffer,
2800                                       SIZE_T len, SIZE_T *res_len )
2801 {
2802     struct file_view *view;
2803     char *base, *alloc_base = 0, *alloc_end = working_set_limit;
2804     struct wine_rb_entry *ptr;
2805     MEMORY_BASIC_INFORMATION *info = buffer;
2806     sigset_t sigset;
2807 
2808     if (info_class != MemoryBasicInformation)
2809     {
2810         switch(info_class)
2811         {
2812             UNIMPLEMENTED_INFO_CLASS(MemoryWorkingSetList);
2813             UNIMPLEMENTED_INFO_CLASS(MemorySectionName);
2814             UNIMPLEMENTED_INFO_CLASS(MemoryBasicVlmInformation);
2815 
2816             default:
2817                 FIXME("(%p,%p,info_class=%d,%p,%ld,%p) Unknown information
class\n", 
2818                       process, addr, info_class, buffer, len, res_len);
2819                 return STATUS_INVALID_INFO_CLASS;
2820         }
2821     }
...
2881     /* Fill the info structure */
2882 
2883     info->AllocationBase = alloc_base;
2884     info->BaseAddress    = base;
2885     info->RegionSize     = alloc_end - base;
2886 
...
--- snip ---

Microsoft docs:

https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/nf-ntifs-ntqueryvirtualmemory

--- quote ---
NtQueryVirtualMemory function

The NtQueryVirtualMemory routine determines the state, protection, and type of
a region of pages within the virtual address space of the subject process.
Syntax

__kernel_entry NTSYSCALLAPI NTSTATUS NtQueryVirtualMemory(
  HANDLE                   ProcessHandle,
  PVOID                    BaseAddress,
  MEMORY_INFORMATION_CLASS MemoryInformationClass,
  PVOID                    MemoryInformation,
  SIZE_T                   MemoryInformationLength,
  PSIZE_T                  ReturnLength
);

Parameters

ProcessHandle

A handle for the process in whose context the pages to be queried reside. Use
the NtCurrentProcess macro to specify the current process.

BaseAddress

The base address of the region of pages to be queried. This value is rounded
down to the next host-page- address boundary.

MemoryInformationClass

The memory information class about which to retrieve information. Currently,
the only supported MEMORY_INFORMATION_CLASS value is MemoryBasicInformation.

MemoryInformation

A pointer to a buffer that receives the specified information. The format and
content of the buffer depend on the specified information class specified in
the MemoryInformationClass parameter. When the value MemoryBasicInformation is
passed to MemoryInformationClass, the MemoryInformationClass parameter value is
a MEMORY_BASIC_INFORMATION.

MemoryInformationLength

Specifies the length in bytes of the memory information buffer.

ReturnLength

An optional pointer which, if specified, receives the number of bytes placed in
the memory information buffer.
Return Value

Returns STATUS_SUCCESS if the call is successful. If the call fails, possible
error codes include the following:
Return code     Description

STATUS_INVALID_PARAMETER

    The specified base address is outside the range of accessible addresses.

STATUS_ACCESS_DENIED

    The caller had insufficient access rights to perform the requested action.

STATUS_INFO_LENGTH_MISMATCH

    The MemoryInformation buffer is larger than MemoryInformationLength.

STATUS_INVALID_INFO_CLASS

    A value other than MemoryBasicInformation was passed to the
MemoryInformationClass parameter. 
--- quote ---

With that part fixed, the Garena client v2 starts fine, showing login dialog.

$ sha1sum Garena-v2.0.exe 
8210d841d7a6debe93189190fa865bdd905058b7  Garena-v2.0.exe

$ du -sh Garena-v2.0.exe 
70M    Garena-v2.0.exe

$ wine --version
wine-4.2

Regards

-- 
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.


More information about the wine-bugs mailing list