[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