[Bug 12830] Xenocode Virtual Appliance Runtime Error

wine-bugs at winehq.org wine-bugs at winehq.org
Sun Sep 14 08:08:42 CDT 2008


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


Anastasius Focht <focht at gmx.net> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |focht at gmx.net




--- Comment #6 from Anastasius Focht <focht at gmx.net>  2008-09-14 08:08:34 ---
Hello,

there are two flavours of Xenocode application bundles - one with .NET bundled
and without .NET (if already installed).
If you operate on clean WINEPREFIX, the installer/downloader/updater selects
the "application with .NET bundled" package.
This will give you the 0xD000003A error which is basically a disguised
0xC000003A (STATUS_OBJECT_PATH_NOT_FOUND).

I will explain the issues with .NET preinstalled (on clean WINEPREFIX) because
the other case won't work due to limitations of wine's builtin CLR shim
(mscoree).
Due to mscoree's "native over builtin preferred" it will always lead to such
load errors when the one shipped with .NET (native) is not present.

Again: Install the required 1.1 or 2.0 .NET prerequisite before running the
installer/updater/downloader.

---

That Xenocode stuff hooks a substantial amount of native API (ntdll) to
establish an "application virtualization" layer between "high level" OS API and
the native API.

A Xenocode application bundle is basically a BLOB/image containing all the
required runtime dependencies as resource (obfuscated).
All system/object/memory/section/file/key/... native API calls which are
subject to "virtualization" are transparently handled in the Xenocode layer.

When you actually try to run the app with +relay thunks enabled to get some
logs you run into first problem: the APIs are not getting hooked.
Xenocode stuff overwrites the first 5 bytes of each API entry with a long jump
to it's own virtualization code.
If it detects the entry outside of PE .text section it won't hook it.

---

Following I present a simplified example how their virtualization works and how
Wine can be made to work/comply with this...
I refer to "xenocode application virtualization layer" with "XAVL" shortcut
from now on.

WINEDEBUG=+tid,+seh,+ntdll,+file,+snoop,+relay

(I used +relay in first snippet on purpose to show the unhooked call sequence):

--- snip ---
001c:Call KERNEL32.CreateFileW(001622d0 L"C:\\Program Files\\Kuma
Games\\KumaClient.exe",80000000,00000001,00000000,00000003,00000000,00000000)
ret=79ea0464
001c:trace:file:CreateFileW L"C:\\Program Files\\Kuma Games\\KumaClient.exe"
GENERIC_READ FILE_SHARE_READ  creation 3 attributes 0x0
001c:trace:file:RtlDosPathNameToNtPathName_U (L"C:\\Program Files\\Kuma
Games\\KumaClient.exe",0x32e6a0,(nil),(nil))
001c:trace:file:RtlGetFullPathName_U (L"C:\\Program Files\\Kuma
Games\\KumaClient.exe" 520 0x32e3e8 (nil))
001c:trace:ntdll:NtCreateFile handle=0x32e694 access=80000000
name=L"\\??\\C:\\Program Files\\Kuma Games\\KumaClient.exe" objattr=00000040
root=(nil) sec=(nil) io=0x32e698 alloc_size=(nil)
attr=00000000 sharing=00000001 disp=1 options=00000050 ea=(nil).0x00000000
001c:trace:file:wine_nt_to_unix_file_name L"\\??\\C:\\Program Files\\Kuma
Games\\KumaClient.exe" -> "/home/focht/.wine/dosdevices/c:/Program Files/Kuma
Games/KumaClient.exe"
001c:trace:file:CreateFileW returning 0x140
001c:Ret  KERNEL32.CreateFileW() retval=00000140 ret=79ea0464
001c:Call KERNEL32.GetFileSize(00000140,0032e7c0) ret=79ea04e9
001c:trace:ntdll:NtQueryInformationFile
(0x140,0x32e6d8,0x32e6e0,0x00000008,0x00000014)
001c:Ret  KERNEL32.GetFileSize() retval=0003ac90 ret=79ea04e9
001c:Call
KERNEL32.CreateFileMappingW(00000140,00000000,00000002,00000000,00000000,00000000)
ret=79ea0493
001c:Ret  KERNEL32.CreateFileMappingW() retval=00000144 ret=79ea0493
001c:Call
KERNEL32.MapViewOfFileEx(00000144,00000004,00000000,00000000,00000000,00000000)
ret=79ea4f44
001c:Ret  KERNEL32.MapViewOfFileEx() retval=03000000 ret=79ea4f44
001c:Call KERNEL32.UnmapViewOfFile(03000000) ret=79ebf917
001c:Ret  KERNEL32.UnmapViewOfFile() retval=00000001 ret=79ebf917
001c:Call KERNEL32.CloseHandle(00000144) ret=7a0e517c
001c:Ret  KERNEL32.CloseHandle() retval=00000001 ret=7a0e517c
001c:Call KERNEL32.CloseHandle(00000140) ret=7a0e5186
001c:Ret  KERNEL32.CloseHandle() retval=00000001 ret=7a0e5186
001c:Call KERNEL32.SetLastError(8013141d) ret=7a0e518f
001c:Ret  KERNEL32.SetLastError() retval=8013141d ret=7a0e518f
001c:Call KERNEL32.GetLastError() ret=79f061ff
001c:Ret  KERNEL32.GetLastError() retval=8013141d ret=79f061ff
001c:Call KERNEL32.GetLastError() ret=79e783fb
001c:Ret  KERNEL32.GetLastError() retval=8013141d ret=79e783fb
001c:Call ntdll.RtlAllocateHeap(00110000,00000000,0000000c) ret=79e78360
001c:Ret  ntdll.RtlAllocateHeap() retval=00167568 ret=79e78360
001c:Call KERNEL32.GetLastError() ret=79e71862
001c:Ret  KERNEL32.GetLastError() retval=8013141d ret=79e71862
001c:CALL MSVCR80._CxxThrowException(<unknown, check return>) ret=7a12b201
001c:Call KERNEL32.RaiseException(e06d7363,00000001,00000003,0032e830)
ret=78158dd3
001c:trace:seh:raise_exception code=e06d7363 flags=1 addr=0x7b844bd8
001c:trace:seh:raise_exception  info[0]=19930520
001c:trace:seh:raise_exception  info[1]=0032e870
001c:trace:seh:raise_exception  info[2]=7a34dfdc
001c:trace:seh:raise_exception  eax=7b82ccf5 ebx=7b8c39ac ecx=00000000
edx=0032e81c esi=0032e81c edi=0032e790
001c:trace:seh:raise_exception  ebp=0032e778 esp=0032e714 cs=0073 ds=007b
es=007b fs=0033 gs=003b flags=00200246
001c:trace:seh:call_stack_handlers calling handler at 0x7a31ea54 code=e06d7363
flags=1
001c:CALL MSVCR80.__CxxFrameHandler3(<unknown, check return>) ret=7bc6f71d
--- snip ---

Now without relay, native API redirected to XAVL:

--- snip ---
0016:trace:file:CreateFileW L"C:\\Program Files\\Kuma Games\\KumaClient.exe"
GENERIC_READ FILE_SHARE_READ  creation 3 attributes 0x0
0016:trace:file:RtlDosPathNameToNtPathName_U (L"C:\\Program Files\\Kuma
Games\\KumaClient.exe",0x32e730,(nil),(nil))
0016:trace:file:RtlGetFullPathName_U (L"C:\\Program Files\\Kuma
Games\\KumaClient.exe" 520 0x32e478 (nil))
0016:warn:file:wine_nt_to_unix_file_name
L"KumaClient.exe_v60664C46\\TheApp\\MODIFIED\\@PROGRAMFILESCOMMON at C:\\Program
Files\\Kuma Games\\KumaClient.exe" not found in
/home/focht/.wine/dosdevices/c:/windows/profiles/focht/Local
Settings/Application Data/Xenocode/ApplianceCaches
0016:warn:ntdll:NtQueryFullAttributesFile
L"\\??\\C:\\windows\\profiles\\focht\\Local Settings\\Application
Data\\Xenocode\\ApplianceCaches\\KumaClient.exe_v60664C46\\TheApp\\MODIFIED\\@PROGRAMFILESCOMMON at C:\\Program
Files\\Kuma Games\\KumaClient.exe" not found (c000003a)
0016:warn:file:wine_nt_to_unix_file_name
L"KumaClient.exe_v60664C46\\TheApp\\DELETED\\@PROGRAMFILESCOMMON at C:\\Program
Files\\Kuma Games\\KumaClient.exe.__deleted__" not found in
/home/focht/.wine/dosdevices/c:/windows/profiles/focht/Local
Settings/Application Data/Xenocode/ApplianceCaches
0016:warn:ntdll:NtQueryFullAttributesFile
L"\\??\\C:\\windows\\profiles\\focht\\Local Settings\\Application
Data\\Xenocode\\ApplianceCaches\\KumaClient.exe_v60664C46\\TheApp\\DELETED\\@PROGRAMFILESCOMMON at C:\\Program
Files\\Kuma Games\\KumaClient.exe.__deleted__" not found (c000003a)
0016:trace:file:CreateFileW returning 0x5a2878
0016:CALL MSVCR80._CxxThrowException(<unknown, check return>) ret=7a12b201
0016:trace:seh:raise_exception code=e06d7363 flags=1 addr=0x7b844bd8
0016:trace:seh:raise_exception  info[0]=19930520
0016:trace:seh:raise_exception  info[1]=0032e870
0016:trace:seh:raise_exception  info[2]=7a34dfdc
0016:trace:seh:raise_exception  eax=7b82ccf5 ebx=7b8c39ac ecx=00000000
edx=00004000 esi=00004000 edi=00158290
0016:trace:seh:raise_exception  ebp=0032e804 esp=0032e7a0 cs=0073 ds=007b
es=007b fs=0033 gs=003b flags=00200246 
--- snip ---

The first thing to notice in second snippet are NtQueryFullAttributesFile()
calls containing unusual path values (the calls originate from Wine's internal
API impl.)
These are actually injected/modified parameters from within XAVL hooks (not
part the problem).

In the second snippet - with native API hooked - the handle value returned by
CreateFileW() is 0x5a2878.
This is actually a "virtual" handle which refers to file object within XAVL (in
contrast to "native" Wine file handle 0x140 from first snippet).

A C++ exception is thrown with last error code 0x8013141D (HRESULT) which is
later turned into managed exception (System.BadImageFormatException) by .NET
runtime.

What actually happens in this snippet is that the .NET runtime verifies the
strong name signature of main executable.
The runtime tries to map the PE as datafile to inspect the .NET metadata.
Because of hooking/virtualization not present, the original main PE file is
mapped - which is not a .NET executable - leading to later error.
In the second snippet, XAVL intercepts the calls and maps the correct .NET
executable from its resources into memory so the verification would ideally
succeed - but it fails too.

One has to debug this mess to actually understand what XAVL expects and why it
fails ;-(

---

The first problem is that a virtualized ntdll.NtQueryInformationFile() call
fails in XAVL with internal error (0xC0000002 -> not supported).
The offending call sequence/chain is: kernel32.GetFileSize() ->
kernel32.GetFileSizeEx() -> ntdll.NtQueryInformationFile().
The culprit seems to be the FILE_INFORMATION_CLASS passed down to
NtQueryInformationFile() from Wine's kernel32.GetFileSizeEx().

The _FILE_INFORMATION_CLASS parameter validation of hooked
NtQueryInformationFile() in XAVL code:

--- snip of XAVL code (commented) ---
mov eax, dword ptr ss:[ebp+10]  ; _FILE_INFORMATION_CLASS
cmp eax, 4  ; FileBasicInformation
je short 0056C587
cmp eax, 5  ; FileStandardInformation
je short 0056C587
cmp eax, 9  ; FileNameInformation
je short 0056C587
cmp eax, 0E ; FilePositionInformation
je short 0056C587
cmp eax, 12 ; FileAllInformation
je short 0056C587
cmp eax, 23 ; FileAttributeTagInformation
je short 0056C587
cmp eax, 22 ; FileNetworkOpenInformation
jne 0056C7E4 ; unsupported error
--- snip of XAVL code (commented) ---

As previously said, XAVL doesn't seem to simulate the whole native API - only a
required subset of it.
In this case Wine's EndOfFileInformation class - passed by GetFileSizeEx() - is
not supported, leading to error.
So how does one get the file size?
Fortunately the FileStandardInformation class gives us the same information.
I modified kernel32.GetFileSizeEx() for this purpose and it successfully
passes, returning the correct information from XAVL.

---

The next problem is that a virtualized ntdll.NtCreateSection() call fails in
XAVL with internal error (0xC0000002 -> not supported).
The offending call sequence/chain is: kernel32.CreateFileMappingW() ->
ntdll.NtCreateSection().
The app/runtime code calls:
CreateFileMappingW( <virtual handle>, Security = NULL, Protect = PAGE_READONLY,
MaxSizeHigh = 0, MaxSizeLow = 0, Name = NULL).

The culprit seems to be the object attributes parameter passed down to
ntdll.NtCreateSection() from Wine's kernel32.CreateFileMappingW().
If non-NULL, 0xC0000002 error is internally raised by XAVL.
What most likely happens is that due to kernel32.CreateFileMappingW() security
attributes parameter being NULL it also expects ntdll.NtCreateSection() object
attributes to be NULL.
A quick modification to kernel32.CreateFileMappingW() which passes
NULL/non-NULL object attributes depending on security attributes ptr makes this
work.

---

With these two modifications present, the Xenocode stuff currently seems happy
and the app successfully starts (tested with "KumaGames").
It should be easy for any taker to write patches for kernel32.GetFileSizeEx()
and kernel32.CreateFileMappingW() with the information I gave.
I already tested it and Wine conformance tests still pass.

Regards


-- 
Configure bugmail: http://bugs.winehq.org/userprefs.cgi?tab=email
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