[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