[Bug 7065] Get SecuROM copy-protection working

wine-bugs at winehq.org wine-bugs at winehq.org
Thu Sep 27 09:31:49 CDT 2007


--- Comment #24 from Anastasius Focht <focht at gmx.net>  2007-09-27 09:31:47 ---

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.


--- 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 |

--- quote ---

This is fine so far.
Wine does not use this field (unlike windows loader) and initializes it to

"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 |

(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
Well thats not the case in wine. Further tracing/debugging shows that securom
populates some registry keys:

"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:


Configure bugmail: http://bugs.winehq.org/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are watching all bug changes.

More information about the wine-bugs mailing list