[PATCH] krnl386.exe16: Improved support for early NE format executables.
Martin Payne
development at martinpayne.me.uk
Fri Dec 15 03:02:23 CST 2017
* If OS type in NE header is 0x00 (unknown) or the linker version is
4.x, check for imports from DOSCALLS and KERNEL to distinguish between
Windows and OS/2 or Multitasking DOS 4 executables.
* Prevents DISCARDABLE segments from link4 4.x executables being loaded
as 32 bit segments.
* Assumes target Windows version is 1.01 if linker version is 4 or there
is no expected version set in NE header.
* Don't try to load NE files with OS type of Windows/386. Even
Windows/386 doesn't recognise these as Windows executables.
Tested on Fedora 26 x86
Signed-off-by: Martin Payne <development at martinpayne.me.uk>
---
dlls/krnl386.exe16/ne_module.c | 70 +++++++++++++++++++++++++++++++++++++++---
1 file changed, 66 insertions(+), 4 deletions(-)
diff --git a/dlls/krnl386.exe16/ne_module.c b/dlls/krnl386.exe16/ne_module.c
index 14f8075166..72043a90ff 100644
--- a/dlls/krnl386.exe16/ne_module.c
+++ b/dlls/krnl386.exe16/ne_module.c
@@ -597,11 +597,10 @@ static HMODULE16 build_module( const void *mapping, SIZE_T mapping_size, LPCSTR
/* We now have a valid NE header */
- /* check to be able to fall back to loading OS/2 programs as DOS
- * FIXME: should this check be reversed in order to be less strict?
- * (only fail for OS/2 ne_exetyp 0x01 here?) */
+ /* check to be able to fall back to loading OS/2 programs as DOS */
if ((ne_header->ne_exetyp != 0x02 /* Windows */)
- && (ne_header->ne_exetyp != 0x04) /* Windows 386 */)
+ && (ne_header->ne_exetyp != 0x00 /* Unknown - possible early 1.x or 2.x app */)
+ && (ne_header->ne_ver == 0x04 /* link4 version 4.x has no ne_exetyp field - possible early 1.x app */))
return ERROR_BAD_FORMAT;
size = sizeof(NE_MODULE) +
@@ -649,6 +648,11 @@ static HMODULE16 build_module( const void *mapping, SIZE_T mapping_size, LPCSTR
pModule->ne_flags &= ~(NE_FFLAGS_BUILTIN | NE_FFLAGS_WIN32);
+ /* The original NE spec (which link4 version 4.x follows) doesn't have an ne_expver field and reserves it for
+ future use. Other early executables have the version set to 0.0. Assume Windows 1.01 in either case. */
+ if ((ne_header->ne_ver == 0x04) || (ne_header->ne_expver == 0x0000))
+ pModule->ne_expver = 0x0101;
+
/* Get the segment table */
pModule->ne_segtab = pData - (BYTE *)pModule;
@@ -658,6 +662,24 @@ static HMODULE16 build_module( const void *mapping, SIZE_T mapping_size, LPCSTR
for (i = ne_header->ne_cseg; i > 0; i--, pSeg++)
{
memcpy( pData, pSeg, sizeof(*pSeg) );
+
+ /* The original NE spec (which link4 version 4.x follows) uses the top four bits of the segment flags to set a
+ discard priority (0 = not discardable, 15 = highest priority). Later specs repurpose the top three bits and
+ use a single bit for the discardable flag. The top three bits must be masked off and discardable flag set
+ appropriately, otherwise a discard priority could get misinterpreted as a 32 bit segment flag which results
+ in an application crash. */
+ if (ne_header->ne_ver == 0x04)
+ {
+ WORD* seg_flags;
+ BOOL isDiscardable;
+
+ seg_flags = (WORD*)(pData + FIELD_OFFSET(SEGTABLEENTRY, flags));
+ isDiscardable = ((*seg_flags & 0xF000) > 0);
+
+ if (isDiscardable)
+ *seg_flags = ((*seg_flags & 0x0FFF) | NE_SEGFLAGS_DISCARDABLE);
+ }
+
pData += sizeof(SEGTABLEENTRY);
}
@@ -697,6 +719,46 @@ static HMODULE16 build_module( const void *mapping, SIZE_T mapping_size, LPCSTR
ne_header->ne_enttab - ne_header->ne_imptab )) goto failed;
pData += ne_header->ne_enttab - ne_header->ne_imptab;
+ /* If the linker version is 4.x or the executable type is unknown, it is necessary to check the import table for
+ known Windows and OS/2 libraries to determine whether it's a Windows executable. Executables with no module table
+ or which don't import known Windows or OS/2 libraries are also assumed to be Windows. */
+ if (((ne_header->ne_ver == 0x04) || (ne_header->ne_exetyp == 0x00)) && pModule->ne_modtab)
+ {
+ /* Array of offsets into the imported names table where module names are located */
+ WORD* module_table;
+
+ /* Series of Pascal strings containing the module names */
+ LPCSTR imported_names;
+
+ int i;
+ BOOL importsDosCalls = FALSE;
+ BOOL importsKernel = FALSE;
+
+ /* Get address of module table and imported names table from their offsets */
+ module_table = (WORD*)(((BYTE*)pModule) + pModule->ne_modtab);
+ imported_names = (LPCSTR)(((BYTE*)pModule) + pModule->ne_imptab);
+
+ /* Look for imports from DOSCALLS and KERNEL */
+ for (i = 0; i < pModule->ne_cmod; i++)
+ {
+ /* Module name is a Pascal string with byte length prefix */
+ LPCSTR module_name = imported_names + module_table[i];
+
+ if (!importsDosCalls && !strncmp("DOSCALLS", &module_name[1], module_name[0]))
+ importsDosCalls = TRUE;
+
+ else if (!importsKernel && !strncmp("KERNEL", &module_name[1], module_name[0]))
+ importsKernel = TRUE;
+
+ if (importsDosCalls && importsKernel)
+ break;
+ }
+
+ /* If the module has imports from DOSCALLS but not KERNEL, assume it's for OS/2 or Multitasking DOS 4. */
+ if (importsDosCalls && !importsKernel)
+ goto failed;
+ }
+
/* Load entry table, convert it to the optimized version used by Windows */
pModule->ne_enttab = pData - (BYTE *)pModule;
--
2.15.1.windows.2
More information about the wine-devel
mailing list