Valid HTML 4.01 Transitional Valid CSS Valid SVG 1.0

Me, myself & IT

Bugs in NTDLL.dll of non-english Editions of Windows Embedded POSReady 2009 alias Windows XP

Bug
Manifestation
Demonstration
Tidbit
Incompatibility

Bug

On non-english editions of Windows XP alias Windows Embedded POSReady 2009, the security update 3126593 alias MS16-014 (and of course all later updates which contain newer versions of NTDLL.dll too) introduces a bug: for message identifiers 0x8020000B and beyond, i.e. all error messages 0xC…, the entries in the MESSAGETABLE resource of NTDLL.dll are off by one!

Note: see the definition of NTSTATUS severity codes.

Manifestation

Due to this bug, Windows’ module loader shows wrong and misleading error messages for all errors encountered during load and initialisation of applications and their dependent DLLs.

Note: the following 6 cases present the message texts from german editions of Windows XP and Windows Embedded POSReady 2009; the bug but exists in all non-english editions of these versions of Windows NT!

  1. If a dependent DLL is empty or corrupted, non-english editions of Windows XP and Windows Embedded POSReady 2009 display the text for the message identifier 0xC000007C alias STATUS_NO_TOKEN

    Es wurde versucht, auf einen Token zu verweisen, der nicht existiert.
    Dies erfolgt im Normalfall durch Ansprechen des mit dem Thread verknüpften Token,
    wenn der Thread nicht die Identität eines Client angenommen hat.
    instead of the proper text for the message identifier 0xC000007B alias STATUS_INVALID_IMAGE_FORMAT
    {Abbild fehlerhaft}
    Die Anwendung oder DLL %hs ist keine gültige Windows-Datei.
    Überprüfen Sie dies mit der Installationsdiskette.
  2. If a dependent DLL is an invalid or wrong portable executable file, non-english editions of Windows XP and Windows Embedded POSReady 2009 display a bogus error message

    {Auslagerungsdatei konnte nicht erstellt werden}
    Exception Processing Message c0000145 Parameters c000007b 75b0bf7c 75b0bf7c 75b0bf7c
    instead of the proper text for the message identifier 0xC0000145 alias STATUS_APP_INIT_FAILURE
    {Fehler in Anwendung}
    Die Anwendung konnte nicht richtig initialisiert werden (0x%lx).
    Klicken Sie auf "OK", um die Anwendung zu beenden.
    Note: the expected wrong error message should but display the text for the message identifier 0xC0000146 alias STATUS_PAGEFILE_CREATE_FAILED
    {Auslagerungsdatei konnte nicht erstellt werden}
    Die Erstellung der Auslagerungsdatei %hs ist fehlgeschlagen (%lx).
    Die angeforderte Größe war %ld.
  3. If an entry point is missing in a dependent DLL, non-english editions of Windows Embedded POSReady 2009 and Windows XP display the text for the message identifier 0xC000013A alias STATUS_CONTROL_C_EXIT

    {Anwendung mit STRG+C beendet}
    Die Anwendung wurde mit STRG+C unterbrochen.
    instead of the proper text for the message identifier 0xC0000139 alias STATUS_ENTRYPOINT_NOT_FOUND
    {Einsprungpunkt nicht gefunden}
    Der Prozedureinsprungpunkt "%hs" wurde in der DLL "%hs" nicht gefunden.
  4. If a dependent DLL is missing, non-english editions of Windows XP and Windows Embedded POSReady 2009 display the text for the message identifier 0xC0000136 alias STATUS_OPEN_FAILED

    NtCreateFile-API ist fehlgeschlagen.
    Dieser Fehler sollte keiner Anwendung zurückgegeben werden,
    da er ein Platzhalter für den Windows-Redirector ist, damit
    dieser seine internen Fehlerzuordnungsroutinen verwenden kann.
    instead of the proper text for the message identifier 0xC0000135 alias STATUS_DLL_NOT_FOUND
    {Komponente nicht gefunden}
    Die Anwendung konnte nicht gestartet werden, weil %hs nicht gefunden wurde.
    Neuinstallation der Anwendung könnte das Problem beheben.
  5. If a dependent DLL is not accessible, non-english editions of Windows XP and Windows Embedded POSReady 2009 display a bogus error message

    {Auslagerungsdatei konnte nicht erstellt werden}
    Exception Processing Message c0000145 Parameters c0000022 75b0bf7c 75b0bf7c 75b0bf7c
    instead of the proper text for the message identifier 0xC0000145 alias STATUS_APP_INIT_FAILURE
    {Fehler in Anwendung}
    Die Anwendung konnte nicht richtig initialisiert werden (0x%lx).
    Klicken Sie auf "OK", um die Anwendung zu beenden.
    Note: the expected wrong error message should but display the text for the message identifier 0xC0000146 alias STATUS_PAGEFILE_CREATE_FAILED
    {Auslagerungsdatei konnte nicht erstellt werden}
    Die Erstellung der Auslagerungsdatei %hs ist fehlgeschlagen (%lx).
    Die angeforderte Größe war %ld.
  6. If initialisation of a dependent DLL fails, non-english editions of Windows Embedded POSReady 2009 and Windows XP display a bogus error message

    {Auslagerungsdatei konnte nicht erstellt werden}
    Exception Processing Message c0000145 Parameters c0000142 75b0bf7c 75b0bf7c 75b0bf7c
    instead of the proper text for the message identifier 0xC0000145 alias STATUS_APP_INIT_FAILURE
    {Fehler in Anwendung}
    Die Anwendung konnte nicht richtig initialisiert werden (0x%lx).
    Klicken Sie auf "OK", um die Anwendung zu beenden.
    Note: the expected wrong error message should but display the text for the message identifier 0xC0000146 alias STATUS_PAGEFILE_CREATE_FAILED
    {Auslagerungsdatei konnte nicht erstellt werden}
    Die Erstellung der Auslagerungsdatei %hs ist fehlgeschlagen (%lx).
    Die angeforderte Größe war %ld.
What happens in cases 2., 5. and 6.?
Why don’t they show the wrong error message with the text for the message identifier 0xC0000146 alias STATUS_PAGEFILE_CREATE_FAILED?

The reason hides in plain sight!

While the first (and only) format placeholder %lx of the proper message text specifies a numerical value, the first format placeholder %hs of the wrong message text but specifies a string value: the NTSTATUS values 0xC000007B, 0xC0000022 and 0xC0000142 are thus interpreted as addresses, resulting in access violation exceptions.
These are caught in Windows’ module loader, which displays its generic (english) error message text instead of the wrong message text, with the (localised) caption of the latter.

Demonstration

Perform the following 9 simple steps to demonstrate the bugs.
  1. Create the text file FUBAR.C with the following content in an arbitrary, preferable empty directory:

    // Copyright © 2016-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    
    #ifdef _DLL
    #pragma comment(linker, "/DEFAULTLIB:KERNEL32.LIB")
    #pragma comment(linker, "/DLL")
    #pragma comment(linker, "/ENTRY:_DllMainCRTStartup")
    #pragma comment(linker, "/EXPORT:_FUBAR,DATA")
    #pragma comment(linker, "/SUBSYSTEM:CONSOLE")
    
    __declspec(dllexport)
    const	CHAR	FUBAR[] = "Fucked up beyond all recognition\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bmessage identifier 0x8020000B\n";
    
    const	LPCSTR	szReason[4] = {"DLL_PROCESS_DETACH\n",
    		               "DLL_PROCESS_ATTACH\n",
    		               "DLL_THREAD_ATTACH\n",
    		               "DLL_THREAD_DETACH\n"};
    
    BOOL	WINAPI	_DllMainCRTStartup(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
    {
    #ifdef VERBOSE
    	DWORD	dwConsole;
    
    	WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), szReason[dwReason], lstrlenA(szReason[dwReason]), &dwConsole, NULL);
    #else
    	OutputDebugStringA(szReason[dwReason]);
    #endif
    	return FALSE;
    }
    #else
    #pragma comment(linker, "/DEFAULTLIB:FUBAR.LIB")
    #pragma comment(linker, "/DEFAULTLIB:KERNEL32.LIB")
    #pragma comment(linker, "/ENTRY:mainCRTStartup")
    #pragma comment(linker, "/SUBSYSTEM:CONSOLE")
    
    __declspec(dllimport)
    extern	const	CHAR	FUBAR[];
    
    __declspec(noreturn)
    VOID	CDECL	mainCRTStartup(VOID)
    {
    #ifdef VERBOSE
    	DWORD	dwConsole;
    
    	WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), FUBAR, lstrlenA(FUBAR), &dwConsole, NULL);
    #else
    	OutputDebugStringA(FUBAR);
    #endif
    	ExitProcess(GetLastError());
    }
    #endif
  2. Build the DLL FUBAR.DLL and the application FUBAR.EXE from the source file FUBAR.C created in step 1.:

    SET CL=/GF /Gy /W4 /wd4100 /Zl
    SET LINK=/NXCOMPAT /OPT:REF /OSVERSION:5.1 /RELEASE
    CL.EXE /LD /MD FUBAR.C
    CL.EXE FUBAR.C
    For details and reference see the MSDN articles Compiler Options and Linker Options.

    Note: if necessary, see the MSDN article Use the Microsoft C++ toolset from the command line for an introduction.

    Note: both portable executables build without the MSVCRT libraries.

    Note: the command lines can be copied and pasted as block into a Command Processor window!

    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    FUBAR.C
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /NXCOMPAT /OPT:REF /OSVERSION:5.1 /RELEASE
    /out:FUBAR.dll
    /dll
    /implib:FUBAR.lib
    FUBAR.obj
       Creating library FUBAR.lib and object FUBAR.exp
    
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    FUBAR.C
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /NXCOMPAT /OPT:REF /OSVERSION:5.1 /RELEASE
    /out:FUBAR.exe
    FUBAR.obj
  3. Execute the application FUBAR.EXE built in step 2. to demonstrate case 6.:

    FUBAR.EXE
    ECHO %ERRORLEVEL%
    [Screen shot of bogus error message for NTSTATUS 0xC0000145 alias STATUS_APP_INIT_FAILURE]
    -1073741502
    Note: -1073741502 is the decimal representation of 0xC0000142 alias STATUS_DLL_INIT_FAILED!
  4. Deny access to the DLL FUBAR.DLL built in step 2., then execute the application FUBAR.EXE built in step 2. to demonstrate case 5.:

    CACLS.EXE FUBAR.DLL /E /D "%USERNAME%"
    FUBAR.EXE
    ECHO %ERRORLEVEL%
    [Screen shot of bogus error message for NTSTATUS 0xC0000145 alias STATUS_APP_INIT_FAILURE]
    -1073741790
    Note: -1073741790 is the decimal representation of 0xC0000022 alias STATUS_ACCESS_DENIED!
  5. Erase the DLL FUBAR.DLL built in step 2., then execute the application FUBAR.EXE built in step 2. to demonstrate case 4.:

    ERASE FUBAR.DLL
    FUBAR.EXE
    ECHO %ERRORLEVEL%
    [Screen shot of wrong german error message for NTSTATUS 0xC0000135 alias STATUS_DLL_NOT_FOUND]
    -1073741515
    Note: -1073741515 is the decimal representation of 0xC0000135 alias STATUS_DLL_NOT_FOUND!
  6. Copy an arbitrary DLL as FUBAR.DLL, then execute the application FUBAR.EXE built in step 2. to demonstrate case 3.:

    COPY "%SystemRoot%\System32\NTDLL.DLL" FUBAR.DLL
    FUBAR.EXE
    ECHO %ERRORLEVEL%
    [Screen shot of wrong german error message for NTSTATUS 0xC0000139 alias STATUS_ENTRYPOINT_NOT_FOUND]
    -1073741511
    Note: -1073741511 is the decimal representation of 0xC0000139 alias STATUS_ENTRYPOINT_NOT_FOUND!
  7. Copy an arbitrary application as FUBAR.DLL, then execute the application FUBAR.EXE built in step 2. to demonstrate case 2.:

    COPY /Y FUBAR.EXE FUBAR.DLL
    FUBAR.EXE
    ECHO %ERRORLEVEL%
    [Screen shot of bogus error message for NTSTATUS 0xC0000145 alias STATUS_APP_INIT_FAILURE]
    -1073741701
    Note: -1073741701 is the decimal representation of 0xC000007B alias STATUS_INVALID_IMAGE_FORMAT!
  8. Copy an arbitrary MS-DOS program as FUBAR.DLL, then execute the application FUBAR.EXE built in step 2. to demonstrate case 1.:

    COPY /Y "%SystemRoot%\System32\Command.com" FUBAR.DLL
    FUBAR.EXE
    ECHO %ERRORLEVEL%
    [Screen shot of wrong german error message for NTSTATUS 0xC000012F alias STATUS_INVALID_IMAGE_NOT_MZ]
    -1073741521
    Note: -1073741521 is the decimal representation of 0xC000012F alias STATUS_INVALID_IMAGE_NOT_MZ!
  9. Copy an empty file as FUBAR.DLL, then execute the application FUBAR.EXE built in step 2. to demonstrate case 1. again:

    COPY /Y NUL: FUBAR.DLL
    FUBAR.EXE
    ECHO %ERRORLEVEL%
    [Screen shot of wrong german error message for NTSTATUS 0xC0000020 alias STATUS_INVALID_FILE_FOR_SECTION]
    -1073741792
    Note: -1073741792 is the decimal representation of 0xC0000020 alias STATUS_INVALID_FILE_FOR_SECTION!
Note: the makefile FUBAR.MAK performs the 9 steps from above.

Tidbit

[Screen shot of wrong german error message for NTSTATUS 0xC0000005 alias STATUS_ACCESS_VIOLATION]

If your application happens to cause an access violation exception on a german edition of Windows XP or Windows Embedded POSReady 2009, you’ll see a wrong error message with the text for the message identifier 0xC0000006 alias STATUS_IN_PAGE_ERROR including a rather long string of 81 hexadecimal digits

Die Anweisung "0x%08lx" verweist auf Speicher bei "0x%08lx".
Die Daten wurden wegen eines E/A-Fehlers in "0x%081x" nicht in den Arbeitsspeicher übertragen.
instead of the proper text for the message identifier 0xC0000005 alias STATUS_ACCESS_VIOLATION including a comprehensible text Lesen, Schreiben or Ausführen
Die Anweisung in "0x%08lx" verweist auf Speicher in "0x%08lx".
Der Vorgang "%s" konnte nicht auf dem Speicher durchgeführt werden.

Incompatibility

The message texts (more precise: their format placeholders) in the MESSAGETABLE resource of NTDLL.dll are not written in the standard format defined by Message Text Files and expected from the Win32 function FormatMessage():
%0 Terminates a message text line without a trailing new line character. This escape sequence can be used to build up long lines or to terminate the message itself without a trailing new line character. It is useful for prompt messages.
%n!format string! Identifies an insert. The value of n can be in the range from 1 through 99. The format string (which must be surrounded by exclamation marks) is optional and defaults to !s! if not specified. […]
All message texts which contain %0 (see the preceding section for two examples) are therefore truncated when retrieved with this Win32 function!

Note: corresponding to the NTSTATUS values 0xC0000144 alias STATUS_UNHANDLED_EXCEPTION and 0xC000021A alias STATUS_SYSTEM_PROCESS_TERMINATED, the Win32 error codes 574 alias ERROR_UNHANDLED_EXCEPTION and 591 alias ERROR_SYSTEM_PROCESS_TERMINATED were added in Windows Vista® and newer versions of Windows NT, but their message texts were copied unchanged from NTDLL.dll to the message tables of the Win32 system DLLs, thus porting this bug: on current versions of Windows NT, the command lines

NET.EXE HELPMSG 574
CERTUTIL.EXE /ERROR 574
NET.EXE HELPMSG 591
CERTUTIL.EXE /ERROR 591
yield the crippled output
{Application Error}
The exception s (0x
0x23e (WIN32: 574) -- 574 (574)
Error message text: {Application Error}
The exception %s (0x
CertUtil: -error command completed successfully.
{Fatal System Error}
The hs system process terminated unexpectedly with a status of 0x
0x24f (WIN32: 591) -- 591 (591)
Error message text: {Fatal System Error}
The %hs system process terminated unexpectedly with a status of 0x
CertUtil: -error command completed successfully.
instead of the proper output
{Application Error}
The exception %s (0x%08lx) occurred in the application at location 0x%08lx.
{Fatal System Error}
The %hs system process terminated unexpectedly with a status of 0x%08x (0x%08x 0x%08x).
The system has been shut down.

Contact

If you miss anything here, have additions, comments, corrections, criticism or questions, want to give feedback, hints or tipps, report broken links, bugs, deficiencies, errors, inaccuracies, misrepresentations, omissions, shortcomings, vulnerabilities or weaknesses, …: don’t hesitate to contact me and feel free to ask, comment, criticise, flame, notify or report!

Use the X.509 certificate to send S/MIME encrypted mail.

Note: email in weird format and without a proper sender name is likely to be discarded!

I dislike HTML (and even weirder formats too) in email, I prefer to receive plain text.
I also expect to see your full (real) name as sender, not your nickname.
I abhor top posts and expect inline quotes in replies.

Terms and Conditions

By using this site, you signify your agreement to these terms and conditions. If you do not agree to these terms and conditions, do not use this site!

Data Protection Declaration

This web page records no (personal) data and stores no cookies in the web browser.

The web service is operated and provided by

Telekom Deutschland GmbH
Business Center
D-64306 Darmstadt
Germany
<‍hosting‍@‍telekom‍.‍de‍>
+49 800 5252033

The web service provider stores a session cookie in the web browser and records every visit of this web site with the following data in an access log on their server(s):


Copyright © 1995–2024 • Stefan Kanthak • <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>