* 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@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;