http://bugs.winehq.org/show_bug.cgi?id=25243
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- Summary|Viva Pinata fails |Microsoft Viva Pinata fails |installation and startup |on startup (Wine-Gecko | |"SetThreadName" MSVC | |exception 0x406d1388 breaks | |SafeDisc debugger)
--- Comment #21 from Anastasius Focht focht@gmx.net 2012-04-20 19:05:17 CDT --- Hello,
I had the right feeling about this one ... bought the game for a few bugs and it was delivered today :)
Two-disc version, installs fine (CD changes: 1-2-1) so the first part of this bug is actually "fixed".
ProtectionID tool gives:
--- snip --- -=[ ProtectionID v0.6.4.0 JULY]=- (c) 2003-2010 CDKiLLER & TippeX Build 07/08/10-17:57:05 Ready...
Scanning -> Z:\home\focht.wine\drive_c\Program Files (x86)\Microsoft Games\Viva Pinata\Startup.exe File Type : 32-Bit Exe (Subsystem : Win GUI / 2), Size : 2010304 (01EACC0h) Byte(s) -> File Appears to be Digitally Signed @ Offset 01E88C0h, size : 02400h / 09216 byte(s) -> File has 1444032 (01608C0h) bytes of appended data starting at offset 088000h [File Heuristics] -> Flag : 00000000000000001100000000000111 (0x0000C007) [!] Safedisc v4.81.000 detected ! [i] Appended data contents.... [.] o: 0x00088028 / t: <0xA8726B03> <0xEF01996C> <0x00000001> / s: 00303952 byte(s) -> ~de02a3.tmp [.] o: 0x000D239F / t: <0xA8726B03> <0xEF01996C> <0x0000044C> / s: 00015887 byte(s) -> clcd32.dll [.] o: 0x000D61D5 / t: <0xA8726B03> <0xEF01996C> <0x0000044C> / s: 00004123 byte(s) -> clcd16.dll [.] o: 0x000D7214 / t: <0xA8726B03> <0xEF01996C> <0x0000044D> / s: 00037971 byte(s) -> mcp.dll [.] o: 0x000E0690 / t: <0xA8726B03> <0xEF01996C> <0x0000000B> / s: 00005446 byte(s) -> SecDrv04.VxD [.] o: 0x000E1BFB / t: <0xA8726B03> <0xEF01996C> <0x00000000> / s: 00072192 byte(s) -> ~e5.0001 [.] o: 0x000F3622 / t: <0xA8726B03> <0xEF01996C> <0x00000000> / s: 00045056 byte(s) -> PfdRun.pfd [.] o: 0x000FE64A / t: <0xA8726B03> <0xEF01996C> <0x00000000> / s: 00959004 byte(s) -> ~df394b.tmp [CompilerDetect] -> Visual C++ 8.0 (Visual Studio 2005) - Scan Took : 0.470 Second(s)
Scanning -> Z:\home\focht.wine\drive_c\Program Files (x86)\Microsoft Games\Viva Pinata\Viva Pinata.exe File Type : 32-Bit Exe (Subsystem : Win GUI / 2), Size : 11056128 (0A8B400h) Byte(s) -> File Appears to be Digitally Signed @ Offset 0A89000h, size : 02400h / 09216 byte(s) [File Heuristics] -> Flag : 00000000000000000000000000000100 (0x00000004) [!] Games For Windows Live detected ! [CompilerDetect] -> Visual C++ 8.0 (Visual Studio 2005) - Scan Took : 0.175 Second(s) --- snip ---
The game exits on startup.
The reason is an unfortunate combination of SafeDisc's design (debugger + debuggee) and Wine-Gecko engine being overly "helpful" with debugging hints.
When the game is started using the launcher it creates a second process by design which acts as debugger to the first one (parent):
--- snip --- 0024:trace:process:__wine_kernel_init starting process name=L"C:\Program Files (x86)\Microsoft Games\Viva Pinata\Startup.exe" argv[0]=L"C:\Program Files (x86)\Microsoft Games\Viva Pinata\Startup.exe" ... 0024:Call KERNEL32.CreateProcessA(0032f4a8 "C:\users\focht\Temp\~e5.0001",0032f5b0 ""C:\users\focht\Temp\~e5.0001" 35 "C:\users\focht\Temp\""~e5.0001.dir.0000"",00000000,00000000,00000000,00000030,00000000,0032fc08 "C:\users\focht\Temp\",0032f460,0032f450) ret=00489eb9 0024:trace:process:create_process_impl app L"C:\users\focht\Temp\~e5.0001" cmdline L""C:\users\focht\Temp\~e5.0001" 35 "C:\users\focht\Temp\""~e5.0001.dir.0000"" 0024:trace:process:open_exe_file looking for L"C:\users\focht\Temp\~e5.0001" 0024:trace:process:create_process_impl starting L"C:\users\focht\Temp\~e5.0001" as Win32 binary (0x400000-0x416000) ... 0026:trace:loaddll:load_builtin_dll Loaded L"KERNEL32.dll" at 0x7b810000: builtin 0026:Call KERNEL32.__wine_kernel_init() ret=7bc530d2 0026:trace:process:init_current_directory starting in L"C:\users\focht\Temp\" 0x10 0026:trace:process:__wine_kernel_init starting process name=L"C:\users\focht\Temp\~e5.0001" argv[0]=L"C:\users\focht\Temp\~e5.0001" 0026:trace:loaddll:load_native_dll Loaded L"C:\users\focht\Temp\~e5.0001" at 0x400000: native 0024:trace:process:create_process_impl started process pid 0025 tid 0026 0024:Ret KERNEL32.CreateProcessA() retval=00000001 ret=00489eb9 ... 0026:Ret KERNEL32.CreateThread() retval=0000003c ret=00402dbd ... 0027:Starting thread proc 0x402cc2 (arg=0x416ee8) ... 0027:Call KERNEL32.DebugActiveProcess(00000023) ret=667ad4da 0027:Ret KERNEL32.DebugActiveProcess() retval=00000001 ret=667ad4da ... 0027:Call KERNEL32.WaitForDebugEvent(0062e3dc,ffffffff) ret=667ad513 0027:Ret KERNEL32.WaitForDebugEvent() retval=00000001 ret=667ad513 --- snip ---
The debugger expects events/exceptions from child as part of the software protection.
For example when an int 3 (safedisc nanomite) is seen by debugger it checks where exception occurred. The debugger decides if the opcode is to be emulated or decrypted by using WriteProcessMemory (writing the original opcode back to child address space and re-executing code).
--- snip --- ... 0024:trace:seh:raise_exception code=80000003 flags=0 addr=0x404ca1 ip=00404ca2 tid=0024 0024:trace:seh:raise_exception eax=00000001 ebx=00000001 ecx=00000000 edx=00000000 esi=0047e2ac edi=0043f4e0 0024:trace:seh:raise_exception ebp=0032fd94 esp=0032fd68 cs=0023 ds=002b es=002b fs=0063 gs=006b flags=00000206 0027:Ret KERNEL32.WaitForDebugEvent() retval=00000001 ret=667ad5b4 0027:Call ntdll.RtlAllocateHeap(00630000,00000000,000002ec) ret=6678eca6 0027:Ret ntdll.RtlAllocateHeap() retval=006499b0 ret=6678eca6 0027:Call KERNEL32.GetVersionExA(0062e330) ret=667ae700 0027:Ret KERNEL32.GetVersionExA() retval=00000001 ret=667ae700 0027:Call KERNEL32.GetThreadContext(00000078,006499c0) ret=667ae2cf 0027:Ret KERNEL32.GetThreadContext() retval=00000001 ret=667ae2cf 0027:Call ntdll.RtlAllocateHeap(00630000,00000000,00000008) ret=6678eca6 0027:Ret ntdll.RtlAllocateHeap() retval=0064ac08 ret=6678eca6 0027:Call ntdll.RtlAllocateHeap(00630000,00000000,00000008) ret=6678eca6 0027:Ret ntdll.RtlAllocateHeap() retval=0064ac80 ret=6678eca6 0027:Call KERNEL32.ReadProcessMemory(00000074,00404ca0,0062de40,00000010,0062dab0) ret=667adf40 0027:Ret KERNEL32.ReadProcessMemory() retval=00000001 ret=667adf40 0027:Call KERNEL32.WriteProcessMemory(00000074,00404ca1,0062dea4,00000002,0062dab0) ret=667adefb 0027:Ret KERNEL32.WriteProcessMemory() retval=00000001 ret=667adefb ... 0027:Call KERNEL32.FlushInstructionCache(00000074,00404ca1,00000008) ret=667ae4f5 0027:Ret KERNEL32.FlushInstructionCache() retval=00000001 ret=667ae4f5 0027:Call KERNEL32.SetThreadContext(00000078,006499c0) ret=667ae29d 0027:Ret KERNEL32.SetThreadContext() retval=00000001 ret=667ae29d 0027:Call ntdll.RtlAllocateHeap(00630000,00000000,00000080) ret=6678eca6 0027:Ret ntdll.RtlAllocateHeap() retval=0064acb8 ret=6678eca6 0027:Call KERNEL32.GetModuleHandleA(0064acb8 "Kernel32") ret=6674f76d 0027:Ret KERNEL32.GetModuleHandleA() retval=7b810000 ret=6674f76d 0027:Call ntdll.RtlFreeHeap(00630000,00000000,0064acb8) ret=6678ea06 0027:Ret ntdll.RtlFreeHeap() retval=00000001 ret=6678ea06 0027:Call ntdll.RtlAllocateHeap(00630000,00000000,00000080) ret=6678eca6 0027:Ret ntdll.RtlAllocateHeap() retval=0064acb8 ret=6678eca6 0027:Call ntdll.RtlFreeHeap(00630000,00000000,0064acb8) ret=6678ea06 0027:Ret ntdll.RtlFreeHeap() retval=00000001 ret=6678ea06 0027:Call KERNEL32.ContinueDebugEvent(00000023,00000024,00010002) ret=6675f9eb 0027:Ret KERNEL32.ContinueDebugEvent() retval=00000001 ret=6675f9eb 0027:Call ntdll.RtlFreeHeap(00630000,00000000,006499b0) ret=6678ea06 0027:Ret ntdll.RtlFreeHeap() retval=00000001 ret=6678ea06 0027:Call KERNEL32.WaitForDebugEvent(0062e3dc,ffffffff) ret=667ad5b4 --- snip ---
This works as designed.
Consider the following snippet when the Wine-Gecko engine is loaded:
--- snip --- 0024:Call mshtml.DllGetClassObject(0032ca08,7e5ea4ec,0032c8bc) ret=7e50ac12 0024:trace:mshtml:DllGetClassObject (CLSID_HTMLDocument {00000001-0000-0000-c000-000000000046} 0x32c8bc) ... 0024:trace:mshtml:load_gecko () ... 0024:trace:mshtml:check_version "Wine Gecko 1.5" 0024:trace:mshtml:load_xul (L"C:\windows\syswow64\gecko\1.5\wine_gecko\\xul.dll") ... 0024:Call KERNEL32.LoadLibraryExW(0032c284 L"C:\windows\syswow64\gecko\1.5\wine_gecko\\xul.dll",00000000,00000008) ret=7d2e1b86 0027:Ret KERNEL32.WaitForDebugEvent() retval=00000001 ret=667ad5b4 ... 0027:Call KERNEL32.ContinueDebugEvent(00000023,00000024,00010002) ret=6675f9eb 0027:Ret KERNEL32.ContinueDebugEvent() retval=00000001 ret=6675f9eb 0027:Call ntdll.RtlFreeHeap(00630000,00000000,0064ac08) ret=6678ea06 0027:Ret ntdll.RtlFreeHeap() retval=00000001 ret=6678ea06 0027:Call KERNEL32.WaitForDebugEvent(0062e3dc,ffffffff) ret=667ad5b4 0024:trace:loaddll:load_builtin_dll Loaded L"C:\windows\system32\msvcrt.dll" at 0x7d1c0000: builtin 0027:Ret KERNEL32.WaitForDebugEvent() retval=00000001 ret=667ad5b4 ... 002d:Call KERNEL32.IsDebuggerPresent() ret=6a89b761 002d:Ret KERNEL32.IsDebuggerPresent() retval=00000001 ret=6a89b761 002d:Call KERNEL32.GetCurrentThreadId() ret=6a89b723 002d:Ret KERNEL32.GetCurrentThreadId() retval=0000002d ret=6a89b723 002d:Call KERNEL32.RaiseException(406d1388,00000000,00000004,00f2e8e0) ret=6a89b7ab 002d:trace:seh:raise_exception code=406d1388 flags=0 addr=0x7b839357 ip=7b839357 tid=002d 002d:trace:seh:raise_exception info[0]=00001000 002d:trace:seh:raise_exception info[1]=00160584 002d:trace:seh:raise_exception info[2]=0000002d 002d:trace:seh:raise_exception info[3]=00000000 002d:trace:seh:raise_exception eax=7b826381 ebx=7b8aa728 ecx=00000000 edx=00f2e814 esi=00f2e8e0 edi=00f2e860 002d:trace:seh:raise_exception ebp=00f2e848 esp=00f2e7e4 cs=0023 ds=002b es=002b fs=0063 gs=006b flags=00000283 --- snip ---
Gecko checks if a debugger is present/active and issues a special "SetThreadName" msvc exception (code 0x406d1388) because it assumes there is someone who debugs the process. Unfortunately it's not the user but SafeDisc itself which acts as debugger by design.
--- snip --- 0027:Ret KERNEL32.WaitForDebugEvent() retval=00000001 ret=667ad5b4 ... 0027:Call KERNEL32.ContinueDebugEvent(00000023,0000002d,80010001) ret=6675f9eb 0027:Ret KERNEL32.ContinueDebugEvent() retval=00000001 ret=6675f9eb 0027:Call ntdll.RtlFreeHeap(00630000,00000000,0064b3f8) ret=6678ea06 0027:Ret ntdll.RtlFreeHeap() retval=00000001 ret=6678ea06 0027:Call KERNEL32.WaitForDebugEvent(0062e3dc,ffffffff) ret=667ad5b4 002d:trace:seh:call_stack_handlers calling handler at 0x7bc91b99 code=406d1388 flags=0 002d:Call KERNEL32.UnhandledExceptionFilter(00f2e2b4) ret=7bc91bd3 002d:Ret KERNEL32.UnhandledExceptionFilter() retval=00000000 ret=7bc91bd3 002d:trace:seh:call_stack_handlers handler at 0x7bc91b99 returned 1 0027:Ret KERNEL32.WaitForDebugEvent() retval=00000001 ret=667ad5b4 ... 0027:Call KERNEL32.WaitForDebugEvent(0062e3dc,ffffffff) ret=667ad5b4 002d:err:seh:raise_exception Unhandled exception code 406d1388 flags 0 addr 0x7b839357 0027:Ret KERNEL32.WaitForDebugEvent() retval=00000001 ret=667ad5b4 --- snip ---
The SafeDisc debugger doesn't expect this legacy exception and can't handle it -> processes get terminated.
For testing I modified the code (ntdll) to swallow this exception and it helps: the game launcher runs further and shows html-based GUI.
One approach could be to modify Wine-Gecko to not issue this exception either by disabling this feature in Wine-Gecko builds or providing config option to turn it off. Actually I don't see much benefit in having some named threads when debugging applications that load Gecko engine.
$ wine --version wine-1.5.2-191-gd080774
Regards