https://bugs.winehq.org/show_bug.cgi?id=46949
Bug ID: 46949 Summary: DDraw games using older windowed mode wrappers such as DirectX Windower Embedded v2.3/D3D Windower v1.x crash (ddraw_palette_vtbl and ddraw_surface{1,2,3,4,7}_vtbl need to be writable) Product: Wine Version: 4.5 Hardware: x86-64 OS: Linux Status: NEW Severity: normal Priority: P2 Component: directx-d3d Assignee: wine-bugs@winehq.org Reporter: focht@gmx.net Distribution: ---
Hello folks,
encountered while investigating bug 44803 ("Age of Empires II Forgotten Empires crashes "Unhandled privileged instruction").
There are various community installers that claim to add windowed mode support for old DDraw-based games. The presence of older windowed mode wrappers causes the games to crash. These old wrappers (over)write function pointers directly to vtables without changing the page protection.
--- snip --- -=[ ProtectionID v0.6.9.0 DECEMBER]=- (c) 2003-2017 CDKiLLER & TippeX Build 24/12/17-21:05:42 Ready... Scanning -> C:\Program Files (x86)\Microsoft Games\Age of Empires II\age2_x1\wndmode.dll File Type : 32-Bit Dll (Subsystem : Win GUI / 2), Size : 778752 (0BE200h) Byte(s) | Machine: 0x14C (I386) Compilation TimeStamp : 0x413D41E7 -> Tue 07th Sep 2004 05:06:47 (GMT) [TimeStamp] 0x413D41E7 -> Tue 07th Sep 2004 05:06:47 (GMT) | PE Header | - | Offset: 0x00000208 | VA: 0x00400208 | - [File Heuristics] -> Flag #1 : 00000000000000001000000000100000 (0x00008020) [Entrypoint Section Entropy] : 6.54 (section #0) ".text " | Size : 0x98400 (623616) byte(s) [DllCharacteristics] -> Flag : (0x0000) -> NONE [SectionCount] 7 (0x7) | ImageSize 0xC8000 (819200) byte(s) [VersionInfo] Product Name : DirectX Windower Embedded v2.3 (based on D3D Windower v1.88) [VersionInfo] File Description : DirectX Windower Embedded [VersionInfo] File Version : 2.3.0.12 [VersionInfo] Original FileName : wndmode.dll [VersionInfo] Legal Copyrights : (C) 2008 VEG veg@tut.by. (C) 2004 menopem menopem@yahoo.co.jp [ModuleReport] [IAT] Modules -> KERNEL32.DLL | ADVAPI32.DLL | COMCTL32.DLL | GDI32.DLL | OLE32.DLL | OLEAUT32.DLL | USER32.DLL | VERSION.DLL | WINMM.DLL [CdKeySerial] found "Invalid code" @ VA: 0x00096E24 / Offset: 0x00096424 [CompilerDetect] -> Borland C/C++ [!] File appears to have no protection or is using an unknown protection - Scan Took : 0.410 Second(s) [00000019Ah (410) tick(s)] [246 of 580 scan(s) done] --- snip ---
--- snip --- $ winedbg --gdb ./age2_x2.exe
Wine-gdb> bt
#0 ddraw1_CreatePalette (iface=0x192a7c, flags=68, entries=0x32c048, palette=0x9261fc, outer_unknown=0x0) at /home/focht/projects/wine/mainline-src/dlls/ddraw/ddraw.c:3521 #1 0x00b21274 in ?? () #2 0x005a1019 in ?? () #3 0x00000000 in ?? ()
...
Wine-gdb> x/10i $eip
=> 0xb21291: movl $0xb212a0,0x18(%edx) 0xb21298: pop %ebx 0xb21299: pop %ebp 0xb2129a: ret $0x14
Wine-gdb> x/10x $edx
0x1c0588: 0x7e0e21c8 0x00000001 0x001c0748 0x00192a70 0x1c0598: 0x00000000 0x00000044 0x00000000 0x001c0580 0x1c05a8: 0x00000080 0x00455355
Wine-gdb> x/10x 0x7e0e21c8
0x7e0e21c8 <ddraw_palette_vtbl>: 0x7e09f667 0x7e09f74d 0x7e09f7fc 0x7e09f9a7 0x7e0e21d8 <ddraw_palette_vtbl+16>: 0x7e09fb32 0x7e09f92c 0x7e09fa42 0x00000000 0x7e0e21e8: 0x00000000 0x00000000 --- snip ---
Obviously can't work because the vtable is const data:
https://source.winehq.org/git/wine.git/blob/HEAD:/dlls/ddraw/palette.c#l221
--- snip --- 221 static const struct IDirectDrawPaletteVtbl ddraw_palette_vtbl = 222 { 223 /*** IUnknown ***/ 224 ddraw_palette_QueryInterface, 225 ddraw_palette_AddRef, 226 ddraw_palette_Release, 227 /*** IDirectDrawPalette ***/ 228 ddraw_palette_GetCaps, 229 ddraw_palette_GetEntries, 230 ddraw_palette_Initialize, 231 ddraw_palette_SetEntries 232 }; --- snip ---
--- snip --- Thread 1 received signal SIGSEGV, Segmentation fault. 0x00b2b130 in ?? ()
Wine-gdb> bt
#0 0x00b2b130 in ?? () #1 0x00b2163c in ?? () #2 0x005a14c4 in ?? () #3 0x001c0fc8 in ?? () #4 0x00192a7c in ?? ()
Wine-gdb> x/10i 0x00b2b130
=> 0xb2b130: rep movsl %ds:(%esi),%es:(%edi) 0xb2b132: pop %edi 0xb2b133: pop %esi 0xb2b134: mov -0x44(%ebp),%ecx 0xb2b137: push %ecx 0xb2b138: mov -0x44(%ebp),%edx 0xb2b13b: test %edx,%edx 0xb2b13d: jne 0xb2b143 0xb2b13f: xor %ecx,%ecx
Wine-gdb> info reg
eax 0x7e070500 2114389248 ecx 0x24 36 edx 0x7e070500 2114389248 ebx 0x1c03c8 1835976 esp 0x32ba7c 0x32ba7c ebp 0x32bb14 0x32bb14 esi 0xbbeae4 12315364 edi 0x7e070500 2114389248 eip 0xb2b130 0xb2b130 eflags 0x210246 [ PF ZF IF RF ID ] cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x63 99 gs 0x6b 107
Wine-gdb> x/10x 0x7e070500
0x7e070500 <ddraw_surface1_vtbl>: 0x7e02f169 0x7e02f7fb 0x7e03030f 0x7e0341b1 0x7e070510 <ddraw_surface1_vtbl+16>: 0x7e034a8f 0x7e033a73 0x7e03648a 0x7e03aa33 0x7e070520 <ddraw_surface1_vtbl+32>: 0x7e034784 0x7e036a03 --- snip ---
https://source.winehq.org/git/wine.git/blob/HEAD:/dlls/ddraw/surface.c#l5600
--- snip --- 5600 static const struct IDirectDrawSurfaceVtbl ddraw_surface1_vtbl = 5601 { 5602 /* IUnknown */ 5603 ddraw_surface1_QueryInterface, 5604 ddraw_surface1_AddRef, 5605 ddraw_surface1_Release, ... --- snip ---
--- snip --- Wine-gdb> bt
#0 0x00b2b1d6 in ?? () #1 0x00b2163c in ?? () #2 0x005a14c4 in ?? () #3 0x001c0fc8 in ?? () #4 0x00192a7c in ?? ()
Wine-gdb> x/10i 0x00b2b1d6
=> 0xb2b1d6: rep movsl %ds:(%esi),%es:(%edi) 0xb2b1d8: pop %edi 0xb2b1d9: pop %esi 0xb2b1da: mov -0x48(%ebp),%ecx 0xb2b1dd: push %ecx 0xb2b1de: mov -0x48(%ebp),%edx 0xb2b1e1: test %edx,%edx 0xb2b1e3: jne 0xb2b1e9 0xb2b1e5: xor %ecx,%ecx 0xb2b1e7: jmp 0xb2b238
ine-gdb> info reg eax 0x7e0704e0 2114389216 ecx 0x27 39 edx 0x7e0704e0 2114389216 ebx 0x1c03c8 1835976 esp 0x32ba7c 0x32ba7c ebp 0x32bb14 0x32bb14 esi 0xbbeb74 12315508 edi 0x7e0704e0 2114389216 eip 0xb2b1d6 0xb2b1d6 eflags 0x210246 [ PF ZF IF RF ID ] cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x63 99 gs 0x6b 107
Wine-gdb> x/10x 0x7e0704e0
0x7e0704e0 <ddraw_surface2_vtbl>: 0x7e02f0c4 0x7e02f737 0x7e03024b 0x7e0340b3 0x7e0704f0 <ddraw_surface2_vtbl+16>: 0x7e0349f0 0x7e033977 0x7e0363de 0x7e03a947 0x7e070500 <ddraw_surface2_vtbl+32>: 0x7e0346db 0x7e036959 --- snip ---
https://source.winehq.org/git/wine.git/blob/HEAD:/dlls/ddraw/surface.c#l5554
--- snip --- 5554 static const struct IDirectDrawSurface2Vtbl ddraw_surface2_vtbl = 5555 { 5556 /* IUnknown */ 5557 ddraw_surface2_QueryInterface, 5558 ddraw_surface2_AddRef, 5559 ddraw_surface2_Release, 5560 /* IDirectDrawSurface */ ... --- snip ---
--- snip --- Wine-gdb> bt
#0 0x00b2b27c in ?? () #1 0x00b2163c in ?? () #2 0x005a14c4 in ?? () #3 0x001c0fc8 in ?? () #4 0x00192a7c in ?? ()
Wine-gdb> x/10i 0x00b2b27c
=> 0xb2b27c: rep movsl %ds:(%esi),%es:(%edi) 0xb2b27e: pop %edi 0xb2b27f: pop %esi 0xb2b280: mov -0x4c(%ebp),%ecx 0xb2b283: push %ecx 0xb2b284: mov -0x4c(%ebp),%edx 0xb2b287: test %edx,%edx 0xb2b289: jne 0xb2b28f 0xb2b28b: xor %ecx,%ecx 0xb2b28d: jmp 0xb2b2de
Wine-gdb> info reg
eax 0x7e0704e0 2114389216 ecx 0x28 40 edx 0x7e0704e0 2114389216 ebx 0x1c03c8 1835976 esp 0x32ba7c 0x32ba7c ebp 0x32bb14 0x32bb14 esi 0xbbec10 12315664 edi 0x7e0704e0 2114389216 eip 0xb2b27c 0xb2b27c eflags 0x210246 [ PF ZF IF RF ID ] cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x63 99 gs 0x6b 107
Wine-gdb> x/10x 0x7e0704e0
0x7e0704e0 <ddraw_surface3_vtbl>: 0x7e02f01f 0x7e02f673 0x7e030187 0x7e033fb5 0x7e0704f0 <ddraw_surface3_vtbl+16>: 0x7e034951 0x7e03387b 0x7e036332 0x7e03a85b 0x7e070500 <ddraw_surface3_vtbl+32>: 0x7e034632 0x7e0368af --- snip ---
https://source.winehq.org/git/wine.git/blob/HEAD:/dlls/ddraw/surface.c#l5506
--- snip --- 5506 static const struct IDirectDrawSurface3Vtbl ddraw_surface3_vtbl = 5507 { 5508 /* IUnknown */ 5509 ddraw_surface3_QueryInterface, 5510 ddraw_surface3_AddRef, 5511 ddraw_surface3_Release, 5512 /* IDirectDrawSurface */ ... --- snip ---
--- snip --- Wine-gdb> bt
#0 0x00b2b322 in ?? () #1 0x00b2163c in ?? () #2 0x005a14c4 in ?? () #3 0x001c0fc8 in ?? () #4 0x00192a7c in ?? ()
Wine-gdb> x/10i 0x00b2b322
=> 0xb2b322: rep movsl %ds:(%esi),%es:(%edi) 0xb2b324: pop %edi 0xb2b325: pop %esi 0xb2b326: mov -0x50(%ebp),%ecx 0xb2b329: push %ecx 0xb2b32a: mov -0x50(%ebp),%edx 0xb2b32d: test %edx,%edx 0xb2b32f: jne 0xb2b335 0xb2b331: xor %ecx,%ecx 0xb2b333: jmp 0xb2b384
Wine-gdb> info reg
eax 0x7e0704e0 2114389216 ecx 0x2d 45 edx 0x7e0704e0 2114389216 ebx 0x1c03c8 1835976 esp 0x32ba7c 0x32ba7c ebp 0x32bb14 0x32bb14 esi 0xbbecb0 12315824 edi 0x7e0704e0 2114389216 eip 0xb2b322 0xb2b322 eflags 0x210246 [ PF ZF IF RF ID ] cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x63 99 gs 0x6b 107
Wine-gdb> x/10x 0x7e0704e0
0x7e0704e0 <ddraw_surface4_vtbl>: 0x7e02ef7a 0x7e02f5af 0x7e0300c3 0x7e033dd2 0x7e0704f0 <ddraw_surface4_vtbl+16>: 0x7e0348b2 0x7e03377f 0x7e036286 0x7e03a76f 0x7e070500 <ddraw_surface4_vtbl+32>: 0x7e034589 0x7e036805 --- snip ---
https://source.winehq.org/git/wine.git/blob/HEAD:/dlls/ddraw/surface.c#l5452
--- snip --- 5452 static const struct IDirectDrawSurface4Vtbl ddraw_surface4_vtbl = 5453 { 5454 /* IUnknown */ 5455 ddraw_surface4_QueryInterface, 5456 ddraw_surface4_AddRef, 5457 ddraw_surface4_Release, 5458 /* IDirectDrawSurface */ ... --- snip ---
--- snip --- Wine-gdb> bt
#0 0x00b2b3c8 in ?? () #1 0x00b2163c in ?? () #2 0x005a14c4 in ?? () #3 0x001c0fc8 in ?? () #4 0x00192a7c in ?? ()
Wine-gdb> x/10i 0x00b2b3c8
=> 0xb2b3c8: rep movsl %ds:(%esi),%es:(%edi) 0xb2b3ca: pop %edi 0xb2b3cb: pop %esi 0xb2b3cc: mov -0x54(%ebp),%ecx 0xb2b3cf: push %ecx 0xb2b3d0: mov -0x54(%ebp),%edx 0xb2b3d3: test %edx,%edx 0xb2b3d5: jne 0xb2b3db 0xb2b3d7: xor %ecx,%ecx 0xb2b3d9: jmp 0xb2b42a
Wine-gdb> info reg
eax 0x7e0704c0 2114389184 ecx 0x31 49 edx 0x7e0704c0 2114389184 ebx 0x1c03c8 1835976 esp 0x32ba7c 0x32ba7c ebp 0x32bb14 0x32bb14 esi 0xbbed64 12316004 edi 0x7e0704c0 2114389184 eip 0xb2b3c8 0xb2b3c8 eflags 0x210246 [ PF ZF IF RF ID ] cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x63 99 gs 0x6b 107
Wine-gdb> x/10x 0x7e0704c0
0x7e0704c0 <ddraw_surface7_vtbl>: 0x7e02e8a8 0x7e02f4eb 0x7e02ffff 0x7e033c96 0x7e0704d0 <ddraw_surface7_vtbl+16>: 0x7e03482d 0x7e032e03 0x7e0361f5 0x7e03a442 0x7e0704e0 <ddraw_surface7_vtbl+32>: 0x7e0344e0 0x7e036526 --- snip ---
https://source.winehq.org/git/wine.git/blob/HEAD:/dlls/ddraw/surface.c#l5393
--- snip --- 5393 static const struct IDirectDrawSurface7Vtbl ddraw_surface7_vtbl = 5394 { 5395 /* IUnknown */ 5396 ddraw_surface7_QueryInterface, 5397 ddraw_surface7_AddRef, 5398 ddraw_surface7_Release, 5399 /* IDirectDrawSurface */ --- snip ---
If you remove the 'const' the old games start successfully in windowed mode.
Tested with AoE2 + expansions. NOTE: I'm not talking about the newer AoE2HD which supports windowed mode out of the box, it's the older versions that didn't support windowed mode at all.
Newer windowed mode wrapper projects such as 'DxWnd' (https://github.com/DxWnd/DxWnd.reloaded) use more safe mechanisms (VirtualProtect) to make sure the vtables are actually writable.
But again, it seems native DDraw has writable vtables.
Regards