[Misc Series #5] How Windows CMD and PowerShell Execute a File?

GhouLSec
4 min readMay 22, 2024

--

This blog will note down some mechanism that Windows implemented to select an application to open a file. Both cmd.exe and powershell.exe were tested in this case.

Let’s try this by using an executable file named calc.exe which has renamed into calc.pdf. During the experiment, cmd.exe able to run the executable regardless of the file extension name. However, powershell.exe will select default application that associated with the file extension name to open the file.

Command used in the experiment:

PS> calc.pdf (Result: Launch MSEdgePDF)
CMD> calc.pdf (Result: Pop calc.exe application)

General explanation on the behavior:

  • In powershell.exe, the file extension name will be extracted and compared it within the user registry to search for default application to open a file. CreateProcessW will execute later based on the application command found in registry.
  • In cmd.exe, windows directly first execute the input file via CreateProcessW before performing any registry search to look for default application to open the file. There are MZ byte check within the kernel code of CreateProcessW and this is why we can run any executable file regardless of the file extension.

Notes for file that executed via powershell.exe

Most of the relevant functions were found in windows.storage.dll, at least for user mode code.

The extracted extension name will fill up the %s from registry subkey HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\%s\UserChoice. It will then be HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.pdf\UserChoice. Registry value ProgId contains the default application for pdf file. In this case, it is MSEdgePDF.

Inside QueryUserAssocAndVerifyHash()

After retrieve the ProgId value, MSEdgePDF, its command value will retrieve from registry key Computer\HKEY_CLASSES_ROOT\%s\shell\open\command. The %s replaced with MSEdgePDF and becomes Computer\HKEY_CLASSES_ROOT\MSEdgePDF\shell\open\command.

Once retrieved the application command from the registry key above, it will fill up any remaining placeholder value and CreateProcessW will be trigger the open the file passed into the parameter.

Notes for file that executed via cmd.exe

In normal case, if NtCreateUserProcess able to create a process in suspended mode, it will break the while loop and continue with image section creation (Based on my roughly check in the function itself) then followed by NtResumeThread to continue with the execution of new process.

Launch an exe file with .pdf extension via cmd.exe > calc.pdf

If the input file passed to in the function CreateProcessW return error code (0xC000012F, STATUS_INVALID_IMAGE_NOT_MZ), It will do some handling before performing the registry check to find associated application to open the file as mentioned in the method above.

Launch an exe file with non-MZ header via cmd.exe > test.txt
Some checking was done before looking for associated app in registry

To find out where does the 0xC000012F error code comes from, I did some tracing within NtCreateUserProcess 🕵️

As the error code mentioned something related with MZ header. I will be using an executable file (In my case is calc.exe) that starts with NZ instead of MZ. So that I can trigger the error and trace it accordingly.

Snippet for code tracing

After some tracing was done, error code 0xC000012F can found in MiCreateImageFileMap🥲😌

Here is the flow to get into the MiCreateImageFileMap function.

nt!NtCreateUserProcess -> MmCreateSpecialImageSection -> MiCreateSection -> MiCreateImageOrDataSection -> MiCreateNewSection -> MiCreateImageFileMap

As you can the code snippet in MiCreateImageFileMap, there is a first two bytes check for MZ strings. If the first two bytes of the input file doesn’t match the condition, it will return with the error code, 0xC000012F which is matched with the error code found in the CreateProcessW in user mode.

The MZ error handling can be found inside MiCreateImageFileMap

By using the malformed file, we are able to identify the code which perform the validation for windows executable file😋. This trick is quite useful when you want to determine something in a code where you don’t have much understanding on it✌️

Conclusion

It’s a “boring” investigation, but now it is good to know that you can run executable file with any extension name in cmd.exe but not powershell.exe 🤣

--

--

GhouLSec
GhouLSec

No responses yet