http://bugs.winehq.org/show_bug.cgi?id=7065
--- Comment #24 from Anastasius Focht focht@gmx.net 2007-09-27 09:31:47 --- Hello,
ok, I tested more game demos which suffer from same issues mentioned in this bug report. Most of the game demos employ late securom versions (7.x) but I think I got all show stoppers for now. Beware: some demos suffer/die very quickly due to wined3d/dx related bugs. Don't blame me on it ;-)
I tested my workarounds with the following demos mentioned in this bug report:
"Bioshock" (dies due to dx bugs, even with "-dx9") "World in Conflict" (same, dies due to dx bugs) "Tomb raider anniversary" "UFO: Afterlight"
=====
The 800x error codes are related to PEB/process flags and heap flags checked by securom protection. It's not much fun to debug the code due to obfuscation and code splicing techniques. Anti-debugging tricks everywhere to make life a misery.
Down to the metal ... what does the protector actually complain about? I removed obfuscation instructions and added some comments for better understanding of the code snippets.
"NtGlobalFlag"
--- quote --- mov eax, fs:[18h] ; get TEB mov eax, [eax+30h] ; get PEB mov eax, [eax+68h] ; PEB->NtGlobalFlag test eax, 70h ; if set -> debugged (FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_VALIDATE_PARAMETERS)
--- quote ---
This is fine so far. Wine does not use this field (unlike windows loader) and initializes it to zero.
"Heap Flags":
--- quote --- mov eax, fs:[18h] ; get TEB mov eax, [eax+30h] ; get PEB mov eax, [eax+18h] ; PEB->ProcessHeap mov eax, [eax+0Ch] ; ProcessHeap->Flags test eax, 60h ; if set -> process is debugged (remark: bit 1 is even set when not debugged = HEAP_GROWABLE) --- quote ---
"Force Flags":
--- quote --- mov eax, fs:[18h] ; get TEB mov eax, [eax+30h] ; get PEB mov eax, [eax+18h] ; PEB->ProcessHeap mov eax, [eax+10h] ; ProcessHeap->ForceFlags test eax, eax ; if set -> process is debugged --- quote ---
If one is really interested in the meanings/workings of these flags, read up MSDN documention about global flags/loader (pietrek columns) and play with the neat "gflags.exe" utility which comes with debugging tools for windows.
Basically the flags are all set by loader when a process is initialized. Because of the flags, debug heaps and the like are used for tracking down app heap related problems.
PEB.NtGlobalFlag = 0x0 if the process is not debugged. PEB.NtGlobalFlag = 0x70 if debugged (FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_VALIDATE_PARAMETERS)
(can be overridden by registry settings, read MSDN or play with gflags.exe)
If PEB.NtGlobalFlags == 0x0 (not debugged) then PEB.ProcessHeap.Flags = 0x0 or 0x2 (HEAP_GROWABLE) PEB.ProcessHeap.ForceFlags = 0x0
If PEB.NtGlobalFlags > 0x0 (process debugged) then some flags are copied to PEB.ProcessHeap.Flags > 0x2 (HEAP_GROWABLE | xxxx flags) PEB.ProcessHeap.ForceFlags > 0x0 (flags copied here too)
=============
Ok, to make securom happy about the 800x error we have to do the following: Make sure PEB.ProcessHeap.Flags and PEB.ProcessHeap.ForceFlags exist as fields in wine internal process HEAP structure and initialize them accordingly.
This is the comment from wine "dlls/ntdll/heap.c":
--- quote dlls/ntdll/heap.c --- /* Note: the heap data structures are based on what Pietrek describes in his * book 'Windows 95 System Programming Secrets'. The layout is not exactly * the same, but could be easily adapted if it turns out some programs * require it. */ --- quote dlls/ntdll/heap.c ---
Uhm well .. talk about "easily adapted" ;-) Unfortunately parts of code make assumptions about the HEAP structure layout/field offsets when creating and initializing heaps/subheaps e.g. HEAP_CreateSubHeap() and HEAP_InitSubHeap().
Just adding some fake variables to HEAP structure to force the field offsets does NOT work without further code changes (SUBHEAP is expected to be the first field member)!
To test the findings with minimal (non intrusive) code changes, use the SUBHEAP structure itself. Just pad the structure with DWORD's to leave room for these fields:
--- snip dlls/ntdll/heap.c --- typedef struct tagSUBHEAP { /* HACKHACK */ DWORD pad1; /* ofs+0 */ DWORD pad2; /* ofs+4 */ DWORD pad3; /* ofs+8 */ DWORD Flags; /* [ofs+0C] = 0 to make securom happy */ DWORD ForceFlags; /* [ofs+10] = 0 to make securom happy */ DWORD align8; /* HACKHACK */
DWORD size; /* Size of the whole sub-heap */ DWORD commitSize; /* Committed size of the sub-heap */ DWORD headerSize; /* Size of the heap header */ ... --- snip dlls/ntdll/heap.c ---
In a real implementation the "/* HACKHACK */" fields would belong to (primary) HEAP structure. You can use the space of DWORD pad1-3 as you like, just make sure the DWORDs at offsets 0xC and 0x10 exist. The additional "align8" DWORD is used for alignment. Some wine code barfs if alignment is not forced (HEAP_FindFreeBlock).
Due to default initialization these fields get initialized to zero and that makes securom happy. Well - at least for this case :|
==================
Now having worked around 800x errors, we jump into next one:
--- snip --- "A required security module can not be activated. This program cannot be executed (7000)" Error --- snip ---
This error occurs when SecuROM has found one or more CD/DVD emulation tools running. Well thats not the case in wine. Further tracing/debugging shows that securom populates some registry keys:
"HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi" ... and "HKEY_LOCAL_MACHINE\System\MountedDevices" ...
The latter one does not exist. Simply creating the Key "MountedDevices" makes securom happy.
==================
And? Well. That's all - the game should start now ;-)
Be aware I did not test any "retail" stuff which comes with real CD/DVD media, just the "media-less" demos.
Have fun filing bug reports for real game issues (hint: WINEDEBUG=+d3d,+d3d_shader,...")
Regards