Hi all,
Uwe and I have been working on several executables which have been compressed by Shrinker. Basically, under win98 it tries to load a VXD which is disallowed. The prospects are better under nt40 mode, but it looks like Shrinker purposely causes read and write exceptions.
After looking at this for a little over a week, I've found that there are two problems which need to be solved.
The first is that the exception handler installed by Shrinker under Wine causes another fault and then a spin. Under WinDbg (in W2K/VMWare) the exception handler does not cause another fault, and execution continues nicely.
The second is that WinDbg finds a write fault where Wine does not. To fix this, Wine must set protections on the executable's image map. I came up with a patch to do this, and it worked for my Shrinkered exe, but not for Uwe's.
Uwe's exe loads MSVCRT20.DLL, and then Wine calls PE_fixup_imports, which attempts to write to that DLL's .idata section, which had been set read-only. Clearly Wine needs to write to that section to set up imports which haven't been implemented.
Now, my patch put the protections on the mapped image right at the end of map_image. This works when map_image is called (eventually) from PROCESS_InitWine, so that the initial executable gets its protections set right.
However, it doesn't work for PE_LoadLibraryExA (called by LoadLibraryExA) because it first checks to see if the library has already been loaded. If it has been loaded, it calls PE_fixup_imports. If it has not been loaded, it calls (eventually) map_image.
So if the library has already been loaded, it has already had its protections set up by map_image at some point in the past. Then when PE_fixup_imports is called, a protection violation occurs.
My current idea is to go into PE_fixup_imports, unprotect the .idata section, do the fixups, and reprotect the section.
The bad thing is that if someone else in the future writes some code that does additional fixups to other sections, they are also going to have to unprotect and reprotect. This might turn out to be a good thing, though, if it forces you to do that.
What does everyone think?
Thanks,
--Rob
Robert Baruch autophile@starband.net writes:
My current idea is to go into PE_fixup_imports, unprotect the .idata section, do the fixups, and reprotect the section.
Imports are not necessarily contained in the .idata section. Also base relocations can touch just about every code page, so you'd need to unprotect everything. Probably easier to only set up the right permissions after all imports and relocations have been done.
Also note that it is allowed for an app to write to the resource section, even though it is marked read-only. NT sets up an exception handler to unprotect it when necessary; this is supposed to help in finding bugs. So you probably have to do the same thing.
Alexandre Julliard wrote:
Imports are not necessarily contained in the .idata section. Also base relocations can touch just about every code page, so you'd need to unprotect everything. Probably easier to only set up the right permissions after all imports and relocations have been done.
That was my first thought. Then I looked at the call tree for map_image.
map_image is only called by MapViewOfFileEx.
MapViewOfFileEx is called by MapViewOfFile, DPLAYX_ConstructData, HEAP_CreateSystemHeap, and VXD_Win32s. At this point my eyes began to glaze over. But I forged on with looking MapViewOfFile. GAAAH! That function is referenced in no less than sixteen places!
So already if we wanted to set protections sometime after map_image is called, we would have to do it in at least four, if not nineteen, different places -- provided all those calls aren't unified somewhere farther up, but not too far up, the call tree. This is just asking for bugs when someone decides to call MapViewOfFileEx from somewhere else and forgets to set up protections.
That's why I felt it was maybe a better idea to set up the protections beforehand, and then require whoever is modifying readonly data to unprotect and then reprotect that data. The worst that can happen then is that Wine segfaults in that place, which is a reminder that the data is protected and needs to be temporarily unprotected. And if currently we only have to do that in maybe two places, that's a much better proposition than in nineteen (and counting).
I took a quick look at PE_fixup_imports, and it *should* be reasonbly simple to do the protect-unprotect there. I haven't proven it, though, and I haven't looked at the other places where this would need to be done such as in do_relocations and in...
Also note that it is allowed for an app to write to the resource section, even though it is marked read-only. NT sets up an exception handler to unprotect it when necessary; this is supposed to help in finding bugs. So you probably have to do the same thing.
Yes, that should be done, too. I haven't dug that far into NT yet, so that mod may have to come in a future patch.
Anyway, what are your thoughts on this?
Thanks,
--Rob
Robert Baruch autophile@starband.net writes:
MapViewOfFileEx is called by MapViewOfFile, DPLAYX_ConstructData, HEAP_CreateSystemHeap, and VXD_Win32s. At this point my eyes began to glaze over. But I forged on with looking MapViewOfFile. GAAAH! That function is referenced in no less than sixteen places!
But none of these are used to map executable images. The only place we do that is from PE_LoadImage, so this is the only call that needs to set the protections.
Now there is a possibility that the app itself would create a mapping with SEC_IMAGE; but we don't implement this correctly anyway, so it don't think it matters if we don't set the protections in that case.
Yes, that should be done, too. I haven't dug that far into NT yet, so that mod may have to come in a future patch.
The problem is once you switch on the protections it will break apps; so you have to do the exception handler stuff at the same time.
Alexandre Julliard wrote:
But none of these are used to map executable images. The only place we do that is from PE_LoadImage, so this is the only call that needs to set the protections.
That's a relief! That makes it very easy. In fact, I've finished that part of the patch.
Yes, that should be done, too. I haven't dug that far into NT yet, so that mod may have to come in a future patch.
The problem is once you switch on the protections it will break apps; so you have to do the exception handler stuff at the same time.
Hmm. Can you suggest an API call that would write to the resource area? Or is this more like, any time anywhere something writes to the resource area of any mapped PE file, allow it?
Thanks,
--Rob
Robert Baruch autophile@starband.net writes:
Hmm. Can you suggest an API call that would write to the resource area? Or is this more like, any time anywhere something writes to the resource area of any mapped PE file, allow it?
Anywhere something writes, yes. The problem is that resource data was writeable in Win16, and some (broken) apps depend on that. So on Win32 the resource area is read-only but if an app writes into it, it gets an exception and the default handler unprotects the resource area. There is an article somewhere on MSDN about this.
Alexandre Julliard wrote:
Robert Baruch autophile@starband.net writes:
MapViewOfFileEx is called by MapViewOfFile, DPLAYX_ConstructData, HEAP_CreateSystemHeap, and VXD_Win32s. At this point my eyes began to glaze over. But I forged on with looking MapViewOfFile. GAAAH! That function is referenced in no less than sixteen places!
But none of these are used to map executable images. The only place we do that is from PE_LoadImage, so this is the only call that needs to set the protections.
Bummer:
MODULE_LoadLibraryExA calls PE_LoadLibraryExA, which first calls PE_LoadImage (which sets protections) and then PE_CreateModule, which calls PE_fixup_imports, which causes a protection violation (see trace below)!
PE_CreateModule is also called from ELF_LoadLibraryExA, load_library, and from start_process.
Maybe the right place to set protections is at the end of PE_CreateModule.
--Rob
trace:module:GetModuleFileNameA J:\targ2001\data\tar2001.exe trace:module:MODULE_LoadLibraryExA Already loaded module 'USER32.dll' at 0x40670000, count=3 trace:win32:PE_fixup_imports Microsoft style imports used trace:win32:PE_fixup_imports --- MessageBoxA USER32.dll.390 trace:win32:MODULE_GetProcAddress (40670000,MessageBoxA) trace:win32:PE_FindExportedFunction (MessageBoxA) trace:seh:EXC_RtlRaiseException code=c0000005 flags=0 First chance exception: page fault on write access to 0x75b6903c in 32-bit code (0x40091b8f). ... =>0 0x40091b8f (PE_fixup_imports+0x2af(wm=0x40382a68) [pe_image.c:353] in libntdll.so) (ebp=405b6a34) 1 0x40092582 (PE_CreateModule+0x292(hModule=0x75b30000, filename=0x403828a8, flags=0x0, hFile=0x30, bu iltin=0x0) [pe_image.c:717] in libntdll.so) (ebp=405b6ac0) 2 0x400926d2 (PE_LoadLibraryExA+0x62(name=0x403828a8, flags=0x0) [pe_image.c:792] in libntdll.so) (ebp =405b6ae8) 3 0x400907fd (MODULE_LoadLibraryExA+0x42d(libname=0x7ffd9cfe, hfile=0x0, flags=0x0, name=0x7ffd9cfe, n ame=0x7ffd9cfe, name=0x7ffd9cfe) [module.c:1477] in libntdll.so) (ebp=405b6b2c)