http://bugs.winehq.org/show_bug.cgi?id=7065
--- Comment #74 from Anastasius Focht focht@gmx.net 2008-01-28 13:52:23 --- Hello,
well I finally received the original media of "Homeworld 2" v1.x game mentioned here.
Listed here: http://appdb.winehq.org/objectManager.php?sClass=version&iId=3803
wine version used: wine-0.9.54 cleaned .wine
Installed fine from CD.
Main executable scan with Protection ID:
--- snip --- Scanning -> c:\Program Files\Sierra\Homeworld2\Bin\Release\Homeworld2.exe File Type : Exe, Size : 5390337 (0524001h) Byte(s) -> File has 57345 (0E001h) bytes of appended data starting at offset 0516000h [!] SecuROM Detected - Version 4.84.84 [!] Possible CD/DVD-Key or Serial Check -> CDKey --- snip ---
There exists a patch for that game, but I did analysis unpatched.
When you start the game after installation, it live locks forever eating CPU.
--- snip --- .. 0009:Call KERNEL32.GetWindowsDirectoryA(0034b760,00000100) ret=007f2902 0009:Ret KERNEL32.GetWindowsDirectoryA() retval=0000000a ret=007f2902 0009:Call KERNEL32.FindFirstFileA(0034b760 "c:\windows\System32\Drivers\*.sys",0034b620) ret=007f2a60 0009:Ret KERNEL32.FindFirstFileA() retval=ffffffff ret=007f2a60 0009:Call KERNEL32.GetTickCount() ret=007f2aeb 0009:Ret KERNEL32.GetTickCount() retval=00001155 ret=007f2aeb 0009:trace:seh:raise_exception code=c0000094 flags=0 addr=0x7f2afa 0009:trace:seh:raise_exception eax=000038bf ebx=7b8ae084 ecx=7bc9fda0 edx=00000000 esi=0000004b edi=00403000 0009:trace:seh:raise_exception ebp=0034b990 esp=0034b880 cs=0073 ds=007b es=007b fs=0033 gs=003b flags=00210202 0009:trace:seh:call_stack_handlers calling handler at 0x7790c0 code=c0000094 flags=0 0009:trace:seh:call_stack_handlers handler at 0x7790c0 returned 0 .. endless exception loop --- snip ---
The copy protection code expects at least one .sys driver in "c:\windows\system32\drivers" which isn't the case on clean .wine with game install. The first *.sys entry found is used to derive a temporary filename from it. This filename is later used to create a copy protection device driver on the fly (from resource) which is later loaded.
You can fake this part by placing an empty "<whatevername>.sys" file into drivers to let FindFirstFileA("c:\windows\System32\Drivers\*.sys") succeed.
Ideally wine should make sure there is at least one .sys driver on clean wine. Fortunately, wine now ships with a builtin one: mount manager (mountmgr.sys). The solution is to let wine create a fake .sys (like fake dll) in drivers:
--- snip patch #1 --- diff --git a/tools/wine.inf b/tools/wine.inf index c3cb89c..719ff5f 100644 --- a/tools/wine.inf +++ b/tools/wine.inf @@ -2230,6 +2230,7 @@ HKLM,%CurrentVersion%\Telephony\Country List\998,"SameAreaRule",,"G" 11,,imaadp32.acm 11,,imagehlp.dll 11,,kernel32.dll +12,,mountmgr.sys 11,,msadp32.acm 11,,msg711.acm 11,,mshtml.dll --- snip patch #1 ---
With this patch it goes further only to crash again ...
--- snip --- .. 0009:Call KERNEL32.CreateFileA(0034cdec "qmountmg.sys",80000000,00000003,0034b918,00000003,00000080,00000000) ret=00808a16 0009:Ret KERNEL32.CreateFileA() retval=ffffffff ret=00808a16 0009:Call KERNEL32.GetLastError() ret=00808a34 0009:Ret KERNEL32.GetLastError() retval=00000002 ret=00808a34 0009:Call KERNEL32.GetTempPathA(00000100,0034ccec) ret=007f297a 0009:Ret KERNEL32.GetTempPathA() retval=00000010 ret=007f297a 0009:Call KERNEL32.CreateFileA(0034ccec "C:\windows\temp\qmountmg.sys",40000000,00000000,00000000,00000002,00000000,00000000) ret=007f4114 0009:Ret KERNEL32.CreateFileA() retval=000000d0 ret=007f4114 0009:Call KERNEL32.WriteFile(000000d0,008c491c,00004600,0034b9a8,00000000) ret=007f413f 0009:Ret KERNEL32.WriteFile() retval=00000001 ret=007f413f 0009:Call KERNEL32.CloseHandle(000000d0) ret=007f4149 0009:Ret KERNEL32.CloseHandle() retval=00000001 ret=007f4149 0009:Call advapi32.OpenSCManagerA(00000000,00000000,000f003f) ret=007f549f 0009:Ret advapi32.OpenSCManagerA() retval=00149520 ret=007f549f 0009:Call advapi32.OpenServiceA(00149520,0034ceec "qmountmg",000f01ff) ret=007f54c6 0009:Ret advapi32.OpenServiceA() retval=00000000 ret=007f54c6 0009:Call advapi32.OpenSCManagerA(00000000,00000000,000f003f) ret=007f5199 0009:Ret advapi32.OpenSCManagerA() retval=00149540 ret=007f5199 0009:Call advapi32.CreateServiceA(00149540,0034ceec,0034ceec,000f01ff,00000001,00000003,00000001,0034ccec,00000000,00000000,00000000,00000000,00000000) ret=007f51ee 0009:Ret advapi32.CreateServiceA() retval=001572b8 ret=007f51ee 0009:Call advapi32.StartServiceA(001572b8,00000000,00000000) ret=007f5254 .. 002b:Call driver init 0x4615e0 (obj=0x602d3a60,str=L"\Registry\Machine\System\CurrentControlSet\Services\xmountmg") 002b:trace:seh:raise_exception code=c0000005 flags=0 addr=0x4615e8 002b:trace:seh:raise_exception info[0]=00000001 002b:trace:seh:raise_exception info[1]=00016740 002b:trace:seh:raise_exception eax=00016740 ebx=602d3674 ecx=7ece6b00 edx=602d3a60 esi=7ece6b08 edi=004615e0 002b:trace:seh:raise_exception ebp=7ece69c8 esp=7ece6940 cs=0073 ds=007b es=007b fs=0033 gs=003b flags=00210206 002b:Call KERNEL32.__wine_emulate_instruction(7ece68e8,7ece661c) ret=6033d092 002b:Ret KERNEL32.__wine_emulate_instruction() retval=00000001 ret=6033d092 *boom* --- snip ---
This one is a bit tricky ... it boils down to following code sequence in driver entry proc (bit simplified):
--- snip --- call sub_463260 or dword ptr [eax], 1 ; *boom* here
.. sub_463260: mov eax, offset dword_466740 retn
.. .data:00466740 dword_466740 dd ? --- snip ---
The offset loaded into eax at run time is always the same: 0x00016740. Let's look at PE mapping:
--- snip --- 003a:trace:module:load_native_dll Trying native dll L"C:\windows\temp\smountmg.sys" 003a:trace:module:map_image mapped PE file at 0x460000-0x469000 003a:trace:module:map_image mapping section .text at 0x461000 off 400 size 3400 virt 32a4 flags 68000020 003a:trace:module:map_image clearing 0x464400 - 0x465000 003a:trace:module:map_image mapping section .rdata at 0x465000 off 3800 size 200 virt 194 flags 48000040 003a:trace:module:map_image clearing 0x465200 - 0x466000 003a:trace:module:map_image mapping section .data at 0x466000 off 3a00 size 200 virt 968 flags c8000040 003a:trace:module:map_image clearing 0x466200 - 0x467000 003a:trace:module:map_image mapping section INIT at 0x467000 off 3c00 size 600 virt 4e8 flags e2000020 003a:trace:module:map_image clearing 0x467600 - 0x468000 003a:trace:module:map_image mapping section .reloc at 0x468000 off 4200 size 400 virt 2e0 flags 42000040 003a:trace:module:map_image clearing 0x468400 - 0x469000 003a:warn:module:alloc_module disabling no-exec because of L"smountmg.sys" --- snip ---
And the preferred image base for the .sys driver: 0x00010000 Looks like relocation fixups problem because there exists a .reloc section in driver which wine didn't apply...
Now lets fix this:
--- snip patch #2 --- diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c index 1c44d49..7f1a1d0 100644 --- a/dlls/ntdll/virtual.c +++ b/dlls/ntdll/virtual.c @@ -1149,6 +1149,7 @@ static NTSTATUS map_image( HANDLE hmapping, int fd, char *base, SIZE_T total_siz
if (ptr != base && ((nt->FileHeader.Characteristics & IMAGE_FILE_DLL) || + (nt->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_NATIVE) || !NtCurrentTeb()->Peb->ImageBaseAddress) ) { const IMAGE_DATA_DIRECTORY *relocs; --- snip patch #2 ---
With this patch applied, fixups are now done when the driver is loaded by winedevice.
It goes further then only to crash at unimplemented ntoskrnl API:
--- snip --- 0013:Call ntoskrnl.exe.IoCreateDevice(601dcd40,00000040,00000000,00000022,00000000,00000001,64be1718) ret=004616ea 0013:trace:ntoskrnl:IoCreateDevice (0x601dcd40, 64, <null>, 34, 0, 1, 0x64be1718) 0013:Call ntdll.RtlAllocateHeap(00110000,00000008,000000f8) ret=602470fc 0013:Ret ntdll.RtlAllocateHeap() retval=0012c538 ret=602470fc 0013:Ret ntoskrnl.exe.IoCreateDevice() retval=00000000 ret=004616ea 0013:Call ntdll.RtlInitUnicodeString(64be16e4,64be16ec L"\Device\CdRom0") ret=00461409 0013:Ret ntdll.RtlInitUnicodeString() retval=0000001c ret=00461409 0013:Call KERNEL32.RaiseException(80000100,00000001,00000002,64be16a8) ret=60247485 0013:trace:seh:raise_exception code=80000100 flags=1 addr=0x7b8414e0 0013:trace:seh:raise_exception info[0]=602474e0 0013:trace:seh:raise_exception info[1]=6024955d wine: Call from 0x7b8414e0 to unimplemented function ntoskrnl.exe.IoGetDeviceObjectPointer, aborting 0013:trace:seh:call_stack_handlers calling handler at 0x7bc37e90 code=80000100 flags=1 0013:Call KERNEL32.UnhandledExceptionFilter(64be10d4) ret=7bc37ed3 wine: Unimplemented function ntoskrnl.exe.IoGetDeviceObjectPointer called at address 0x7b8414e0 (thread 0013), starting debugger... 0013:trace:seh:start_debugger Starting debugger "winedbg --auto 16 120" 0013:Ret KERNEL32.UnhandledExceptionFilter() retval=00000000 ret=7bc37ed3 0013:trace:seh:call_stack_handlers handler at 0x7bc37e90 returned 1 Unhandled exception: unimplemented function ntoskrnl.exe.IoGetDeviceObjectPointer called in 32-bit code (0x7b841558). --- snip ---
The security driver requests device object for "\Device\CdRom0" to create/forward synchronous fs requests to lower device drivers by using IofCallDriver(). There is no point to stub/implement IoGetDeviceObjectPointer without additional wine server infrastructure present (support of layered drivers).
Regards