Valid HTML 4.01 Transitional Valid CSS Valid SVG 1.0

Me, myself & IT

Microsoft®’s Excellence in Disinformation and Incompetence

Purpose
Reason
Reference
Trivia
Blunder № 1
Falsification
Blunder № 2
Falsification
Blunder № 3
Falsification
Blunder № 4
Falsification
Blunder № 5
Falsification
Blunder № 6
Falsification
Blunder № 7
Falsification
Blunder № 8
Falsification
Blunder № 9
Falsification
Blunder № 10
Demonstration
Blunder № 11
Falsification
Blunder № 12
Falsification
Blunder № 13
Demonstration
Blunder № 14
Background Information
Demonstration
Blunder № 15
Demonstration
Blunder № 16
Blunder № 17
Blunder № 18
Blunder № 19
Blunder № 20
Blunder № 21
Falsification
Blunder № 22
Falsification
Blunder № 23
Falsification
Blunder № 24
Demonstration
Blunder № 25
Demonstration
Blunder № 26
Demonstration
Blunder № 27
Blunder № 28
Work-Around
Blunder № 29
Blunder № 30
Falsification
Blunder № 31
Falsification
Blunder № 32
Demonstration
Blunder № 33
Demonstration
Blunder № 34
Demonstration
Blunder № 35
Demonstration
Blunder № 36
Demonstration
Blunder № 37
Demonstration
Blunder № 38
Demonstration
Blunder № 39
Demonstration
Blunder № 40
Demonstration
Blunder № 41
Demonstration
Blunder № 42
Demonstration
Blunder № 43
Falsification

Purpose

Present multiple cases of Microsoft’s Art of Computer Programming Excellence in Disinformation and Incompetence.

Note: some cases of disinformation presented below were published 25 (in words: twenty-five) years ago and never corrected!

Reason

In computer science and software engineering, information hiding and the need-to-know principle are well-known concepts which are but not applicable to documentation, as done by Microsoft since more than 40 years.

45 years ago I participated in the research project Entwicklung von Entwurfstechnologien für Familien von Programmsystemen (Development of Design Technologies for Families of Program Systems) initiated and led by David Parnas (who coined the term information hiding) to design and implement the prototype of a modular operating system built from formally specified independent components where both concepts played the crucial role.

Reference

David Lorge Parnas, On the Design and Development of Program Families, IEEE Transactions on Software Engineering, Volume SE-2(1):1-9, March 1976, ISSN 0098-5589, 1939-3520.

Trivia

Was für Plunder! (What rubbish!)
Gebhard Leberecht von Blücher (1742-1819), Fürst von Wahlstatt, Prussian field marshal.

Blunder № 1

The documentation for each of the Win32 functions NetAddServiceAccount(), NetEnumerateServiceAccounts(), NetIsServiceAccount(), NetQueryServiceAccount() and NetRemoveServiceAccount() states since 2009:
This function has no associated import library. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Logoncli.dll.

[…]

Requirement Value
Header lmaccess.h
DLL Netapi32.dll
Note: while the Requirements sections name Netapi32.dll, all texts but specify Logoncli.dll!

Falsification

Perform the following 12 simple steps to prove the documentation cited above wrong.
  1. Determine which of the 2 DLLs Logoncli.dll and Netapi32.dll provides the 5 functions:

    FOR %? IN (Logoncli.dll) DO SET LOGONCLI=%~$PATH:?
    LINK.EXE /DUMP /EXPORTS /OUT:logoncli.txt "%LOGONCLI%"
    FOR %? IN (Netapi32.dll) DO SET NETAPI32=%~$PATH:?
    LINK.EXE /DUMP /EXPORTS /OUT:netapi32.txt "%NETAPI32%"
    FIND.EXE "ServiceAccount" logoncli.txt netapi32.txt
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    SET LOGONCLI=C:\Windows\System32\logoncli.dll
    
    Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    SET NETAPI32=C:\Windows\System32\netapi32.dll
    
    Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    ---------- LOGONCLI.TXT
             61   3C 0000F520 NetAddServiceAccount
             62   3D 0000F5AC NetEnumerateServiceAccounts
             66   41 0000F64C NetIsServiceAccount
             69   44 0000F6AC NetQueryServiceAccount
             70   45 0000F568 NetRemoveServiceAccount
    
    ---------- NETAPI32.TXT
             98   61          NetAddServiceAccount (forwarded to LOGONCLI.NetAddServiceAccount)
            142   8D          NetEnumerateServiceAccounts (forwarded to LOGONCLI.NetEnumerateServiceAccounts)
            164   A3          NetIsServiceAccount (forwarded to LOGONCLI.NetIsServiceAccount)
            186   B9          NetQueryServiceAccount (forwarded to LOGONCLI.NetQueryServiceAccount)
            191   BE          NetRemoveServiceAccount (forwarded to LOGONCLI.NetRemoveServiceAccount)
    Logoncli.dll exports the 5 functions; Netapi32.dll exports them too, but as forwarders to the functions implemented in Logoncli.dll, i.e. either DLL can be linked!
  2. Determine whether the highlighted claim This function has no associated import library. is true – or false:

    FOR %? IN (Logoncli.lib) DO SET LOGONCLI=%~$LIB:?
    FOR %? IN (Netapi32.lib) DO SET NETAPI32=%~$LIB:?
    LINK.EXE /DUMP /EXPORTS /OUT:netapi32.txt "%NETAPI32%"
    FIND.EXE "ServiceAccount" netapi32.txt
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    SET LOGONCLI=
    SET NETAPI32=C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\NetAPI32.Lib
    
    Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    ---------- NETAPI32.TXT
                      _NetAddServiceAccount@16
                      _NetEnumerateServiceAccounts@16
                      _NetIsServiceAccount@12
                      _NetQueryServiceAccount@16
                      _NetRemoveServiceAccount@12
    OUCH¹: contrary to the statements in all 5 MSDN articles cited above, not just the Windows Software Development Kit for Windows 7 but ships with an associated import library NetAPI32.lib!
  3. Create the text file lmaccess.c with the following content in an arbitrary, preferably empty directory:

    // Copyleft © 2009-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <lmaccess.h>
    
    #define STATUS_SUCCESS	0L
    
    __declspec(noreturn)
    VOID	CDECL	mainCRTStartup(VOID)
    {
    #ifndef BLUNDER
    	DWORD	dwCount;
    	LPWSTR	*lpArray;
    	LONG	ntStatus = NetEnumerateServiceAccounts((LPWSTR) NULL,
    		                                       0UL,
    		                                       &dwCount,
    		                                       &lpArray);
    #elif BLUNDER == 1
    	BOOL	blunder;
    	LPBYTE	lpBuffer;
    	LONG	ntStatus = -NetIsServiceAccount((LPWSTR) NULL,
    		                                (LPWSTR) NULL,
    		                                &blunder);
    	if (ntStatus == -STATUS_SUCCESS)
    		ntStatus = NetQueryServiceAccount((LPWSTR) NULL,
    		                                  (LPWSTR) NULL,
    		                                  0UL,
    		                                  &lpBuffer);
    #else
    	LONG	ntStatus = NetAddServiceAccount((LPWSTR) NULL,
    		                                L"",
    		                                (LPWSTR) NULL,
    		                                SERVICE_ACCOUNT_FLAG_LINK_TO_HOST_ONLY);
    	if (ntStatus == STATUS_SUCCESS)
    		ntStatus = -NetRemoveServiceAccount((LPWSTR) NULL,
    		                                    L"",
    		                                    SERVICE_ACCOUNT_FLAG_UNLINK_FROM_HOST_ONLY);
    #endif
    	ExitProcess(ntStatus);
    }
  4. Compile and link the source file lmaccess.c created in step 3. to test whether the associated import library Netapi32.lib can be linked statically – or not:

    SET CL=/W4 /Zl
    SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    CL.EXE lmaccess.c kernel32.lib netapi32.lib
    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.
    
    lmaccess.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:lmaccess.exe
    lmaccess.obj
    kernel32.lib
    netapi32.lib
    lmaccess.obj : error LNK2019: unresolved external symbol _NetEnumerateServiceAccounts referenced in function _mainCRTStartup
    lmaccess.exe : fatal error LNK1120: 1 unresolved externals
    OUCH²: the undecorated symbol name _NetEnumerateServiceAccounts in the error message indicates 2 omissions bugs in the declaration of the function prototype for NetEnumerateServiceAccounts() in the header file lmaccess.h – the mandatory calling convention __stdcall and the (optional) storage class attribute __declspec(dllimport) are missing!

    Note: most obviously nobody at Microsoft tests or uses their own products crap with the default settings of their own development tools – and their quality miserability assurance is sound asleep!

    Note: without the optional __declspec(dllimport) the Visual C compiler generates a CALL ‹function name› that the linker needs to resolve with a stub function, an indirect JMP through the Import Address Table; with __declspec(dllimport) it just generates an indirect CALL through the Import Address Table.

    Note: properly declared, the symbol name would be __imp__NetEnumerateServiceAccounts@16 – see the MSDN article Decorated Names for details.

  5. Repeat the previous step 4., now with the /Gz compiler option:

    CL.EXE /Gz lmaccess.c kernel32.lib netapi32.lib
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    lmaccess.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:lmaccess.exe
    lmaccess.obj
    kernel32.lib
    netapi32.lib
  6. Execute the console application lmaccess.exe built in step 5. and evaluate its exit code:

    .\lmaccess.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    0xc0020012 (NT: 0xc0020012 RPC_NT_UNKNOWN_IF) -- 3221356562 (-1073610734)
    Error message text: The interface is unknown.
    CertUtil: -error command completed successfully.
    OUCH³: WTF?
  7. Repeat step 4., but with the preprocessor macro BLUNDER defined:

    CL.EXE /DBLUNDER lmaccess.c kernel32.lib netapi32.lib
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    lmaccess.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:lmaccess.exe
    lmaccess.obj
    kernel32.lib
    netapi32.lib
    lmaccess.obj : error LNK2019: unresolved external symbol _NetIsServiceAccount referenced in function _mainCRTStartup
    lmaccess.obj : error LNK2019: unresolved external symbol _NetQueryServiceAccount referenced in function _mainCRTStartup
    lmaccess.exe : fatal error LNK1120: 2 unresolved externals
    OUCH⁴: the declarations of the function prototypes for NetIsServiceAccount() and NetQueryServiceAccount() are as faulty as that for NetEnumerateServiceAccounts()!
  8. Repeat the previous step 7., now with the /Gz compiler option too:

    CL.EXE /DBLUNDER /Gz lmaccess.c kernel32.lib netapi32.lib
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    lmaccess.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:lmaccess.exe
    lmaccess.obj
    kernel32.lib
    netapi32.lib
  9. Execute the console application lmaccess.exe built in step 8. and evaluate its exit code:

    .\lmaccess.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    0xc0030009 (NT: 0xc0030009 RPC_NT_NULL_REF_POINTER) -- 3221422089 (-1073545207)
    Error message text: A null reference pointer was passed to the stub.
    CertUtil: -error command completed successfully.
    OUCH⁵: while the NetIsServiceAccount() function accepts a NULL pointer for the AccountName parameter, the NetQueryServiceAccount() function rejects it with NTSTATUS 0xC0030009 alias RPC_NT_NULL_REF_POINTER!
  10. Repeat step 7., now with the preprocessor macro BLUNDER defined as 0:

    CL.EXE /DBLUNDER=0 lmaccess.c kernel32.lib netapi32.lib
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    lmaccess.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:lmaccess.exe
    lmaccess.obj
    kernel32.lib
    netapi32.lib
    lmaccess.obj : error LNK2019: unresolved external symbol _NetAddServiceAccount referenced in function _mainCRTStartup
    lmaccess.obj : error LNK2019: unresolved external symbol _NetRemoveServiceAccount referenced in function _mainCRTStartup
    lmaccess.exe : fatal error LNK1120: 2 unresolved externals
    OUCH⁶: the declarations of the function prototypes for NetAddServiceAccount() and NetRemoveServiceAccount() are as faulty as those for NetEnumerateServiceAccounts(), NetIsServiceAccount() and NetQueryServiceAccount()!
  11. Repeat the previous step 10., now with the /Gz compiler option too:

    CL.EXE /DBLUNDER=0 /Gz lmaccess.c kernel32.lib netapi32.lib
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    lmaccess.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:lmaccess.exe
    lmaccess.obj
    kernel32.lib
    netapi32.lib
  12. Finally execute the console application lmaccess.exe built in step 11. and evaluate its exit code:

    .\lmaccess.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    0xc0020012 (NT: 0xc0020012 RPC_NT_UNKNOWN_IF) -- 3221356562 (-1073610734)
    Error message text: The interface is unknown.
    CertUtil: -error command completed successfully.
    OUCH⁷: WTF?
Note: a repetition of this falsification in the 64-bit development and execution environments is left as an exercise to the reader.

Blunder № 2

The documentation for the Win32 function RtlDecryptMemory() states since 2001:
Note This function has no associated import library. This function is available as a resource named SystemFunction041 in Advapi32.dll. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Advapi32.dll.
[…]
NTSTATUS RtlDecryptMemory(
  PVOID Memory,
  ULONG MemorySize,
  ULONG OptionFlags
);

[…]

Requirement Value
Header ntsecapi.h
DLL Advapi32.dll
Ouch¹: a function is not a (named) resource – see the MSDN article Menus and Other Resources for their definition!

The documentation for the Win32 function RtlEncryptMemory() states since 2001:

Note This function has no associated import library. This function is available as a resource named SystemFunction040 in Advapi32.dll. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Advapi32.dll.
[…]
NTSTATUS RtlEncryptMemory(
  PVOID Memory,
  ULONG MemorySize,
  ULONG OptionFlags
);

[…]

Requirement Value
Header ntsecapi.h
DLL Advapi32.dll
Ouch²: a function is not a (named) resource – see the MSDN article Menus and Other Resources for their definition!

The documentation for the Win32 function RtlGenRandom() states since 2001:

Note This function has no associated import library. This function is available as a resource named SystemFunction036 in Advapi32.dll. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Advapi32.dll.
[…]
BOOLEAN RtlGenRandom(
  PVOID RandomBuffer,
  ULONG RandomBufferLength
);

[…]

Requirement Value
Header ntsecapi.h
DLL Advapi32.dll
Ouch³: a function is not a (named) resource – see the MSDN article Menus and Other Resources for their definition!

Falsification

Perform the following 5 simple steps to prove the documentation cited above wrong.
  1. Determine whether the highlighted claim This function has no associated import library. is true – or false:

    FOR %? IN (Advapi32.lib) DO SET ADVAPI32=%~$LIB:?
    LINK.EXE /DUMP /EXPORTS /OUT:advapi32.txt "%ADVAPI32%"
    FIND.EXE "SystemFunction0" advapi32.txt
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    SET ADVAPI32=C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\Advapi32.Lib
    
    Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    ---------- ADVAPI32.TXT
                      _SystemFunction001@12
                      _SystemFunction002@12
                      _SystemFunction003@8
                      _SystemFunction004@12
                      _SystemFunction005@12
                      _SystemFunction006@8
                      _SystemFunction007@8
                      _SystemFunction008@12
                      _SystemFunction009@12
                      _SystemFunction010@12
                      _SystemFunction011@12
                      _SystemFunction012@12
                      _SystemFunction013@12
                      _SystemFunction014@12
                      _SystemFunction015@12
                      _SystemFunction016@12
                      _SystemFunction017@12
                      _SystemFunction018@12
                      _SystemFunction019@12
                      _SystemFunction020@12
                      _SystemFunction021@12
                      _SystemFunction022@12
                      _SystemFunction023@12
                      _SystemFunction024@12
                      _SystemFunction025@12
                      _SystemFunction026@12
                      _SystemFunction027@12
                      _SystemFunction028@8
                      _SystemFunction029@8
                      _SystemFunction030@8
                      _SystemFunction031@8
                      _SystemFunction032@8
                      _SystemFunction033@8
                      _SystemFunction034@12
                      _SystemFunction036@8
                      _SystemFunction040@12
                      _SystemFunction041@12
    OUCH¹: contrary to the statements in all 3 MSDN articles cited above, not just the Windows Software Development Kit for Windows 7 but ships with an associated import library Advapi32.lib!
  2. Create the text file ntsecapi.c with the following content in an arbitrary, preferably empty directory:

    // Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <ntsecapi.h>
    
    #define STATUS_SUCCESS		0L
    #define STATUS_UNSUCCESSFUL	0xC0000001L
    
    __declspec(noreturn)
    VOID	CDECL	mainCRTStartup(VOID)
    {
    	BYTE	cbMemory[RTL_ENCRYPT_MEMORY_SIZE];
    	LONG	ntStatus;
    
    	if (RtlGenRandom(cbMemory, sizeof(cbMemory)))
    	{
    		ntStatus = RtlEncryptMemory(cbMemory, sizeof(cbMemory), 0UL);
    
    		if (ntStatus == STATUS_SUCCESS)
    			ntStatus = RtlDecryptMemory(cbMemory, sizeof(cbMemory), 0UL);
    	}
    	else
    		ntStatus = STATUS_UNSUCCESSFUL;
    
    	ExitProcess(ntStatus);
    }
  3. Compile and link the source file ntsecapi.c created in step 2. to test whether the associated import library Advapi32.lib can be linked statically – or not:

    SET CL=/W4 /Zl
    SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    CL.EXE ntsecapi.c advapi32.lib kernel32.lib
    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.
    
    ntsecapi.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:ntsecapi.exe
    ntsecapi.obj
    advapi32.lib
    kernel32.lib
    ntsecapi.obj : error LNK2019: unresolved external symbol _SystemFunction041 referenced in function _mainCRTStartup
    ntsecapi.obj : error LNK2019: unresolved external symbol _SystemFunction040 referenced in function _mainCRTStartup
    ntsecapi.obj : error LNK2019: unresolved external symbol _SystemFunction036 referenced in function _mainCRTStartup
    ntsecapi.exe : fatal error LNK1120: 3 unresolved externals
    OUCH²: the undecorated symbol names _SystemFunction036, _SystemFunction040 and _SystemFunction041 in the error messages indicate 2 omissions bugs in the declarations of the function prototypes for RtlDecryptMemory(), RtlEncryptMemory() and RtlGenRandom() in the header file ntsecapi.h – the mandatory calling convention __stdcall and the (optional) storage class attribute __declspec(dllimport) are missing!

    Note: properly declared, the symbol names were but __imp__SystemFunction036@8, __imp__SystemFunction040@12 and __imp__SystemFunction041@12 – see the MSDN article Decorated Names for details.

  4. Repeat the previous step 3., now with the /Gz compiler option:

    CL.EXE /Gz ntsecapi.c advapi32.lib kernel32.lib
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    ntsecapi.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:ntsecapi.exe
    ntsecapi.obj
    advapi32.lib
    kernel32.lib
  5. Finally execute the console application ntsecapi.exe built in step 4. and evaluate its exit code:

    .\ntsecapi.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0)
    Error message text: The operation completed successfully.
    CertUtil: -error command completed successfully.
Note: a repetition of this falsification in the 64-bit development and execution environments is left as an exercise to the reader.

Blunder № 3

The documentation for both of the Win32 functions InstallPerfDllA() and InstallPerfDllW() states since 1999:
This function has no associated import library; you must call it using the LoadLibrary and GetProcAddress functions.

[…]

Requirement Value
Header Loadperf.h
DLL Loadperf.dll

Falsification

Perform the following 3 simple steps to prove the documentation cited above wrong.
  1. Determine whether the highlighted claim This function has no associated import library. is true – or false:

    FOR %? IN (Loadperf.lib) DO SET LOADPERF=%~$LIB:?
    LINK.EXE /DUMP /EXPORTS /OUT:loadperf.txt "%LOADPERF%"
    FIND.EXE "InstallPerfDll" loadperf.txt
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    SET LOADPERF=C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\LoadPerf.Lib
    
    Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    ---------- LOADPERF.TXT
                      _InstallPerfDllA@12
                      _InstallPerfDllW@12
  2. Create the text file loadperf.c with the following content in an arbitrary, preferably empty directory:

    // Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <loadperf.h>
    
    FARPROC	loadperf[] = {InstallPerfDllA,
    	              InstallPerfDllW};
  3. Compile and link the source file loadperf.c created in step 2. to show that the associated import library Loadperf.lib can be linked statically:

    SET CL=/W4 /Zl
    SET LINK=/DLL /NODEFAULTLIB /NOENTRY
    CL.EXE loadperf.c loadperf.lib
    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.
    
    loadperf.c
    loadperf.c(9) : warning C4232: nonstandard extension used : 'loadperf' : address of dllimport 'InstallPerfDllA' is not static, identity not guaranteed
            C:\Program Files\Microsoft SDKs\Windows\v7.1\Include\loadperf.h(49) : see declaration of 'InstallPerfDllA'
    loadperf.c(9) : warning C4057: 'initializing' : 'FARPROC' differs in indirection to slightly different base types from 'DWORD (__stdcall *)(LPCSTR,LPCSTR,ULONG_PTR)'
    loadperf.c(10) : warning C4232: nonstandard extension used : 'loadperf' : address of dllimport 'InstallPerfDllW' is not static, identity not guaranteed
            C:\Program Files\Microsoft SDKs\Windows\v7.1\Include\loadperf.h(42) : see declaration of 'InstallPerfDllW'
    loadperf.c(10) : warning C4057: 'initializing' : 'FARPROC' differs in indirection to slightly different base types from 'DWORD (__stdcall *)(LPCWSTR,LPCWSTR,ULONG_PTR)'
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /DLL /NODEFAULTLIB /NOENTRY
    /out:loadperf.exe
    loadperf.obj
    loadperf.lib
    OOPS: contrary to their documentation all functions can be linked statically with their import library loadperf.lib!
Note: a repetition of this falsification in the 64-bit development environment is left as an exercise to the reader.

Blunder № 4

The documentation for each of the Win32 functions CryptCATAdminAcquireContext(), CryptCATAdminAcquireContext2(), CryptCATAdminAddCatalog(), CryptCATAdminCalcHashFromFileHandle(), CryptCATAdminCalcHashFromFileHandle2(), CryptCATAdminReleaseCatalogContext(), CryptCATAdminReleaseContext(), CryptCATAdminRemoveCatalog(), CryptCATCatalogInfoFromContext(), CryptCATClose(), CryptCATEnumerateAttr(), CryptCATEnumerateCatAttr() and CryptCATGetMemberInfo() states since 2001:
This function has no associated import library. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Wintrust.dll.

[…]

Requirement Value
Header mscat.h
Library Wintrust.lib
DLL Wintrust.dll
Ouch: the highlighted reference to the import library Wintrust.lib in the Requirements section but contradicts the highlighted claim This function has no associated import library. given in the text!

The documentation for both of the Win32 functions CryptCATAdminResolveCatalogPath() and IsCatalogFile() states since 2001:

Note This function has no associated import library. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Wintrust.dll.

[…]

Requirement Value
Header mscat.h
DLL Wintrust.dll
The documentation for the Win32 function CryptCATOpen() states since 2001:
Note Some older versions of Wintrust.lib do not contain the export information for this function. In this case, you must use the LoadLibrary and GetProcAddress functions to dynamically link to Wintrust.dll.

[…]

Requirement Value
Header mscat.h
Library Wintrust.lib
DLL Wintrust.dll
The documentation for both of the Win32 functions CryptCATCDFEnumAttributesWithCDFTag() and CryptCATCDFEnumMembersByCDFTagEx() states since 2001:
Note This function has no associated header file or import library. To call this function, you must create a user-defined header file and use the LoadLibrary and GetProcAddress functions to dynamically link to Mssign32.dll.

[…]

Requirement Value
DLL Wintrust.dll
Ouch: the highlighted reference to Wintrust.dll in the Requirements section but contradicts the highlighted claim dynamically link to Mssign32.dll. given in the text!

Falsification

Perform the following 3 simple steps to prove the documentation cited above wrong.
  1. Determine whether the highlighted claim This function has no associated import library. is true – or false:

    FOR %? IN (Wintrust.lib) DO SET WINTRUST=%~$LIB:?
    LINK.EXE /DUMP /EXPORTS /OUT:wintrust.txt "%WINTRUST%"
    FIND.EXE "CryptCAT" wintrust.txt
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    SET WINTRUST=C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\Wintrust.Lib
    
    Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    ---------- WINTRUST.TXT
                      ?CryptCATVerifyMember@@YGHPAXPAUCRYPTCATMEMBER_@@0@Z (int __stdcall CryptCATVerifyMember(void *,struct CRYPTCATMEMBER_ *,void *))
                      _CryptCATAdminAcquireContext@12
                      _CryptCATAdminAddCatalog@16
                      _CryptCATAdminCalcHashFromFileHandle@16
                      _CryptCATAdminEnumCatalogFromHash@20
                      _CryptCATAdminPauseServiceForBackup@8
                      _CryptCATAdminReleaseCatalogContext@12
                      _CryptCATAdminReleaseContext@8
                      _CryptCATAdminRemoveCatalog@12
                      _CryptCATAdminResolveCatalogPath@16
                      _CryptCATAllocSortedMemberInfo@8
                      _CryptCATCDFClose@4
                      _CryptCATCDFEnumAttributes@16
                      _CryptCATCDFEnumAttributesWithCDFTag@20
                      _CryptCATCDFEnumCatAttributes@12
                      _CryptCATCDFEnumMembers@12
                      _CryptCATCDFEnumMembersByCDFTag@16
                      _CryptCATCDFEnumMembersByCDFTagEx@24
                      _CryptCATCDFOpen@8
                      _CryptCATCatalogInfoFromContext@12
                      _CryptCATClose@4
                      _CryptCATEnumerateAttr@12
                      _CryptCATEnumerateCatAttr@8
                      _CryptCATEnumerateMember@8
                      _CryptCATFreeSortedMemberInfo@8
                      _CryptCATGetAttrInfo@12
                      _CryptCATGetCatAttrInfo@8
                      _CryptCATGetMemberInfo@8
                      _CryptCATHandleFromStore@4
                      _CryptCATOpen@20
                      _CryptCATPersistStore@4
                      _CryptCATPutAttrInfo@24
                      _CryptCATPutCatAttrInfo@20
                      _CryptCATPutMemberInfo@28
                      _CryptCATStoreFromHandle@4
    OUCH: contrary to the statements in all 16 MSDN articles cited above, not just the Windows Software Development Kit for Windows 7 but ships with an associated import library Wintrust.lib!
  2. Create the text file mscat.c with the following content in an arbitrary, preferably empty directory:

    // Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <wintrust.h>
    #include <mscat.h>
    
    CRYPTCATATTRIBUTE* WINAPI CryptCATCDFEnumAttributesWithCDFTag(
      _In_ CRYPTCATCDF                  *pCDF,
      _In_ LPWSTR                       pwszMemberTag,
      _In_ CRYPTCATMEMBER               *pMember,
      _In_ CRYPTCATATTRIBUTE            *pPrevAttr,
      _In_ PFN_CDF_PARSE_ERROR_CALLBACK pfnParseError
    );
    
    LPWSTR WINAPI CryptCATCDFEnumMembersByCDFTagEx(
      _In_    CRYPTCATCDF                  *pCDF,
      _Inout_ LPWSTR                       pwszPrevCDFTag,
      _In_    PFN_CDF_PARSE_ERROR_CALLBACK pfnParseError,
      _In_    CRYPTCATMEMBER               **ppMember,
      _In_    BOOL                         fContinueOnError,
      _In_    LPVOID                       pvReserved
    );
    
    FARPROC	mscat[] = {CryptCATAdminAcquireContext,
    #if _WIN32_WINNT >= 0x0602
    	           CryptCATAdminAcquireContext2,
    #endif
    	           CryptCATAdminAddCatalog,
    	           CryptCATAdminCalcHashFromFileHandle,
    #if _WIN32_WINNT >= 0x0602
    	           CryptCATAdminCalcHashFromFileHandle2,
    #endif
    	           CryptCATAdminReleaseCatalogContext,
    	           CryptCATAdminReleaseContext,
    	           CryptCATAdminRemoveCatalog,
    	           CryptCATAdminResolveCatalogPath,
    	           CryptCATCatalogInfoFromContext,
    	           CryptCATCDFEnumAttributesWithCDFTag,
    	           CryptCATCDFEnumMembersByCDFTagEx,
    	           CryptCATClose,
    	           CryptCATEnumerateAttr,
    	           CryptCATEnumerateCatAttr,
    	           CryptCATGetMemberInfo,
    	           CryptCATOpen,
    	           IsCatalogFile};
  3. Compile and link the source file mscat.c created in step 2. to show that the associated import library Wintrust.lib can be linked statically:

    SET CL=/W4 /Zl
    SET LINK=/DLL /NODEFAULTLIB /NOENTRY
    CL.EXE mscat.c wintrust.lib
    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.
    
    mscat.c
    c:\program files\microsoft sdks\windows\v7.1\include\mssip.h(92) : warning C4201 : nonstandard extension used : nameless struct/union
    mscat.c(31) : warning C4047: 'initializing' : 'FARPROC' differs in levels of indirection from 'HCATINFO (__stdcall *)(HCATADMIN,PWSTR,PWSTR,DWORD)'
    mscat.c(44) : warning C4047: 'initializing' : 'FARPROC' differs in levels of indirection from 'CRYPTCATATTRIBUTE *(__stdcall *)(HANDLE,CRYPTCATMEMBER *,CRYPTCATATTRIBUTE *)'
    mscat.c(45) : warning C4047: 'initializing' : 'FARPROC' differs in levels of indirection from 'CRYPTCATATTRIBUTE *(__stdcall *)(HANDLE,CRYPTCATATTRIBUTE *)'
    mscat.c(46) : warning C4047: 'initializing' : 'FARPROC' differs in levels of indirection from 'CRYPTCATMEMBER *(__stdcall *)(HANDLE,LPWSTR)'
    mscat.c(47) : warning C4047: 'initializing' : 'FARPROC' differs in levels of indirection from 'HANDLE (__stdcall *)(LPWSTR,DWORD,HCRYPTPROV,DWORD,DWORD)'
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /DLL /NODEFAULTLIB /NOENTRY
    /out:mscat.exe
    mscat.obj
    wintrust.lib
    OOPS: contrary to their documentation all functions can be linked statically with their import library wintrust.lib!
Note: a repetition of this falsification in the 64-bit development environment is left as an exercise to the reader.

Blunder № 5

The documentation for both of the Win32 functions OpenPersonalTrustDBDialog() and OpenPersonalTrustDBDialogEx() states since 2003:
Note This function has no associated header file or import library. You must define the function yourself and use the LoadLibrary and GetProcAddress functions to dynamically link to Wintrust.dll.

[…]

Requirement Value
Header wintrust.h
DLL Wintrust.dll
Ouch¹: the highlighted reference to the header file wintrust.h in the Requirements section but contradicts the highlighted claim This function has no associated header file or import library. given in the text!

The documentation for both of the Win32 functions WintrustGetRegPolicyFlags() and WintrustSetRegPolicyFlags() states since 2001:

Note This function has no associated import library. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Wintrust.dll.

[…]

Requirement Value
Header wintrust.h
DLL Wintrust.dll
The documentation for the Win32 function WTHelperCertFindIssuerCertificate() states since 2001:

Note

This function has no associated import library. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Wintrust.dll.

[…]

Requirement Value
DLL Wintrust.dll
Ouch²: no header file is specified in the Requirements section!

The documentation for each of the Win32 functions WintrustAddActionID(), WintrustLoadFunctionPointers(), WintrustRemoveActionID(), WintrustSetDefaultIncludePEPageHashes() WTHelperCertIsSelfSigned(), WTHelperGetProvCertFromChain(), WTHelperGetProvPrivateDataFromChain(), WTHelperGetProvSignerFromChain() and WTHelperProvDataFromStateData() states since 2001:

This function has no associated import library. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Wintrust.dll.

[…]

Requirement Value
Header wintrust.h
Library Wintrust.lib
DLL Wintrust.dll
Ouch³: the highlighted reference to the import library Wintrust.lib in the Requirements section but contradicts the highlighted claim This function has no associated import library. given in the text!

Falsification

Perform the following 3 simple steps to prove the documentation cited above wrong.
  1. Determine whether the highlighted claim This function has no associated import library. is true – or false:

    FOR %? IN (Wintrust.lib) DO SET WINTRUST=%~$LIB:?
    LINK.EXE /DUMP /EXPORTS /OUT:wintrust.txt "%WINTRUST%"
    FIND.EXE "OpenPersonalTrustDBDialog" wintrust.txt
    FIND.EXE "Wintrust" wintrust.txt
    FIND.EXE "WTHelper" wintrust.txt
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    SET WINTRUST=C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\Wintrust.Lib
    
    Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    ---------- WINTRUST.TXT
                      _OpenPersonalTrustDBDialog@4
                      _OpenPersonalTrustDBDialogEx@12
    
    ---------- WINTRUST.TXT
                      _WintrustAddActionID@12
                      _WintrustAddDefaultForUsage@8
                      _WintrustCertificateTrust@4
                      _WintrustGetDefaultForUsage@12
                      _WintrustGetRegPolicyFlags@4
                      _WintrustLoadFunctionPointers@8
                      _WintrustRemoveActionID@4
                      _WintrustSetDefaultIncludePEPageHashes@4
                      _WintrustSetRegPolicyFlags@4
    
    ---------- WINTRUST.TXT
                      _WTHelperCertCheckValidSignature@4
                      _WTHelperCertIsSelfSigned@8
                      _WTHelperCheckCertUsage@8
                      _WTHelperGetAgencyInfo@12
                      _WTHelperGetFileHandle@4
                      _WTHelperGetFileHash@24
                      _WTHelperGetFileName@4
                      _WTHelperGetKnownUsages@8
                      _WTHelperGetProvCertFromChain@8
                      _WTHelperGetProvPrivateDataFromChain@8
                      _WTHelperGetProvSignerFromChain@16
                      _WTHelperIsInRootStore@8
                      _WTHelperOpenKnownStores@4
                      _WTHelperProvDataFromStateData@4
    OUCH: contrary to the statements in all 13 MSDN articles cited above, not just the Windows Software Development Kit for Windows 7 but ships with an associated import library Wintrust.lib!
  2. Create the text file wintrust.c with the following content in an arbitrary, preferably empty directory:

    // Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <wintrust.h>
    
    FARPROC	wintrust[] = {OpenPersonalTrustDBDialog,
    	              OpenPersonalTrustDBDialogEx,
    	              WintrustAddActionID,
    	              WintrustGetRegPolicyFlags,
    	              WintrustLoadFunctionPointers,
    	              WintrustRemoveActionID,
    	              WintrustSetDefaultIncludePEPageHashes,
    	              WintrustSetRegPolicyFlags,
    	              WTHelperCertIsSelfSigned,
    	              WTHelperGetProvCertFromChain,
    	              WTHelperGetProvPrivateDataFromChain,
    	              WTHelperGetProvSignerFromChain,
    	              WTHelperProvDataFromStateData};
  3. Compile and link the source file wintrust.c created in step 2. to show that the associated import library Wintrust.lib can be linked statically:

    SET CL=/W4 /Zl
    SET LINK=/DLL /NODEFAULTLIB /NOENTRY
    CL.EXE wintrust.c wintrust.lib
    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.
    
    wintrust.c
    wintrust.c(12) : warning C4133: 'initializing' : incompatible types - from 'void (__stdcall *)(DWORD *)' to 'FARPROC'
    wintrust.c(15) : warning C4133: 'initializing' : incompatible types - from 'void (__stdcall *)(BOOL)' to 'FARPROC'
    wintrust.c(18) : warning C4047: 'initializing' : 'FARPROC' differs in levels of indirection from 'CRYPT_PROVIDER_CERT *(__stdcall *)(CRYPT_PROVIDER_SGNR *,DWORD)'
    wintrust.c(19) : warning C4047: 'initializing' : 'FARPROC' differs in levels of indirection from 'CRYPT_PROVIDER_PRIVDATA *(__stdcall *)(CRYPT_PROVIDER_DATA *,GUID *)'
    wintrust.c(20) : warning C4047: 'initializing' : 'FARPROC' differs in levels of indirection from 'CRYPT_PROVIDER_SGNR *(__stdcall *)(CRYPT_PROVIDER_DATA *,DWORD,BOOL,DWORD)'
    wintrust.c(21) : warning C4047: 'initializing' : 'FARPROC' differs in levels of indirection from 'CRYPT_PROVIDER_DATA *(__stdcall *)(HANDLE)'
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /DLL /NODEFAULTLIB /NOENTRY
    /out:wintrust.exe
    wintrust.obj
    wintrust.lib
    OOPS: contrary to their documentation all functions can be linked statically with their import library wintrust.lib!
Note: a repetition of this falsification in the 64-bit development environment is left as an exercise to the reader.

Blunder № 6

The documentation for the Win32 function SetEnvironmentStrings() states:
Sets the environment strings of the calling process (both the system and the user environment variables) for the current process.
BOOL SetEnvironmentStrings(
  LPWCH NewEnvironment
);
[…]

The environment variable string using the following format:

Var1 Value1 Var2 Value2 Var3 Value3 VarN ValueN

[…]

Requirement Value
Minimum supported client Windows 10 Build 20348
Minimum supported server Windows 10 Build 20348
Header processenv.h
Library kernel32.lib
DLL kernel32.dll
OUCH: the SetEnvironmentStrings() function was but introduced with Windows Server 2003; its A and W variants were declared in the header file winbase.h shipped with the corresponding Platform SDK!

Falsification

Perform the following 3 simple steps to prove the highlighted statement of the documentation cited above wrong.
  1. Create the text file blunder.c with the following content in an arbitrary, preferable empty directory:

    // Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    
    __declspec(noreturn)
    VOID	CDECL	mainCRTStartup(VOID)
    {
    	LPSTR	lpBlock;
    	LPSTR	lpString;
    	DWORD	dwString;
    	DWORD	dwError = ERROR_SUCCESS;
    	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
    #ifndef BLUNDER
    	if (!SetEnvironmentStrings("NAME value BLUNDER blunder"))
    #else
    	if (!SetEnvironmentStrings("NAME=value\0EMPTY=\0BLUNDER=blunder\0"))
    #endif
    		dwError = GetLastError();
    	else
    	{
    		lpBlock = GetEnvironmentStrings();
    
    		if (lpBlock == NULL)
    			dwError = GetLastError();
    		else
    		{
    			for (lpString = lpBlock;
    			     lpString[0] != '\0';
    			     lpString[dwString = strlen(lpString)] = '\n', lpString += dwString + 1)
    				continue;
    
    			if (!WriteConsole(hError, lpBlock, lpString - lpBlock, &dwString, NULL))
    				dwError = GetLastError();
    			else
    				if (dwString != lpString - lpBlock)
    					dwError = ERROR_WRITE_FAULT;
    
    			if (!FreeEnvironmentStrings(lpBlock))
    				dwError = GetLastError();
    		}
    	}
    
    	ExitProcess(dwError);
    }
  2. Compile and link the source file blunder.c created in step 1.:

    SET CL=/Oi /W4 /Zl
    SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    CL.EXE blunder.c kernel32.lib
    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.
    
    blunder.c
    blunder.c(38) : warning C4389: '!=' : signed/unsigned mismatch
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:blunder.exe
    blunder.obj
    kernel32.lib
  3. Execute the console application blunder.exe built in step 2. and evaluate its exit code to prove the highlighted statement of the documentation cited above wrong:

    .\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    0x57 (WIN32: 87 ERROR_INVALID_PARAMETER) -- 87 (87)
    Error message text: The parameter is incorrect.
    CertUtil: -error command completed successfully.
  4. Compile and link the source file blunder.c created in step 1. a second time, now with the preprocessor macro BLUNDER defined:

    CL.EXE /DBLUNDER blunder.c kernel32.lib
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.c
    blunder.c(38) : warning C4389: '!=' : signed/unsigned mismatch
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:blunder.exe
    blunder.obj
    kernel32.lib
  5. Execute the console application blunder.exe built in step 4. and evaluate its exit code to demonstrate the true behaviour:

    .\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    NAME=value
    EMPTY=
    BLUNDER=blunder
    
    0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0)
    Error message text: The operation completed successfully.
    CertUtil: -error command completed successfully.

Blunder № 7

The documentation for the Win32 function ExpandEnvironmentStrings() states:
Expands environment-variable strings and replaces them with the values defined for the current user.

Falsification

Perform the following 3 simple steps to prove the highlighted statement of the documentation cited above wrong.
  1. Create the text file blunder.c with the following content in an arbitrary, preferable empty directory:

    // Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    
    __declspec(noreturn)
    VOID	CDECL	wmainCRTStartup(VOID)
    {
    	WCHAR	szExpand[1234];
    	DWORD	dwExpand = ExpandEnvironmentStrings(L"%COMPUTERNAME%\n%PATH%\n", szExpand, sizeof(szExpand) / sizeof(*szExpand));
    	DWORD	dwError = ERROR_SUCCESS;
    	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
    
    	if (dwExpand == 0UL)
    		dwError = GetLastError();
    	else
    		if (!WriteConsole(hError, szExpand, dwExpand - 1UL, (LPDWORD) NULL, NULL))
    			dwError = GetLastError();
    
    	ExitProcess(dwError);
    }
  2. Compile and link the source file blunder.c created in step 1.:

    SET CL=/W4 /Zl
    SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    CL.EXE blunder.c kernel32.lib
    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.
    
    blunder.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:blunder.exe
    blunder.obj
    kernel32.lib
  3. Execute the console application blunder.exe built in step 2. to prove the highlighted statement of the documentation cited above wrong:

    REG.EXE QUERY "HKEY_CURRENT_USER\Environment" /V COMPUTERNAME
    REG.EXE QUERY "HKEY_CURRENT_USER\Environment" /V PATH
    REG.EXE QUERY "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /V COMPUTERNAME
    REG.EXE QUERY "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /V PATH
    ECHO %COMPUTERNAME%
    ECHO %PATH%
    .\blunder.exe
    ECHO %ERRORLEVEL%
    ERROR: The specified registry key or value was not found.
    
    HKEY_CURRENT_USER\Environment
        Path    REG_EXPAND_SZ    %USERPROFILE%\AppData\Local\Microsoft\WindowsApps;
    
    ERROR: The specified registry key or value was not found.
    
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
        Path    REG_EXPAND_SZ    %SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\;%SystemRoot%\System32\OpenSSH\
    
    AMNESIAC
    
    C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Users\Stefan\AppData\Local\Microsoft\WindowsApps;
    
    AMNESIAC
    C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Users\Stefan\AppData\Local\Microsoft\WindowsApps;
    
    0
    OOPS: contrary to the highlighted statement of the documentation cited above, the Win32 function ExpandEnvironmentStrings() but replaces environment variable strings with values defined for the current process!

Blunder № 8

The documentation for the Win32 function ExpandEnvironmentStringsForUser() states:
Expands the source string by using the environment block established for the specified user.

[…]

BOOL ExpandEnvironmentStringsForUser(
  [in, optional] HANDLE  hToken,
  [in]           LPCTSTR lpSrc,
  [out]          LPTSTR  lpDest,
  [in]           DWORD   dwSize
);
[…]

If hToken is NULL, the environment block contains system variables only.

Falsification

Perform the following 3 simple steps to prove the highlighted statement of the documentation cited above wrong.
  1. Create the text file blunder.c with the following content in an arbitrary, preferable empty directory:

    // Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <userenv.h>
    
    __declspec(noreturn)
    VOID	CDECL	wmainCRTStartup(VOID)
    {
    	WCHAR	szExpand[1234];
    	DWORD	dwError = ERROR_SUCCESS;
    	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
    
    	if (!ExpandEnvironmentStringsForUser((HANDLE) NULL, L"%USERNAME%\n%USERPROFILE%\n",
    	                                     szExpand, sizeof(szExpand) / sizeof(*szExpand)))
    		dwError = GetLastError();
    	else
    		if (!WriteConsole(hError, szExpand, wcslen(szExpand), (LPDWORD) NULL, NULL))
    			dwError = GetLastError();
    
    	ExitProcess(dwError);
    }
  2. Compile and link the source file blunder.c created in step 1.:

    SET CL=/Oi /W4 /Zl
    SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    CL.EXE blunder.c kernel32.lib userenv.lib
    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.
    
    blunder.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:blunder.exe
    blunder.obj
    kernel32.lib
    userenv.lib
  3. Execute the console application blunder.exe built in step 2. to prove the highlighted statement of the documentation cited above wrong:

    REG.EXE QUERY "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /V USERPROFILE
    .\blunder.exe
    ECHO %ERRORLEVEL%
    ERROR: The specified registry key or value was not found.
    
    SYSTEM
    C:\Users\Default
    
    0
    OOPS: contrary to the highlighted statement of the documentation cited above, the Win32 function ExpandEnvironmentStringsForUser() but expands (some) user environment variables when its first argument is NULL!

Blunder № 9

The documentation for the Win32 functions GetModuleHandle() and GetModuleHandleEx() states:
Retrieves a module handle for the specified module. The module must have been loaded by the calling process.

[…]

HMODULE GetModuleHandle(
  [in, optional] LPCTSTR lpModuleName
);

[…]

[in, optional] lpModuleName

[…]

If this parameter is NULL, GetModuleHandle returns a handle to the file used to create the calling process (.exe file).

Retrieves a module handle for the specified module and increments the module's reference count unless GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT is specified. The module must have been loaded by the calling process.

[…]

BOOL GetModuleHandleEx(
  [in]           DWORD   dwFlags,
  [in, optional] LPCTSTR lpModuleName,
  [out]          HMODULE *phModule
);

[…]

[in, optional] lpModuleName

[…]

If this parameter is NULL, the function returns a handle to the file used to create the calling process (.exe file).

OUCH: both functions don’t return a handle to the .exe file from which a process was loaded, but the handle alias load address of the process, equal to the address of the symbol __ImageBase supplied by the linker!

Falsification

Perform the following 5 simple steps to prove the highlighted statements of the documentation cited above wrong.
  1. Create the text file blunder.c with the following content in an arbitrary, preferable empty directory:

    // Copyleft © 2009-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    
    extern	const	IMAGE_DOS_HEADER	__ImageBase;
    
    __declspec(noreturn)
    VOID	CDECL	wmainCRTStartup(VOID)
    {
    #ifndef BLUNDER
    	HMODULE	hProcess = GetModuleHandle((LPCWSTR) NULL);
    	DWORD	dwError = (DWORD_PTR) hProcess ^ (DWORD_PTR) &__ImageBase;
    #else
    	HMODULE	hProcess;
    	DWORD	dwError = ERROR_SUCCESS;
    
    	if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
    	                       (LPCWSTR) NULL,
    	                       &hProcess)
    	 || !CloseHandle(hProcess))
    		dwError = GetLastError();
    #endif
    	ExitProcess(dwError);
    }
  2. Compile and link the source file blunder.c created in step 1.:

    SET CL=/W4 /Zl
    SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    CL.EXE blunder.c kernel32.lib
    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.
    
    blunder.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:blunder.exe
    blunder.obj
    kernel32.lib
  3. Execute the console application blunder.exe built in step 2. and evaluate its exit code:

    .\blunder.exe
    ECHO %ERRORLEVEL%
    0
  4. Compile and link the source file blunder.c created in step 1. a second time, now with the preprocessor macro BLUNDER defined:

    CL.EXE /DBLUNDER blunder.c kernel32.lib
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:blunder.exe
    blunder.obj
    kernel32.lib
  5. Execute the console application blunder.exe built in step 4. and evaluate its exit code:

    .\blunder.exe
    CERTUTIL.EXE /ERROR %ErrorLevel%
    0x6 (WIN32: 6 ERROR_INVALID_HANDLE) -- 6 (6)
    Error message text: The handle is invalid.
    CertUtil: -error command completed successfully.

Blunder № 10

The documentation for the Win32 function GetWindowModuleFileName() states:
Retrieves the full path and file name of the module associated with the specified window handle.
Note: this documentation fails to specify error conditions and restrictions!

The MSKB article 228469 but states:

GetWindowModuleFileName and GetModuleFileName correctly retrieve information about windows and modules in the calling process. In Windows 95 and 98, they return information about windows and modules in other processes. However, in Windows NT 4.0 and Windows 2000, since module handles are no longer shared by all processes as they were on Windows 95 and 98, these APIs do not return information about windows and modules in other processes.
OUCH: Windows NT 4.0 and Windows 2000 were released in the last millennium, but nobody at Microsoft was able to add the information provided in the MSKB article 228469 to the documentation for the Win32 function GetWindowModuleFileName() for more than 24 (in words: twenty-four) years – it's a real shame!

Demonstration

Perform the following 5 simple steps to show the blunder.
  1. Create the text file blunder.c with the following content in an arbitrary, preferable empty directory:

    // Copyright © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    
    __declspec(noreturn)
    VOID	CDECL	wmainCRTStartup(VOID)
    {
    	WCHAR	szBuffer[MAX_PATH];
    	DWORD	dwBuffer;
    	DWORD	dwError = ERROR_SUCCESS;
    	DWORD	dwConsole;
    	HANDLE	hConsole = GetStdHandle(STD_ERROR_HANDLE);
    	HWND	hWindow = HWND_DESKTOP;
    
    	if (hConsole == INVALID_HANDLE_VALUE)
    		dwError = GetLastError();
    	else
    	{
    #ifdef BLUNDER
    		SetLastError(123456789);
    #if BLUNDER == 1
    		hWindow = GetActiveWindow();
    #elif BLUNDER == 2
    		hWindow = GetConsoleWindow();
    #elif BLUNDER == 3
    		hWindow = GetDesktopWindow();
    #elif BLUNDER == 4
    		hWindow = GetForegroundWindow();
    #elif BLUNDER == 5
    		hWindow = GetOpenClipboardWindow();
    #elif BLUNDER == 6
    		hWindow = GetShellWindow();
    #elif BLUNDER == 7
    		hWindow = GetTopWindow(HWND_DESKTOP);
    #else
    #error
    #endif
    		if (hWindow == NULL)
    			dwError = GetLastError();
    		else
    #endif // BLUNDER
    		{
    			dwBuffer = GetWindowModuleFileName(hWindow,
    			                                   szBuffer,
    			                                   sizeof(szBuffer) / sizeof(*szBuffer));
    			if (dwBuffer == 0UL)
    				dwError = GetLastError();
    			else
    			{
    				szBuffer[dwBuffer++] = L'\n';
    
    				if (!WriteConsole(hConsole, szBuffer, dwBuffer, &dwConsole, NULL))
    					dwError = GetLastError();
    				else
    					if (dwConsole != dwBuffer)
    						dwError = ERROR_WRITE_FAULT;
    			}
    		}
    	}
    
    	ExitProcess(dwError);
    }
  2. Compile and link the source file blunder.c created in step 1.:

    SET CL=/W4 /Zl
    SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    CL.EXE blunder.c kernel32.lib
    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.
    
    blunder.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:blunder.exe
    blunder.obj
    kernel32.lib
  3. Execute the console application blunder.exe built in step 2. and evaluate its exit code:

    .\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    0x578 (WIN32: 1400 ERROR_INVALID_WINDOW_HANDLE) -- 1400 (1400)
    Error message text: Invalid window handle.
    CertUtil: -error command completed successfully.
    Note: this exit code is expected – the preprocessor macro HWND_DESKTOP is defined as ((HWND) 0) alias NULL!
  4. Compile and link the source file blunder.c created in step 1. seven more times, now with the preprocessor macro BLUNDER defined as 1 to 7 in turn, execute the seven console applications blunder.exe and evaluate their exit code:

    FOR /L %? IN (1,1,7) DO @(
    CL.EXE /DBLUNDER=%? /nologo blunder.c kernel32.lib
    .\blunder.exe
    ECHO !ERRORLEVEL!)
    blunder.c
    123456789
    
    blunder.c
    C:\Users\Stefan\Desktop\blunder.exe
    0
    
    blunder.c
    123456789
    
    blunder.c
    C:\Users\Stefan\Desktop\blunder.exe
    0
    
    blunder.c
    123456789
    
    blunder.c
    123456789
    
    blunder.c
    C:\Users\Stefan\Desktop\blunder.exe
    0
    OUCH: upon error the Win32 functions GetActiveWindow(), GetOpenClipboardWindow() and GetWindowModuleFileName() don’t call the SetLastError() function!

    OOPS: upon success the Win32 function GetWindowModuleFileName() returns the pathname of the calling (console) application!

Blunder № 11

The documentation for the Win32 function WriteFile() states:
Writes data to the specified file or input/output (I/O) device.

[…]

BOOL WriteFile(
  [in]                HANDLE       hFile,
  [in]                LPCVOID      lpBuffer,
  [in]                DWORD        nNumberOfBytesToWrite,
  [out, optional]     LPDWORD      lpNumberOfBytesWritten,
  [in, out, optional] LPOVERLAPPED lpOverlapped
);
[…]

[out, optional] lpNumberOfBytesWritten

A pointer to the variable that receives the number of bytes written when using a synchronous hFile parameter. WriteFile sets this value to zero before doing any work or error checking. Use NULL for this parameter if this is an asynchronous operation to avoid potentially erroneous results.

This parameter can be NULL only when the lpOverlapped parameter is not NULL.

Windows 7: This parameter can not be NULL.

[…]

If the function succeeds, the return value is nonzero (TRUE).

If the function fails, or is completing asynchronously, the return value is zero (FALSE). To get extended error information, call the GetLastError function.

Ouch: 232−2 nonzero BOOL values differ from the value of the preprocessor macro TRUE!

Falsification

Perform the following 3 simple steps to prove the highlighted statement of the documentation cited above wrong.
  1. Create the text file blunder.c with the following content in an arbitrary, preferable empty directory:

    // Copyleft © 2009-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    
    __declspec(noreturn)
    VOID	CDECL	mainCRTStartup(VOID)
    {
    	DWORD	dwError = ERROR_SUCCESS;
    	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
    
    	if (!WriteFile(hError, "BLUNDER!\r\n", 10UL, (LPDWORD) NULL, (LPOVERLAPPED) NULL))
    		dwError = GetLastError();
    
    	ExitProcess(dwError);
    }
  2. Compile and link the source file blunder.c created in step 1.:

    SET CL=/W4 /Zl
    SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    CL.EXE blunder.c kernel32.lib
    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.
    
    blunder.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:blunder.exe
    blunder.obj
    kernel32.lib
  3. Execute the console application blunder.exe built in step 2. on Windows 7 and evaluate its exit code to prove the highlighted statement of the documentation cited above wrong:

    VER
    .\blunder.exe
    ECHO %ERRORLEVEL%
    Microsoft Windows [Version 6.1.7601]
    BLUNDER!
    0
    OOPS: contrary to the highlighted statement of the documentation cited above, lpNumberOfBytesWritten can be NULL on Windows 7!

Blunder № 12

The documentation for the Win32 functions CreateProcess(), CreateProcessAsUser(), CreateProcessWithLogonW() and CreateProcessWithTokenW() specifies:
[…] the first white space–delimited token of the command line specifies the module name. If you are using a long file name that contains a space, use quoted strings to indicate where the file name ends and the arguments begin (see the explanation for the lpApplicationName parameter). If the file name does not contain an extension, .exe is appended. Therefore, if the file name extension is .com, this parameter must include the .com extension. If the file name ends in a period (.) with no extension, or if the file name contains a path, .exe is not appended. If the file name does not contain a directory path, the system searches for the executable file in the following sequence:
  1. The directory from which the application loaded.
  2. The current directory for the parent process.
  3. The 32-bit Windows system directory. Use the GetSystemDirectory function to get the path of this directory.
  4. The 16-bit Windows system directory. There is no function that obtains the path of this directory, but it is searched.
  5. The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
  6. The directories that are listed in the PATH environment variable. Note that this function does not search the per-application path specified by the App Paths registry key. To include this per-application path in the search sequence, use the ShellExecute function.
OUCH¹: the second position is the current directory of the application current process, not that of its parent process!

OOPS: in the 64-bit execution environment, the GetSystemDirectory() function yields but the path of the 64-bit system directory!

Falsification

Perform the following 15 (plus 1 optional) simple steps to prove the documentation cited above wrong and show a lot of blunder.
  1. Create the text file blunder.c with the following content in an arbitrary, preferable empty directory:

    // Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    
    const	STARTUPINFO	si = {sizeof(si)};
    
    __declspec(noreturn)
    VOID	CDECL	wmainCRTStartup(VOID)
    {
    	PROCESS_INFORMATION pi;
    
    	DWORD	dwError = ERROR_SUCCESS;
    	WCHAR	szBlunder[] = L"blunder.bat";
    #ifdef BLUNDER
    	if (!SetEnvironmentVariable(L"COMSPEC",
    #if BLUNDER == 1
    	                            L"blunder.com"))
    #elif BLUNDER == 2
    	                            L"blunder.exe"))
    #elif BLUNDER == 3
    	                            L"blunder.bat"))
    #elif BLUNDER == 4
    	                            L"blunder.cmd"))
    #elif BLUNDER == 5
    	                            L"blunder"))
    #else
    	                            (LPCWSTR) NULL))
    #endif
    		dwError = GetLastError();
    	else
    #endif
    		if (!CreateProcess((LPCWSTR) NULL,
    		                   szBlunder,
    		                   (LPSECURITY_ATTRIBUTES) NULL,
    		                   (LPSECURITY_ATTRIBUTES) NULL,
    		                   FALSE,
    		                   CREATE_DEFAULT_ERROR_MODE | CREATE_UNICODE_ENVIRONMENT,
    		                   (LPWSTR) NULL,
    		                   (LPCWSTR) NULL,
    		                   &si,
    		                   &pi))
    			dwError = GetLastError();
    		else
    		{
    			if (WaitForSingleObject(pi.hThread, INFINITE) == WAIT_FAILED)
    				dwError = GetLastError();
    
    			if (!CloseHandle(pi.hThread))
    				dwError = GetLastError();
    
    			if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
    				dwError = GetLastError();
    
    			if (!CloseHandle(pi.hProcess))
    				dwError = GetLastError();
    		}
    
    	ExitProcess(dwError);
    }
  2. Compile and link the source file blunder.c created in step 1.:

    SET CL=/W4 /Zl
    SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    CL.EXE blunder.c kernel32.lib
    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.
    
    blunder.c
    blunder.c(44) : warning C4090: 'function' : different 'const' qualifiers
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:blunder.exe
    blunder.obj
    kernel32.lib
  3. Execute the console application blunder.exe built in step 2. and evaluate its exit code:

    .\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    0x2 (WIN32: 2 ERROR_FILE_NOT_FOUND) -- 2 (2)
    Error message text: The system cannot find the file specified.
    CertUtil: -error command completed successfully.
    Note: the Win32 error code 2 alias ERROR_FILE_NOT_FOUND is expected here – the file blunder.bat does not exist yet.
  4. Create the text file blunder.bat with the following content next to the console application blunder.exe built in step 2.:

    @REM Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    @ECHO %CMDCMDLINE%
    @ECHO %~f0
    @EXIT
  5. Execute the console application blunder.exe built in step 2. a second time and evaluate its exit code:

    .\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    C:\Windows\system32\cmd.exe /c blunder.bat
    C:\Users\Stefan\Desktop\blunder.bat
    
    0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0)
    Error message text: The operation completed successfully.
    CertUtil: -error command completed successfully.
    Note: since the current directory is also the application directory here this doesn’t tell whether the batch script blunder.bat is executed from the first or the second position of the documented search sequence.
  6. Move the console application blunder.exe built in step 2. into another directory, for example the parent directory, then execute it there and evaluate its exit code:

    MOVE blunder.exe ..
    ..\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    C:\Windows\system32\cmd.exe /c blunder.bat
    C:\Users\Stefan\Desktop\blunder.bat
    
    0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0)
    Error message text: The operation completed successfully.
    CertUtil: -error command completed successfully.
    Note: the batch script blunder.bat is executed from the current directory.
  7. Move the batch script blunder.bat created in step 4. into the same directory as the console application blunder.exe built in step 1., then execute the latter there again and evaluate its exit code to prove the documentation cited above wrong:

    MOVE blunder.bat ..
    ..\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    'blunder.bat' is not recognized as an internal or external command,
    operable program or batch file.
    
    0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0)
    Error message text: The operation completed successfully.
    CertUtil: -error command completed successfully.
    OUCH²: contrary to its documentation cited above, the CreateProcess() function fails to execute the batch script blunder.bat from the application directory – the cause for this bug blunder is the unqualified file name of the batch script it passes to the Command Processor which performs its own search then!
  8. Compile and link the source file blunder.c created in step 1. a second time, now with the preprocessor macro BLUNDER defined:

    CL.EXE /DBLUNDER blunder.c kernel32.lib
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.c
    blunder.c(44) : warning C4090: 'function' : different 'const' qualifiers
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:blunder.exe
    blunder.obj
    kernel32.lib
  9. Move the batch script blunder.bat back into the current directory, then execute the console application blunder.exe built in step 8. and evaluate its exit code to prove the documentation cite above incomplete:

    MOVE ..\blunder.bat .
    .\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    0x2 (WIN32: 2 ERROR_FILE_NOT_FOUND) -- 2 (2)
    Error message text: The system cannot find the file specified.
    CertUtil: -error command completed successfully.
    OUCH³: the CreateProcess() function exhibits undocumented behaviour – in order to execute a batch script it evaluates the environment variable COMSPEC to locate the Command Processor, indicated here with the Win32 error code 2 alias ERROR_FILE_NOT_FOUND!
  10. Create the empty file blunder.com in the current directory, then execute the console application blunder.exe built in step 8. a second time and evaluate its exit code:

    COPY NUL: blunder.com
    .\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    0xc1 (WIN32: 193 ERROR_BAD_EXE_FORMAT) -- 193 (193)
    Error message text: %1 is not a valid Win32 application.
    CertUtil: -error command completed successfully.
    OUCH⁴: in order to execute a batch script the CreateProcess() function executes an arbitrary executable whose file name is set in the environment variable COMSPEC from the application directory or the current directory, indicated here with the Win32 error code 193 alias ERROR_BAD_EXE_FORMAT due to the empty file blunder.com!
  11. Move the console application blunder.exe built in step 8. into another directory, for example the parent directory, then execute it there and evaluate its exit code:

    MOVE /Y blunder.exe ..
    ..\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    0xc1 (WIN32: 193 ERROR_BAD_EXE_FORMAT) -- 193 (193)
    Error message text: %1 is not a valid Win32 application.
    CertUtil: -error command completed successfully.
    OUCH⁵: in order to execute a batch script the CreateProcess() function executes an arbitrary executable whose file name is set in the environment variable COMSPEC from the current directory!
  12. Move the empty file blunder.com created in step 10. into the same directory as the console application blunder.exe created in step 8., then execute the latter a third time and evaluate its exit code:

    MOVE blunder.com ..
    ..\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    0xc1 (WIN32: 193 ERROR_BAD_EXE_FORMAT) -- 193 (193)
    Error message text: %1 is not a valid Win32 application.
    CertUtil: -error command completed successfully.
    OUCH⁶: in order to execute a batch script the CreateProcess() function executes an arbitrary executable whose file name is set in the environment variable COMSPEC from the application directory too!
  13. Optionally move the empty file blunder.com from the parent directory into an arbitrary directory listed in the environment variable PATH, for example the user-writable directory %USERPROFILE%\AppData\Local\Microsoft\WindowsApps\ present since Windows 8, then execute the console application blunder.exe created in step 8. a fourth time and evaluate its exit code:

    MOVE ..\blunder.com "%USERPROFILE%\AppData\Local\Microsoft\WindowsApps"
    ..\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    0xc1 (WIN32: 193 ERROR_BAD_EXE_FORMAT) -- 193 (193)
    Error message text: %1 is not a valid Win32 application.
    CertUtil: -error command completed successfully.
    OUCH⁷: in order to execute a batch script the CreateProcess() function executes an arbitrary executable whose file name is set in the environment variable COMSPEC from any directory in the search path!
  14. Compile and link the source file blunder.c created in step 1. a third time, now with the preprocessor macro BLUNDER defined as 0:

    CL.EXE /DBLUNDER=0 blunder.c kernel32.lib
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.c
    blunder.c(44) : warning C4090: 'function' : different 'const' qualifiers
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:blunder.exe
    blunder.obj
    kernel32.lib
  15. Execute the console application blunder.exe built in step 14. and evaluate its exit code:

    .\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    C:\Windows\system32\cmd.exe /c blunder.bat
    C:\Users\Stefan\Desktop\blunder.bat
    
    0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0)
    Error message text: The operation completed successfully.
    CertUtil: -error command completed successfully.
    Note: with the environment variable COMSPEC unset, the Win32 function CreateProcess() executes %SystemRoot%\System32\cmd.exe as the Command Processor for batch scripts!
  16. Finally erase all files which were moved outside the current directory:

    ERASE ..\blunder.exe ..\blunder.com "%USERPROFILE%\AppData\Local\Microsoft\WindowsApps\blunder.com"
CAVEAT: (ab)using a (user-controlled) environment variable to locate an executable file is a well-known weakness, documented as CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') and CWE-73: External Control of File Name or Path in the CWE; it allows well-known attacks like CAPEC-13: Subverting Environment Variable Values documented in the CAPEC.

Note: an evaluation of the (mis)behaviour with a batch script Windows NT.bat or %OS%.bat is left as an exercise to the reader.

Note: an evaluation of the (mis)behaviour with the environment variable COMSPEC set to blunder.bat, blunder.exe or just blunder is left as an exercise to the reader.

Note: a repetition of this falsification for the CreateProcessAsUser(), CreateProcessWithLogonW() and CreateProcessWithTokenW() functions is also left as an exercise to the reader.

Blunder № 13

The documentation for the Win32 function NeedCurrentDirectoryForExePath() specifies in section Remarks:
If CreateProcess() is called with a relative executable name, it will automatically search for the executable, calling this function to determine the search path.

[…]

The value of the NoDefaultCurrentDirectoryInExePath environment variable determines the value this function returns. […] the existence of the NoDefaultCurrentDirectoryInExePath environment variable is checked, and not its value.

An example of an instance when this function should be called instead of relying on the default search path resolution algorithm in CreateProcess() is the "cmd.exe" executable. It calls this function to determine the command search path because it does its own path resolution before calling CreateProcess. If this function returns TRUE, cmd.exe uses the path ".;%PATH%" for the executable search. If it returns FALSE, cmd.exe uses the path "%PATH%" for the search.

NOTE: the use of a (user-controlled) environment variable to remove the current directory from (its prominent second position in) the search path for applications is not safe – properly implemented, the NeedCurrentDirectoryForExePath() function would query a registry entry writable only by privileged users, similar to CWDIllegalInDllSearch documented in the MSKB article 2264107!

Note: properly implemented in the first place, the Process Creation Flags of the four CreateProcess*() functions would support some CREATE_PROCESS_SEARCH_* flags, similar to the LOAD_LIBRARY_SEARCH_* flags of the LoadLibraryEx() function.

Note: properly implemented in the second place, functions similar to AddDllDirectory(), RemoveDllDirectory() and SetDefaultDllDirectories() or SetDllDirectory() would have been added.

OUCH⁰: the documentation for all four CreateProcess*() functions fails to specify that the environment variable NoDefaultCurrentDirectoryInExePath alters their search sequence!

OUCH¹: the first highlighted statement of the documentation cited above contradicts the documentation for the CreateProcess*() functions cited earlier – the latter search the executable file only if its name contains no (directory) path at all, i.e. neither an absolute nor a relative path!

OUCH²: the last highlighted statements of the documentation cited above even contradict each other – the second statement specifies the true behaviour!

Demonstration

Perform the following 8 simple steps to show the true behaviour.
  1. Create the text file blunder.c with the following content in an arbitrary, preferable empty directory:

    // Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    
    const	STARTUPINFO	si = {sizeof(si)};
    
    __declspec(noreturn)
    VOID	CDECL	wmainCRTStartup(VOID)
    {
    	PROCESS_INFORMATION pi;
    
    	DWORD	dwError = ERROR_SUCCESS;
    	WCHAR	szBlunder[] = L"blunder.com";
    
    	if (!SetEnvironmentVariable(L"NoDefaultCurrentDirectoryInExePath",
    #ifdef BLUNDER
    	                            L""))
    #else
    	                            (LPCWSTR) NULL))
    #endif
    		dwError = GetLastError();
    	else
    		if (!CreateProcess((LPCWSTR) NULL,
    		                   szBlunder,
    		                   (LPSECURITY_ATTRIBUTES) NULL,
    		                   (LPSECURITY_ATTRIBUTES) NULL,
    		                   FALSE,
    		                   CREATE_DEFAULT_ERROR_MODE | CREATE_UNICODE_ENVIRONMENT,
    		                   (LPWSTR) NULL,
    		                   (LPCWSTR) NULL,
    		                   &si,
    		                   &pi))
    			dwError = GetLastError();
    		else
    		{
    			if (WaitForSingleObject(pi.hThread, INFINITE) == WAIT_FAILED)
    				dwError = GetLastError();
    
    			if (!CloseHandle(pi.hThread))
    				dwError = GetLastError();
    
    			if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
    				dwError = GetLastError();
    
    			if (!CloseHandle(pi.hProcess))
    				dwError = GetLastError();
    		}
    
    	ExitProcess(dwError);
    }
  2. Compile and link the source file blunder.c created in step 1.:

    SET CL=/W4 /Zl
    SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    CL.EXE blunder.c kernel32.lib
    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.
    
    blunder.c
    blunder.c(35) : warning C4090: 'function' : different 'const' qualifiers
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:blunder.exe
    blunder.obj
    kernel32.lib
  3. Execute the console application blunder.exe built in step 2. and evaluate its exit code:

    .\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    0x2 (WIN32: 2 ERROR_FILE_NOT_FOUND) -- 2 (2)
    Error message text: The system cannot find the file specified.
    CertUtil: -error command completed successfully.
    Note: the Win32 error code 2 alias ERROR_FILE_NOT_FOUND is expected here – the file blunder.com does not exist yet.
  4. Create the empty file blunder.com in the current directory, then execute the console application blunder.exe built in step 2. a second time and evaluate its exit code:

    COPY NUL: blunder.com
    .\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    0xc1 (WIN32: 193 ERROR_BAD_EXE_FORMAT) -- 193 (193)
    Error message text: %1 is not a valid Win32 application.
    CertUtil: -error command completed successfully.
    Note: the Win32 error code 193 alias ERROR_BAD_EXE_FORMAT is expected here – the empty file blunder.com can’t be mapped into memory, i.e. the CreateProcess() function handles batch scripts like (portable executable) application files.
  5. Compile and link the source file blunder.c created in step 1. a second time, now with the preprocessor macro BLUNDER defined:

    CL.EXE /DBLUNDER blunder.c kernel32.lib
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.c
    blunder.c(35) : warning C4090: 'function' : different 'const' qualifiers
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:blunder.exe
    blunder.obj
    kernel32.lib
  6. Execute the console application blunder.exe built in step 5. and evaluate its exit code:

    .\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    0xc1 (WIN32: 193 ERROR_BAD_EXE_FORMAT) -- 193 (193)
    Error message text: %1 is not a valid Win32 application.
    CertUtil: -error command completed successfully.
    Note: the Win32 error code 193 alias ERROR_BAD_EXE_FORMAT is expected here – the empty file blunder.com is now found in the application directory!
  7. Move the console application blunder.exe built in step 5. into another directory, for example the parent directory, then execute it there and evaluate its exit code:

    MOVE blunder.exe ..
    ..\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    0x2 (WIN32: 2 ERROR_FILE_NOT_FOUND) -- 2 (2)
    Error message text: The system cannot find the file specified.
    CertUtil: -error command completed successfully.
    Note: with the environment variable NoDefaultCurrentDirectoryInExePath set, the CreateProcess() function fails to execute the file blunder.com – it doesn’t search the current directory any more!
  8. Finally erase the file blunder.exe which was moved outside the current directory:

    ERASE ..\blunder.exe

Blunder № 14

This complements Blunder № 12.

Background Information

When a file is double-clicked, Windows graphical shell Explorer.exe looks up the file type alias Programmatic Identifier associated with its extension, retrieves the command line template registered with the default verb of that associated file type, replaces the various tokens %‹digit›, %‹letter› and %* in this command line template with file or path names and arguments, then feeds the completed command line to one of the CreateProcess(), CreateProcessAsUser(), CreateProcessWithLogonW() or CreateProcessWithTokenW() functions.

The MSDN articles File Types and File Associations and Launching Applications (ShellExecute, ShellExecuteEx, SHELLEXECUTEINFO) provide details.

Demonstration

Perform the following 8 simple steps to show that Windows graphical shell can’t distinguish batch scripts from applications.
  1. [Screen shot of error message box from module loader on Windows 7] Create the empty file blunder.bat in an arbitrary, preferable empty directory, then open it via double-click.

    OOPS: according to the error message text, Windows treats batch scripts as applications, i.e. portable executable files!

    Note: as already shown with Blunder № 12, the Win32 error code is 193 alias ERROR_BAD_EXE_FORMAT!

  2. Create the text file blunder.c with the following content in the same directory as the empty file blunder.bat:

    // Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <shellapi.h>
    
    __declspec(noreturn)
    VOID	CDECL	mainCRTStartup(VOID)
    {
    	SHELLEXECUTEINFO sei = {sizeof(sei),
    	                        SEE_MASK_NOASYNC | SEE_MASK_NO_CONSOLE,
    	                        HWND_DESKTOP,
    	                        (LPCWSTR) NULL,
    	                        L".\\blunder.bat",
    	                        L"/?",
    	                        L".",
    	                        SW_SHOWNORMAL,
    	                        (HINSTANCE) NULL,
    	                        NULL,
    	                        (LPCWSTR) NULL,
    	                        HKEY_CLASSES_ROOT,
    	                        0UL,
    	                        (HANDLE) NULL,
    	                        (HANDLE) NULL};
    
    	DWORD	dwError = ERROR_SUCCESS;
    
    	if (!ShellExecuteEx(&sei))
    		dwError = GetLastError();
    
    	ExitProcess(dwError);
    }
    Note: this program performs the equivalent of a double-click on the file blunder.bat in the current directory.
  3. Compile and link the source file blunder.c created in step 2.:

    SET CL=/W4 /Zl
    SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    CL.EXE blunder.c kernel32.lib shell32.lib
    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.
    
    blunder.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:blunder.exe
    blunder.obj
    kernel32.lib
    shell32.lib
  4. Execute the console application blunder.exe built in step 3. and evaluate its exit code to verify the Win32 error code 193:

    .\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    0xc1 (WIN32: 193 ERROR_BAD_EXE_FORMAT) -- 193 (193)
    Error message text: %1 is not a valid Win32 application.
    CertUtil: -error command completed successfully.
  5. Display the file types associated with the extensions .bat, .cmd, .com and .exe plus their command line templates to show the cause for this undocumented behaviour:

    ASSOC .bat
    FTYPE batfile
    ASSOC .cmd
    FTYPE cmdfile
    ASSOC .com
    FTYPE comfile
    ASSOC .exe
    FTYPE exefile
    .bat=batfile
    batfile="%1" %*
    .cmd=cmdfile
    cmdfile="%1" %*
    .com=comfile
    cpmfile="%1" %*
    .exe=exefile
    exefile="%1" %*
  6. Overwrite the empty file blunder.bat created in step 1. with the following content:

    @REM Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    @ECHO %CMDCMDLINE%
    @ECHO %~f0
    @EXIT
  7. Execute the console application blunder.exe built in step 3. a second time and evaluate its exit code:

    .\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    C:\Windows\system32\cmd.exe /c ""C:\Users\Stefan\Desktop\blunder.bat" /?"
    C:\Users\Stefan\Desktop\blunder.bat
    
    0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0)
    Error message text: The operation completed successfully.
    CertUtil: -error command completed successfully.
    OUCH: the ShellExecuteEx() function (which Explorer.exe calls internally) expands the token "%1" in the command line template to the fully qualified quoted path name of the batch script blunder.bat, but fails to double the inner double quotes!
  8. Rename the console application blunder.exe built in step 3 as blunder.cmd and execute it via double-click:

    RENAME blunder.exe *.cmd

Note: an evaluation of the (mis)behaviour with a batch script Windows NT.bat or %OS%.bat is left as an exercise to the reader.

Blunder № 15

Matching the header files of the Platform Windows Software Development Kits, the MSDN documents the prototype declarations of the Win32 functions AdjustTokenPrivileges(), CheckTokenMembership(), CompareFileTime(), CreateDirectory(), ConvertSecurityDescriptorToStringSecurityDescriptor(), ConvertSidToStringSid(), ConvertStringSecurityDescriptorToSecurityDescriptor(), ConvertStringSidToSid(), CreateEnvironmentBlock(), CreateFile(), CreateProcess(), DestroyEnvironmentBlock(), FreeEnvironmentStrings(), GetUserObjectSecurity(), IsValidAcl(), IsValidSecurityDescriptor(), IsValidSid(), LocalFree(), RegisterClass(), RegisterClassEx(), SetSecurityInfo() and SetUserObjectSecurity() (just to pick a few examples, in alphabetical order) as follows:
BOOL AdjustTokenPrivileges(
  [in]            HANDLE            TokenHandle,
  [in]            BOOL              DisableAllPrivileges,
  [in, optional]  PTOKEN_PRIVILEGES NewState,
  [in]            DWORD             BufferLength,
  [out, optional] PTOKEN_PRIVILEGES PreviousState,
  [out, optional] PDWORD            ReturnLength
);
BOOL CheckTokenMembership(
  [in, optional] HANDLE TokenHandle,
  [in]           PSID   SidToCheck,
  [out]          PBOOL  IsMember
);
LONG CompareFileTime(
  [in] const FILETIME *lpFileTime1,
  [in] const FILETIME *lpFileTime2
);
BOOL ConvertSecurityDescriptorToStringSecurityDescriptor(
  [in]  PSECURITY_DESCRIPTOR SecurityDescriptor,
  [in]  DWORD                RequestedStringSDRevision,
  [in]  SECURITY_INFORMATION SecurityInformation,
  [out] LPTSTR               *StringSecurityDescriptor,
  [out] PULONG               StringSecurityDescriptorLen
);
BOOL ConvertSidToStringSid(
  [in]  PSID   Sid,
  [out] LPTSTR *StringSid
);
BOOL ConvertStringSecurityDescriptorToSecurityDescriptor(
  [in]  LPCTSTR              StringSecurityDescriptor,
  [in]  DWORD                StringSDRevision,
  [out] PSECURITY_DESCRIPTOR *SecurityDescriptor,
  [out] PULONG               SecurityDescriptorSize
);
BOOL ConvertStringSidToSid(
  [in]  LPCTSTR StringSid,
  [out] PSID    *Sid
);
BOOL CreateDirectory(
  [in]           LPCTSTR               lpPathName,
  [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
BOOL CreateEnvironmentBlock(
  [out]          LPVOID *lpEnvironment,
  [in, optional] HANDLE hToken,
  [in]           BOOL   bInherit
);
HANDLE CreateFile(
  [in]           LPCTSTR               lpFileName,
  [in]           DWORD                 dwDesiredAccess,
  [in]           DWORD                 dwShareMode,
  [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  [in]           DWORD                 dwCreationDisposition,
  [in]           DWORD                 dwFlagsAndAttributes,
  [in, optional] HANDLE                hTemplateFile
);
BOOL CreateProcess(
  [in, optional]      LPCTSTR               lpApplicationName,
  [in, out, optional] LPTSTR                lpCommandLine,
  [in, optional]      LPSECURITY_ATTRIBUTES lpProcessAttributes,
  [in, optional]      LPSECURITY_ATTRIBUTES lpThreadAttributes,
  [in]                BOOL                  bInheritHandles,
  [in]                DWORD                 dwCreationFlags,
  [in, optional]      LPVOID                lpEnvironment,
  [in, optional]      LPCTSTR               lpCurrentDirectory,
  [in]                LPSTARTUPINFO         lpStartupInfo,
  [out]               LPPROCESS_INFORMATION lpProcessInformation
);
BOOL DestroyEnvironmentBlock(
  [in] LPVOID lpEnvironment
);
BOOL FreeEnvironmentStrings(
  [in] LPTCH penv
);
BOOL GetUserObjectSecurity(
  [in]                HANDLE                hObj,
  [in]                PSECURITY_INFORMATION pSIRequested,
  [in, out, optional] PSECURITY_DESCRIPTOR  pSID,
  [in]                DWORD                 nLength,
  [out]               LPDWORD               lpnLengthNeeded
);
BOOL IsValidAcl(
  [in] PACL pAcl
);
BOOL IsValidSecurityDescriptor(
  [in] PSECURITY_DESCRIPTOR pSecurityDescriptor
);
BOOL IsValidSid(
  [in] PSID pSid
);
HLOCAL LocalFree(
  [in] HLOCAL hMem
);
ATOM RegisterClass(
  [in] const WNDCLASS *lpWndClass
);
ATOM RegisterClassEx(
  [in] const WNDCLASSEX *unnamedParam1
);
DWORD SetSecurityInfo(
  [in]           HANDLE               handle,
  [in]           SE_OBJECT_TYPE       ObjectType,
  [in]           SECURITY_INFORMATION SecurityInfo,
  [in, optional] PSID                 psidOwner,
  [in, optional] PSID                 psidGroup,
  [in, optional] PACL                 pDacl,
  [in, optional] PACL                 pSacl
);
BOOL SetUserObjectSecurity(
  [in] HANDLE hObj,
  [in] PSECURITY_INFORMATION pSIRequested,
  [in] PSECURITY_DESCRIPTOR pSID
);
OUCH: except for the properly declared prototypes of the Win32 functions CompareFileTime(), RegisterClass() and RegisterClassEx() shown above, they all fail to specify their read-only input parameters as const or const *, due to which the Visual C compiler throws the superfluous warning C4090 for arguments of type pointer to constant!

Demonstration

Perform the following 3 simple steps to demonstrate this blunder.
  1. Create the text file blunder.c with the following content in an arbitrary, preferable empty directory:

    // Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    #include <userenv.h>
    #include <shellapi.h>
    #include <sddl.h>
    #include <aclapi.h>
    
    #define SE_CHANGE_NOTIFY_PRIVILEGE	23	// "SeChangeNotifyPrivilege"
    
    const	TOKEN_PRIVILEGES	tp = {1, {SE_CHANGE_NOTIFY_PRIVILEGE, 0, SE_PRIVILEGE_ENABLED}};
    
    const	SID	group = {SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID},
    		owner = {SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID};
    
    typedef	struct	_ace
    {
    	ACE_HEADER	Header;
    	ACCESS_MASK	Mask;
    	SID		Trustee;
    } ACE;
    
    const	struct	_acl
    {
    	ACL	acl;
    	ACE	ace;
    } dacl = {{ACL_REVISION, 0, sizeof(dacl), 1, 0},
    	// (A;NP;FA;;;AU)
             {{ACCESS_ALLOWED_ACE_TYPE, NO_PROPAGATE_INHERIT_ACE, sizeof(ACE)},
              FILE_ALL_ACCESS,
              {SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_AUTHENTICATED_USER_RID}}},
      sacl = {{ACL_REVISION, 0, sizeof(sacl), 1, 0},
    	// (ML;;NRNWNX;;;ME)
              {{SYSTEM_MANDATORY_LABEL_ACE_TYPE, 0, sizeof(ACE)},
               SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP | SYSTEM_MANDATORY_LABEL_NO_READ_UP | SYSTEM_MANDATORY_LABEL_NO_WRITE_UP,
               {SID_REVISION, 1, SECURITY_MANDATORY_LABEL_AUTHORITY, SECURITY_MANDATORY_MEDIUM_RID}}};
    
    const	SECURITY_DESCRIPTOR	sd = {SECURITY_DESCRIPTOR_REVISION,
    				      0,
    				      SE_DACL_PRESENT | SE_SACL_PRESENT,
    				      &owner,
    				      &group,
    				      &sacl,
    				      &dacl};
    
    const	SECURITY_ATTRIBUTES	sa = {sizeof(sa), &sd, FALSE};
    
    const	SECURITY_INFORMATION	si = DACL_SECURITY_INFORMATION
    				   | GROUP_SECURITY_INFORMATION
    				   | LABEL_SECURITY_INFORMATION
    				   | OWNER_SECURITY_INFORMATION
    				   | SACL_SECURITY_INFORMATION;
    
    const	STARTUPINFO		st = {sizeof(st)};
    
    const	WNDCLASS		wc = {0};
    
    const	WNDCLASSEX		wce = {sizeof(wce)};
    
    __declspec(noreturn)
    VOID	CDECL	wmainCRTStartup(VOID)
    {
    	SECURITY_DESCRIPTOR	const	*lpSD;
    	SID			const	*lpSID;
    
    	PROCESS_INFORMATION	pi;
    
    	FILETIME	ft = {0UL, 0UL};
    
    	INT	nArguments;
    	BOOL	b;
    	WCHAR	sz[] = L".";
    	DWORD	dwSD;
    	HANDLE	h;
    	LPCWSTR	lpBlock;
    	LPCWSTR	lpStrings = GetEnvironmentStrings();
    	LPCWSTR	lpCmdLine = GetCommandLine();
    	LPCWSTR	*lpArguments = CommandLineToArgvW(lpCmdLine, &nArguments);
    
    	if (lpArguments != NULL)
    		LocalFree(lpArguments);
    
    	if (lpStrings != NULL)
    		FreeEnvironmentStrings(lpStrings);
    
    	AdjustTokenPrivileges(INVALID_HANDLE_VALUE,
    	                      FALSE,
    	                      &tp,
    	                      sizeof(tp),
    	                      (TOKEN_PRIVILEGES *) NULL,
    	                      (LPDWORD) NULL);
    
    	CheckTokenMembership(NULL,
    	                     &owner,
    	                     &b);
    
    	CompareFileTime(&ft, &ft);	// no warning C4090 here!
    
    	if (ConvertSecurityDescriptorToStringSecurityDescriptor(&sd,
    	                                                        SDDL_REVISION_1,
    	                                                        si,
    	                                                        &lpStrings,
    	                                                        (LPDWORD) NULL))
    		LocalFree(lpStrings);
    
    	if (ConvertSidToStringSid(&owner,
    	                          &lpStrings))
    		LocalFree(lpStrings);
    
    	if (ConvertStringSidToSid(sz,	// no warning C4090 here!
    	                          &lpSID))
    		LocalFree(lpSID);
    
    	if (ConvertStringSecurityDescriptorToSecurityDescriptor(sz,
    	                                                        SDDL_REVISION_1,
    	                                                        &lpSD,
    	                                                        &dwSD))
    		LocalFree(lpSD);
    
    	if (CreateDirectory(sz,		// no warning C4090 here!
    	                    &sa))
    		RemoveDirectory(sz);
    
    	if (CreateEnvironmentBlock(&lpBlock,
    	                           NULL,
    	                           FALSE))
    		DestroyEnvironmentBlock(lpBlock);
    
    	h = CreateFile(sz,		// no warning C4090 here!
    	               MAXIMUM_ALLOWED,
    	               FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
    	               &sa,
    	               OPEN_EXISTING,
    	               FILE_FLAG_BACKUP_SEMANTICS,
    	               (HANDLE) NULL);
    
    	if (h != INVALID_HANDLE_VALUE)
    		CloseHandle(h);
    
    	CreateProcess((LPCWSTR) NULL,
    	              (LPWSTR) NULL,
    	              &sa,
    	              &sa,
    	              FALSE,
    	              CREATE_UNICODE_ENVIRONMENT,
    	              lpStrings,
    	              sz,		// no warning C4090 here!
    	              &st,
    	              &pi);
    
    	GetUserObjectSecurity(INVALID_HANDLE_VALUE,
    	                      &si,
    	                      (SECURITY_DESCRIPTOR *) NULL,
    	                      0,
    	                      &dwSD);
    
    	IsValidAcl(&dacl);
    	IsValidAcl(&sacl);
    	IsValidSecurityDescriptor(&sd);
    	IsValidSid(&owner);
    	IsValidSid(&group);
    
    	if (RegisterClass(&wc) != 0U)	// no warning C4090 here!
    		UnregisterClass(wc.lpszClassName,
    		                wc.hInstance);
    
    	if (RegisterClassEx(&wce) != 0U)// no warning C4090 here!
    		UnregisterClass(wce.lpszClassName,
    		                wce.hInstance);
    
    	SetSecurityInfo(INVALID_HANDLE_VALUE,
    	                SE_FILE_OBJECT,
    	                si,
    	                &owner,
    	                &group,
    	                &dacl,
    	                &sacl);
    
    	SetUserObjectSecurity(INVALID_HANDLE_VALUE,
    	                      &si,
    	                      &sd);
    
    	ExitProcess(nArguments);
    }
    Note: in order to exercise defensive programming, all pointers are intentionally defined as const * – their targets are not (to be) written here!
  2. Run a syntax check on the source file blunder.c created in step 1.:

    CL.EXE /Zs blunder.c
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.c
    blunder.c(45) : warning C4090: 'initializing' : different 'const' qualifiers
    blunder.c(46) : warning C4090: 'initializing' : different 'const' qualifiers
    blunder.c(47) : warning C4090: 'initializing' : different 'const' qualifiers
    blunder.c(48) : warning C4090: 'initializing' : different 'const' qualifiers
    blunder.c(50) : warning C4090: 'initializing' : different 'const' qualifiers
    blunder.c(85) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(88) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(92) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(98) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(103) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(106) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(108) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(110) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(111) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(112) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(115) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(116) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(120) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(122) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(125) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(128) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(131) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(136) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(146) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(147) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(150) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(152) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(156) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(160) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(161) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(162) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(163) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(164) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(177) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(178) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(179) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(180) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(183) : warning C4090: 'function' : different 'const' qualifiers
    blunder.c(184) : warning C4090: 'function' : different 'const' qualifiers
    OOPS: Was für Plunder!
  3. Run a syntax check on the source file blunder.c created in step 1. a second time, now as C++ source:

    CL.EXE /TP /Zs blunder.c
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.c
    blunder.c(45) : error C2440: 'initializing' : cannot convert from 'const SID *' to 'PSID'
            Conversion loses qualifiers
    blunder.c(46) : error C2440: 'initializing' : cannot convert from 'const SID *' to 'PSID'
            Conversion loses qualifiers
    blunder.c(47) : error C2440: 'initializing' : cannot convert from 'const _acl *' to 'PACL'
            Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
    blunder.c(48) : error C2440: 'initializing' : cannot convert from 'const _acl *' to 'PACL'
            Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
    blunder.c(50) : error C2440: 'initializing' : cannot convert from 'const SECURITY_DESCRIPTOR *' to 'LPVOID'
            Conversion loses qualifiers
    blunder.c(82) : error C2440: 'initializing' : cannot convert from 'LPWSTR *' to 'LPCWSTR *'
            Conversion loses qualifiers
    blunder.c(88) : error C2664: 'FreeEnvironmentStringsW' : cannot convert parameter 1 from 'LPCWSTR' to 'LPWCH'
            Conversion loses qualifiers
    blunder.c(95) : error C2664: 'AdjustTokenPrivileges' : cannot convert parameter 3 from 'const TOKEN_PRIVILEGES *' to 'PTOKEN_PRIVILEGES'
            Conversion loses qualifiers
    blunder.c(99) : error C2664: 'CheckTokenMembership' : cannot convert parameter 2 from 'const SID *' to 'PSID'
            Conversion loses qualifiers
    blunder.c(107) : error C2664: 'ConvertSecurityDescriptorToStringSecurityDescriptorW' : cannot convert parameter 1 from 'const SECURITY_DESCRIPTOR *' to 'PSECURITY_DESCRIPTOR'
            Conversion loses qualifiers
    blunder.c(108) : error C2664: 'LocalFree' : cannot convert parameter 1 from 'LPCWSTR' to 'HLOCAL'
            Conversion loses qualifiers
    blunder.c(111) : error C2664: 'ConvertSidToStringSidW' : cannot convert parameter 1 from 'const SID *' to 'PSID'
            Conversion loses qualifiers
    blunder.c(112) : error C2664: 'LocalFree' : cannot convert parameter 1 from 'LPCWSTR' to 'HLOCAL'
            Conversion loses qualifiers
    blunder.c(115) : error C2664: 'ConvertStringSidToSidW' : cannot convert parameter 2 from 'const SID **' to 'PSID *'
            Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
    blunder.c(116) : error C2664: 'LocalFree' : cannot convert parameter 1 from 'const SID *' to 'HLOCAL'
            Conversion loses qualifiers
    blunder.c(121) : error C2664: 'ConvertStringSecurityDescriptorToSecurityDescriptorW' : cannot convert parameter 3 from 'const SECURITY_DESCRIPTOR **' to 'PSECURITY_DESCRIPTOR *'
            Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
    blunder.c(122) : error C2664: 'LocalFree' : cannot convert parameter 1 from 'const SECURITY_DESCRIPTOR *' to 'HLOCAL'
            Conversion loses qualifiers
    blunder.c(125) : error C2664: 'CreateDirectoryW' : cannot convert parameter 2 from 'const SECURITY_ATTRIBUTES *' to 'LPSECURITY_ATTRIBUTES'
            Conversion loses qualifiers
    blunder.c(130) : error C2664: 'CreateEnvironmentBlock' : cannot convert parameter 1 from 'LPCWSTR *' to 'LPVOID *'
            Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
    blunder.c(131) : error C2664: 'DestroyEnvironmentBlock' : cannot convert parameter 1 from 'LPCWSTR' to 'LPVOID'
            Conversion loses qualifiers
    blunder.c(139) : error C2664: 'CreateFileW' : cannot convert parameter 4 from 'const SECURITY_ATTRIBUTES *' to 'LPSECURITY_ATTRIBUTES'
            Conversion loses qualifiers
    blunder.c(153) : error C2664: 'CreateProcessW' : cannot convert parameter 3 from 'const SECURITY_ATTRIBUTES *' to 'LPSECURITY_ATTRIBUTES'
            Conversion loses qualifiers
    blunder.c(159) : error C2664: 'GetUserObjectSecurity' : cannot convert parameter 2 from 'const SECURITY_INFORMATION *' to 'PSECURITY_INFORMATION'
            Conversion loses qualifiers
    blunder.c(160) : error C2664: 'IsValidAcl' : cannot convert parameter 1 from 'const _acl *' to 'PACL'
            Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
    blunder.c(161) : error C2664: 'IsValidAcl' : cannot convert parameter 1 from 'const _acl *' to 'PACL'
            Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
    blunder.c(162) : error C2664: 'IsValidSecurityDescriptor' : cannot convert parameter 1 from 'const SECURITY_DESCRIPTOR *' to 'PSECURITY_DESCRIPTOR'
            Conversion loses qualifiers
    blunder.c(163) : error C2664: 'IsValidSid' : cannot convert parameter 1 from 'const SID *' to 'PSID'
            Conversion loses qualifiers
    blunder.c(164) : error C2664: 'IsValidSid' : cannot convert parameter 1 from 'const SID *' to 'PSID'
            Conversion loses qualifiers
    blunder.c(180) : error C2664: 'SetSecurityInfo' : cannot convert parameter 4 from 'const SID *' to 'PSID'
            Conversion loses qualifiers
    blunder.c(184) : error C2664: 'SetUserObjectSecurity' : cannot convert parameter 2 from 'const SECURITY_INFORMATION *' to 'PSECURITY_INFORMATION'
            Conversion loses qualifiers
    OUCH: Was für Plunder!

Blunder № 16

The documentation for the Win32 functions CertRDNValueToStr() and GetVolumePathNamesForVolumeNameW() (just to pick some examples) as well as the window message LVM_GETISEARCHSTRING (just to pick another example) exhibit an unfortunately very common mistake blunder, present in many other MSDN articles like String manipulation (CRT) too:
Size, in characters, allocated for the returned string. The size must include the terminating NULL character.

[…]

Returns the number of characters converted, including the terminating NULL character. If psz is NULL or csz is zero, returns the required size of the destination string.

The list is a array of null-terminated strings terminated by an additional NULL character. […]

[…] including all NULL characters.

Returns the number of characters in the incremental search string, not including the terminating NULL character, or zero if the list-view control is not in incremental search mode.

[…]

Make sure that the buffer is large enough to hold the string and the terminating NULL character.

These routines operate on null-terminated single-byte character, wide-character, and multibyte-character strings. Use the buffer-manipulation routines, described in Buffer manipulation, to work with character arrays that don't end with a NULL character.
OUCH: there is no NULL characterNULL is a preprocessor macro defined as ((void *) 0) – and the highlighted text parts should be replaced with null character, NUL, '\0' or L'\0'!

Blunder № 17

The header file winnt.h shipped with the Platform Windows Software Development Kits defines the SID alias Security Identifier as follows:
#define ANYSIZE_ARRAY 1

[…]

////////////////////////////////////////////////////////////////////////
//                                                                    //
//              Security Id     (SID)                                 //
//                                                                    //
////////////////////////////////////////////////////////////////////////
//
//
// Pictorially the structure of an SID is as follows:
//
//         1   1   1   1   1   1
//         5   4   3   2   1   0   9   8   7   6   5   4   3   2   1   0
//      +---------------------------------------------------------------+
//      |      SubAuthorityCount        |Reserved1 (SBZ)|   Revision    |
//      +---------------------------------------------------------------+
//      |                   IdentifierAuthority[0]                      |
//      +---------------------------------------------------------------+
//      |                   IdentifierAuthority[1]                      |
//      +---------------------------------------------------------------+
//      |                   IdentifierAuthority[2]                      |
//      +---------------------------------------------------------------+
//      |                                                               |
//      +- -  -  -  -  -  -  -  SubAuthority[]  -  -  -  -  -  -  -  - -+
//      |                                                               |
//      +---------------------------------------------------------------+
//
//

#ifndef SID_IDENTIFIER_AUTHORITY_DEFINED
#define SID_IDENTIFIER_AUTHORITY_DEFINED
typedef struct _SID_IDENTIFIER_AUTHORITY {
    BYTE  Value[6];
} SID_IDENTIFIER_AUTHORITY, *PSID_IDENTIFIER_AUTHORITY;
#endif

#ifndef SID_DEFINED
#define SID_DEFINED
typedef struct _SID {
   BYTE  Revision;
   BYTE  SubAuthorityCount;
   SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
   DWORD SubAuthority[ANYSIZE_ARRAY];
} SID, *PISID;
#endif

#define SID_REVISION                     (1)    // Current revision level
#define SID_MAX_SUB_AUTHORITIES          (15)
#define SID_RECOMMENDED_SUB_AUTHORITIES  (1)    // Will change to around 6
                                                // in a future release.
#define SECURITY_MAX_SID_SIZE  \
      (sizeof(SID) - sizeof(DWORD) + (SID_MAX_SUB_AUTHORITIES * sizeof(DWORD)))

// 2 (S-)
// 4 (Rev(max: 255)-)
// 15 (
//      If (Auth < 2^32): Auth(max:4294967295)-
//      Else:             0xAuth(max:FFFFFFFFFFFF)-
//    )
// (11 * SID_MAX_SUB_AUTHORITIES) (SubN(max:4294967295)-)
// 1 (NULL character)
// = 187 (assuming SID_MAX_SUB_AUTHORITIES = 15)
#define SECURITY_MAX_SID_STRING_CHARACTERS \
    (2 + 4 + 15 + (11 * SID_MAX_SUB_AUTHORITIES) + 1)

[…]

/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// Universal well-known SIDs                                               //
//                                                                         //
//     Null SID                     S-1-0-0                                //
//     World                        S-1-1-0                                //
//     Local                        S-1-2-0                                //
//     Creator Owner ID             S-1-3-0                                //
//     Creator Group ID             S-1-3-1                                //
//     Creator Owner Server ID      S-1-3-2                                //
//     Creator Group Server ID      S-1-3-3                                //
//                                                                         //
//     (Non-unique IDs)             S-1-4                                  //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////

#define SECURITY_NULL_SID_AUTHORITY         {0,0,0,0,0,0}
#define SECURITY_WORLD_SID_AUTHORITY        {0,0,0,0,0,1}
#define SECURITY_LOCAL_SID_AUTHORITY        {0,0,0,0,0,2}
#define SECURITY_CREATOR_SID_AUTHORITY      {0,0,0,0,0,3}
#define SECURITY_NON_UNIQUE_AUTHORITY       {0,0,0,0,0,4}
#define SECURITY_RESOURCE_MANAGER_AUTHORITY {0,0,0,0,0,9}

[…]

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// NT well-known SIDs                                                        //
//                                                                           //
//     NT Authority            S-1-5                                         //
//     Dialup                  S-1-5-1                                       //
//                                                                           //
//     Network                 S-1-5-2                                       //
//     Batch                   S-1-5-3                                       //
//     Interactive             S-1-5-4                                       //
//     (Logon IDs)             S-1-5-5-X-Y                                   //
//     Service                 S-1-5-6                                       //
//     AnonymousLogon          S-1-5-7       (aka null logon session)        //
//     Proxy                   S-1-5-8                                       //
//     Enterprise DC (EDC)     S-1-5-9       (aka domain controller account) //
//     Self                    S-1-5-10      (self RID)                      //
//     Authenticated User      S-1-5-11      (Authenticated user somewhere)  //
//     Restricted Code         S-1-5-12      (Running restricted code)       //
//     Terminal Server         S-1-5-13      (Running on Terminal Server)    //
//     Remote Logon            S-1-5-14      (Remote Interactive Logon)      //
//     This Organization       S-1-5-15                                      //
//                                                                           //
//     IUser                   S-1-5-17                                      //
//     Local System            S-1-5-18                                      //
//     Local Service           S-1-5-19                                      //
//     Network Service         S-1-5-20                                      //
//                                                                           //
//     (NT non-unique IDs)     S-1-5-0x15-... (NT Domain Sids)               //
//                                                                           //
//     (Built-in domain)       S-1-5-0x20                                    //
//                                                                           //
//     (Security Package IDs)  S-1-5-0x40                                    //
//     NTLM Authentication     S-1-5-0x40-10                                 //
//     SChannel Authentication S-1-5-0x40-14                                 //
//     Digest Authentication   S-1-5-0x40-21                                 //
//                                                                           //
//     Other Organization      S-1-5-1000    (>=1000 can not be filtered)    //
//                                                                           //
//                                                                           //
// NOTE: the relative identifier values (RIDs) determine which security      //
//       boundaries the SID is allowed to cross.  Before adding new RIDs,    //
//       a determination needs to be made regarding which range they should  //
//       be added to in order to ensure proper "SID filtering"               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////


#define SECURITY_NT_AUTHORITY               {0,0,0,0,0,5}   // ntifs

[…]

#define SECURITY_APP_PACKAGE_AUTHORITY      {0,0,0,0,0,15}

[…]

#define SECURITY_MANDATORY_LABEL_AUTHORITY  {0,0,0,0,0,16}

[…]

#define SECURITY_SCOPED_POLICY_ID_AUTHORITY {0,0,0,0,0,17}

[…]

#define SECURITY_AUTHENTICATION_AUTHORITY   {0,0,0,0,0,18}

[…]

#define SECURITY_PROCESS_TRUST_AUTHORITY    {0,0,0,0,0,19}
Oops¹: the definition of the SID_IDENTIFIER_AUTHORITY structure fails to match the ASCII art – according to the latter it should be WORD Value[3] instead of BYTE Value[6]!

Oops²: the value of the preprocessor macro SECURITY_MAX_SID_SIZE fails to match the definition of the SID structure – according to the latter it should be (sizeof(SID) + sizeof(DWORD) * (SID_MAX_SUB_AUTHORITIES - ANYSIZE_ARRAY))!

Oops³: the value of the preprocessor macro SECURITY_MAX_SID_STRING_CHARACTERS (introduced with Windows 10 1703 alias Creators Update, codenamed Redstone 2) counts one minus sign to many and matches neither the ASCII art nor (the use of) the SID_IDENTIFIER_AUTHORITY structure – it should be (sizeof("S-15-65535") + sizeof("4294967295") * SID_MAX_SUB_AUTHORITIES) to match the former or (sizeof("S-15-255") + sizeof("4294967295") * SID_MAX_SUB_AUTHORITIES) to match the latter!

Blunder № 18

The MSDN article Compiler Command-Line Syntax states:
You can specify any number of options, filenames, and library names, as long as the number of characters on the command line does not exceed 1024, the limit dictated by the operating system.
OUCH: Windows NT supports but up to 32767 characters on the command line!

Note: according to the MSKB article 830473, the command interpreter Cmd.exe supports up to 8191 characters on the command line.

Blunder № 19

The documentation for the /GS compiler option states:
The /GS compiler option requires that the security cookie be initialized before any function that uses the cookie is run. The security cookie must be initialized immediately on entry to an EXE or DLL. This is done automatically if you use the default VCRuntime entry points: mainCRTStartup, wmainCRTStartup, WinMainCRTStartup, wWinMainCRTStartup, or _DllMainCRTStartup. If you use an alternate entry point, you must manually initialize the security cookie by calling __security_init_cookie.
OOPS¹: contrary to the first highlighted statement, the code generated by the Visual C compiler requires only that the (arbitrary) value of the security cookie does not change between entry and exit of any function which uses it!

OOPS²: contrary to the second highlighted statement there is absolutely no need to call the __security_init_cookie() function to (re)initialise the security cookie!

OOPS³: the documentation cited above fails to provide the following (implementation) details:

Blunder № 20

The documentation for the /Gs compiler option states in section Remarks:
A stack probe is a sequence of code that the compiler inserts at the beginning of a function call. When initiated, a stack probe reaches benignly into memory by the amount of space required to store the function's local variables. This probe causes the operating system to transparently page in more stack memory if necessary, before the rest of the function runs.

By default, the compiler generates code that initiates a stack probe when a function requires more than one page of stack space. This default is equivalent to a compiler option of /Gs4096 for x86, x64, ARM, and ARM64 platforms. This value allows an application and the Windows memory manager to increase the amount of memory committed to the program stack dynamically at run time.

The documentation for the Visual C compiler helper routine _chkstk() states since more than 20 years in section Remarks:
_chkstk Routine is a helper routine for the C compiler. For x86 compilers, _chkstk Routine is called when the local variables exceed 4K bytes; for x64 compilers it is 8K.
OUCH: the correct value for x64 alias AMD64 compilers processors is but 4096 too – 8192 is was used only by (compilers for) IA64 alias Itanium® processors!

Blunder № 21

The documentation for the Visual C compiler helper routine _alloca() states since more than 20 years in section Remarks:
Allocates memory on the stack. […]

The _alloca routine returns a void pointer to the allocated space, which is guaranteed to be suitably aligned for storage of any type of object. […]

Falsification

Perform the following 3 simple steps to prove the highlighted claim of the documentation cited above wrong.
  1. Create the text file alloca.c with the following content in an arbitrary, preferably empty directory:

    // Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    void	*mainCRTStartup(void)
    {
    	return _alloca(42);
    }
  2. Compile and link the source file alloca.c created in step 1.:

    SET CL=/GAFS- /Oxy /W4 /X
    SET LINK=/ENTRY:mainCRTStartup /MACHINE:I386 /SUBSYSTEM:CONSOLE
    CL.EXE alloca.c
    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.
    
    alloca.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:mainCRTStartup /MACHINE:I386 /SUBSYSTEM:CONSOLE
    /out:alloca.exe
    alloca.obj
  3. Execute the console application alloca.exe built in step 2. and evaluate its exit code to prove the highlighted claim of the documentation cited above wrong:

    .\alloca.exe
    ECHO %ERRORLEVEL%
    SET /A %ERRORLEVEL% % 16
    3733996
    12
    OUCH¹: memory objects referenced by SSE instructions require but a 16 byte alignment!

    OUCH²: for constant arguments less than 64 the Visual C compiler generates calls to the _alloca_probe routine which returns but an unaligned pointer!

Blunder № 22

The MSDN article #pragma comment states:
Places a comment record into an object file or executable file.

Syntax

#pragma comment( comment-type [ , "comment-string" ] )

Remarks

The comment-type is one of the predefined identifiers, described below, that specifies the type of comment record. The optional comment-string is a string literal that provides additional information for some comment types. Because comment-string is a string literal, it obeys all the rules for string literals on use of escape characters, embedded quotation marks ("), and concatenation.

[…]

linker

Places a linker option in the object file. You can use this comment-type to specify a linker option instead of passing it to the command line or specifying it in the development environment. For example, you can specify the /include option to force the inclusion of a symbol:

#pragma comment(linker, "/include:__mySymbol")
Only the following (comment-type) linker options are available to be passed to the linker identifier:
OUCH: the highlighted statement is misleading and wrong – the Visual C compiler accepts arbitrary strings as linker options and writes them into the .drectve section of the object file; the linker but rejects all options except the 6 options cited above plus the 15 options enumerated below with error LNK1276 or warning LNK4229!
/ALIGN
/ALTERNATENAME
/ASSEMBLYMODULE
/BASE
/CLRTHREADATTRIBUTE
/DLL
/DISALLOWLIB
/ENTRY
/HEAP
/INCREMENTAL
/NODEFAULTLIB
/OUT
/STACK
/SUBSYSTEM
/VERSION

Falsification

Perform the following 2 simple steps to prove the highlighted statement of the documentation cited above wrong.
  1. Create the text file blunder.c with the following content in an arbitrary, preferable empty directory:

    // Copyleft © 2001-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    int	blunder(void *module, int reason, void *context)
    {
    	return module != context, reason == 1;
    
    #pragma comment(linker, "/ALIGN:4096")
    #pragma comment(linker, "/ALTERNATENAME:@=" __FUNCDNAME__)
    #pragma comment(linker, "/ASSEMBLYMODULE:@")
    #pragma comment(linker, "/BASE:0")
    #pragma comment(linker, "/CLRTHREADATTRIBUTE:NONE")
    #pragma comment(linker, "/DEFAULTLIB:libcmt.lib")
    #pragma comment(linker, "/DISALLOWLIB:msvcrt.lib")
    #pragma comment(linker, "/DISALLOWLIB:oldnames.lib")
    #pragma comment(linker, "/DLL")
    #pragma comment(linker, "/ENTRY:" __FUNCTION__)
    #pragma comment(linker, "/EXPORT:@")
    #pragma comment(linker, "/HEAP:0,0")
    #pragma comment(linker, "/INCLUDE:@")
    #pragma comment(linker, "/INCREMENTAL")
    #pragma comment(linker, "/MANIFESTDEPENDENCY:name='@'")
    #pragma comment(linker, "/MERGE:.rdata=.const")
    #pragma comment(linker, "/MERGE:.text=.code")
    #pragma comment(linker, "/NODEFAULTLIB")
    #pragma comment(linker, "/OUT:blunder.dll")
    #pragma comment(linker, "/SECTION:.bss,d")
    #pragma comment(linker, "/STACK:0,0")
    #pragma comment(linker, "/SUBSYSTEM:WINDOWS")
    #pragma comment(linker, "/VERBOSE")
    #pragma comment(linker, "/VERSION:0.815")
    }
  2. Compile and link the source file blunder.c created in step 1.:

    SET CL=/Gz /W4 /X /Zl
    SET LINK=
    CL.EXE blunder.c
    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.
    
    blunder.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /out:blunder.exe
    blunder.obj
    blunder.obj : warning LNK4070: /OUT:blunder.dll directive in .EXP differs from output filename 'blunder.exe'; ignoring directive
    blunder.obj : warning LNK4229: invalid directive '/VERBOSE' encountered; ignored
       Creating library blunder.lib and object blunder.exp
Note: a repetition of this falsification in the 64-bit development environment is left as an exercise to the reader.

Blunder № 23

The documentation for the /ENTRY:‹symbol› linker option states under the heading Remarks:
Remarks

The /ENTRY option specifies an entry point function as the starting address for an .exe file or DLL.

The function must be defined to use the __stdcall calling convention. The parameters and return value depend on if the program is a console application, a windows application or a DLL. It is recommended that you let the linker set the entry point so that the C run-time library is initialized correctly, and C++ constructors for static objects are executed.

By default, the starting address is a function name from the C run-time library. The linker selects it according to the attributes of the program, as shown in the following table.

Function name Default for
mainCRTStartup
(or wmainCRTStartup)
An application that uses /SUBSYSTEM:CONSOLE; calls main (or wmain)
WinMainCRTStartup
(or wWinMainCRTStartup)
An application that uses /SUBSYSTEM:WINDOWS; calls WinMain (or wWinMain), which must be defined to use __stdcall
_DllMainCRTStartup A DLL; calls DllMain if it exists, which must be defined to use __stdcall

If the /DLL or /SUBSYSTEM option is not specified, the linker selects a subsystem and entry point depending on whether main or WinMain is defined.

The functions main, WinMain, and DllMain are the three forms of the user-defined entry point.

Ouch: no prototypes are provided for the entry point functions!

Falsification

Perform the following 3 simple steps to prove the highlighted statement of the documentation cited above wrong.
  1. Create the text file blunder.c with the following content in an arbitrary, preferable empty directory:

    // Copyleft © 2001-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define NULL	(void *) 0
    
    extern	struct	_IMAGE_DOS_HEADER	__ImageBase;
    
    typedef	unsigned short	wchar_t;
    
    #ifndef BLUNDER
    #error
    #elif BLUNDER == 0 // DLL
    int	DllMain(void *module, int reason, void *context)
    {
    	return module != context, reason == 1;
    }
    #elif BLUNDER == 1 // DLL entry point function
    int	DllMain(void *module, int reason, void *context);
    
    int	_DllMainCRTStartup(void *module, int reason, void *context)
    {
    	return DllMain(module, reason, context);
    }
    #elif BLUNDER == 2 // ANSI console program
    int	main(int argc, char *argv[], char *envp[])
    {
    	return *envp != argv[argc];
    }
    #elif BLUNDER == 3 // ANSI console program entry point function
    int	__cdecl	main(int argc, char *argv[], char *envp[]);
    
    static	char	*argv[] = {"main.exe", "/?", NULL};
    static	char	*envp[] = {"name=value", NULL};
    
    int	mainCRTStartup(void)
    {
    	return main(sizeof(argv) / sizeof(*argv) - 1, argv, envp);
    }
    #elif BLUNDER == 4 // UNICODE console program
    int	wmain(int argc, wchar_t *argv[], wchar_t *envp[])
    {
    	return *envp != argv[argc];
    }
    #elif BLUNDER == 5 // UNICODE console program entry point function
    int	__cdecl	wmain(int argc, wchar_t *argv[], wchar_t *envp[]);
    
    static	wchar_t	*argv[] = {L"wmain.exe", L"/?", NULL};
    static	wchar_t	*envp[] = {L"name=value", NULL};
    
    int	wmainCRTStartup(void)
    {
    	return wmain(sizeof(argv) / sizeof(*argv) - 1, argv, envp);
    }
    #elif BLUNDER == 6 // ANSI Windows program
    int	WinMain(void *current, void *previous, char cmdline[], int show)
    {
    	return current != previous, *cmdline != show;
    }
    #elif BLUNDER == 7 // ANSI Windows program entry point function
    int	WinMain(void *current, void *previous, char cmdline[], int show);
    
    int	WinMainCRTStartup(void)
    {
    	return WinMain(&__ImageBase, NULL, "/?", 0);
    }
    #elif BLUNDER == 8 // UNICODE Windows program
    int	wWinMain(void *current, void *previous, wchar_t cmdline[], int show)
    {
    	return current != previous, *cmdline != show;
    }
    #elif BLUNDER == 9 // UNICODE Windows program entry point function
    int	wWinMain(void *current, void *previous, wchar_t cmdline[], int show);
    
    int	wWinMainCRTStartup(void)
    {
    	return wWinMain(&__ImageBase, NULL, L"/?", 0);
    }
    #else
    #error
    #endif // BLUNDER
  2. Start the command prompt of the Visual C development environment for the i386 platform, then execute the following 7 command lines to compile the source file blunder.c created in step 1. to generate the 5 object files dllmain.obj, main.obj, wmain.obj, winmain.obj plus wwinmain.obj with the entry point functions and put them into the new object library libcmt.lib:

    SET CL=/c /Gyz /Oxy /W4 /X /Zl
    CL.EXE /DBLUNDER=1 /Fodllmain.obj blunder.c
    CL.EXE /DBLUNDER=3 /Fomain.obj blunder.c
    CL.EXE /DBLUNDER=5 /Fowmain.obj blunder.c
    CL.EXE /DBLUNDER=7 /Fowinmain.obj blunder.c
    CL.EXE /DBLUNDER=9 /Fowwinmain.obj blunder.c
    LINK.EXE /LIB /MACHINE:I386 /NODEFAULTLIB /OUT:libcmt.lib dllmain.obj main.obj wmain.obj winmain.obj wwinmain.obj
    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.
    
    blunder.c
    
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.c
    
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.c
    
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.c
    
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.c
    
    Microsoft (R) Library Manager Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
  3. Execute the following 7 command lines to compile the source file blunder.c created in step 1. 5 times and (try to) link each generated object file blunder.obj with the entry point functions from the object library libcmt.lib generated in step 2.:

    SET CL=/Gy /MT /Oxy /W4 /X
    SET LINK=/MACHINE:I386
    CL.EXE /DBLUNDER=0 /Gz /LD blunder.c
    CL.EXE /DBLUNDER=2 /Gd blunder.c
    CL.EXE /DBLUNDER=4 /Gd blunder.c
    CL.EXE /DBLUNDER=6 /Gz blunder.c
    CL.EXE /DBLUNDER=8 /Gz blunder.c
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /MACHINE:I386
    /out:blunder.dll
    /dll
    /implib:blunder.lib
    blunder.obj
    
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /MACHINE:I386
    /out:blunder.exe
    blunder.obj
    LINK : error LNK2001: unresolved external symbol _mainCRTStartup
    blunder.exe : fatal error LNK1120: 1 unresolved externals
    
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /MACHINE:I386
    /out:blunder.exe
    blunder.obj
    LINK : error LNK2001: unresolved external symbol _wmainCRTStartup
    blunder.exe : fatal error LNK1120: 1 unresolved externals
    
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /MACHINE:I386
    /out:blunder.exe
    blunder.obj
    LINK : error LNK2001: unresolved external symbol _WinMainCRTStartup
    blunder.exe : fatal error LNK1120: 1 unresolved externals
    
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /MACHINE:I386
    /out:blunder.exe
    blunder.obj
    LINK : error LNK2001: unresolved external symbol _wWinMainCRTStartup
    blunder.exe : fatal error LNK1120: 1 unresolved externals
    OUCH: contrary to the documentation cited above, the linker expects the entry point functions for console as well as Windows applications to be defined using the __cdecl calling and naming convention!

Blunder № 24

Demonstration

Perform the following 5 simple steps to show the blunder.
  1. Create the text file blunder.c with the following content in an arbitrary, preferable empty directory:

    // Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    static	long	bss;
    static	long	data = 'DATA';
    const	long	rdata = 'VOID';
    
    long	mainCRTStartup(void)
    {
    	return bss = data = rdata;
    }
  2. Compile the source file blunder.c created in step 1. to generate the object file blunder.obj, then enumerate the names and sizes of its COFF sections:

    CL.EXE /c /W4 /X /Zl blunder.c
    LINK.EXE /DUMP blunder.obj
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.c
    
    Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    
    Dump of file blunder.obj
    
    File Type: COFF OBJECT
    
      Summary
    
               4 .bss
               4 .data
              74 .debug$S
               3 .drectve
               4 .rdata
              20 .text
    Note: the Visual C compiler puts uninitialised static variables in the .bss section, initialised static variables in the .data section, and constants in the .rdata section.
  3. Link the object file blunder.obj created in step 1. to generate the executable image file blunder.exe, using the undocumented /TEST linker option, then enumerate its sections:

    LINK.EXE /LINK /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /TEST blunder.obj
    LINK.EXE /DUMP blunder.exe
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    LINK : file alignment: 512, section alignment: 4096
    LINK : section '.sdata' (C0000040) merged into '.data' (C0000040)
    LINK : section '.xdata' (40000040) merged into '.rdata' (40000040)
    
    Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    
    Dump of file blunder.exe
    
    File Type: EXECUTABLE IMAGE
    
      Summary
    
            1000 .data
            1000 .rdata
            1000 .reloc
            1000 .text
    Oops: the linker merges the .bss section into the .data section, but fails to print the corresponding message although their characteristics differ – 0xC0000080 alias uninitialised data, readable, writable vs. 0xC0000040 alias initialised data, readable, writable!
  4. Link the object file blunder.obj created in step 1. a second time to generate the executable image file blunder.exe, now merging the .bss section into the .blunder section, the .sdata section into the .static section and the .xdata section into the .xcept section, again using the undocumented /TEST linker option, then enumerate its headers and give the 3 sections their original names back:

    LINK.EXE /LINK /BREPRO /ENTRY:mainCRTStartup /MERGE:.bss=.blunder /MERGE:.sdata=.static /MERGE:.xdata=.xcept /NODEFAULTLIB /SUBSYSTEM:CONSOLE /TEST blunder.obj
    LINK.EXE /DUMP /HEADERS blunder.exe
    LINK.EXE /EDIT /SECTION:.blunder=.bss /SECTION:.static=.sdata /SECTION:.xcept=.xdata blunder.exe
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    LINK : file alignment: 512, section alignment: 4096
    LINK : section '.bss' (C0000080) merged into '.blunder' (C0000080)
    LINK : section '.sdata' (C0000040) merged into '.static' (C0000040)
    LINK : section '.xdata' (40000040) merged into '.xcept' (40000040)
    LINK : warning LNK4253: section '.sdata' not merged into '.data';  already merged into '.static'
    LINK : warning LNK4253: section '.xdata' not merged into '.rdata';  already merged into '.xcept'
    
    Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    
    Dump of file blunder.exe
    
    PE signature found
    
    File Type: EXECUTABLE IMAGE
    
    FILE HEADER VALUES
                 14C machine (x86)
                   5 number of sections
            FFFFFFFF time date stamp Sun Feb 07 07:28:15 2106
                   0 file pointer to symbol table
                   0 number of symbols
                  E0 size of optional header
                 102 characteristics
                       Executable
                       32 bit word machine
    
    OPTIONAL HEADER VALUES
                 10B magic # (PE32)
               10.00 linker version
                 200 size of code
                 600 size of initialized data
                 200 size of uninitialized data
                1000 entry point (00401000)
                1000 base of code
                2000 base of data
              400000 image base (00400000 to 00405FFF)
                1000 section alignment
                 200 file alignment
                5.01 operating system version
                0.00 image version
                5.01 subsystem version
                   0 Win32 version
                6000 size of image
                 400 size of headers
                   0 checksum
                   3 subsystem (Windows CUI)
                8540 DLL characteristics
                       Dynamic base
                       NX compatible
                       No structured exception handler
                       Terminal Server Aware
              100000 size of stack reserve
                1000 size of stack commit
              100000 size of heap reserve
                1000 size of heap commit
                   0 loader flags
                  10 number of directories
                   0 [       0] RVA [size] of Export Directory
                   0 [       0] RVA [size] of Import Directory
                   0 [       0] RVA [size] of Resource Directory
                   0 [       0] RVA [size] of Exception Directory
                   0 [       0] RVA [size] of Certificates Directory
                5000 [      14] RVA [size] of Base Relocation Directory
                   0 [       0] RVA [size] of Debug Directory
                   0 [       0] RVA [size] of Architecture Directory
                   0 [       0] RVA [size] of Global Pointer Directory
                   0 [       0] RVA [size] of Thread Storage Directory
                   0 [       0] RVA [size] of Load Configuration Directory
                   0 [       0] RVA [size] of Bound Import Directory
                   0 [       0] RVA [size] of Import Address Table Directory
                   0 [       0] RVA [size] of Delay Import Directory
                   0 [       0] RVA [size] of COM Descriptor Directory
                   0 [       0] RVA [size] of Reserved Directory
    
    
    SECTION HEADER #1
       .text name
          20 virtual size
        1000 virtual address (00401000 to 0040101F)
         200 size of raw data
         400 file pointer to raw data (00000400 to 000005FF)
           0 file pointer to relocation table
           0 file pointer to line numbers
           0 number of relocations
           0 number of line numbers
    60000020 flags
             Code
             Execute Read
    
    SECTION HEADER #2
    .blunder name
           4 virtual size
        2000 virtual address (00402000 to 00402003)
           0 size of raw data
           0 file pointer to raw data
           0 file pointer to relocation table
           0 file pointer to line numbers
           0 number of relocations
           0 number of line numbers
    C0000080 flags
             Uninitialized Data
             Read Write
    
    SECTION HEADER #3
      .rdata name
           4 virtual size
        3000 virtual address (00403000 to 00403003)
         200 size of raw data
         600 file pointer to raw data (00000600 to 000007FF)
           0 file pointer to relocation table
           0 file pointer to line numbers
           0 number of relocations
           0 number of line numbers
    40000040 flags
             Initialized Data
             Read Only
    
    SECTION HEADER #4
       .data name
           4 virtual size
        4000 virtual address (00404000 to 00404003)
         200 size of raw data
         800 file pointer to raw data (00000800 to 000009FF)
           0 file pointer to relocation table
           0 file pointer to line numbers
           0 number of relocations
           0 number of line numbers
    C0000040 flags
             Initialized Data
             Read Write
    
    SECTION HEADER #5
      .reloc name
          32 virtual size
        5000 virtual address (00405000 to 00405031)
         200 size of raw data
         A00 file pointer to raw data (00000A00 to 00000BFF)
           0 file pointer to relocation table
           0 file pointer to line numbers
           0 number of relocations
           0 number of line numbers
    42000040 flags
             Initialized Data
             Discardable
             Read Only
    
      Summary
    
            1000 .blunder
            1000 .data
            1000 .rdata
            1000 .reloc
            1000 .text
    
    Microsoft (R) COFF/PE Editor Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    
    blunder.exe : warning LNK4039: section '.xcept' specified with /SECTION option does not exist
    blunder.exe : warning LNK4039: section '.static' specified with /SECTION option does not exist
    Oops: the linker places the (writable) .bss alias .blunder section not last, i.e. not after the also writable .data section, but between the read-only .text and .rdata sections!

    OOPS: the linker prints bogus informational messages and even warnings LNK4253 for sections which exist neither in its input files nor the output file!

  5. Link the object file blunder.obj created in step 1. a third time to generate the executable image file blunder.exe, again merging the .bss section into the .blunder section, now using the undocumented /LAST linker option, then enumerate its headers:

    LINK.EXE /LINK /BREPRO /ENTRY:mainCRTStartup /LAST:.blunder /MERGE:.bss=.blunder /NODEFAULTLIB /SUBSYSTEM:CONSOLE /TEST blunder.obj
    LINK.EXE /DUMP /HEADERS blunder.exe
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    LINK : file alignment: 512, section alignment: 4096
    LINK : section '.bss' (C0000080) merged into '.blunder' (C0000080)
    LINK : section '.sdata' (C0000040) merged into '.data' (C0000040)
    LINK : section '.xdata' (40000040) merged into '.rdata' (40000040)
    
    Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    
    Dump of file blunder.exe
    
    PE signature found
    
    File Type: EXECUTABLE IMAGE
    
    FILE HEADER VALUES
                 14C machine (x86)
                   5 number of sections
            FFFFFFFF time date stamp Sun Feb 07 07:28:15 2106
                   0 file pointer to symbol table
                   0 number of symbols
                  E0 size of optional header
                 102 characteristics
                       Executable
                       32 bit word machine
    
    OPTIONAL HEADER VALUES
                 10B magic # (PE32)
               10.00 linker version
                 200 size of code
                 600 size of initialized data
                 200 size of uninitialized data
                1000 entry point (00401000)
                1000 base of code
                2000 base of data
              400000 image base (00400000 to 00405FFF)
                1000 section alignment
                 200 file alignment
                5.01 operating system version
                0.00 image version
                5.01 subsystem version
                   0 Win32 version
                6000 size of image
                 400 size of headers
                   0 checksum
                   3 subsystem (Windows CUI)
                8540 DLL characteristics
                       Dynamic base
                       NX compatible
                       No structured exception handler
                       Terminal Server Aware
              100000 size of stack reserve
                1000 size of stack commit
              100000 size of heap reserve
                1000 size of heap commit
                   0 loader flags
                  10 number of directories
                   0 [       0] RVA [size] of Export Directory
                   0 [       0] RVA [size] of Import Directory
                   0 [       0] RVA [size] of Resource Directory
                   0 [       0] RVA [size] of Exception Directory
                   0 [       0] RVA [size] of Certificates Directory
                5000 [      14] RVA [size] of Base Relocation Directory
                   0 [       0] RVA [size] of Debug Directory
                   0 [       0] RVA [size] of Architecture Directory
                   0 [       0] RVA [size] of Global Pointer Directory
                   0 [       0] RVA [size] of Thread Storage Directory
                   0 [       0] RVA [size] of Load Configuration Directory
                   0 [       0] RVA [size] of Bound Import Directory
                   0 [       0] RVA [size] of Import Address Table Directory
                   0 [       0] RVA [size] of Delay Import Directory
                   0 [       0] RVA [size] of COM Descriptor Directory
                   0 [       0] RVA [size] of Reserved Directory
    
    
    SECTION HEADER #1
       .text name
          20 virtual size
        1000 virtual address (00401000 to 0040101F)
         200 size of raw data
         400 file pointer to raw data (00000400 to 000005FF)
           0 file pointer to relocation table
           0 file pointer to line numbers
           0 number of relocations
           0 number of line numbers
    60000020 flags
             Code
             Execute Read
    
    SECTION HEADER #2
      .rdata name
           4 virtual size
        2000 virtual address (00402000 to 00402003)
         200 size of raw data
         600 file pointer to raw data (00000600 to 000007FF)
           0 file pointer to relocation table
           0 file pointer to line numbers
           0 number of relocations
           0 number of line numbers
    40000040 flags
             Initialized Data
             Read Only
    
    SECTION HEADER #3
       .data name
           4 virtual size
        3000 virtual address (00403000 to 00403003)
         200 size of raw data
         800 file pointer to raw data (00000800 to 000009FF)
           0 file pointer to relocation table
           0 file pointer to line numbers
           0 number of relocations
           0 number of line numbers
    C0000040 flags
             Initialized Data
             Read Write
    
    SECTION HEADER #4
    .blunder name
           4 virtual size
        4000 virtual address (00404000 to 00404003)
           0 size of raw data
           0 file pointer to raw data
           0 file pointer to relocation table
           0 file pointer to line numbers
           0 number of relocations
           0 number of line numbers
    C0000080 flags
             Uninitialized Data
             Read Write
    
    SECTION HEADER #5
      .reloc name
          32 virtual size
        5000 virtual address (00405000 to 00405031)
         200 size of raw data
         A00 file pointer to raw data (00000A00 to 00000BFF)
           0 file pointer to relocation table
           0 file pointer to line numbers
           0 number of relocations
           0 number of line numbers
    42000040 flags
             Initialized Data
             Discardable
             Read Only
    
      Summary
    
            1000 .blunder
            1000 .data
            1000 .rdata
            1000 .reloc
            1000 .text
    Note: the undocumented /LAST:‹section› linker option places the specified section last, after the .text, .rdata and .data sections created by the Visual C compiler, but before the .reloc section generated by the linker.

Blunder № 25

Demonstration

Perform the following 4 simple steps to show the blunder.
  1. Create the text file blunder.c with the following content in an arbitrary, preferable empty directory:

    // Copyright © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    
    VOID	mainCRTStartup(VOID)
    {
    	ExitProcess('VOID');
    }
  2. Compile the source file blunder.c created in step 1. to generate the object file blunder.obj, then enumerate the names and sizes of its COFF sections:

    CL.EXE /c /W4 /Zl blunder.c
    LINK.EXE /DUMP blunder.obj
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.c
    
    Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    
    Dump of file blunder.obj
    
    File Type: COFF OBJECT
    
      Summary
    
              74 .debug$S
               3 .drectve
              10 .text
  3. LINK.EXE /LINK /ENTRY:mainCRTStartup /EXPORT:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /TEST blunder.obj kernel32.lib
    LINK.EXE /DUMP /IMPORTS blunder.exe
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    LINK : file alignment: 512, section alignment: 4096
    LINK : warning LNK4216: Exported entry point _mainCRTStartup
       Creating library blunder.lib and object blunder.exp
    LINK : section '.sdata' (C0000040) merged into '.data' (C0000040)
    LINK : section '.xdata' (40000040) merged into '.rdata' (40000040)
    
    Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    
    Dump of file blunder.exe
    
    File Type: EXECUTABLE IMAGE
    
      Section contains the following imports:
    
        KERNEL32.dll
                    402000 Import Address Table
                    402030 Import Name Table
                         0 time date stamp
                         0 Index of first forwarder reference
    
                      119 ExitProcess
    
      Summary
    
            1000 .rdata
            1000 .reloc
            1000 .text
    Oops: the linker fails to print messages indicating that the .edata and .idata sections it generates itself are both merged into the .rdata section!
  4. LINK.EXE /LINK /ENTRY:mainCRTStartup /EXPORT:mainCRTStartup /NODEFAULTLIB /NOOPTIDATA /SUBSYSTEM:CONSOLE /TEST blunder.obj kernel32.lib
    LINK.EXE /DUMP /IMPORTS blunder.exe
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    LINK : file alignment: 512, section alignment: 4096
    LINK : warning LNK4216: Exported entry point _mainCRTStartup
       Creating library blunder.lib and object blunder.exp
    LINK : section '.sdata' (C0000040) merged into '.data' (C0000040)
    LINK : section '.xdata' (40000040) merged into '.rdata' (40000040)
    
    Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    
    Dump of file blunder.exe
    
    File Type: EXECUTABLE IMAGE
    
      Section contains the following imports:
    
        KERNEL32.dll
                    403030 Import Address Table
                    403028 Import Name Table
                         0 time date stamp
                         0 Index of first forwarder reference
    
                      119 ExitProcess
    
      Summary
    
            1000 .idata
            1000 .rdata
            1000 .reloc
            1000 .text
    Note: with the undocumented /NOOPTIDATA linker option the .idata section is not merged into the .rdata section!

    Note: there exists but no corresponding /NOOPTEDATA linker option to disable merging of the .edata section into the .rdata section – use the /MERGE:.edata=.export linker option to generate a separate .export section for the Export Directory instead.

Blunder № 26

Demonstration

Perform the following 4 (plus 1 optional) simple steps to show a quirk and a bug in the Microsoft® Incremental Linker Link.exe.
  1. Create the text file blunder.c with the following content in an arbitrary, preferable empty directory:

    // Copyleft © 2001-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    int	main(void)
    {}
  2. Compile and link the source file blunder.c created in step 1.:

    SET CL=/W4 /X /Zl
    SET LINK=/ALTERNATENAME:_wmainCRTStartup=_main /BREPRO /FIXED /MANIFEST /MAP /NODEFAULTLIB /RELEASE
    CL.EXE blunder.c
    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.
    
    blunder.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ALTERNATENAME:_mainCRTStartup=_main /BREPRO /FIXED /MAP /MANIFEST /NODEFAULTLIB /RELEASE
    /out:blunder.exe
    blunder.obj
  3. Display the Application Manifest blunder.exe.manifest created in step 2. to show the quirk:

    TYPE blunder.exe.manifest
    <?xml version='1.0' encoding='UTF-8' standalone='yes'?>
    <assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
      <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
        <security>
          <requestedPrivileges>
            <requestedExecutionLevel level='asInvoker' uiAccess='false' />
          </requestedPrivileges>
        </security>
      </trustInfo>
    </assembly>
    Oops: I suppose there must be a really compelling reason to mix single and double quotes here!
  4. Display the linker map file blunder.map and the headers of the console application blunder.exe built in step 2. to show the bug:

    TYPE blunder.map
    LINK.EXE /DUMP /HEADERS blunder.exe
     blunder
    
     Timestamp is ffffffff (Sun Feb 07 07:28:15 2106)
    
     Preferred load address is 00400000
    
     Start         Length     Name                   Class
     0001:00000000 00000007H .text                   CODE
    
      Address         Publics by Value              Rva+Base       Lib:Object
    
     0000:00000000       ___safe_se_handler_count   00000000     <absolute>
     0000:00000000       ___safe_se_handler_table   00000000     <absolute>
     0001:00000000       _mainCRTStartup            00401000 f   blunder.obj
     0001:00000000       _main                      00401000 f   blunder.obj
    
     entry point at        0001:00000000
    
     Static symbols
    
    Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    
    Dump of file blunder.exe
    
    PE signature found
    
    File Type: EXECUTABLE IMAGE
    
    FILE HEADER VALUES
                 14C machine (x86)
                   1 number of sections
            FFFFFFFF time date stamp Sun Feb 07 07:28:15 2106
                   0 file pointer to symbol table
                   0 number of symbols
                  E0 size of optional header
                 103 characteristics
                       Relocations stripped
                       Executable
                       32 bit word machine
    
    OPTIONAL HEADER VALUES
                 10B magic # (PE32)
               10.00 linker version
                 200 size of code
                   0 size of initialized data
                   0 size of uninitialized data
                   8 entry point (00400008)
                1000 base of code
                2000 base of data
              400000 image base (00400000 to 00401FFF)
    […]
    OUCH: while the linker map shows the entry point as expected at the address 0x00401000 of the main() function, the header dump shows it but inside the non-executable DOS header, i.e. the console application blunder.exe will crash upon start with an execute access violation!
  5. (Optional) If you have the Debugging Tools for Windows installed, execute the console application blunder.exe built in step 2. under the debugger:

    CDB.EXE /C g;q /G /g .\blunder.exe
    Note: if necessary, see the MSDN articles Debugging Using CDB and NTSD and CDB Command-Line Options for an introduction.
    Microsoft (R) Windows Debugger Version 6.11.0001.404 X86
    Copyright (c) Microsoft Corporation. All rights reserved.
    
    CommandLine: .\blunder.exe
    Symbol search path is: srv*
    Executable search path is: 
    ModLoad: 00400000 00402000   image00400000
    ModLoad: 779c0000 77b40000   ntdll.dll
    ModLoad: 75610000 75720000   C:\Windows\syswow64\kernel32.dll
    ModLoad: 752d0000 75317000   C:\Windows\syswow64\KERNELBASE.dll
    ModLoad: 75540000 755e1000   C:\Windows\syswow64\ADVAPI32.DLL
    ModLoad: 757a0000 7584c000   C:\Windows\syswow64\msvcrt.dll
    ModLoad: 77420000 77439000   C:\Windows\SysWOW64\sechost.dll
    ModLoad: 77300000 773f0000   C:\Windows\syswow64\RPCRT4.dll
    ModLoad: 750e0000 75140000   C:\Windows\syswow64\SspiCli.dll
    ModLoad: 750d0000 750dc000   C:\Windows\syswow64\CRYPTBASE.dll
    (a34.149c): Access violation - code c0000005 (first chance)
    First chance exceptions are reported before any exception handling.
    This exception may be expected and handled.
    eax=7562342b ebx=7efde000 ecx=00000000 edx=00400008 esi=00000000 edi=00000000
    eip=00400008 esp=000dff8c ebp=000dff94 iopl=0         nv up ei pl zr na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
    *** ERROR: Module load completed but symbols could not be loaded for image00400000
    image00400000+0x8:
    00400008 0400            add     al,0
    0:000:x86> cdb: Reading initial command 'g;q'
    (a34.149c): Access violation - code c0000005 (!!! second chance !!!)
    quit:
Note: a repetition of this demonstration in the 64-bit development and execution environments is left as an exercise to the reader.

Blunder № 27

The specification of the PE Format states under the heading The Load Configuration Structure (Image Only):
The load configuration structure (IMAGE_LOAD_CONFIG_DIRECTORY) was formerly used in very limited cases in the Windows NT operating system itself to describe various features too difficult or too large to describe in the file header or optional header of the image. Current versions of the Microsoft linker and Windows XP and later versions of Windows use a new version of this structure for 32-bit x86-based systems that include reserved SEH technology.
[…]
The Microsoft linker automatically provides a default load configuration structure to include the reserved SEH data. If the user code already provides a load configuration structure, it must include the new reserved SEH fields. Otherwise, the linker cannot include the reserved SEH data and the image is not marked as containing reserved SEH.
OUCH¹: the highlighted statement is but wrongLINK.exe neither provides an IMAGE_LOAD_CONFIG_DIRECTORY structure nor reports its omission with an error message!

The documentation for the /SAFESEH linker option provides but the correct information:

If you link with /NODEFAULTLIB and you want a table of safe exception handlers, you need to supply a load config struct (…) that contains all the entries defined for Visual C++. For example:
#include <windows.h>
extern DWORD_PTR __security_cookie;  /* /GS security cookie */

/*
* The following two names are automatically created by the linker for any
* image that has the safe exception table present.
*/

extern PVOID __safe_se_handler_table[]; /* base of safe handler entry table */
extern BYTE  __safe_se_handler_count;  /* absolute symbol whose address is
                                           the count of table entries */

const IMAGE_LOAD_CONFIG_DIRECTORY32 _load_config_used = {
    sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32),
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    &__security_cookie,
    __safe_se_handler_table,
    (DWORD)(DWORD_PTR) &__safe_se_handler_count
};
The specification of the PE Format continues with the following disinformation:
Load Configuration Layout

The load configuration structure has the following layout for 32-bit and 64-bit PE files:

Offset Size Field Description
0 4 Characteristics Flags that indicate attributes of the file, currently unused.
54/78 2 Reserved Must be zero.
OUCH²: the documentation for the IMAGE_LOAD_CONFIG_DIRECTORY structure but states that the field at offset 0 stores the size of the structure, and the field at offset 54 (for 32-bit images) or 78 (for 64-bit images) stores the /DEPENDENTLOADFLAG!

Blunder № 28

The specification of the PE Format states under the heading The .tls section:
The .tls section provides direct PE and COFF support for static thread local storage (TLS). […] a static TLS variable can be defined as follows, without using the Windows API:

__declspec (thread) int tlsFlag = 1;

To support this programming construct, the PE and COFF .tls section specifies the following information: initialization data, callback routines for per-thread initialization and termination, and the TLS index, which are explained in the following discussion.

Note

Statically declared TLS data objects can be used only in statically loaded image files. This fact makes it unreliable to use static TLS data in a DLL unless you know that the DLL, or anything statically linked with it, will never be loaded dynamically with the LoadLibrary API function.

Executable code accesses a static TLS data object through the following steps:

  1. At link time, the linker sets the Address of Index field of the TLS directory. This field points to a location where the program expects to receive the TLS index.

    The Microsoft run-time library facilitates this process by defining a memory image of the TLS directory and giving it the special name "__tls_used" (Intel x86 platforms) or "_tls_used" (other platforms). The linker looks for this memory image and uses the data there to create the TLS directory. Other compilers that support TLS and work with the Microsoft linker must use this same technique.

  2. When a thread is created, the loader communicates the address of the thread's TLS array by placing the address of the thread environment block (TEB) in the FS register. A pointer to the TLS array is at the offset of 0x2C from the beginning of TEB. This behavior is Intel x86-specific.

  3. The loader assigns the value of the TLS index to the place that was indicated by the Address of Index field.

  4. The executable code retrieves the TLS index and also the location of the TLS array.

  5. The code uses the TLS index and the TLS array location (multiplying the index by 4 and using it as an offset to the array) to get the address of the TLS data area for the given program and module. Each thread has its own TLS data area, but this is transparent to the program, which does not need to know how data is allocated for individual threads.

  6. An individual TLS data object is accessed as some fixed offset into the TLS data area.

Ouch: even the very first (highlighted) sentence is wrong – the IMAGE_TLS_DIRECTORY provides the TLS support.

Note: the .tls section is required only when TLS data is initialised, it is not needed when data is just declared.

Ouch: the initial note is but obsolete and wrongWindows Vista and later versions of Windows NT support static TLS data in dynamically loaded DLLs!

Note: the multiplier 4 is of course only correct for 32-bit platforms – 64-bit platforms require the multiplier 8.

The documentation misses the following part for the x64 alias AMD64 processor architecture (and corresponding parts for other processor architectures as well):

  1. When a thread is created, the loader communicates the address of the thread's TLS array by placing the address of the thread environment block (TEB) in the GS register. A pointer to the TLS array is at the offset of 0x58 from the beginning of the TEB. This behavior is Intel x64-specific.

Note: on the i386 alias x86 platform, the Visual C compiler references (the absolute address of) the external symbol __tls_array despite the fixed value of this offset.

The specification of the PE Format continues:

The TLS directory has the following format:

Offset (PE32/PE32+) Size (PE32/PE32+) Field Description
0 4/8 Raw Data Start VA The starting address of the TLS template. The template is a block of data that is used to initialize TLS data. The system copies all of this data each time a thread is created, so it must not be corrupted. Note that this address is not an RVA; it is an address for which there should be a base relocation in the .reloc section.
4/8 4/8 Raw Data End VA The address of the last byte of the TLS, except for the zero fill. As with the Raw Data Start VA field, this is a VA, not an RVA.
8/16 4/8 Address of Index The location to receive the TLS index, which the loader assigns. This location is in an ordinary data section, so it can be given a symbolic name that is accessible to the program.
12/24 4/8 Address of Callbacks The pointer to an array of TLS callback functions. The array is null-terminated, so if no callback function is supported, this field points to 4 bytes set to zero. For information about the prototype for these functions, see TLS Callback Functions.
16/32 4 Size of Zero Fill The size in bytes of the template, beyond the initialized data delimited by the Raw Data Start VA and Raw Data End VA fields. The total template size should be the same as the total size of TLS data in the image file. The zero fill is the amount of data that comes after the initialized nonzero data.
20/36 4 Characteristics The four bits [23:20] describe alignment info. Possible values are those defined as IMAGE_SCN_ALIGN_*, which are also used to describe alignment of section in object files. The other 28 bits are reserved for future use.
OOPS: the Raw Data End VA field contains the address of the first byte after the TLS template!

OUCH: the Size of Zero Fill field is not supported at all!

Note: if the size of the initialised data of the .tls section in the image file is less than the section size, the module loader fills the additional uninitialised data with zeroes, i.e. the Size of Zero Fill field is superfluous.

Caveat: the documentation lacks the information that the Visual C compiler puts all data for the TLS template in COFF sections .tls$‹suffix› – which it declares but writable instead of read-only, i.e. it fails to protect the template data against corruption, an easily avoidable safety hazard!

Work-Around

Use the /SECTION:.tls,r linker option to set the .tls section read-only.

Blunder № 29

The MSDN article Inline Assembler states:
Inline assembly is not supported on the Itanium and x64 processors.
The documentation for the _emit Pseudoinstruction but states:
[…] if _emit generates an instruction that modifies the rax register, the compiler does not know that rax has changed. […]
OUCH: the RAX register exists only on the AMD64 platform, where inline assembly via the __asm keyword is but not supported!

Blunder № 30

The documentation for the operator HIGH32 of MASM states:
HIGH32 expression
Returns the low 32 bits of expression. MASM expressions are 64-bit values.
The documentation for the operator LOW32 of MASM states:
LOW32 expression
Returns the low 32 bits of expression. MASM expressions are 64-bit values.

Falsification

Perform the following 2 simple steps to prove the highlighted statements of the documentation cited above wrong.
  1. Create the text file blunder.asm with the following content in an arbitrary, preferable empty directory:

    ; Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    	.model	flat, C
    	.code
    
    	mov	eax, LOW32 0123456789ABCDEFh
    	mov	edx, HIGH32 0123456789ABCDEFh
    
    	.const
    
    	qword	0123456789ABCDEFh
    	oword	0123456789ABCDEF0123456789ABCDEFh
    
    	end
  2. Assemble the source file blunder.asm created in step 1.:

    SET ML=/c /W3 /X
    ML.EXE /FoNUL: blunder.asm
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    Microsoft (R) Macro Assembler Version 10.00.40219.01
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
     Assembling: blunder.asm
    blunder.asm(4) : error A2084:constant value too large
    blunder.asm(5) : error A2084:constant value too large
    OUCH: contrary to both highlighted statements of the documentation cited above, (constant) expressions are but 32-bit values!

Blunder № 31

Falsification

Perform the following 6 (plus 2 optional) simple steps to show that even Microsoft’s kernel developers can’t distinguish 0 from 231 or 263.
  1. Create the text file blunder.asm with the following content in an arbitrary, preferably empty directory:

    ; Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    	.386
    	.model	flat, C
    	.code
    
    blunder	proc	public
    
    	mov	ecx, 1 shl 31	; ecx = divisor = 0x80000000,
    				; edx:eax = arbitrary dividend
    	div	ecx		; eax = arbitrary quotient,
    				; edx = arbitrary remainder
    				;     < divisor
    	not	edx		; edx:eax = dividend
    				;         = divisor << 32 | arbitrary quotient
    				;         > divisor << 32
    	div	ecx		; raise #DE (divide error exception) via
    				;  quotient overflow
    ;;	ret
    
    blunder	endp
    	end	blunder		; writes "/ENTRY:blunder" to '.drectve' section
  2. Assemble and link the source file blunder.asm created in step 1.:

    SET ML=/W3 /X
    SET LINK=/NODEFAULTLIB /RELEASE /SUBSYSTEM:CONSOLE
    ML.EXE blunder.asm
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    Microsoft (R) Macro Assembler Version 10.00.40219.01
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
     Assembling: blunder.asm
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /NODEFAULTLIB /RELEASE /SUBSYSTEM:CONSOLE
    /OUT:blunder.exe
    blunder.obj
  3. Execute the console application blunder.exe built in step 2. to show that 231 is equal 0:

    VER
    .\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    Microsoft Windows [Version 6.1.7601]
    
    0xc0000094 (NT: 0xc0000094 STATUS_INTEGER_DIVIDE_BY_ZERO) -- 3221225620 (-1073741676)
    Error message text: {EXCEPTION}
    Integer division by zero
    CertUtil: -error command completed successfully.
    OUCH: (at least) for the divisor 231, which most obviously differs from 0, Windows’ kernel maps the processor’s #DE to the wrong NTSTATUS 0xC0000094 alias STATUS_INTEGER_DIVIDE_BY_ZERO instead to the correct 0xC0000095 alias STATUS_INTEGER_OVERFLOW!
  4. (Optional) If you have the Debugging Tools for Windows installed, execute the console application blunder.exe built in step 2. under the debugger:

    CDB.EXE /C g;q /G /g .\blunder.exe
    Note: if necessary, see the MSDN articles Debugging Using CDB and NTSD and CDB Command-Line Options for an introduction.
    Microsoft (R) Windows Debugger Version 6.11.0001.404 X86
    Copyright (c) Microsoft Corporation. All rights reserved.
    
    CommandLine: .\blunder.exe
    Symbol search path is: srv*
    Executable search path is: 
    ModLoad: 00ee0000 00ee2000   image00ee0000
    ModLoad: 779c0000 77b40000   ntdll.dll
    ModLoad: 774d0000 775e0000   C:\Windows\syswow64\kernel32.dll
    ModLoad: 76080000 760c7000   C:\Windows\syswow64\KERNELBASE.dll
    ModLoad: 75790000 75831000   C:\Windows\syswow64\ADVAPI32.DLL
    ModLoad: 75fd0000 7607c000   C:\Windows\syswow64\msvcrt.dll
    ModLoad: 75e70000 75e89000   C:\Windows\SysWOW64\sechost.dll
    ModLoad: 75ed0000 75fc0000   C:\Windows\syswow64\RPCRT4.dll
    ModLoad: 750e0000 75140000   C:\Windows\syswow64\SspiCli.dll
    ModLoad: 750d0000 750dc000   C:\Windows\syswow64\CRYPTBASE.dll
    (22f0.26a0): Integer divide-by-zero - code c0000094 (first chance)
    First chance exceptions are reported before any exception handling.
    This exception may be expected and handled.
    eax=01dc2000 ebx=7efde000 ecx=80000000 edx=88b1cbd4 esi=00000000 edi=00000000
    eip=00ee1009 esp=001bf898 ebp=001bf8a0 iopl=0         nv up ei pl zr na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
    *** ERROR: Module load completed but symbols could not be loaded for image00ee0000
    image00ee0000+0x1009:
    00ee1009 f7f1            div     eax,ecx
    0:000:x86> cdb: Reading initial command 'g;q'
    (22f0.26a0): Integer divide-by-zero - code c0000094 (!!! second chance !!!)
    quit:
    Oops: also notice the bunch of not explicitly referenced DLLs loaded with the debuggee every application!
  5. Overwrite the text file blunder.asm created in step 1. with the following content:

    ; Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    	.code
    
    blunder	proc	public
    
    	mov	rcx, 1 shl 63	; rcx = divisor = 0x8000000000000000,
    				; rdx:rax = arbitrary dividend
    	div	rcx		; rax = arbitrary quotient,
    				; rdx = arbitrary remainder
    				;     < divisor
    	not	rdx		; rdx:rax = dividend
    				;         = divisor << 64 | arbitrary quotient
    				;         > divisor << 64
    	div	rcx		; raise #DE (divide error exception) via
    				;  quotient overflow
    ;;	ret
    
    blunder	endp
    	end
  6. Assemble and link the source file blunder.asm modified in step 5.:

    SET ML=/W3 /X
    SET LINK=/ENTRY:blunder /NODEFAULTLIB /RELEASE /SUBSYSTEM:CONSOLE
    ML64.EXE blunder.asm
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    Microsoft (R) Macro Assembler (x64) Version 10.00.40219.01
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
     Assembling: blunder.asm
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:blunder /NODEFAULTLIB /RELEASE /SUBSYSTEM:CONSOLE
    /OUT:blunder.exe
    blunder.obj
  7. Execute the console application blunder.exe built in step 6. to show that 263 is equal 0:

    VER
    .\blunder.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    Microsoft Windows [Version 6.1.7601]
    
    0xc0000094 (NT: 0xc0000094 STATUS_INTEGER_DIVIDE_BY_ZERO) -- 3221225620 (-1073741676)
    Error message text: {EXCEPTION}
    Integer division by zero
    CertUtil: -error command completed successfully.
    OUCH: (at least) for the divisor 263, which most obviously differs from 0, Windows’ kernel maps the processor’s #DE to the wrong NTSTATUS 0xC0000094 alias STATUS_INTEGER_DIVIDE_BY_ZERO instead to the correct 0xC0000095 alias STATUS_INTEGER_OVERFLOW!
  8. (Optional) If you have the Debugging Tools for Windows installed, execute the console application blunder.exe built in step 6. under the debugger:

    CDB.EXE /C g;q /G /g .\blunder.exe
    Note: if necessary, see the MSDN articles Debugging Using CDB and NTSD and CDB Command-Line Options for an introduction.
    Microsoft (R) Windows Debugger Version 6.1.7601.17514 AMD64
    Copyright (c) Microsoft Corporation. All rights reserved.
    
    CommandLine: .\blunder.exe
    Symbol search path is: srv*
    Executable search path is: 
    ModLoad: 00000001`3ff10000 00000001`3ff12000   image00000001`3ff10000
    ModLoad: 00000000`77800000 00000000`7799f000   ntdll.dll
    ModLoad: 00000000`775e0000 00000000`776ff000   C:\Windows\system32\kernel32.dll
    ModLoad: 000007fe`fd510000 000007fe`fd577000   C:\Windows\system32\KERNELBASE.dll
    ModLoad: 000007fe`fd980000 000007fe`fda5b000   C:\Windows\system32\ADVAPI32.DLL
    ModLoad: 000007fe`fe1c0000 000007fe`fe25f000   C:\Windows\system32\msvcrt.dll
    ModLoad: 000007fe`fe260000 000007fe`fe27f000   C:\Windows\SYSTEM32\sechost.dll
    ModLoad: 000007fe`ff250000 000007fe`ff37c000   C:\Windows\system32\RPCRT4.dll
    (2274.3570): Integer divide-by-zero - code c0000094 (first chance)
    First chance exceptions are reported before any exception handling.
    This exception may be expected and handled.
    *** ERROR: Module load completed but symbols could not be loaded for image00000001`3ff10000
    image00000001_3ff10000+0x1010:
    00000001`3ff11010 48f7f1          div     rax,rcx
    0:000> cdb: Reading initial command 'g;q'
    (2274.3570): Integer divide-by-zero - code c0000094 (!!! second chance !!!)
    quit:
    Oops: again notice the slew of not explicitly referenced DLLs loaded with the debuggee every application!

Blunder № 32

Demonstration

Perform the following 7 simple steps to demonstrate the misbehaviour and shortcomings of the Resource Compiler RC.exe:
  1. Start the Command Processor Cmd.exe, then execute the Resource Compiler RC.exe with code page 1200 alias UTF-16LE set on the command line:

    RC.EXE /C 1200 /X NUL:
    Microsoft (R) Windows (R) Resource Compiler Version 6.1.7600.16385
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    fatal error RC1206: code page specified on command line not in registry
    OUCH¹: the Resource Compiler fails to accept or support the native encoding of Windows NT!
  2. Create the text file blunder.rc with the following content in an arbitrary, preferable empty directory:

    // Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #if 0
    #pragma code_page(1200) // UTF-16LE
    #endif
  3. Compile the source file blunder.rc created in step 2. with code page 20127 alias ASCII set on the command line:

    RC.EXE /C 20127 /X blunder.rc
    TYPE RCa?????
    Microsoft (R) Windows (R) Resource Compiler Version 6.1.7600.16385
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.rc(3) : fatal error RC4213: Codepage 1200 (Unicode) not allowed:  ignored
    
    RCa12345
    
    
      l i n e   1 " b l u n d e r . r c "
     # l i n e   1
     / /   C o p y l e f t   ®   2 0 0 4 - 2 0 2 4 ,   S t e f a n   K a n t h a k   < s t e f a n . k a n t h a k @ n e x g o . d e >
     # l i n e   3
     # i f   0
    OUCH²: contrary to the Visual C preprocessor, the preprocessor of the Resource Compiler evaluates its #pragma code_page() directive even when it is guarded by #if… #endif!

    OOPS¹: although the preprocessor of the Resource Compiler supports UTF-16LE encoded source files, it rejects this encoding!

    OOPS²: when it terminates with this error, the Resource Compiler fails to delete the intermediary UTF-16LE encoded preprocessor output file RCa‹5 decimal digits› – contrary to Microsoft’s own recommendation written without Byte Order Mark!

  4. Overwrite the text file blunder.rc created in step 2. with the following content:

    // Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    STRINGTABLE
    BEGIN
        0,   "[\0] NUL"
        1,   "[\a] Audible Alarm"
        2,   "[\b] Backspace"
        3,   "[\f] Form Feed"
        4,   "[\n] Line Feed"
        5,   "[\r] Carriage Return"
        6,   "[\t] Horizontal Tabulator"
        7,   "[\v] Vertical Tabulator"
    #ifndef BLUNDER
        8,   "[\"] Double Quote"
    #else
        8,   "[""] Double Quote"
    #endif
        9,   "[\'] Single Quote"
       10,   "[\?] Question Mark"
       11,   "[\\] Backslash"
       12,   "[\177] DEL"
       13,   "[\u20AC] Euro Sign"
       14,   "[\xFEFF] Byte Order Mark"
       15,   "[\x]"
    
       16,   L"[\0] NUL"
       17,   L"[\a] Audible Alarm"
       18,   L"[\b] Backspace"
       19,   L"[\f] Form Feed"
       20,   L"[\n] Line Feed"
       21,   L"[\r] Carriage Return"
       22,   L"[\t] Horizontal Tabulator"
       23,   L"[\v] Vertical Tabulator"
    #ifndef BLUNDER
       24,   L"[\"] Double Quote"
    #else
       24,   L"[""] Double Quote"
    #endif
       25,   L"[\'] Single Quote"
       26,   L"[\?] Question Mark"
       27,   L"[\\] Backslash"
       28,   L"[\177] DEL"
       29,   L"[\u20AC] Euro Sign"
       30,   L"[\xFEFF] Byte Order Mark"
       31,   L"[\x]"
    END
  5. Compile the source file blunder.rc modified in step 4.:

    RC.EXE /C 20127 /X blunder.rc
    Microsoft (R) Windows (R) Resource Compiler Version 6.1.7600.16385
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    blunder.rc.blunder.rc(14) : error RC2104 : undefined keyword or key name: ]
    OUCH³: the Resource Compiler fails to support the standard ANSI C89 escape sequence \" for the Double Quote!
  6. Compile the source file blunder.rc modified in step 4. a second time, now with the preprocessor macro BLUNDER defined:

    RC.EXE /C 20127 /D BLUNDER /X blunder.rc
    Microsoft (R) Windows (R) Resource Compiler Version 6.1.7600.16385
    Copyright (C) Microsoft Corporation.  All rights reserved.
    OOPS³: double quotes must be doubled inside strings!
  7. Dump the raw data of the resource file blunder.res generated in step 6.:

    CERTUTIL.EXE /DUMP blunder.res
      0000  ...
      0454
        0000  00 00 00 00 20 00 00 00  ff ff 00 00 ff ff 00 00   .... ...........
        0010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
        0020  08 02 00 00 20 00 00 00  ff ff 06 00 ff ff 01 00   .... ...........
        0030  00 00 00 00 30 10 09 04  00 00 00 00 00 00 00 00   ....0...........
        0040  07 00 5b 00 00 00 5d 00  20 00 4e 00 55 00 4c 00   ..[...]. .N.U.L.
        0050  11 00 5b 00 08 00 5d 00  20 00 41 00 75 00 64 00   ..[...]. .A.u.d.
        0060  69 00 62 00 6c 00 65 00  20 00 41 00 6c 00 61 00   i.b.l.e. .A.l.a.
        0070  72 00 6d 00 0e 00 5b 00  5c 00 62 00 5d 00 20 00   r.m...[.\.b.]. .
        0080  42 00 61 00 63 00 6b 00  73 00 70 00 61 00 63 00   B.a.c.k.s.p.a.c.
        0090  65 00 0e 00 5b 00 5c 00  66 00 5d 00 20 00 46 00   e...[.\.f.]. .F.
        00a0  6f 00 72 00 6d 00 20 00  46 00 65 00 65 00 64 00   o.r.m. .F.e.e.d.
        00b0  0d 00 5b 00 0a 00 5d 00  20 00 4c 00 69 00 6e 00   ..[...]. .L.i.n.
        00c0  65 00 20 00 46 00 65 00  65 00 64 00 13 00 5b 00   e. .F.e.e.d...[.
        00d0  0d 00 5d 00 20 00 43 00  61 00 72 00 72 00 69 00   ..]. .C.a.r.r.i.
        00e0  61 00 67 00 65 00 20 00  52 00 65 00 74 00 75 00   a.g.e. .R.e.t.u.
        00f0  72 00 6e 00 18 00 5b 00  09 00 5d 00 20 00 48 00   r.n...[...]. .H.
        0100  6f 00 72 00 69 00 7a 00  6f 00 6e 00 74 00 61 00   o.r.i.z.o.n.t.a.
        0110  6c 00 20 00 54 00 61 00  62 00 75 00 6c 00 61 00   l. .T.a.b.u.l.a.
        0120  74 00 6f 00 72 00 17 00  5b 00 5c 00 76 00 5d 00   t.o.r...[.\.v.].
        0130  20 00 56 00 65 00 72 00  74 00 69 00 63 00 61 00    .V.e.r.t.i.c.a.
        0140  6c 00 20 00 54 00 61 00  62 00 75 00 6c 00 61 00   l. .T.a.b.u.l.a.
        0150  74 00 6f 00 72 00 10 00  5b 00 22 00 5d 00 20 00   t.o.r...[.".]. .
        0160  44 00 6f 00 75 00 62 00  6c 00 65 00 20 00 51 00   D.o.u.b.l.e. .Q.
        0170  75 00 6f 00 74 00 65 00  11 00 5b 00 5c 00 27 00   u.o.t.e...[.\.'.
        0180  5d 00 20 00 53 00 69 00  6e 00 67 00 6c 00 65 00   ]. .S.i.n.g.l.e.
        0190  20 00 51 00 75 00 6f 00  74 00 65 00 12 00 5b 00    .Q.u.o.t.e...[.
        01a0  5c 00 3f 00 5d 00 20 00  51 00 75 00 65 00 73 00   \.?.]. .Q.u.e.s.
        01b0  74 00 69 00 6f 00 6e 00  20 00 4d 00 61 00 72 00   t.i.o.n. .M.a.r.
        01c0  6b 00 0d 00 5b 00 5c 00  5d 00 20 00 42 00 61 00   k...[.\.]. .B.a.
        01d0  63 00 6b 00 73 00 6c 00  61 00 73 00 68 00 07 00   c.k.s.l.a.s.h...
        01e0  5b 00 7f 00 5d 00 20 00  44 00 45 00 4c 00 12 00   [...]. .D.E.L...
        01f0  5b 00 5c 00 75 00 32 00  30 00 41 00 43 00 5d 00   [.\.u.2.0.A.C.].
        0200  20 00 45 00 75 00 72 00  6f 00 20 00 53 00 69 00    .E.u.r.o. .S.i.
        0210  67 00 6e 00 15 00 5b 00  fe 00 46 00 46 00 5d 00   g.n...[...F.F.].
        0220  20 00 42 00 79 00 74 00  65 00 20 00 4f 00 72 00    .B.y.t.e. .O.r.
        0230  64 00 65 00 72 00 20 00  4d 00 61 00 72 00 6b 00   d.e.r. .M.a.r.k.
        0240  03 00 5b 00 00 00 5d 00  ec 01 00 00 20 00 00 00   ..[...]..... ...
        0250  ff ff 06 00 ff ff 02 00  00 00 00 00 30 10 00 00   ............0...
        0260  00 00 00 00 00 00 00 00  07 00 5b 00 00 00 5d 00   ..........[...].
        0270  20 00 4e 00 55 00 4c 00  11 00 5b 00 08 00 5d 00    .N.U.L...[...].
        0280  20 00 41 00 75 00 64 00  69 00 62 00 6c 00 65 00    .A.u.d.i.b.l.e.
        0290  20 00 41 00 6c 00 61 00  72 00 6d 00 0c 00 5b 00    .A.l.a.r.m...[.
        02a0  5d 00 20 00 42 00 61 00  63 00 6b 00 73 00 70 00   ]. .B.a.c.k.s.p.
        02b0  61 00 63 00 65 00 0c 00  5b 00 5d 00 20 00 46 00   a.c.e...[.]. .F.
        02c0  6f 00 72 00 6d 00 20 00  46 00 65 00 65 00 64 00   o.r.m. .F.e.e.d.
        02d0  0d 00 5b 00 0a 00 5d 00  20 00 4c 00 69 00 6e 00   ..[...]. .L.i.n.
        02e0  65 00 20 00 46 00 65 00  65 00 64 00 13 00 5b 00   e. .F.e.e.d...[.
        02f0  0d 00 5d 00 20 00 43 00  61 00 72 00 72 00 69 00   ..]. .C.a.r.r.i.
        0300  61 00 67 00 65 00 20 00  52 00 65 00 74 00 75 00   a.g.e. .R.e.t.u.
        0310  72 00 6e 00 18 00 5b 00  09 00 5d 00 20 00 48 00   r.n...[...]. .H.
        0320  6f 00 72 00 69 00 7a 00  6f 00 6e 00 74 00 61 00   o.r.i.z.o.n.t.a.
        0330  6c 00 20 00 54 00 61 00  62 00 75 00 6c 00 61 00   l. .T.a.b.u.l.a.
        0340  74 00 6f 00 72 00 15 00  5b 00 5d 00 20 00 56 00   t.o.r...[.]. .V.
        0350  65 00 72 00 74 00 69 00  63 00 61 00 6c 00 20 00   e.r.t.i.c.a.l. .
        0360  54 00 61 00 62 00 75 00  6c 00 61 00 74 00 6f 00   T.a.b.u.l.a.t.o.
        0370  72 00 10 00 5b 00 22 00  5d 00 20 00 44 00 6f 00   r...[.".]. .D.o.
        0380  75 00 62 00 6c 00 65 00  20 00 51 00 75 00 6f 00   u.b.l.e. .Q.u.o.
        0390  74 00 65 00 0f 00 5b 00  5d 00 20 00 53 00 69 00   t.e...[.]. .S.i.
        03a0  6e 00 67 00 6c 00 65 00  20 00 51 00 75 00 6f 00   n.g.l.e. .Q.u.o.
        03b0  74 00 65 00 10 00 5b 00  5d 00 20 00 51 00 75 00   t.e...[.]. .Q.u.
        03c0  65 00 73 00 74 00 69 00  6f 00 6e 00 20 00 4d 00   e.s.t.i.o.n. .M.
        03d0  61 00 72 00 6b 00 0d 00  5b 00 5c 00 5d 00 20 00   a.r.k...[.\.]. .
        03e0  42 00 61 00 63 00 6b 00  73 00 6c 00 61 00 73 00   B.a.c.k.s.l.a.s.
        03f0  68 00 07 00 5b 00 7f 00  5d 00 20 00 44 00 45 00   h...[...]. .D.E.
        0400  4c 00 10 00 5b 00 32 00  30 00 41 00 43 00 5d 00   L...[.2.0.A.C.].
        0410  20 00 45 00 75 00 72 00  6f 00 20 00 53 00 69 00    .E.u.r.o. .S.i.
        0420  67 00 6e 00 13 00 5b 00  ff fe 5d 00 20 00 42 00   g.n...[...]. .B.
        0430  79 00 74 00 65 00 20 00  4f 00 72 00 64 00 65 00   y.t.e. .O.r.d.e.
        0440  72 00 20 00 4d 00 61 00  72 00 6b 00 03 00 5b 00   r. .M.a.r.k...[.
        0450  00 00 5d 00                                        ..].
    CertUtil: -dump command completed successfully.
    OUCH⁴: the Resource Compiler translates the standard ANSI C89 escape sequence \a for Audible Alarm to the wrong Unicode code point U+0008 instead of its correct code point U+0007!

    OUCH⁵: it fails to support the standard ANSI C89 escape sequences \b for Backspace, \f for Form Feed, \v for Vertical Tabulator, \' for the Single Quote, and \? for the Question Mark!

    OOPS⁴: it also fails to support the standard ANSI C99 escape sequence \u‹1 to 4 hexadecimal digits› for UTF-16 code points U+‹1 to 4 hexadecimal digits›.

    OUCH⁶: it accepts the incomplete and invalid escape sequence \x without any following hexadecimal digit(s) and treats it as \0!

    OOPS⁵: its (mis)behaviour for escape sequences in ANSI strings differs from the (mis)behaviour for escape sequences in Unicode strings!

Ryan Liptak’s extensive blog post Every bug/quirk of the Windows resource compiler (rc.exe), probably documents quite some more surprises lurking in the Resource Compiler RC.exe.

Blunder № 33

The specification of the PE Format states under the heading The .rsrc section:
Resources are indexed by a multiple-level binary-sorted tree structure. The general design can incorporate 2**31 levels. By convention, however, Windows uses three levels:
Type Name Language

[…]

Each Resource Data entry describes an actual unit of raw data in the Resource Data area. A Resource Data entry has the following format:

Offset Size Field Description
0 4 Data RVA The address of a unit of resource data in the Resource Data area.
4 4 Size The size, in bytes, of the resource data that is pointed to by the Data RVA field.
8 4 Codepage The code page that is used to decode code point values within the resource data. Typically, the code page would be the Unicode code page.
12 4 Reserved, must be 0.

Demonstration

Perform the following 7 simple steps to show the misbehaviour.
  1. Create the text file blunder.c with the following content in an arbitrary, preferable empty directory:

    // Copyright © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    
    __declspec(safebuffers)
    BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
    {
    	WCHAR	szOutput[1024];
    	DWORD	dwOutput;
    	DWORD	dwConsole;
    
    	va_list	vaInput;
    	va_start(vaInput, lpFormat);
    
    	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
    
    	va_end(vaInput);
    
    	if (dwOutput == 0UL)
    		return FALSE;
    
    	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
    		return FALSE;
    
    	return dwConsole == dwOutput;
    }
    
    const	LPCWSTR	szType[] = {NULL,
    		            L"CURSOR", L"BITMAP", L"ICON", L"MENU", L"DIALOG", L"STRINGTABLE",
    		            L"FONTDIR", L"FONT", L"ACCELERATOR", L"RCDATA", L"MESSAGETABLE", L"GROUP_CURSOR",
    		            L"MENUEX", L"GROUP_ICON", L"NAMETABLE", L"VERSION", L"DLGINCLUDE", L"DIALOGEX",
    		            L"PLUGPLAY", L"VXD", L"ANICURSOR", L"ANIICON", L"HTML", L"MANIFEST"};
    
    VOID	WINAPI	Resource(HANDLE                   hConsole,
    		         IMAGE_RESOURCE_DIRECTORY *lpRoot,
    		         IMAGE_RESOURCE_DIRECTORY *lpLevel,
    		         DWORD                    dwLevel)	// 0 = Type, 1 = Id, 2 = Language
    {
    	DWORD	dwEntry;
    
    	IMAGE_RESOURCE_DIRECTORY_ENTRY	*lpEntry;
    	IMAGE_RESOURCE_DIR_STRING_U	*lpUnicode;
    	IMAGE_RESOURCE_DATA_ENTRY	*lpData;
    
    	for (lpEntry = (IMAGE_RESOURCE_DIRECTORY_ENTRY *) (lpLevel + 1),
    	     dwEntry = 0UL;
    	     dwEntry < lpLevel->NumberOfNamedEntries + lpLevel->NumberOfIdEntries;
    	     dwEntry++)
    	{
    		if ((lpEntry[dwEntry].Name & IMAGE_RESOURCE_NAME_IS_STRING) == IMAGE_RESOURCE_NAME_IS_STRING)
    		{
    			lpUnicode = (IMAGE_RESOURCE_DIR_STRING_U *) ((BYTE *) lpRoot + (lpEntry[dwEntry].Name ^ IMAGE_RESOURCE_NAME_IS_STRING));
    
    			PrintConsole(hConsole,
    			             L"\t\t\t\tName   = %ls\n" + 2 - dwLevel,
    			             lpUnicode->NameString, lpUnicode->Length);
    		}
    		else if (dwLevel > 1UL)
    			PrintConsole(hConsole,
    			             L"\t\t\t\tLanguage = %hu\n",
    			             lpEntry[dwEntry].Id);
    		else if (dwLevel > 0UL)
    			PrintConsole(hConsole,
    			             L"\t\t\tId     = %hu\n",
    			             lpEntry[dwEntry].Id);
    		else
    			PrintConsole(hConsole,
    			             L"\t\tType   = %hu (%ls)\n",
    			             lpEntry[dwEntry].Id, szType[lpEntry[dwEntry].Id]);
    
    		PrintConsole(hConsole,
    		             L"\t\t\t\tOffset = 0x%08lX\n" + 2 - dwLevel,
    		             lpEntry[dwEntry].OffsetToData);
    
    		if ((lpEntry[dwEntry].OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY) != IMAGE_RESOURCE_DATA_IS_DIRECTORY)
    		{
    			lpData = (IMAGE_RESOURCE_DATA_ENTRY *) ((BYTE *) lpRoot + lpEntry[dwEntry].OffsetToData);
    
    			PrintConsole(hConsole,
    			             L"\t\t\t\t\tAddress   = 0x%08lX\n"
    			             L"\t\t\t\t\tSize      = %lu\n"
    			             L"\t\t\t\t\tCode Page = %lu\n"
    			             L"\t\t\t\t\tReserved  = 0x%08lX\n",
    			             lpData->OffsetToData, lpData->Size, lpData->CodePage, lpData->Reserved);
    		}
    		else
    			Resource(hConsole,
    			         lpRoot,
    			         (IMAGE_RESOURCE_DIRECTORY *) ((BYTE *) lpRoot + (lpEntry[dwEntry].OffsetToData ^ IMAGE_RESOURCE_DATA_IS_DIRECTORY)),
    			         dwLevel + 1UL);
    	}
    }
    
    extern	const	IMAGE_DOS_HEADER	__ImageBase;
    
    __declspec(noreturn)
    VOID	CDECL	wmainCRTStartup(VOID)
    {
    	IMAGE_RESOURCE_DIRECTORY	*lpResource;
    
    	IMAGE_NT_HEADERS	*lpPE = (IMAGE_NT_HEADERS *) ((BYTE *) &__ImageBase + __ImageBase.e_lfanew);
    
    	DWORD	dwError = ERROR_SUCCESS;
    	HANDLE	hConsole = GetStdHandle(STD_ERROR_HANDLE);
    
    	if (hConsole == INVALID_HANDLE_VALUE)
    		dwError = GetLastError();
    	else
    	{
    		if ((lpPE->Signature != IMAGE_NT_SIGNATURE)
    		 || (lpPE->OptionalHeader.NumberOfRvaAndSizes < IMAGE_DIRECTORY_ENTRY_RESOURCE)
    		 || (lpPE->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress == 0UL)
    		 || (lpPE->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size == 0UL))
    			dwError = ERROR_BAD_EXE_FORMAT;
    		else
    		{
    			lpResource = (IMAGE_RESOURCE_DIRECTORY *) ((BYTE *) &__ImageBase + lpPE->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
    
    			PrintConsole(hConsole,
    			             L"Resource Directory:\n"
    			             L"\tCharacteristics = 0x%08lX\n"
    			             L"\tTime/Date Stamp = 0x%08lX\n"
    			             L"\tVersion         = %hu.%hu\n"
    			             L"\tNamed Entries   = %hu\n"
    			             L"\tUnnamed Entries = %hu\n"
    			             L"\tEntries:\n",
    			             lpResource->Characteristics,
    			             lpResource->TimeDateStamp,
    			             lpResource->MajorVersion,
    			             lpResource->MinorVersion,
    			             lpResource->NumberOfNamedEntries,
    			             lpResource->NumberOfIdEntries);
    
    			Resource(hConsole, lpResource, lpResource, 0UL);
    		}
    
    		if (!CloseHandle(hConsole))
    			PrintConsole(hConsole,
    			             L"CloseHandle() returned error %lu\n",
    			             GetLastError());
    	}
    
    	ExitProcess(dwError);
    }
  2. Compile and link the source file blunder.c created in step 1.:

    SET CL=/W4 /Zl
    SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    CL.EXE blunder.c kernel32.lib user32.lib
    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.
    
    blunder.c
    blunder.c(51) : warning C4018: '<' : signed/unsigned mismatch
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:blunder.exe
    blunder.obj
    kernel32.lib
    user32.lib
  3. Create the text file blunder.xml with the following content in the current directory:

    <?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
    <assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1' />
  4. Embed the Application Manifest blunder.xml created in step 3. in the console application blunder.exe built in step 2.:

    MT.EXE /Manifest blunder.xml /OutputResource:blunder.exe
    Microsoft (R) Manifest Tool version 6.1.7716.0
    Copyright (c) Microsoft Corporation 2009.
    All rights reserved.
  5. Execute the console application blunder.exe modified in step 4. and evaluate its exit code:

    .\blunder.exe
    ECHO %ERRORLEVEL%
    Resource Directory:
    	Characteristics = 0x00000000
    	Time/Date Stamp = 0x00000000
    	Version         = 4.0
    	Named Entries   = 0
    	Unnamed Entries = 1
    	Entries:
    		Type   = 24 (MANIFEST)
    		Offset = 0x80000018
    			Id     = 1
    			Offset = 0x80000030
    				Language = 1033
    				Offset = 0x00000048
    					Address   = 0x00003058
    					Size      = 84
    					Code Page = 1252
    					Reserved  = 0x00000000
    0
    OUCH¹: although Application Manifests must be encoded in UTF-8, the Manifest Tool MT.exe emits code page identifier 1252 alias Windows-1252 instead of the proper 65001 alias CP_UTF8 in the MANIFEST resource metadata!

    OUCH²: additionally it emits language identifier 1033 alias MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH) instead of the proper language identifier 0 alias MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL) and the time stamp 0x0 instead of the current date and time in the resource directory metadata!

    Note: contrary to other resource types, especially dialogues, menues, messages and strings, which are selected at runtime to match the users’ preferred language, Windows’ module loader always uses the first MANIFEST resource present in the .rsrc section!

  6. Link the console application blunder.exe a second time, now with the resource file blunder.res created in Blunder № 32:

    LINK.EXE /LINK blunder.obj blunder.res kernel32.lib user32.lib
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
  7. Execute the console application blunder.exe built in step 6. and evaluate its exit code:

    .\blunder.exe
    ECHO %ERRORLEVEL%
    Resource Directory:
    	Characteristics = 0x00000000
    	Time/Date Stamp = 0x00000000
    	Version         = 4.0
    	Named Entries   = 0
    	Unnamed Entries = 1
    	Entries:
    		Type   = 6 (STRINGTABLE)
    		Offset = 0x80000018
    			Id     = 1
    			Offset = 0x80000038
    				Language = 1033
    				Offset = 0x00000068
    					Address   = 0x00003090
    					Size      = 520
    					Code Page = 0
    					Reserved  = 0x00000000
    			Id     = 2
    			Offset = 0x80000050
    				Language = 1033
    				Offset = 0x00000078
    					Address   = 0x00003298
    					Size      = 492
    					Code Page = 0
    					Reserved  = 0x00000000
    0
    OUCH³: the Resource Compiler RC.exe also fails to set the proper code page identifier 1200 alias UTF-16LE as well as the time stamp and sets the language identifier 1033 alias MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH)!

Blunder № 34

The MSDN article Message Text Files specifies:
Messages are defined in a message text file. The message compiler assigns numbers to each message, and generates a C/C++ include file which the application can use to access a message using a symbolic constant.

[…]

You can specify the following escape sequences for formatting message text for use by the event viewer or your application. The percent sign character (%) begins all escape sequences. Any other character following a percent sign is displayed without the percent sign.

%n[!format_specifier!]

Describes an insert. Each insert is an entry in the Arguments array in the FormatMessage function. The value of n can be a number between 1 and 99. The format specifier is optional. If no value is specified, the default is !s!. For information about the format specifier, see wsprintf.

The format specifier can use * for either the precision or the width. When specified, they consume inserts numbered n+1 and n+2.

%0

Terminates a message text line without a trailing newline character. This can be used to build a long line or terminate a prompt message without a trailing newline character.

The documentation for the Win32 function FormatMessage() specifies likewise in section Remarks:
Formats a message string. The function requires a message definition as input. The message definition can come from a buffer passed into the function. It can come from a message table resource in an already-loaded module. Or the caller can ask the function to search the system's message table resource(s) for the message definition. The function finds the message definition in a message table resource based on a message identifier and a language identifier. The function copies the formatted message text to an output buffer, processing any embedded insert sequences if requested.

[…]

Escape sequence Meaning
%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. […]
The MSDN article System Error Codes (500-999) documents but message texts with %0 escape sequences:
ERROR_UNHANDLED_EXCEPTION

574 (0x23E)

{Application Error} The exception %s (0x%08lx) occurred in the application at location 0x%08lx.

[…]

ERROR_SYSTEM_PROCESS_TERMINATED

591 (0x24F)

{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.

Demonstration

Perform the following 5 simple steps to show misbehaviour first, then proper behaviour, and again misbehaviour.
  1. Start the Command Processor Cmd.exe, then execute the following four command lines to show the blunder:

    NET.EXE HELPMSG 574
    CERTUTIL.EXE /ERROR 574
    NET.EXE HELPMSG 591
    CERTUTIL.EXE /ERROR 591
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    {Application Error}
    The exception s (0x
    
    0x23e (WIN32: 574 ERROR_UNHANDLED_EXCEPTION) -- 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 ERROR_SYSTEM_PROCESS_TERMINATED) -- 591 (591)
    Error message text: {Fatal System Error}
    The %hs system process terminated unexpectedly with a status of 0x
    CertUtil: -error command completed successfully.
    OUCH¹: message texts defined with a stray %0, for example in 0x%08lX, are truncated when retrieved with the FormatMessage() function!
  2. Execute the following two command lines to show proper behaviour:

    CERTUTIL.EXE /ERROR 0xC0000144
    CERTUTIL.EXE /ERROR 0xC000021A
    0xc0000144 (NT: 0xc0000144 STATUS_UNHANDLED_EXCEPTION) -- 3221225796 (-1073741500)
    Error message text: {Application Error}
    The exception %s (0x%08lx) occurred in the application at location 0x%08lx.
    CertUtil: -error command completed successfully.
    
    0xc000021a (NT: 0xc000021a STATUS_SYSTEM_PROCESS_TERMINATED) -- 3221226010 (-1073741286)
    Error message text: {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.
    CertUtil: -error command completed successfully.
    Note: most obviously CertUtil.exe doesn’t use the FormatMessage() function for NTSTATUS values – the message texts for the 0xC0000144 alias STATUS_UNHANDLED_EXCEPTION and 0xC000021A alias STATUS_SYSTEM_PROCESS_TERMINATED, from which both Win32 error codes were derived and their message texts copied, display properly.
  3. Create the text file blunder.c with the following content in an arbitrary, preferable empty directory:

    // Copyleft © 2004-2024, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    
    #define STATUS_UNHANDLED_EXCEPTION		0xC0000144L
    #define STATUS_SYSTEM_PROCESS_TERMINATED	0xC000021AL
    
    const	DWORD	dwArray[] = {ERROR_SYSTEM_PROCESS_TERMINATED, ERROR_UNHANDLED_EXCEPTION,
    		             STATUS_SYSTEM_PROCESS_TERMINATED, STATUS_UNHANDLED_EXCEPTION};
    __declspec(noreturn)
    VOID	CDECL	wmainCRTStartup(VOID)
    {
    	WCHAR	szBuffer[1234];
    	DWORD	dwBuffer;
    	DWORD	dwConsole;
    	DWORD	dwError = ERROR_SUCCESS;
    	DWORD	dwIndex = 0UL;
    	HMODULE	hModule = GetModuleHandle(L"NTDLL");
    	HANDLE	hConsole = GetStdHandle(STD_ERROR_HANDLE);
    
    	if (hConsole == INVALID_HANDLE_VALUE)
    		dwError = GetLastError();
    	else
    		do
    		{
    			dwBuffer = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
    			                         hModule,
    			                         dwArray[dwIndex],
    			                         MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
    			                         szBuffer,
    			                         sizeof(szBuffer) / sizeof(*szBuffer),
    			                         (va_list *) NULL);
    			if (dwBuffer == 0UL)
    				dwError = GetLastError();
    			else
    			{
    				szBuffer[dwBuffer++] = L'\n';
    
    				if (!WriteConsole(hConsole, szBuffer, dwBuffer, &dwConsole, NULL))
    					dwError = GetLastError();
    				else
    					if (dwConsole != dwBuffer)
    						dwError = ERROR_WRITE_FAULT;
    			}
    		} while (++dwIndex < sizeof(dwArray) / sizeof(*dwArray));
    
    	ExitProcess(dwError);
    }
  4. Compile and link the source file blunder.c created in step 3.:

    SET CL=/W4 /Zl
    SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    CL.EXE blunder.c kernel32.lib
    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.
    
    blunder.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:blunder.exe
    blunder.obj
    kernel32.lib
  5. Execute the console application blunder.exe built in step 4. and evaluate its exit code:

    .\blunder.exe
    ECHO %ErrorLevel%
    {Fatal System Error}
    The hs system process terminated unexpectedly with a status of 0x
    {Application Error}
    The exception s (0x
    {Fatal System Error}
    The hs system process terminated unexpectedly with a status of 0x
    {Application Error}
    The exception s (0x
    
    0
    OUCH:

Blunder № 35

The documentation for the Certutil command specifies:

The following table describes the verbs that can be used with the certutil command.

Verbs Description
-dump Dump configuration information or files

Demonstration

Perform the following 9 simple steps to show the blunder as well as undocumented behaviour.
  1. Start the Command Processor Cmd.exe, then execute the following command lines to create the UTF-16LE encoded text file blunder.txt twice and dump it using the Certutil.exe utility:

    1>blunder.txt "%COMSPEC%" /U /D /C ECHO PAUSE
    CERTUTIL.EXE /DUMP blunder.txt
    1>blunder.txt "%COMSPEC%" /U /D /C ECHO pause
    CERTUTIL.EXE /DUMP blunder.txt
    DIR blunder.txt
        3c 05 12 14                                        <...
    CertUtil: -error command completed successfully.
    
        a5 ab ac 7c                                        ...|
    CertUtil: -error command completed successfully.
    
     Volume in drive C has no label.
     Volume Serial Number is 1957-0427
    
     Directory of C:\Users\Stefan\Desktop
    
    04/27/2018  08:15 PM                14 blunder.txt
                   1 File(s)             14 bytes
                   0 Dir(s)    9,876,543,210 bytes free
    OUCH¹: � WTF?
  2. Create the UTF-16LE encoded text file blunder.txt containing the Greek alphabet in capital and small letters in an arbitrary, preferable empty directory:

    ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρστυφχψω
    Note: the file blunder.txt contains 51 Unicode code points in 102 bytes – a leading Byte Order Mark U+FEFF, one line of 48 letters from U+0391 to U+03C9 (not contiguous), followed by a trailing CR/LF pair.
  3. Display the file blunder.txt using the internal Type command:

    TYPE blunder.txt
    ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρστυφχψω
  4. Create the ANSI encoded file blunder.tmp using the internal Type command and display it afterwards:

    1>blunder.tmp TYPE blunder.txt
    TYPE blunder.tmp
    ??G????Ø?????????S??F??Oaß?de??????µ???p?st?f???
  5. Dump the file blunder.txt created in step 2. using the Certutil.exe utility:

    CERTUTIL.EXE /DUMP blunder.txt
      0000  ...
      0032
        0000  3f 3f 47 3f 3f 3f 3f 54  3f 3f 3f 3f 3f 3f 3f 3f   ??G????T????????
        0010  3f 53 3f 3f 46 3f 3f 4f  61 df 3f 64 65 3f 3f 3f   ?S??F??Oa.?de???
        0020  3f 3f 3f b5 3f 3f 3f 70  3f 73 74 3f 66 3f 3f 3f   ???.???p?st?f???
        0030  0d 0a                                              ..
    CertUtil: -dump command completed successfully.
    OUCH²: Certutil.exe converts UTF-16LE encoded files to ANSI before it dumps their now destroyed content – a completely braindead approach – and even dares to call this epic failure successful!
  6. Dump the file blunder.txt created in step 2. using the undocumented /ENCODEHEX verb of the Certutil.exe utility and display the output:

    CERTUTIL.EXE /ENCODEHEX /F blunder.txt blunder.tmp
    TYPE blunder.tmp
    Input Length = 102
    Output Length = 508
    CertUtil: -encodehex command completed successfully.
    
    0000	ff fe 91 03 92 03 93 03  94 03 95 03 96 03 97 03   ................
    0010	98 03 99 03 9a 03 9b 03  9c 03 9d 03 9e 03 9f 03   ................
    0020	a0 03 a1 03 a3 03 a4 03  a5 03 a6 03 a7 03 a8 03   ................
    0030	a9 03 b1 03 b2 03 b3 03  b4 03 b5 03 b6 03 b7 03   ................
    0040	b8 03 b9 03 ba 03 bb 03  bc 03 bd 03 be 03 bf 03   ................
    0050	c0 03 c1 03 c3 03 c4 03  c5 03 c6 03 c7 03 c8 03   ................
    0060	c9 03 0d 00 0a 00                                  ......
  7. Dump the executable file of the Command Processor, specified by its absolute, fully qualified path name provided in the environment variable COMSPEC:

    CERTUTIL.EXE /DUMP "%COMSPEC%"
    C:\Windows\system32\cmd.exe: Lang 04b00409 (1200.1033)  File 6.1:7601.23403  Product 6.1:7601.23403
    CertUtil: -dump command completed successfully.
    OOPS¹: instead of the expected (hexadecimal) dump, Certutil.exe displays some parts of the VERSIONINFO resource embedded in the Portable Executable file Cmd.exe.
  8. Dump some other executable files located in the system directory %SystemRoot%\System32\, specified by their unqualified name and extension:

    CERTUTIL.EXE /DUMP main.cpl
    CERTUTIL.EXE /DUMP slmgr.vbs
    SET PATHEXT
    main.cpl: Lang 04b00409 (1200.1033)  File 6.1:7601.17514  Product 6.1:7601.17514
    CertUtil: -dump command completed successfully.
    
    CertUtil: -dump command FAILED: 0x80070002 (WIN32: 2 ERROR_FILE_NOT_FOUND)
    CertUtil: The system cannot find the file specified.
    
    PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
    OOPS²: Certutil.exe searches the PATH, but does not evaluate the environment variable PATHEXT!
  9. Finally dump another executable file located in the system directory, specified by just its unqualified name without extension:

    CERTUTIL.EXE /DUMP ntdll
    ntdll: Lang 04b00409 (1200.1033)  File 6.1:7601.24545  Product 6.1:7601.24545
    CertUtil: -dump command completed successfully.
    OOPS³: when no extension is specified, Certutil.exe searches the PATH for DLLs!

Blunder № 36

The documentation for the More command states:
Displays one screen of output at a time.

[…]

<Command> | more [/c] [/p] [/s] [/t<N>] [+<N>]
more [[/c] [/p] [/s] [/t<N>] [+<N>]] < [<Drive>:][<Path>]<FileName>
more [/c] [/p] [/s] [/t<N>] [+<N>] [<Files>]

Demonstration

Perform the following 4 simple steps to show the blunder.
  1. Create the UTF-16LE encoded text file blunder.txt containing the Cyrillic alphabet in capital and small letters in an arbitrary, preferable empty directory:

    ЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ
    ёђѓєѕіїјљњћќѝўџабвгдежзийклмнопрстуфхцчшщъыьэюя
    Note: the file blunder.txt contains 99 Unicode code points in 198 bytes – a leading Byte Order Mark U+FEFF, a first line of 47 capital letters from U+0401 to U+042F, followed by an intermediate CR/LF pair, a second line of 47 small letters from U+0451 to U+045F and U+0430 to U+044F, followed by a trailing CR/LF pair.
  2. Display the file blunder.txt using the internal Type command:

    TYPE blunder.txt
    ЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ
    ёђѓєѕіїјљњћќѝўџабвгдежзийклмнопрстуфхцчшщъыьэюя
  3. Display the file blunder.txt using the More utility:

    MORE.COM blunder.txt
    ???????????????????????????????????????????????
    ???????????????????????????????????????????????
    OUCH: More.com converts files from UTF-16LE to ANSI before it displays their now destroyed content – a completely braindead approach!
  4. Display the file blunder.txt a second time:

    TYPE blunder.txt | MORE.COM
    ????????????????????????????????????????????????
    ????????????????????????????????????????????????

Blunder № 37

The documentation for the Makecab command specifies:
Package existing files into a cabinet (.cab) file.
makecab [/v[n]] [/d var=‹value› ...] [/l ‹dir›] ‹source› [‹destination›]
makecab [/v[‹n›]] [/d var=‹value› ...] /f ‹directives_file› […]

Demonstration

  1. Start the Command Processor Cmd.exe, then execute the following command lines:

    SET TMP=NUL:
    MAKECAB.EXE "%COMSPEC%"
    Cabinet Maker - Lossless Data Compression Tool
    
    [Wed Apr 1 01:23:45] File open error (Win32Error=0x7B), retrying NUL:\cab_815_2
    [Wed Apr 1 01:23:45] File open error (Win32Error=0x7B), retrying NUL:\cab_815_2
    [Wed Apr 1 01:23:45] File open error (Win32Error=0x7B), retrying NUL:\cab_815_2
    …
    ^C
    OUCH: proper error handling is most obviously (next to) impossible for Microsoft’s developers!

    Note: the Win32 error code 123 means ERROR_INVALID_NAME – a repetition using the same filename is useless utter nonsense!

Blunder № 38

The TechNet article Reg add specifies:
Adds a new subkey or entries from the registry.
reg add <KeyName> [{/v ValueName | /ve}] [/t DataType] [/s Separator] [/d Data] [/f]
Parameter Description
‹KeyName› Specifies the full path of the subkey or entry to be added. To specify a remote computer, include the computer name (in the format \\<ComputerName>\) as part of the KeyName. Omitting \\ComputerName\ causes the operation to default to the local computer. The KeyName must include a valid root key. Valid root keys for the local computer are: HKLM, HKCU, HKCR, HKU, and HKCC. If a remote computer is specified, valid root keys are: HKLM and HKU.
/v <ValueName> Specifies the name of the registry entry to be added under the specified subkey.
/f Adds the registry entry without prompting for confirmation.
/? Displays help for reg add at the command prompt.
The TechNet article Reg delete specifies:
Deletes a subkey or entries from the registry.
Reg delete ‹KeyName› [{/v ‹ValueName› | /ve | /va}] [/f]
Parameter Description
/f Deletes the existing registry subkey or entry without asking for confirmation.
/? Displays help for reg delete at the command prompt.

Demonstration

  1. Start the Command Processor Cmd.exe, then execute the following command lines:

    REG.EXE ADD HKCU\Environment /V TMP 0<NUL:
    Value TMP exists, overwrite (Yes/No)? Value TMP exists, overwrite (Yes/No)? Value TMP exists, overwrite (Yes/No)? Value TMP exists, overwrite (Yes/No)? Value TMP exists, overwrite (Yes/No)? …
    ^C
    OUCH: proper error handling (here: detecting end of file on standard input) is most obviously underestimated and not deemed necessary in Redmond!

    Note: the demonstration of this blunder beginner’s error with other subcommands of the Reg utility is left as an exercise to the reader.

Blunder № 39

The TechNet article Reg export specifies:
Copies the specified subkeys, entries, and values of the local computer into a file for transfer to other servers.
The TechNet article Reg import specifies:
Copies the contents of a file that contains exported registry subkeys, entries, and values into the registry of the local computer.
The MSKB article 310516 specifies the format and syntax of registry editor script files that is missing in the documentation referenced above, but is awfully bad!

Note: especially the notation =hex(2):‹comma separated list of hexadecimal values› required for REG_EXPAND_SZ values, =hex(7):‹comma separated list of hexadecimal values› required for REG_MULTI_SZ values and =hex(11):‹comma separated list of 8 hexadecimal values› required for REG_QWORD alias REG_QWORD_LITTLE_ENDIAN values is user-unfriendly.

Demonstration

Perform the following 5 (plus 1) simple steps to show the blunder.
  1. Create the text file blunder.reg with the following content in an arbitrary, preferable empty directory:

    REGEDIT4
    
    [HKEY_CURRENT_USER\Blunder]
    ""="Blunder"
    "none"=hex(0):0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f
    "binary"=hex(3):0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z
    "dword"=dword:-1
    "dword_little_endian"=hex(4):01,23,45,67,89,ab,cd,ef
    "dword_big_endian"=hex(5):01,23,45,67,89,ab,cd,ef
    "qword"=qword:0123456789abcdef
    "link"=hex(6):
    "expand"=expand:"%windir%"
    "multi"=multi:"first","last"
    "string"="left\right"
    ''="''"
    @='@'
  2. Import the registry entries from the script file blunder.reg into the Registry:

    REG.EXE IMPORT blunder.reg
    ECHO %ERRORLEVEL%
    The operation completed successfully.
    0
    OUCH: despite the multiple syntax errors in the script file blunder.reg, the Registry Console Tool Reg.exe yields an explicit success message!

    OOPS: to specify the (unnamed) default entry of a registry key, instead of the @ an empty string can be used as registry value name.

  3. Query the imported registry entries:

    REG.EXE QUERY HKEY_CURRENT_USER\Blunder
    ECHO %ERRORLEVEL%
    HKEY_CURRENT_USER\Blunder
        (Default)    REG_SZ    Blunder
        none    REG_NONE    000102030405060708090A0B0C0D0E0F
        dword_little_endian    REG_DWORD    0x67452301
        dword_big_endian    REG_DWORD_BIG_ENDIAN    0x67452301
        link    REG_LINK    
    
    0
  4. Export the just imported registry entries from the registry key HKEY_CURRENT_USER\Blunder to the file blunder.txt:

    REG.EXE EXPORT HKEY_CURRENT_USER\Blunder blunder.txt
    ECHO %ERRORLEVEL%
    The operation completed successfully.
    0
  5. Display the exported registry key and its entries:

    TYPE blunder.txt
    Windows Registry Editor Version 5.00
    
    [HKEY_CURRENT_USER\Blunder]
    @="Blunder"
    "none"=hex(0):00,01,02,03,04,05,06,07,08,09,0a,0b,0c,0d,0e,0f
    "dword_little_endian"=hex(4):01,23,45,67,89,ab,cd,ef
    "dword_big_endian"=hex(5):01,23,45,67,89,ab,cd,ef
    "link"=hex(6):
    Note: indicated by the tag line Windows Registry Editor Version 5.00, the Registry Console Tool Reg.exe exports in UTF-16LE encoding.
  6. Delete the registry key HKEY_CURRENT_USER\Blunder with all its entries:

    REG.EXE DELETE HKEY_CURRENT_USER\Blunder /F
    ECHO %ERRORLEVEL%
    The operation completed successfully.
    0
Note: a repetition of steps 2., 4. and 6. using the (graphical) Registry Editor RegEdit.exe instead of the Registry Console Tool Reg.exe is left as an exercise to the reader.

Blunder № 40

The MSKB article Insert ASCII or Unicode Latin-based symbols and characters states:
To insert a Unicode character, type the character code, press ALT, and then press X. For example, to type a dollar symbol ($), type 0024, press ALT, and then press X.
The MSKB articles Keyboard shortcuts for international characters and Keyboard shortcuts to add language accent marks in Word and Outlook state:
To insert this Press
The Unicode character for the specified Unicode (hexadecimal) character code The character code, ALT+X
For example, to insert the euro currency symbol €, type 20AC, and then hold down the ALT key and press X.
Contrary to the MSKB articles cited above, the hexadecimal Unicode character code doesn’t need to be typed before the Alt X key combination, and this function is supported in all programs which use an arbitrary Rich Edit Control, not just in Microsoft Office applications! ,

Demonstration

Perform the following 2 simple steps to show the proper use of the Alt X key combination.
  1. Create the text file blunder.txt with the following content in an arbitrary, preferably empty directory:

    24
    a9
    ae
    3b1
    3c9
    20ac
    2122
    2190
    2191
    2192
    2193
    2194
  2. Open the text file blunder.txt created in step 1. with WordPad.exe, then place the cursor at the end of each line and press the Alt X key combination twice – first it replaces the 2, 3 or 4 characters left to the cursor with the $, ©, ®, α, ω, €, ™, ←, ↑, →, ↓ respectively ↔ symbol, second it replaces each symbol with its (uppercase) hexadecimal Unicode character code.

Note: a repetition of this demonstration with other programs which use a Rich Edit Control is left as an exercise to the reader; if a symbol is not replaced with its (uppercase) Unicode character code there, try the Alt Shift X key combination instead.

Blunder № 41

Policies are supposed to be reserved for use by (local) administrators, they are not supposed to be (ab)used by software vendors.

Despite this well-known rule, Microsoft but ships the system images of Windows Vista and later versions with a bunch of Policies set only in the Registry – the subdirectories %SystemRoot%\System32\GroupPolicy\Machine\ and %SystemRoot%\System32\GroupPolicy\User\ with the registry policy files Registry.pol where these registry entries are supposed to be stored are missing!

Due to this omission blunder the Local Group Policy Editor and the Local Security Policy snap-in of the Microsoft Management Console show these Policies as not configured!

Also missing are the archive files NTUser.pol in the directories %ALLUSERSPROFILE% alias %ProgramData% for the machine and %USERPROFILE% for each user where the added as well as the original, now overwritten or removed registry entries are supposed to be saved for restoration and roll-back.

The MSDN article Registry Policy File Format provides some details.

Demonstration

Perform the following 5 simple steps on a fresh installation of Windows NT to display the policy registry keys and entries present in the system image or set during the installation.

Note: Windows 10 20H2 was used here.

  1. Start the Command Processor Cmd.exe, then execute the following command lines to export the registry keys HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies and HKEY_CURRENT_USER\Software\Policies of an arbitrary user account and display them:

    REG.EXE EXPORT "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies" blunder.reg
    TYPE blunder.reg
    REG.EXE EXPORT "HKEY_CURRENT_USER\Software\Policies" blunder.reg /Y
    TYPE blunder.reg
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    The operation completed successfully.
    
    Windows Registry Editor Version 5.00
    
    [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies]
    
    [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer]
    "NoDriveTypeAutoRun"=dword:00000091
    
    The operation completed successfully.
    
    Windows Registry Editor Version 5.00
    
    [HKEY_CURRENT_USER\Software\Policies]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\CA]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\CA\Certificates]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\CA\CRLs]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\CA\CTLs]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\Disallowed]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\Disallowed\Certificates]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\Disallowed\CRLs]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\Disallowed\CTLs]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\trust]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\trust\Certificates]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\trust\CRLs]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\trust\CTLs]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\TrustedPeople\Certificates]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\TrustedPeople\CRLs]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\TrustedPeople\CTLs]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\CloudContent]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\CurrentVersion]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\5.0]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\Cache]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\Cache]
    
    [HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\DataCollection]
    
    [HKEY_CURRENT_USER\Software\Policies\Power]
    
    [HKEY_CURRENT_USER\Software\Policies\Power\PowerSettings]
  2. Export the respective registry keys of the builtin NT AUTHORITY\SYSTEM alias LocalSystem user account and display them:

    REG.EXE EXPORT "HKEY_USERS\S-1-5-18\Software\Policies" blunder.reg /Y
    TYPE blunder.reg
    The operation completed successfully.
    
    Windows Registry Editor Version 5.00
    
    [HKEY_USERS\S-1-5-18\Software\Policies]
    
    [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft]
    
    [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates]
    
    [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\CA]
    
    [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\CA\Certificates]
    
    [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\CA\CRLs]
    
    [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\CA\CTLs]
    
    [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\Disallowed]
    
    [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\Disallowed\Certificates]
    
    [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\Disallowed\CRLs]
    
    [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\Disallowed\CTLs]
    
    [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\trust]
    
    [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\trust\Certificates]
    
    [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\trust\CRLs]
    
    [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\trust\CTLs]
    
    [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\TrustedPeople]
    
    [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\TrustedPeople\Certificates]
    
    [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\TrustedPeople\CRLs]
    
    [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\TrustedPeople\CTLs]
  3. Export the respective registry keys of the NT AUTHORITY\LOCAL SERVICE alias LocalService, user account and display them:

    REG.EXE EXPORT "HKEY_USERS\S-1-5-19\Software\Policies" blunder.reg /Y
    TYPE blunder.reg
    Note: this operation requires administrative access rights!
    The operation completed successfully.
    
    Windows Registry Editor Version 5.00
    
    [HKEY_USERS\S-1-5-19\Software\Policies]
    
    [HKEY_USERS\S-1-5-19\Software\Policies\Microsoft]
    
    [HKEY_USERS\S-1-5-19\Software\Policies\Microsoft\Windows]
    
    [HKEY_USERS\S-1-5-19\Software\Policies\Microsoft\Windows\CurrentVersion]
    
    [HKEY_USERS\S-1-5-19\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings]
    
    [HKEY_USERS\S-1-5-19\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\5.0]
    
    [HKEY_USERS\S-1-5-19\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\Cache]
    
    [HKEY_USERS\S-1-5-19\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\Cache]
    
    [HKEY_USERS\S-1-5-19\Software\Policies\Power]
    
    [HKEY_USERS\S-1-5-19\Software\Policies\Power\PowerSettings]
  4. Export the respective registry keys of the NT AUTHORITY\NETWORK SERVICE alias NetworkService, user account and display them:

    REG.EXE EXPORT "HKEY_USERS\S-1-5-20\Software\Policies" blunder.reg /Y
    TYPE blunder.reg
    Note: this operation requires administrative access rights!
    The operation completed successfully.
    
    Windows Registry Editor Version 5.00
    
    [HKEY_USERS\S-1-5-20\Software\Policies]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\CA]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\CA\Certificates]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\CA\CRLs]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\CA\CTLs]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\Disallowed]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\Disallowed\Certificates]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\Disallowed\CRLs]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\Disallowed\CTLs]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\trust]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\trust\Certificates]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\trust\CRLs]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\trust\CTLs]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\TrustedPeople]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\TrustedPeople\Certificates]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\TrustedPeople\CRLs]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\TrustedPeople\CTLs]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\Windows]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\Windows\CurrentVersion]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\5.0]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\Cache]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\Cache]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Power]
    
    [HKEY_USERS\S-1-5-20\Software\Policies\Power\PowerSettings]
  5. Export the registry keys HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies and HKEY_LOCAL_MACHINE\SOFTWARE\Policies of the machine and display them:

    REG.EXE EXPORT "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies" blunder.reg /Y
    TYPE blunder.reg
    REG.EXE EXPORT "HKEY_LOCAL_MACHINE\SOFTWARE\Policies" blunder.reg /Y
    TYPE blunder.reg
    The operation completed successfully.
    
    Windows Registry Editor Version 5.00
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\ActiveDesktop]
    "NoAddingComponents"=dword:00000001
    "NoComponents"=dword:00000001
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Attachments]
    "ScanWithAntiVirus"=dword:00000003
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection\Users]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer]
    "ForceActiveDesktopOn"=dword:00000000
    "NoActiveDesktop"=dword:00000001
    "NoActiveDesktopChanges"=dword:00000001
    "NoRecentDocsHistory"=dword:00000000
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Ext]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Ext\CLSID]
    "{1FD49718-1D00-4B19-AF5F-070AF6D5D54C}"="1"
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\NonEnum]
    "{0DF44EAA-FF21-4412-828E-260A8728E7F1}"=dword:00000020
    "{6DFD7C5C-2451-11d3-A299-00C04F8EF6AF}"=dword:40000021
    "{BDEADF00-C265-11D0-BCED-00A0C90AB50F}"=dword:00000001
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Servicing]
    "CountryCode"="EN"
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System]
    "ConsentPromptBehaviorAdmin"=dword:00000005
    "ConsentPromptBehaviorUser"=dword:00000003
    "DSCAutomationHostEnabled"=dword:00000002
    "EnableCursorSuppression"=dword:00000001
    "EnableFullTrustStartupTasks"=dword:00000002
    "EnableInstallerDetection"=dword:00000001
    "EnableLUA"=dword:00000001
    "EnableSecureUIAPaths"=dword:00000001
    "EnableUIADesktopToggle"=dword:00000000
    "EnableUwpStartupTasks"=dword:00000002
    "EnableVirtualization"=dword:00000001
    "PromptOnSecureDesktop"=dword:00000001
    "SupportFullTrustStartupTasks"=dword:00000001
    "SupportUwpStartupTasks"=dword:00000001
    "ValidateAdminCodeSignatures"=dword:00000000
    "dontdisplaylastusername"=dword:00000000
    "legalnoticecaption"=""
    "legalnoticetext"=""
    "scforceoption"=dword:00000000
    "shutdownwithoutlogon"=dword:00000001
    "undockwithoutlogon"=dword:00000001
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Audit]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\UIPI]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\UIPI\Clipboard]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\UIPI\Clipboard\ExceptionFormats]
    "CF_BITMAP"=dword:00000002
    "CF_DIB"=dword:00000008
    "CF_DIBV5"=dword:00000011
    "CF_OEMTEXT"=dword:00000007
    "CF_PALETTE"=dword:00000009
    "CF_TEXT"=dword:00000001
    "CF_UNICODETEXT"=dword:0000000d
    
    The operation completed successfully.
    
    Windows Registry Editor Version 5.00
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Cryptography]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Cryptography\Configuration]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Cryptography\Configuration\SSL]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Cryptography\Configuration\SSL\00010002]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Peernet]
    "Disabled"=dword:00000000
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\CA]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\CA\Certificates]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\CA\CRLs]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\CA\CTLs]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\Disallowed]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\Disallowed\Certificates]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\Disallowed\CRLs]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\Disallowed\CTLs]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\Root]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\Root\Certificates]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\Root\CRLs]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\Root\CTLs]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\trust]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\trust\Certificates]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\trust\CRLs]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\trust\CTLs]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\TrustedPeople]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\TrustedPeople\Certificates]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\TrustedPeople\CRLs]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\TrustedPeople\CTLs]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\TPM]
    "OSManagedAuthLevel"=dword:00000005
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Appx]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\BITS]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings]
    "CallLegacyWCMPolicies"=dword:00000000
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\Cache]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\DataCollection]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\DriverSearching]
    "DriverUpdateWizardWuSearchEnabled"=dword:00000001
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\EnhancedStorageDevices]
    "TCGSecurityActivationDisabled"=dword:00000000
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\IPSec]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\IPSec\Policy]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Network Connections]
    "NC_PersonalFirewallConfig"=dword:00000000
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\NetworkConnectivityStatusIndicator]
    @=""
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\NetworkProvider]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\NetworkProvider\HardenedPaths]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\safer]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\safer\codeidentifiers]
    "authenticodeenabled"=dword:00000000
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\SettingSync]
    "EnableBackupForWin8Apps"=dword:00000001
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\System]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WcmSvc]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WcmSvc\GroupPolicy]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WcmSvc\Local]
    "WCMPresent"=dword:00000001
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WorkplaceJoin]
    @=""
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WSDAPI]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WSDAPI\Discovery Proxies]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows Advanced Threat Protection]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows Defender\Policy Manager]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows NT]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\Client]
    "fEnableUsbBlockDeviceBySetupClass"=dword:00000001
    "fEnableUsbNoAckIsochWriteToDevice"=dword:00000050
    "fEnableUsbSelectDeviceByInterface"=dword:00000001
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\Client\UsbBlockDeviceBySetupClasses]
    "1000"="{3376f4ce-ff8d-40a2-a80f-bb4359d1415c}"
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\Client\UsbSelectDeviceByInterfaces]
    "1000"="{6bdd1fc6-810f-11d0-bec7-08002be2092f}"
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows NT\Windows File Protection]
    "KnownDllList"="nlhtml.dll"

Blunder № 42

The MSKB article 142982 states:
Windows supports long file names up to 255 characters in length. Windows also generates an MS-DOS-compatible (short) file name in 8.3 format to allow MS-DOS-based or 16-bit Windows-based programs to access the files.
The MSKB article 896458 states:
The x64-based versions of Windows don't support 16-bit programs, 16-bit processes, or 16-bit components.
Microsoft but ships the installation media for 64-bit all editions of Windows 8 and later versions with superfluous 8.3 alias short file names in the system images!

Note: the installation media of Windows 7 have no 8.3 alias short file names in their system images – disabling 8.3 file name generation on the target drive before running the setup program is therefore sufficient to perform a clean installation!

Demonstration

Perform the following 3 simple steps to determine the (number of) short file and directory names on a current, preferable fresh installation of Windows 10 or Windows 11.
  1. Start the Command Processor Cmd.exe, then execute the following command lines:

    VER
    1>blunder.log (
    FOR /D /R "%SystemDrive%\" %? IN (*) DO @IF NOT "%~nx?" == "%~snx?" ECHO "%~dp?%~snx?"
    )
    FIND.EXE /C /V "" blunder.log
    1>blunder.txt (
    FOR /R "%SystemDrive%\" %? IN (*) DO @IF NOT "%~nx?" == "%~snx?" ECHO "%~dp?%~snx?"
    )
    FIND.EXE /C /V "" blunder.txt
    Note: the command lines can be copied and pasted as block into a Command Processor window.

    Caveat: the command lines miss hidden directories and files as well as directories and files with short name equal to their long name!

    Caveat: unless run under the NT AUTHORITY\SYSTEM alias LocalSystem user account or with the privilege SeBackupPrivilege enabled the command lines also miss subdirectories and files in all directories without List Directory access permission for the current user account!

    Microsoft Windows [Version 10.0.26100.1742]
    
    ---------- blunder.log: 16034
    ---------- blunder.txt: 55140
  2. Start the Command Processor Cmd.exe with administrative access rights, then repeat the previous step:

    Microsoft Windows [Version 10.0.26100.1742]
    
    ---------- blunder.log: 18206
    ---------- blunder.txt: 66172
  3. Finally execute the following command line:

    FSUTIL.EXE 8dot3name scan /l "%TMP%\blunder.log" /s "%SystemDrive%\\"
    Scanning registry...
    
    Total affected registry keys:                 222
    
    Scanning 8dot3 names...
    
    Total files and directories scanned:       148550
    Total 8dot3 names found:                    84138
    Total 8dot3 names stripped:                     0
    
    For details on the operations performed please see the log:
      "C:\Users\ADMINI~1\AppData\Local\Temp\blunder.log"
    Note: all (here: 222) enumerated registry keys (really: registry entries) are false positivesFSUtil.exe (really: its most obviously incompetent developer) considers file names like $WINDOWS.~BT and Clipchamp.Clipchamp_3.0.10220.0_neutral_~_yxz26nhyzhsrt just due to the ~ to be short!

Blunder № 43

The MSDN article File System Redirector states:
32-bit applications can access the native system directory by substituting %windir%\Sysnative for %windir%\System32. WOW64 recognizes Sysnative as a special alias used to indicate that the file system should not redirect the access. This mechanism is flexible and easy to use, therefore, it is the recommended mechanism to bypass file system redirection. Note that 64-bit applications cannot use the Sysnative alias as it is a virtual directory not a real one.

Falsification

Perform the following simple step to prove the highlighted statement of the documentation cited above wrong – at least when using the 32-bit FSUtil.exe console application to enumerate hardlinks of files residing in the native system directory.
  1. Start the Command Processor Cmd.exe with administrative access rights, then execute the following single command line:

    IF EXIST "%SystemRoot%\SysWoW64\FSUtil.exe" "%SystemRoot%\SysWoW64\FSUtil.exe" HARDLINK LIST "%SystemRoot%\Sysnative\NTDLL.dll"
    Error:  The parameter is incorrect.
    OUCH: the File System Utility has one job, but fails to interact with the File System Redirector!

Contact and Feedback

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‍>