Defense in depth -- the Microsoft way (part 14): incomplete, misleading and dangerous documentation

From: Stefan Kanthak <>
Subject: Defense in depth -- the Microsoft way (part 14): incomplete, misleading and dangerous documentation

Hi @ll,

here's another (well-known, but undocumented) idiosyncrasy of Windows'
CreateProcess*() functions with a "nice" side-effect:-P

From <>:

| BOOL WINAPI CreateProcess(
|  _In_opt_     LPCTSTR lpApplicationName,
|  _Inout_opt_  LPTSTR  lpCommandLine,
| To run a batch file, you must start the command interpreter; set
| lpApplicationName to cmd.exe and set lpCommandLine to the following
| arguments: /c plus the name of the batch file.

This is but wrong (and of course dangerous too: you should NEVER set
lpApplicationName to an unqualified filename, but ALWAYS use the fully
qualified pathname; see <>
and <>)!

Now the REAL behaviour (of Windows NT4, Windows NT5.x, Windows NT6.x):

CreateProcess("<pathname>.cmd", ...)
CreateProcess("<pathname>.bat", ...)
CreateProcess(NULL, "<pathname>.cmd[ ...]", ...)
CreateProcess(NULL, "<pathname>.bat[ ...]", ...)

(and of course CreateProcessAsUser(), CreateProcessWithLogonW() and
CreateProcessWithTokenW() too) execute a rogue program CMD.EXE from
the 'application directory' (i.e. the directory where the program that
calls CreateProcess*() is located) instead of the expected %COMSPEC%
alias %SystemRoot%\System32\cmd.exe, with the command line set to
"cmd /c \"<pathname>.cmd\"" resp. "cmd /c <pathname>.cmd[ ...]".

From <>:

| lpCommandLine [in, out, optional]
| If the file name does not contain an extension, .exe is 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.

JFTR: ShellExecute*() calls CreateProcess*() to start <pathname>.bat
      and <pathname>.cmd, so Windows Explorer (and of course any
      other program calling ShellExecute*() too) start CMD.EXE from
      their 'application directory' or the 'current working directory'

      The 'application directory' of Windows Explorer is %SystemRoot%,
      and Windows Explorer sets the 'current working directory' to the
      path of the file to be opened.

      %SystemRoot%\System32\CMD.EXE is found via the documented search
      path ... until someone creates a CMD.EXE in %SystemRoot% or the
      'current working directory' (i.e. the directory where
      <filename>.bat or <filename>.cmd is located).

FIX (for ShellExecute*() and Windows Explorer):

change the value of the default registry entries of
from @="%1 %*"
to   @=expand:"%SystemRoot%\\System32\\cmd.exe /C Call \"%L\" %*"
or   @=expand:"\"%COMSPEC%\" /C Call \"%L\" %*"

Additionally, see <> as well
as <> and set

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager]

stay tuned
Stefan Kanthak

PS: when <filename>.bat or <filename>.cmd are started from Windows
    Explorer the console window of the new process shows the icon of
    the CMD.EXE found in the 'current working directory' (i.e. the
    directory where <filename>.bat or <filename>.cmd is located), not
    the icon of the command processor %SystemRoot%\System32\cmd.exe
    ... independent of the above mentioned fixes!

    But that's just another of Windows' many idiosyncrasies!


2013-10-23    informed vendor

2013-10-24    vendor replies: see


2013-10-24    NO, this does NOT describe the behaviour of CreateProcess()
              or ShellExecute() for *.CMD and *.BAT

2013-10-24    vendor replies: MSRC case 15734 opened


2013-11-18    requested status from vendor

2013-11-24    no answer, report published

Copyright © 1995-2020 All rights reserved.