https://bugs.winehq.org/show_bug.cgi?id=45349
Bug ID: 45349 Summary: Multiple applications and games crash due to missing support for 64-bit syscall thunks (StreetFighter V) Product: Wine Version: 3.10 Hardware: x86-64 OS: Linux Status: NEW Severity: normal Priority: P2 Component: ntdll Assignee: wine-bugs@winehq.org Reporter: focht@gmx.net Distribution: ---
Hello folks,
had this one lying around for some time, sadly got distracted from further analysis (there are more bugs).
The game is "StreetFighter V Arcade Edition" and was gifted to me by Valve for investigation, thanks ;-)
https://store.steampowered.com/app/310950/Street_Fighter_V_Arcade_Edition/
It has some custom DRM/protection scheme which uses various 64-bit anti-debug and obfuscation techniques. There are a lot of single-step (TF) exceptions in the code which are there by design (decrypt continuations). At one point it crashes with invalid instruction exception:
--- snip --- ... 002e:trace:seh:NtRaiseException code=80000004 flags=0 addr=0x143fc99e9 ip=143fc99e9 tid=002e 002e:trace:seh:NtRaiseException rax=00000000006000aa rbx=000000000000005e rcx=000000000000005f rdx=000000000000071c 002e:trace:seh:NtRaiseException rsi=0000000143fcf826 rdi=0000000000000000 rbp=00000000005ffbd8 rsp=00000000005ffbd0 002e:trace:seh:NtRaiseException r8=0000000143fd2240 r9=000000000000000c r10=0000000143fd26f8 r11=0000000000000000 002e:trace:seh:NtRaiseException r12=000000000000005f r13=0000000000000000 r14=0000000000000000 r15=0000000000000000 002e:trace:seh:RtlVirtualUnwind type 1 rip 143fc99e9 rsp 5ffbd0 002e:trace:seh:dump_unwind_info **** func 3fc9980-3fca910 002e:trace:seh:dump_unwind_info unwind info at 0x143fd26f0 flags 1 prolog 0x0 bytes function 0x143fc9980-0x143fca910 002e:trace:seh:dump_unwind_info handler 0x143fd2240 data at 0x143fd26f8 002e:trace:seh:call_handler calling handler 0x143fd2240 (rec=0x5ffa90, frame=0x5ffbd0 context=0x5fed30, dispatch=0x5ff200) 002e:trace:seh:call_handler handler at 0x143fd2240 returned 0 002e:trace:module:LdrGetDllHandle L"Kernel32.dll" -> 0x7b460000 (load path L"Z:\home\focht\Downloads;C:\windows\system32;C:\windows\system;C:\windows;.;C:\windows\system32;C:\windows;C:\windows\system32\wbem") 002e:trace:ntdll:NtQueryInformationProcess (0xffffffffffffffff,0x00000007,0x5ffcb0,0x00000008,0x5ffcb0) 002e: get_process_info( handle=ffffffff ) 002e: get_process_info() = 0 { pid=002d, ppid=0000, affinity=000000ff, peb=7fffffeaf000, start_time=1d3e17d69097156 (-0.0309210), end_time=0, exit_code=259, priority=2, cpu=x86_64, debugger_present=0, debug_children=1 } 002e:trace:module:LdrGetDllHandle L"Kernel32.dll" -> 0x7b460000 (load path L"Z:\home\focht\Downloads;C:\windows\system32;C:\windows\system;C:\windows;.;C:\windows\system32;C:\windows;C:\windows\system32\wbem") 002e:trace:seh:NtRaiseException code=c0000096 flags=0 addr=0x143fca968 ip=143fca968 tid=002e 002e:trace:seh:NtRaiseException rax=00000000564d5868 rbx=000000008685d465 rcx=000000000000000a rdx=0000000143fc5658 002e:trace:seh:NtRaiseException rsi=00000000005ffd58 rdi=0000000143fd3344 rbp=00000000005ffc18 rsp=00000000005ffc08 002e:trace:seh:NtRaiseException r8=0000000143fd26f8 r9=000000000000000c r10=0000000143fd26f8 r11=0000000000000000 002e:trace:seh:NtRaiseException r12=0000000000000000 r13=0000000000000000 r14=0000000000000000 r15=0000000000000000 002e:trace:seh:RtlVirtualUnwind type 1 rip 143fca968 rsp 5ffc08 002e:trace:seh:dump_unwind_info **** func 3fca910-3fcfbbc 002e:trace:seh:dump_unwind_info unwind info at 0x143fd26f0 flags 1 prolog 0x0 bytes function 0x143fca910-0x143fcfbbc 002e:trace:seh:dump_unwind_info handler 0x143fcbbe8 data at 0x143fd26f8 002e:trace:seh:call_handler calling handler 0x143fcbbe8 (rec=0x5ffac0, frame=0x5ffc08 context=0x5fed60, dispatch=0x5ff230) 002e:trace:seh:call_handler handler at 0x143fcbbe8 returned 0 ... --- snip ---
One has to debug it to see the mess. For that you need to defeat a few anti-debug trickery ;-)
The game protection copies native API entry point code around and assumes 64-bit Windows API entries which have a distinct signature/sequence. A similar problem is described in bug 21232 which is about 32-bit syscall thunks.
Partial copy of 'ntdll.NtQueryInformationProcess' entry point:
--- snip --- 0000000143FCFA88 | 55 | push rbp 0000000143FCFA89 | 48 89 E5 | mov rbp, rsp 0000000143FCFA8C | 57 | push rdi 0000000143FCFA8D | 56 | push rsi 0000000143FCFA8E | 48 81 EC A0 00 00 00 | sub rsp, A0 0000000143FCFA95 | 48 83 E4 F0 | and rsp, FFFFFFFFFFFFFFF0 0000000143FCFA99 | 48 81 EC 40 03 00 00 | sub rsp, 340 0000000143FCFAA0 | 0F 11 B5 50 FF FF FF | movups xmmword ptr ss:[rbp - B0], xmm6 0000000143FCFAA7 | 0F C4 08 33 | pinsrw mm1, word ptr ds:[rax], 33 0000000143FCFAAB | C0 48 83 C4 | ror byte ptr ds:[rax - 7D], C4 0000000143FCFAAF | 28 C3 | sub bl, al 0000000143FCFAB1 | CC | int3 0000000143FCFAB2 | CC | int3 0000000143FCFAB3 | CC | int3 0000000143FCFAB4 | CC | int3 0000000143FCFAB5 | CC | int3 0000000143FCFAB6 | CC | int3 0000000143FCFAB7 | CC | int3 0000000143FCFAB8 | 48 89 5C 24 08 | mov qword ptr ss:[rsp + 8], rbx 0000000143FCFABD | 48 89 6C 24 10 | mov qword ptr ss:[rsp + 10], rbp 0000000143FCFAC2 | 48 89 74 24 18 | mov qword ptr ss:[rsp + 18], rsi 0000000143FCFAC7 | 57 | push rdi 0000000143FCFAC8 | 48 83 EC 30 | sub rsp, 30 0000000143FCFACC | 41 8B D9 | mov ebx, r9d 0000000143FCFACF | 49 8B F0 | mov rsi, r8 0000000143FCFAD2 | 8B EA | mov ebp, edx 0000000143FCFAD4 | 48 8B F9 | mov rdi, rcx 0000000143FCFAD7 | EB 02 | jmp streetfighterv.143FCFADB 0000000143FCFAD9 | 48 EB E8 | jmp streetfighterv.143FCFAC4 --- snip ---
This obviously can't work. On 64-bit Windows the native API entry code is much smaller (< 16 bytes). Due to partial opcode copy a fault is triggered when executing.
Original 64-bit Wine API entry code:
--- snip --- 000000007BCEF058 55 push rbp 000000007BCEF059 48 89 E5 mov rbp, rsp 000000007BCEF05C 57 push rdi 000000007BCEF05D 56 push rsi 000000007BCEF05E 48 81 EC A0 00 00 00 sub rsp, 0A0h 000000007BCEF065 48 83 E4 F0 and rsp, 0FFFFFFFFFFFFFFF0h 000000007BCEF069 48 81 EC 40 03 00 00 sub rsp, 340h 000000007BCEF070 0F 11 B5 50 FF FF FF movups [rbp+var_B0], xmm6 000000007BCEF077 0F 11 BD 60 FF FF FF movups [rbp+var_A0], xmm7 000000007BCEF07E 44 0F 11 85 70 FF FF FF movups [rbp+var_90], xmm8 000000007BCEF086 44 0F 11 4D 80 movups [rbp+var_80], xmm9 000000007BCEF08B 44 0F 11 55 90 movups [rbp+var_70], xmm10 000000007BCEF090 44 0F 11 5D A0 movups [rbp+var_60], xmm11 000000007BCEF095 44 0F 11 65 B0 movups [rbp+var_50], xmm12 000000007BCEF09A 44 0F 11 6D C0 movups [rbp+var_40], xmm13 000000007BCEF09F 44 0F 11 75 D0 movups [rbp+var_30], xmm14 000000007BCEF0A4 44 0F 11 7D E0 movups [rbp+var_20], xmm15 000000007BCEF0A9 48 89 4D 10 mov [rbp+ProcessHandle], rcx 000000007BCEF0AD 89 55 18 mov [rbp+ProcessInformationClass], edx 000000007BCEF0B0 4C 89 45 20 mov [rbp+ProcessInformation], r8 000000007BCEF0B4 44 89 4D 28 mov [rbp+ProcessInformationLength], r9d 000000007BCEF0B8 C7 84 24 3C 03 00 00 00+ mov dword ptr [rsp+3F0h+n+4], 0 000000007BCEF0C3 C7 84 24 38 03 00 00 00+ mov dword ptr [rsp+3F0h+n], 0 000000007BCEF0CE 48 B8 70 CA D9 7B 00 00+ mov rax, offset __wine_dbch_ntdll_6 ... --- snip ---
Wine-Staging patchset: https://github.com/wine-staging/wine-staging/tree/HEAD/patches/winebuild-Fak...
Relevant part: https://github.com/wine-staging/wine-staging/blob/HEAD/patches/winebuild-Fak... (+prerequisite).
Generated 64-bit thunks:
--- snip --- ... 0000000143FCFA88 | B8 70 00 00 00 | mov eax, 70 0000000143FCFA8D | 65 FF 14 25 00 01 00 00 | call qword ptr gs:[100] 0000000143FCFA95 | C3 | ret 0000000143FCFA96 | 00 00 | add byte ptr ds:[rax], al 0000000143FCFA98 | B8 71 00 00 00 | mov eax, 71 0000000143FCFA9D | 65 FF 14 25 00 01 00 00 | call qword ptr gs:[100] 0000000143FCFAA5 | C3 | ret ... --- snip ---
NOTE: I wanted to keep a separate ticket for 64-bit syscall thunks to track/validate it separately from 32-bit parts (bug 21232).
Regards