[Bug 20466] Brothers in Arms: Hell's Highway crashes on startup (TLS slot index allocation must start at non-zero indexes)

wine-bugs at winehq.org wine-bugs at winehq.org
Wed Jan 8 03:04:39 CST 2014


http://bugs.winehq.org/show_bug.cgi?id=20466

Anastasius Focht <focht at gmx.net> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |obfuscation
             Status|UNCONFIRMED                 |NEW
                 CC|                            |focht at gmx.net
          Component|-unknown                    |ntdll
            Summary|Brothers In Arms Hell's     |Brothers in Arms: Hell's
                   |Highway fails to start      |Highway crashes on startup
                   |                            |(TLS slot index allocation
                   |                            |must start at non-zero
                   |                            |indexes)
     Ever confirmed|0                           |1

--- Comment #26 from Anastasius Focht <focht at gmx.net> ---
Hello folks,

confirming, I bought the game for a few bucks and had a look at it :)

First, it seems there exist at least two variants of this game - one with
SecuROM protection (the 'gore' variant) and one without DRM scheme (the
'non-gore' variant).

'PC_Release_Ship':

--- snip ---
-=[ ProtectionID v0.6.5.5 OCTOBER]=-
(c) 2003-2013 CDKiLLER & TippeX
Build 31/10/13-21:09:09
Ready...
Scanning -> Z:\home\focht\.wine\drive_c\Program Files\Ubisoft\Gearbox
Software\Brothers in Arms - Hell's Highway\Binaries\biahh.exe
File Type : 32-Bit Exe (Subsystem : Win GUI / 2), Size : 30102144 (01CB5280h)
Byte(s)
-> File Appears to be Digitally Signed @ Offset 01CB4000h, size : 01280h /
04736 byte(s)
[File Heuristics] -> Flag : 00000100000000000000000000000101 (0x04000005)
[Entrypoint Section Entropy] : 6.65
[Debug Info]
Characteristics : 0x0 | TimeDateStamp : 0x48D5096D | MajorVer : 0 / MinorVer :
0 -> (0.0)
Type : 2 -> CodeView | Size : 0x6F (111)
AddressOfRawData : 0x213201C | PointerToRawData : 0x1B3E01C
CvSig : 0x53445352 | SigGuid A8D678DE-5D6F-4786-BD2F11415E52AE0D
Age : 0xA | Pdb :
r:\gbxbuilder\Build268\Incremental\Binaries\Lib\PC_Release_Ship\PCLaunch-SumacGame.pdb

[!] SecuROM Detected - Version 07.38.0006
[!] Possible CD/DVD-Key or Serial Check -> cdkey
[CompilerDetect] -> Visual C++ 8.0 (Visual Studio 2005)
- Scan Took : 1.417 Second(s) [00000062Fh tick(s)] [533 scan(s) done]

--- snip ---

'PC_Release_Ship_LowGore' (german):

--- snip ---
-=[ ProtectionID v0.6.5.5 OCTOBER]=-
(c) 2003-2013 CDKiLLER & TippeX
Build 31/10/13-21:09:09
Ready...

Scanning -> Z:\home\focht\.wine\drive_c\Program Files\Ubisoft\Gearbox
Software\Brothers in Arms - Hell's Highway\Binaries\biahh.exe
File Type : 32-Bit Exe (Subsystem : Win GUI / 2), Size : 25448448 (01845000h)
Byte(s)
[File Heuristics] -> Flag : 00000100000000000000000000000000 (0x04000000)
[Entrypoint Section Entropy] : 6.55
[Debug Info]
Characteristics : 0x0 | TimeDateStamp : 0x48E26A2C | MajorVer : 0 / MinorVer :
0 -> (0.0)
Type : 2 -> CodeView | Size : 0x79 (121)
AddressOfRawData : 0x150B6B8 | PointerToRawData : 0x150B6B8
CvSig : 0x53445352 | SigGuid 1C8247A3-5444-42E8-B870DCBAED28AF99
Age : 0x1 | Pdb :
c:\Workspace\sumac_releases_final_pc\Binaries\Lib\PC_Release_Ship_LowGore\PCLaunch-SumacGame.pdb

[!] Possible CD/DVD-Key or Serial Check -> cdkey
[CompilerDetect] -> Visual C++ 8.0 (Visual Studio 2005)
[!] File appears to have no protection or is using an unknown protection
- Scan Took : 1.321 Second(s) [00000068Fh tick(s)] [533 scan(s) done] 
--- snip ---

Both versions exhibit the same problem (crash/backtrace).

Short version: I came to conclusion this is actually a bug in the game engine
itself which just works due to the way Windows allocates/manages TLS indexes.

The crash happens shortly after the PhysX SDK got initialized. TLS slots are
used in many places in the engine.
At one point of the crash, data from one TLS slot accessed and the returned
data is interpreted (casted) to member data.
Unfortunately the data (pointer) from this slot doesn't belong to the game
engine but Wine itself because it reserved the slot and data early before main
entry point.

TLS slot data retrieval:

--- snip ---
...
004042C0  55               PUSH EBP
004042C1  8BEC             MOV EBP,ESP
004042C3  6A FF            PUSH -1
004042C5  68 58895101      PUSH biahh.01518958
004042CA  64:A1 00000000   MOV EAX,DWORD PTR FS:[0]
004042D0  50               PUSH EAX
004042D1  83EC 0C          SUB ESP,0C
004042D4  53               PUSH EBX
004042D5  56               PUSH ESI
004042D6  57               PUSH EDI
004042D7  A1 C0E0A501      MOV EAX,DWORD PTR DS:[1A5E0C0]
004042DC  33C5             XOR EAX,EBP
004042DE  50               PUSH EAX
004042DF  8D45 F4          LEA EAX,DWORD PTR SS:[EBP-C]
004042E2  64:A3 00000000   MOV DWORD PTR FS:[0],EAX
004042E8  8BF9             MOV EDI,ECX
004042EA  A1 1048A901      MOV EAX,DWORD PTR DS:[1A94810] ; *bad* TLS index 0
004042EF  50               PUSH EAX
004042F0  FF15 44F16701    CALL DWORD PTR DS:[<&KERNEL32.TlsGetValue>]
004042F6  85C0             TEST EAX,EAX     ; not zero -> problem!
004042F8  75 78            JNZ SHORT biahh.00404372
--- snip ---

The problem is the slot index stored/referenced at 0x1A94810
After searching the whole process address space for all instructions
referencing this address I found two code chunks:

Code that ought to allocate and zero-init the slot:

--- snip ---
0063605E  CC               INT3
0063605F  CC               INT3
00636060  FF15 D8F16701    CALL DWORD PTR DS:[<&KERNEL32.TlsAlloc>]
00636066  6A 00            PUSH 0
00636068  50               PUSH EAX
00636069  A3 1048A901      MOV DWORD PTR DS:[1A94810],EAX
0063606E  FF15 40F16701    CALL DWORD PTR DS:[<&KERNEL32.TlsSetValue>]
00636074  C3               RETN
00636075  CC               INT3
00636076  CC               INT3
--- snip ---

Code that sets the TLS slot value:

--- snip ---
00401F10  55               PUSH EBP
00401F11  8BEC             MOV EBP,ESP
00401F13  8B45 08          MOV EAX,DWORD PTR SS:[EBP+8]
00401F16  8B0D 1048A901    MOV ECX,DWORD PTR DS:[1A94810]
00401F1C  50               PUSH EAX
00401F1D  51               PUSH ECX
00401F1E  FF15 40F16701    CALL DWORD PTR DS:[<&KERNEL32.TlsSetValue>]
00401F24  5D               POP EBP
00401F25  C2 0400          RETN 4 
--- snip ---

Looks good? In theory yes. In practice there are no references to these chunks
to be found. It's dead code.

There are many similar code chunks for other TLS slots/indexes which can be
either found by looking at +relay log (return addresses) or searching for
referencing callers in disassembly.
The only code that actually references the 'magic' slot is the function at
0x004042C0.

The initial value of 0x1A94810 is zero = TLS index zero.
As already said earlier, Wine builtins allocate TLS indexes in their dll
main/startup phase, hence index zero is taken and initialized to non-zero
value.
Due to the bug in the game, slot 0 data is read and misinterpreted, leading to
later crash.

Fortunately the Win32 API specification itself says there is no guarantee that
application code can grab TLS index zero.

MSDN:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686801%28v=vs.85%29.aspx

--- snip ---
The threads of the process can use the TLS index in subsequent calls to the
TlsFree, TlsSetValue, or TlsGetValue functions. The value of the TLS index
should be treated as an opaque value; do not assume that it is an index into a
zero-based array. 
--- snip ---

Either slot indexes that are allocated/exposed by TlsXXX API must start at
non-zero values -> ntdll change, keeping index zero 'reserved' (with zero init
value!).

A quick hack that also seems to be sufficient for the game is to 'steal' index
zero for any further public allocation during process startup -> kernel32
('singleton' TlsAlloc in process attach) before any higher level Wine builtins
can grab it.

Both approaches allow to run the game flawlessly, no more crashes.

Regards

-- 
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.



More information about the wine-bugs mailing list