https://bugs.winehq.org/show_bug.cgi?id=47052
Bug ID: 47052 Summary: MRAC Anti-Cheat (My.Com Warface) fails to start game process, 'ntoskrnl.exe.ObReferenceObjectByHandle' fails to lookup event handles created by MRAC service process Product: Wine Version: 4.6 Hardware: x86-64 OS: Linux Status: NEW Severity: normal Priority: P2 Component: wineserver Assignee: wine-bugs@winehq.org Reporter: focht@gmx.net Distribution: ---
Hello folks,
as it says. Continuation of bug 47044 , bug 47047 , bug 37355 ...
--- snip --- $ pwd /home/focht/.wine/drive_c/users/focht/Local Settings/Application Data/GameCenter
$ WINEDEBUG=+seh,+loaddll,+process,+ntoskrnl,+ntdll,+relay,+server wine ./GameCenter.exe >>log.txt 2>&1 ... 0009:Call KERNEL32.CreateProcessW(00000000,0a0a782c L""C:\users\focht\Local Settings\Application Data\GameCenter\GameCenter.exe" -job=2_8 -job_pipe=GameCenterV5_AAE21A18E0399D09F3AAF4874C40186E -job_hint=GCJobGameLaunch",00000000,00000000,00000000,01000000,00000000,00000000,0033f0b8,0033f0a8) ret=00435f19 ... 0009:trace:process:CreateProcessInternalW starting L"C:\users\focht\Local Settings\Application Data\GameCenter\GameCenter.exe" as Win32 binary (400000-da1000, x86) ... 0009:trace:process:CreateProcessInternalW started process pid 00c8 tid 00c9 ... --- snip ---
MRAC service start:
--- snip --- ... 00cd:Call KERNEL32.CreateProcessW(00000000,00022e50 L"C:\windows\System32\mracsvc.exe",00000000,00000000,00000000,00000400,00340000,00000000,00aaf130,00aaf000) ret=7fe720baedc6
00cd:trace:process:CreateProcessInternalW starting L"C:\windows\System32\mracsvc.exe" as Win64 binary (140000000-14106f000, x86_64) 00cd: *fd* 39 <- 714 00cd: new_process( inherit_all=0, create_flags=00000400, socket_fd=39, exe_file=0178, access=001fffff, cpu=x86_64, info_size=616, objattr={rootdir=0000,attributes=00000000,sd={},name=L""}, info={... ) 00cd: new_process() = 0 { info=017c, pid=00ce, handle=0180 } 00cd: new_thread( process=0180, access=001fffff, suspend=0, request_fd=-1, objattr={rootdir=0000,attributes=00000000,sd={},name=L""} ) 00cd: *fd* 0244 -> 716 ... 00d7: init_thread( unix_pid=30363, unix_tid=30371, debug_level=1, teb=7fffffe8c000, entry=7bcbd070, reply_fd=36, wait_fd=38, cpu=x86_64 ) 00d7: init_thread() = 0 { pid=00ce, tid=00d7, server_start=1d4f7628a8ec3a2 (-43.8978330), info_size=0, version=580, all_cpus=00000003, suspend=0 } ... --- snip ---
MRAC service opening MRAC Anti-Cheat kernel service/driver:
NOTE: I've rearranged the log output in sequential request order to be more readable. Trace messages from follow-up IOCTL issued by service process overlapped/interleaved with kernel driver still processing IRP_MJ_CREATE.
--- snip --- ... 00d7:Call KERNEL32.CreateFileW(1400718a0 L"\\.\mracdrv",00000000,00000003,00000000,00000003,00000000,00000000) ret=14000b3f3 00d7:trace:ntdll:FILE_CreateFile handle=0x98e868 access=00100080 name=L"\??\mracdrv" objattr=00000040 root=(nil) sec=(nil) io=0x98e880 alloc_size=(nil) attr=00000000 sharing=00000003 disp=1 options=00000060 ea=(nil).0x00000000 00d7: open_file_object( access=00100080, attributes=00000040, rootdir=0000, sharing=00000003, options=00000060, filename=L"\??\mracdrv" ) 00dd: *wakeup* signaled=1 00d7: open_file_object() = 0 { handle=00a4 } 00dd:Ret KERNEL32.WaitForMultipleObjectsEx() retval=00000001 ret=7f489a8c8c46 00d7:Ret KERNEL32.CreateFileW() retval=000000a4 ret=14000b3f3 00dd: get_next_device_request( manager=0030, prev=0000, status=00000103 ) 00dd: get_next_device_request() = 0 { params={major=CREATE,access=00100080,sharing=00000003,options=00000060,device=00031e00}, next=0044, client_tid=0000, client_thread=00000000, in_size=0, out_size=0, next_data={} } ... 00dd:trace:ntoskrnl:dispatch_create device 0x31e00 -> file 0x31f70 ... 00dd:trace:ntoskrnl:IoAllocateIrp 1, 0 ... 00dd:trace:ntoskrnl:IoInitializeIrp 0x32040, 280, 1 00dd:Call driver dispatch 0x1400291d4 (device=0x31e00,irp=0x32040) ... 00dd:Call ntoskrnl.exe.IofCompleteRequest(00032040,00000000) ret=140011ce1 ... 00dd:trace:ntoskrnl:IofCompleteRequest 0x32040 0 00dd:trace:ntoskrnl:IoCompleteRequest 0x32040 0 00dd:trace:ntoskrnl:IoCompleteRequest calling 0x7f489a8c6330( 0x31e00, 0x32040, 0x44 ) 00dd: set_irp_result( handle=0044, status=00000000, size=0, file_ptr=00031f70, data={} ) 00dd: set_irp_result() = 0 00dd:trace:ntoskrnl:IoCompleteRequest CompletionRoutine returned 0 00dd:trace:ntoskrnl:IoFreeIrp 0x32040 ... 00dd:Ret ntoskrnl.exe.IofCompleteRequest() retval=00000001 ret=140011ce1 ... 00dd:Ret driver dispatch 0x1400291d4 (device=0x31e00,irp=0x32040) retval=00000000 ... --- snip ---
MRAC service creates an event and passes the handle to the MRAC Anti-Cheat kernel service/driver via private IOCTL:
--- snip ---- ... 00d7:Call KERNEL32.CreateEventW(00000000,00000001,00000000,00000000) ret=14000b42c 00d7:Call ntdll.NtCreateEvent(0098e998,001f0003,0098e9b0,00000000,00000000) ret=7b4a76e3 00d7: create_event( access=001f0003, manual_reset=1, initial_state=0, objattr={rootdir=0000,attributes=00000080,sd={},name=L""} ) 00d7: create_event() = 0 { handle=00a8 } 00d7:Ret ntdll.NtCreateEvent() retval=00000000 ret=7b4a76e3 00d7:Ret KERNEL32.CreateEventW() retval=000000a8 ret=14000b42c ... 00d7:Call rpcrt4.RpcImpersonateClient(00000000) ret=14000b450 ... 00d7:Call advapi32.ImpersonateNamedPipeClient(00000078) ret=7fee32a7e672 ... 00d7:trace:ntdll:NtFsControlFile (0x78,(nil),(nil),(nil),0x98e880,0x0011001c,(nil),0x00000000,(nil),0x00000000) ... 00d7:fixme:ntdll:NtFsControlFile FSCTL_PIPE_IMPERSONATE: impersonating self 00d7:trace:ntdll:RtlImpersonateSelf (00000002) 00d7:trace:ntdll:NtOpenProcessTokenEx (0xffffffffffffffff,0x00000002,0x00000000,0x98e5b0) ... 00d7: open_token( handle=ffffffff, access=00000002, attributes=00000000, flags=00000000 ) 00d7: open_token() = 0 { token=00ac } ... 00d7:trace:ntdll:NtDuplicateToken (0xac,0x00000004,{name=<null>, attr=0x00000000, hRoot=(nil), sd=(nil)} ,0x00000002,0x00000002,0x98e5b8) ... 00d7: duplicate_token( handle=00ac, access=00000004, primary=0, impersonation_level=2, objattr={rootdir=0000,attributes=00000000,sd={},name=L""} ) ... 00d7: duplicate_token() = 0 { new_handle=00b0 } ... 00d7: set_thread_info( handle=fffffffe, mask=4, priority=0, affinity=00000000, entry_point=00000000, token=00b0 ) 00d7: set_thread_info() = 0 ... 00d7: close_handle( handle=00b0 ) 00d7: close_handle() = 0 ... 00d7: close_handle( handle=00ac ) 00d7: close_handle() = 0 00d7:Ret advapi32.ImpersonateNamedPipeClient() retval=00000001 ret=7fee32a7e672 00d7:Ret rpcrt4.RpcImpersonateClient() retval=00000000 ret=14000b450 ... 00d7:Call KERNEL32.DeviceIoControl(000000a4,81002200,0006d250,00000096,0098eba0,00000004,0098ec28,00000000) ret=14000b5b4 ... 00d7:trace:ntdll:NtDeviceIoControlFile (0xa4,(nil),(nil),(nil),0x98e9d0,0x81002200,0x6d250,0x00000096,0x98eba0,0x00000004) ... 00d7: ioctl( code=81002200, async={handle=00a4,event=0000,iosb=0098e9d0,user=0006c120,apc=00000000,apc_context=00000000}, in_data={66,00,00,00,00,00,00,00,a8,00,00,00,00,00,00,00,5c,00,3f,00,3f,00,5c,00,43,00,3a,00,5c,00,4d,00,79,00,47,00,61,00,6d,00,65,00,73,00,5c,00,57,00,61,00,72,00,66,00,61,00,63,00,65,00,20,00,4d,00,79,00,2e,00,43,00,6f,00,6d,00,5c,00,42,00,69,00,6e,00,33,00,32,00,52,00,65,00,6c,00,65,00,61,00,73,00,65,00,5c,00,47,00,61,00,6d,00,65,00,2e,00,65,00,78,00,65,00,01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00} ) 00d7: ioctl() = PENDING { wait=00ac, options=00000060, out_data={} } ... 00d7: select( flags=2, cookie=0098e3dc, timeout=infinite, prev_apc=0000, result={}, data={WAIT_ALL,handles={00ac}} ) 00d7: select() = PENDING { timeout=infinite, call={APC_NONE}, apc_handle=0000 } ... --- snip ---
MRAC Anti-Cheat kernel service/driver processes the IOCTL which contains the sync event and the process name to be started as unicode string.
66,00,00,00,00,00,00,00 -> buflen a8,00,00,00,00,00,00,00 -> handle 00,5c,00,3f ... -> ??\C:\MyGames\Warface My.Com\Bin32Release\Game.exe
--- snip --- ... 00dd: get_next_device_request( manager=0030, prev=0000, status=00000000 ) 00dd: get_next_device_request() = 0 { params={major=DEVICE_CONTROL,code=81002200,file=00031f70}, next=0044, client_tid=00d7, client_thread=00000000, in_size=150, out_size=4, next_data={66,00,00,00,00,00,00,00,a8,00,00,00,00,00,00,00,5c,00,3f,00,3f,00,5c,00,43,00,3a,00,5c,00,4d,00,79,00,47,00,61,00,6d,00,65,00,73,00,5c,00,57,00,61,00,72,00,66,00,61,00,63,00,65,00,20,00,4d,00,79,00,2e,00,43,00,6f,00,6d,00,5c,00,42,00,69,00,6e,00,33,00,32,00,52,00,65,00,6c,00,65,00,61,00,73,00,65,00,5c,00,47,00,61,00,6d,00,65,00,2e,00,65,00,78,00,65,00,01,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00} } 00dd:trace:ntoskrnl:dispatch_ioctl ioctl 81002200 device 0x31e00 file 0x31f70 in_size 150 out_size 4 00dd:trace:ntoskrnl:IoBuildDeviceIoControlRequest 81002200, 0x31e00, 0x26740, 150, 0x26740, 150, 0, (nil), (nil) 00dd:trace:ntoskrnl:IoAllocateIrp 1, 0 ... 00dd:trace:ntoskrnl:IoInitializeIrp 0x32040, 280, 1 ... 00dd:Call driver dispatch 0x1400291d4 (device=0x31e00,irp=0x32040) ... 00dd:Call ntoskrnl.exe.ObReferenceObjectByHandle(000000a8,00000002,7f489a919330,00000000,0043f5e0,00000000) ret=1400125d4 00dd:trace:ntoskrnl:ObReferenceObjectByHandle 0xa8 2 0x7f489a919330 0 0x43f5e0 (nil) 00dd: get_kernel_object_ptr( manager=0030, handle=00a8 ) 00dd: get_kernel_object_ptr() = INVALID_HANDLE { user_ptr=00000000 } 00dd:Ret ntoskrnl.exe.ObReferenceObjectByHandle() retval=c0000008 ret=1400125d4 00dd:Call ntoskrnl.exe.IofCompleteRequest(00032040,00000000) ret=140012665 00dd:trace:ntoskrnl:IofCompleteRequest 0x32040 0 00dd:trace:ntoskrnl:IoCompleteRequest 0x32040 0 00dd:trace:ntoskrnl:IoCompleteRequest calling 0x7f489a8c6330( 0x31e00, 0x32040, 0x44 ) 00dd: set_irp_result( handle=0044, status=c0000008, size=0, file_ptr=00031f70, data={} ) 00d7: *wakeup* signaled=192 00dd: set_irp_result() = 0 00dd:trace:ntoskrnl:IoCompleteRequest CompletionRoutine returned 0 00d7: select( flags=2, cookie=0098e3dc, timeout=infinite, prev_apc=0000, result={}, data={WAIT_ALL,handles={00ac}} ) ... 00d7: select() = USER_APC { timeout=infinite, call={APC_ASYNC_IO,user=0006c120,sb=0098e9d0,status=INVALID_HANDLE}, apc_handle=00b0 } ... 00dd:trace:ntoskrnl:IoFreeIrp 0x32040 ... 00dd:Ret ntoskrnl.exe.IofCompleteRequest() retval=00000001 ret=140012665 ... 00d7: select( flags=2, cookie=0098e3dc, timeout=infinite, prev_apc=00b0, result={APC_ASYNC_IO,status=INVALID_HANDLE,total=0}, data={WAIT_ALL,handles={00ac}} ) ... 00d7: select() = 0 { timeout=infinite, call={APC_NONE}, apc_handle=0000 } ... 00d7:Ret KERNEL32.DeviceIoControl() retval=00000000 ret=14000b5b4 ... 00d7:Call KERNEL32.GetLastError() ret=14000b5cb ... 00d7:Ret KERNEL32.GetLastError() retval=00000006 ret=14000b5cb ... 00d7:Call rpcrt4.RpcRevertToSelfEx(00000000) ret=14000b5e4 ... 00d7:Call advapi32.RevertToSelf() ret=7fee32a7e74d 00dd:Ret driver dispatch 0x1400291d4 (device=0x31e00,irp=0x32040) retval=c0000008 ... 00c9:Call KERNEL32.WideCharToMultiByte(0000fde9,00000000,00e4825c L"Message: 20.04.2019 12:20:14.415 200.201 [TJobClient] GameLaunch: CreateProcess ErrorCode=0x00000006 ProcessHandle=0x00000000 ProcessId=0\r\n",0000008b,09bcfdac,000001a2,00000000,00000000) ret=0040c31c --- snip ---
This obviously can't work since the requestor (winedevice hosting process for the MRAC kernel driver) is not the process which created the handle (MRAC service process).
The methodology to pass usermode process created handles to drivers is pretty much text book. For reference, one of the Microsoft Windows driver examples which essentially does the same thing:
https://github.com/Microsoft/Windows-driver-samples/tree/master/general/even...
Specifically:
https://github.com/Microsoft/Windows-driver-samples/blob/master/general/even...
--- quote --- ... The purpose of this sample is to demonstrate how a kernel-mode driver can notify an user-app about a device event. There are several different techniques. This sample will demonstrate two very commonly used techniques. 1) Using an event: The application creates an event object using CreateEvent(). The app passes the event handle to the driver in a private IOCTL. The driver is running in the app's thread context during the IOCTL so there is a valid user-mode handle at that time. The driver dereferences the user-mode handle into system space & saves the event object pointer for later use. The driver signals the event via KeSetEvent() at IRQL <= DISPATCH_LEVEL. The driver deletes the references to the event object. --- snip ---
$ sha1sum WarfaceMycomLoader_805e0da40d16630c2fe73ed12399cb48_.exe b07e87a029d6697ad823dc03fdbf297c406a91b9 WarfaceMycomLoader_805e0da40d16630c2fe73ed12399cb48_.exe
$ du -sh WarfaceMycomLoader_805e0da40d16630c2fe73ed12399cb48_.exe 6.8M WarfaceMycomLoader_805e0da40d16630c2fe73ed12399cb48_.exe
$ wine --version wine-4.6-61-g085e58878f
Regards
https://bugs.winehq.org/show_bug.cgi?id=47052
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- Keywords| |download, obfuscation, | |win64 URL| |https://web.archive.org/web | |/20190331063634/http://stat | |ic.gc.my.com/WarfaceMycomLo | |ader.exe#0.7927247509897362
https://bugs.winehq.org/show_bug.cgi?id=47052
Jacek Caban jacek@codeweavers.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |jacek@codeweavers.com
--- Comment #1 from Jacek Caban jacek@codeweavers.com --- Created attachment 64758 --> https://bugs.winehq.org/attachment.cgi?id=64758 hack
The attached hack works around the problem for MRAC.
For the proper fix, we'd need server handle code to be aware of kernel threads acting as user threads. I experimented with that at some point and even got a hackish version working with MRAC, but it requires server-wide changes and tends to break more things than it fixes. It still requires much more work.
https://bugs.winehq.org/show_bug.cgi?id=47052
Alistair Leslie-Hughes leslie_alistair@hotmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- Keywords| |patch