https://bugs.winehq.org/show_bug.cgi?id=45632
Anastasius Focht focht@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@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@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@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@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/f244c3b5ebfae0bcbe13b127c4b3abd3...
--- 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/...
--- 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