https://bugs.winehq.org/show_bug.cgi?id=44585
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- Staged patchset| |https://github.com/wine-sta | |ging/wine-staging/tree/mast | |er/patches/ntdll-LdrRegiste | |rDllNotification URL| |https://eu.battle.net/accou | |nt/download/ Status|UNCONFIRMED |STAGED Keywords| |download, obfuscation Component|-unknown |ntdll CC| |focht@gmx.net Summary|Diablo III doesn't launch, |Blizzard Diablo III v2. 6. |stops at |1. 49286+ crashes with |"setup_exception_record |setup_exception_record |stack overflow ..." |stack overflow (needs | |ntdll.LdrRegisterDllNotific | |ation/LdrUnregisterDllNotif | |ication implementation) Ever confirmed|0 |1
--- Comment #1 from Anastasius Focht focht@gmx.net --- Hello folks,
confirming.
The game executable seems to be wrapped with some custom protection scheme.
TLS callback installs VEH to decrypt entry point code via privileged instruction exception handling (HLT, UD2).
--- snip --- $ pwd /home/focht/wine-games/wineprefix64-bnet/drive_c/Program Files (x86)/Diablo III
$ WINEDEBUG=+seh,+relay wine ./Diablo\ III.exe -launch >>log.txt 2>&1 ... 002e:Ret TLS callback (proc=0x6aa080,module=0x400000,reason=PROCESS_ATTACH,reserved=0) 002e:Call TLS callback (proc=0x40d450,module=0x400000,reason=PROCESS_ATTACH,reserved=0) 002e:Call ntdll.RtlAddVectoredExceptionHandler(00000001,00422ae0) ret=0040bd2c 002e:Ret ntdll.RtlAddVectoredExceptionHandler() retval=0012df98 ret=0040bd2c 002e:trace:seh:raise_exception code=c0000096 flags=0 addr=0x40bdc4 ip=0040bdc4 tid=002e 002e:trace:seh:raise_exception eax=00000000 ebx=7bc30000 ecx=7bc37070 edx=7bcf7604 esi=7bcf07e8 edi=00000000 002e:trace:seh:raise_exception ebp=0033e2a0 esp=0033e26c cs=0023 ds=002b es=002b fs=0063 gs=006b flags=00010246 002e:trace:seh:call_vectored_handlers calling handler at 0x422ae0 code=c0000096 flags=0 002e:trace:seh:call_vectored_handlers handler at 0x422ae0 returned ffffffff 002e:trace:seh:raise_exception code=c0000096 flags=0 addr=0x40c0b1 ip=0040c0b1 tid=002e 002e:trace:seh:raise_exception eax=7b42ea00 ebx=7b6416b0 ecx=0000ea60 edx=7b420000 esi=7b642a4c edi=00000000 002e:trace:seh:raise_exception ebp=0033e2a0 esp=0033e26c cs=0023 ds=002b es=002b fs=0063 gs=006b flags=00010246 002e:trace:seh:call_vectored_handlers calling handler at 0x422ae0 code=c0000096 flags=0 ... 002e:Call KERNEL32.VirtualAlloc(00000000,000111f8,00003000,00000040) ret=0040e721 002e:Ret KERNEL32.VirtualAlloc() retval=00340000 ret=0040e721 002e:Call ntdll.NtCreateSection(0033e7f0,000f001f,00000000,0033e7c8,00000040,08400000,00000000) ret=0040e820 002e:Ret ntdll.NtCreateSection() retval=00000000 ret=0040e820 002e:Call KERNEL32.MapViewOfFileEx(00000040,000f001f,00000000,00000000,00000000,00000000) ret=0040e917 002e:Ret KERNEL32.MapViewOfFileEx() retval=025e0000 ret=0040e917 002e:Call KERNEL32.VirtualProtect(00400000,021d7000,00000040,0033e7e8) ret=0040e970 002e:Ret KERNEL32.VirtualProtect() retval=00000001 ret=0040e970 002e:trace:seh:raise_exception code=c0000096 flags=0 addr=0x40ea2b ip=0040ea2b tid=002e 002e:trace:seh:raise_exception eax=5f703102 ebx=0033e954 ecx=00000000 edx=00000002 esi=025d7000 edi=0033e3c0 002e:trace:seh:raise_exception ebp=0033e940 esp=0033e2b0 cs=0023 ds=002b es=002b fs=0063 gs=006b flags=00010202 002e:trace:seh:call_vectored_handlers calling handler at 0x422ae0 code=c0000096 flags=0 002e:trace:seh:call_vectored_handlers handler at 0x422ae0 returned ffffffff 002e:Call KERNEL32.UnmapViewOfFile(00400000) ret=00351179 002e:Ret KERNEL32.UnmapViewOfFile() retval=00000001 ret=00351179 002e:Call KERNEL32.MapViewOfFileEx(00000040,00000024,00000000,00000000,01770000,00400000) ret=003511d9 002e:Ret KERNEL32.MapViewOfFileEx() retval=00400000 ret=003511d9 002e:Call KERNEL32.MapViewOfFileEx(00000040,00000006,00000000,01770000,00420000,01b70000) ret=003511d9 002e:Ret KERNEL32.MapViewOfFileEx() retval=01b70000 ret=003511d9 002e:Call KERNEL32.MapViewOfFileEx(00000040,00000004,00000000,01b90000,00000000,01f90000) ret=003511d9 002e:Ret KERNEL32.MapViewOfFileEx() retval=01f90000 ret=003511d9 002e:trace:seh:raise_exception code=c0000096 flags=0 addr=0x40ebd1 ip=0040ebd1 tid=002e 002e:trace:seh:raise_exception eax=00000003 ebx=0033e954 ecx=0033e144 edx=00000000 esi=00415960 edi=003511f8 002e:trace:seh:raise_exception ebp=0033e940 esp=0033e2b0 cs=0023 ds=002b es=002b fs=0063 gs=006b flags=00010213 002e:trace:seh:call_vectored_handlers calling handler at 0x422ae0 code=c0000096 flags=0 002e:trace:seh:call_vectored_handlers handler at 0x422ae0 returned ffffffff ... 002e:Call ntdll.RtlRemoveVectoredExceptionHandler(0012df98) ret=004101e9 002e:Ret ntdll.RtlRemoveVectoredExceptionHandler() retval=00000001 ret=004101e9 ... --- snip ---
Custom imports resolver, with dynamic thunk creation
--- snip --- ... 002e:Call KERNEL32.LoadLibraryA(0033dedc "KERNEL32.dll") ret=00418c89 002e:Ret KERNEL32.LoadLibraryA() retval=7b420000 ret=00418c89 002e:Call KERNEL32.VirtualAlloc(00000000,00001000,00001000,00000040) ret=00419133 002e:Ret KERNEL32.VirtualAlloc() retval=00370000 ret=00419133 002e:Call KERNEL32.GetProcAddress(7b420000,0033d9ec "FlushViewOfFile") ret=0041953e 002e:Ret KERNEL32.GetProcAddress() retval=7b42fcd8 ret=0041953e 002e:Call KERNEL32.GetProcAddress(7b420000,0033d9ec "UnlockFileEx") ret=0041953e 002e:Ret KERNEL32.GetProcAddress() retval=7b433998 ret=0041953e ... --- snip ---
Example of win32 API call via generated thunk:
--- snip --- ... 0115AF01 FF75 0C PUSH DWORD PTR SS:[EBP+C] 0115AF04 50 PUSH EAX ; thunk for 0115AF05 FF15 24157301 CALL DWORD PTR DS:[1731524] ; KERNEL32.GetProcAddress 0115AF0B 8BF0 MOV ESI,EAX 0115AF0D 85F6 TEST ESI,ESI 0115AF0F 74 13 JE SHORT 0115AF24
thunk:
--- snip --- ... 01731524 003F0A6E ... 003F0A6E B8 1276EBCA MOV EAX,CAEB7612 003F0A73 E9 B4FDFFFF JMP 003F082C ... 003F082C 2D 413A9229 SUB EAX,29923A41 003F0831 05 0B7E7417 ADD EAX,17747E0B 003F0836 35 EBE9F83C XOR EAX,3CF8E9EB 003F083B E9 62060000 JMP 003F0EA2 ... 003F0EA2 2D 93E96315 SUB EAX,1563E993 003F0EA7 E9 1CFFFFFF JMP 003F0DC8 ... 003F0DC8 05 9FD9740C ADD EAX,0C74D99F 003F0DCD FFE0 JMP EAX ; -> real KERNEL32.GetProcAddress
Wine KERNEL32.GetProcAddress:
7B464043 8D4C24 04 LEA ECX,DWORD PTR SS:[ESP+4] 7B464047 83E4 F0 AND ESP,FFFFFFF0 7B46404A FF71 FC PUSH DWORD PTR DS:[ECX-4] 7B46404D 55 PUSH EBP 7B46404E 89E5 MOV EBP,ESP 7B464050 51 PUSH ECX 7B464051 83EC 04 SUB ESP,4 7B464054 89C8 MOV EAX,ECX 7B464056 83EC 08 SUB ESP,8 7B464059 FF70 04 PUSH DWORD PTR DS:[EAX+4] 7B46405C FF30 PUSH DWORD PTR DS:[EAX] 7B46405E E8 C7FFFFFF CALL 7B46402A ... --- snip ---
Mappings:
--- snip --- 00400000 00001000 Diablo_III PE header Map R E R E 00401000 01330000 Diablo_III .text Code Map R E R E 01731000 0043F000 Diablo_III .rdata Imports Map R E R E 01B70000 0000B000 Diablo_III .rdata Exports Map RW RW 01B7B000 00403000 Diablo_III .data Data Map RW RW 01F7E000 00001000 Diablo_III stkresv Map RW RW 01F7F000 00001000 Diablo_III .gfids Map RW RW 01F80000 00001000 Diablo_III .tls Map RW RW 01F81000 0000A000 Diablo_III _RDATA Map RW RW 01F8B000 00005000 Diablo_III .rsrc Resources Map RW RW 01F90000 0052C000 Diablo_III .rsrc Resources Map R R 024BC000 0011B000 Diablo_III .reloc Relocations Map R R --- snip ---
A few native API functions get directly resolved in memory by walking the export table, with no visible 'GetProcAddress' calls.
--- snip --- ... 002e:Call KERNEL32.GetStartupInfoW(0033fe30) ret=0111c114 002e:Ret KERNEL32.GetStartupInfoW() retval=00000011 ret=0111c114 002e:Call KERNEL32.VirtualQuery(0033fe34,0033fe38,0000001c) ret=006ad5db 002e:Ret KERNEL32.VirtualQuery() retval=0000001c ret=006ad5db 002e:trace:seh:raise_exception code=c000001d flags=0 addr=0x6a2008 ip=006a2008 tid=002e 002e:trace:seh:raise_exception eax=95a775b1 ebx=0033ff00 ecx=0033fe00 edx=0111b32f esi=0013703b edi=00340000 002e:trace:seh:raise_exception ebp=0033fe1c esp=0033fde8 cs=0023 ds=002b es=002b fs=0063 gs=006b flags=00010297 002e:trace:seh:call_stack_handlers calling handler at 0x111e8c8 code=c000001d flags=0 002e:Call iphlpapi.GetAdaptersInfo(00339330,0033e438) ret=004f26c1 ... 002e:Ret iphlpapi.GetAdaptersInfo() retval=00000000 ret=004f26c1 002e:Call KERNEL32.GetVolumeInformationW(00000000,00000000,00000000,0033e44c,00000000,00000000,00000000,00000000) ret=004f28af 002e:Ret KERNEL32.GetVolumeInformationW() retval=00000001 ret=004f28af 002e:trace:seh:raise_exception code=c0000005 flags=0 addr=(nil) ip=00000000 tid=002e 002e:trace:seh:raise_exception info[0]=00000008 002e:trace:seh:raise_exception info[1]=00000000 002e:trace:seh:raise_exception eax=00000000 ebx=00000023 ecx=00000000 edx=0033f8f0 esi=0000002b edi=0000002b 002e:trace:seh:raise_exception ebp=00000000 esp=0033f900 cs=0023 ds=002b es=002b fs=0063 gs=006b flags=00010286 002e:trace:seh:call_stack_handlers calling handler at (nil) code=c0000005 flags=0 002e:trace:seh:raise_exception code=c0000005 flags=0 addr=(nil) ip=00000000 tid=002e 002e:trace:seh:raise_exception info[0]=00000008 002e:trace:seh:raise_exception info[1]=00000000 002e:trace:seh:raise_exception eax=0033f49c ebx=00000023 ecx=00000000 edx=7bc8fecc esi=0000002b edi=0000002b 002e:trace:seh:raise_exception ebp=0033f448 esp=0033f41c cs=0023 ds=002b es=002b fs=0063 gs=006b flags=00010216 002e:trace:seh:call_stack_handlers calling handler at 0x7bc8fecc code=c0000005 flags=0 002e:trace:seh:call_stack_handlers handler at 0x7bc8fecc returned 2 002e:trace:seh:call_stack_handlers calling handler at (nil) code=c0000005 flags=10 --- snip ---
The resolver doesn't handle the case properly when a native API function is missing. It happily generates thunks that decrypt NULL pointers before the jump.
The Wine-Staging 2.21 trace log reveals the missing native API:
--- snip --- ... 0009:Call KERNEL32.GetVolumeInformationW(00000000,00000000,00000000,0033e3dc,00000000,00000000,00000000,00000000) ret=004f28af 0009:Ret KERNEL32.GetVolumeInformationW() retval=00000001 ret=004f28af 0009:Call ntdll.LdrRegisterDllNotification(00000000,006a0ce0,00000000,0033e5b8) ret=004db5d0 0009:Ret ntdll.LdrRegisterDllNotification() retval=00000000 ret=004db5d0 0009:Call ntdll.NtCreateFile(0033e338,00120089,0033e2bc,0033e2b4,00000000,00000000,00000001,00000001,00000020,00000000,00000000) ret=004e0a5f 0009:Ret ntdll.NtCreateFile() retval=00000000 ret=004e0a5f 0009:Call ntdll.NtQueryInformationFile(000000c0,0033e2ac,0033e294,00000018,00000005) ret=004e0f91 0009:Ret ntdll.NtQueryInformationFile() retval=00000000 ret=004e0f91 0009:Call ntdll.NtAllocateVirtualMemory(ffffffff,0033e394,00000000,0033e304,00001000,00000040) ret=004e143d 0009:Ret ntdll.NtAllocateVirtualMemory() retval=00000000 ret=004e143d 0009:Call ntdll.NtReadFile(000000c0,00000000,00000000,00000000,0033e2b4,05070000,00013000,00000000,00000000) ret=004e1786 0009:Ret ntdll.NtReadFile() retval=00000000 ret=004e1786 0009:Call ntdll.NtAllocateVirtualMemory(ffffffff,0033e5c8,00000000,0033e628,00001000,00000004) ret=004dbcee 0009:Ret ntdll.NtAllocateVirtualMemory() retval=00000000 ret=004dbcee 0009:Call user32.SetWinEventHook(00000001,7fffffff,00000000,006a3f20,00000000,00000000,00000002) ret=004ddc86 0009:Ret user32.SetWinEventHook() retval=0002004e ret=004ddc86 ... --- snip ---
These are Vista+ native API:
* ntdll.LdrRegisterDllNotification * ntdll.LdrUnregisterDllNotification
https://github.com/wine-staging/wine-staging/tree/master/patches/ntdll-LdrRe...
The callback is likely used for anti-cheat/hack protection to:
* filter for known/unwanted modules * consistency check in combination with other module enumeration methods * resolving API at very early stage/hooking * pre-patching code before module attach/detach notifications are being sent * replace/redirect the loaded module entry point ... * profit!
ProtectionID scan for exact version:
--- snip --- -=[ ProtectionID v0.6.9.0 DECEMBER]=- (c) 2003-2017 CDKiLLER & TippeX Build 24/12/17-21:05:42 Ready... Scanning -> Z:\home\focht\wine-games\wineprefix64-bnet\drive_c\Program Files (x86)\Diablo III\Diablo III.exe File Type : 32-Bit Exe (Subsystem : Win GUI / 2), Size : 31663592 (01E325E8h) Byte(s) | Machine: 0x14C (I386) Compilation TimeStamp : 0x5A839757 -> Wed 14th Feb 2018 01:56:39 (GMT) [TimeStamp] 0x5A839757 -> Wed 14th Feb 2018 01:56:39 (GMT) | PE Header | - | Offset: 0x00000178 | VA: 0x00400178 | - [TimeStamp] 0x5A83974F -> Wed 14th Feb 2018 01:56:31 (GMT) | Export | - | Offset: 0x0176EC84 | VA: 0x01B70484 | - [TimeStamp] 0x5A839757 -> Wed 14th Feb 2018 01:56:39 (GMT) | DebugDirectory | - | Offset: 0x015380D4 | VA: 0x019398D4 | - [TimeStamp] 0x5A839757 -> Wed 14th Feb 2018 01:56:39 (GMT) | DebugDirectory | - | Offset: 0x015380F0 | VA: 0x019398F0 | - [TimeStamp] 0x5A839757 -> Wed 14th Feb 2018 01:56:39 (GMT) | DebugDirectory | - | Offset: 0x0153810C | VA: 0x0193990C | - -> File Appears to be Digitally Signed @ Offset 01E30E00h, size : 017E8h / 06120 byte(s) [!] Executable uses TLS callbacks (3 total... 0 invalid addresses) [LoadConfig] Struct determined as v8 (Expected size 140 | Actual size 64) [!] Executable uses SEH Tables (/SAFESEH) (33371 calculated 33199 recorded... 70 invalid addresses) [!] * table may be compressed / encrypted * [LoadConfig] CodeIntegrity -> Flags 0x7C34 | Catalog 0xBFD3 (49107) | Catalog Offset 0x2FE93D5F | Reserved 0x49CB461F [LoadConfig] GuardAddressTakenIatEntryTable 0x4F29FF9C | Count 0x992AA633 (2569709107) [LoadConfig] GuardLongJumpTargetTable 0x12E9CCF3 | Count 0xB9F3D522 (3119764770) [LoadConfig] HybridMetadataPointer 0x21CA7CB6 | DynamicValueRelocTable 0xF1654DDC [LoadConfig] FailFastIndirectProc 0x64AE2974 | FailFastPointer 0xCA03C693 [LoadConfig] UnknownZero1 0x6B52C00 [!] Warning: Imports (+size) goes outside of the file [File Heuristics] -> Flag #1 : 00000100000001001101000100110100 (0x0404D134) [Entrypoint Section Entropy] : 8.00 (section #0) ".text " | Size : 0x132F368 (20116328) byte(s) [DllCharacteristics] -> Flag : (0x8140) -> ASLR | DEP | TSA [SectionCount] 9 (0x9) | ImageSize 0x21D7000 (35483648) byte(s) [Export] 96% of function(s) (357 of 369) are in file | 0 are forwarded | 343 code | 26 data | 0 uninit data | 0 unknown | [VersionInfo] Company Name : Blizzard Entertainment [VersionInfo] Product Name : Diablo III [VersionInfo] Product Version : Version 2.6 [VersionInfo] File Description : Diablo III Retail [VersionInfo] File Version : 2. 6. 1. 49286 [VersionInfo] Original FileName : Diablo III.exe [VersionInfo] Internal Name : Diablo III [VersionInfo] Legal Copyrights : Copyright © 2011-2014 [ModuleReport] [IAT] Modules -> USER32.dll [Debug Info] (record 1 of 3) (file offset 0x15380D0) Characteristics : 0x0 | TimeDateStamp : 0x5A839757 (Wed 14th Feb 2018 01:56:39 (GMT)) | MajorVer : 0 / MinorVer : 0 -> (0.0) Type : 2 (0x2) -> CodeView | Size : 0x53 (83) AddressOfRawData : 0x158C96C | PointerToRawData : 0x158B16C CvSig : 0x53445352 | SigGuid E65D58F8-37DC-47BC-B32D251EF458F1F8 Age : 0x1 (1) | Pdb : D:\BuildServer\4\work\code\branches\Release\Diablo III.pdb [Debug Info] (record 2 of 3) (file offset 0x15380EC) Characteristics : 0x0 | TimeDateStamp : 0x5A839757 (Wed 14th Feb 2018 01:56:39 (GMT)) | MajorVer : 0 / MinorVer : 0 -> (0.0) Type : 12 (0xC) -> Undocumented | Size : 0x14 (20) AddressOfRawData : 0x158C9C0 | PointerToRawData : 0x158B1C0 [Debug Info] (record 3 of 3) (file offset 0x1538108) Characteristics : 0x0 | TimeDateStamp : 0x5A839757 (Wed 14th Feb 2018 01:56:39 (GMT)) | MajorVer : 0 / MinorVer : 0 -> (0.0) Type : 13 (0xD) -> Undocumented | Size : 0x828 (2088) AddressOfRawData : 0x158C9D4 | PointerToRawData : 0x158B1D4 [CdKeySerial] found "Invalid code" @ VA: 0x0133D078 / Offset: 0x0133B878 [CdKeySerial] found "SerialNumber" @ VA: 0x0147899F / Offset: 0x0147719F [CdKeySerial] found "Invalid code" @ VA: 0x014C7980 / Offset: 0x014C6180 [CompilerDetect] -> Visual C++ 14.0 (Visual Studio 2015) [!] File appears to have no protection or is using an unknown protection - Scan Took : 5.583 Second(s) [000001529h (5417) tick(s)] [506 of 580 scan(s) done] --- snip ---
$ wine --version wine-3.2-173-gea82a00a42
Regards