PS_CREATE_INFO

The PS_CREATE_INFO structure is exchanged between
user and kernel modes for the NtCreateUserProcess
function, which was new for Windows Vista.

Documentation Status

The PS_CREATE_INFO structure is not documented. Microsoft
is known to have disclosed its existence only in two ways: type information in public
symbol files for Windows 8 and higher; an unreferenced type in a C-language header
that appears to have been published by mistake in a Windows Driver Kit (WDK) for
Windows 10.

Curiously, though the PS_CREATE_INFO is prepared
by KERNEL32.DLL or KERNELBASE.DLL, depending on the Windows version, and is passed
through NTDLL.DLL to kernel mode, and is then interpreted by the kernel, public
symbol files for none of these modules have type information for the
PS_CREATE_INFO in any known Windows version. Where this
type information turns up instead is in public symbol files for a handful of higher-level
user-mode modules that ought never to see the structure. Especially prominent as
examples are OLE32.DLL and URLMON.DLL. The latter is conspicuous as a key component
of Internet Explorer. Government-appointed reviewers of Microsoft’s settlement compliance
seem to have missed the irony that Internet Explorer is built with more detailed
knowledge of Windows internals than Microsoft publishes even for kernel-mode programmers.

A pointer to a PS_CREATE_INFO is passed to the
NtCreateUserProcess function as its second-last argument.
Microsoft published a C-language prototype of this function, as its alias
ZwCreateUserProcess, in a ZWAPI.H with the Enterprise
edition of the Windows 10 WDK for the 1511 release. No other header in the same
WDK even declares the pointer’s type.

Layout

The PS_CREATE_INFO structure is 0x48 or 0x58 bytes
in 32-bit and 64-bit Windows, respectively, in version 6.0 and higher.

The point to the State member is to indicate which
branch to select from the unnamed union that follows. While the
PS_CREATE_STATE enumeration is not known to be used elsewhere,
it is as well given here. The defined values are:

0 as PsCreateInitialState;

1 as PsCreateFailOnFileOpen;

2 as PsCreateFailOnSectionCreate;

3 as PsCreateFailExeFormat;

4 as PsCreateFailMachineMismatch;

5 as PsCreateFailExeName;

6 as PsCreateSuccess;

7 as PsCreateMaximumStates.

Input

The PsCreateInitialState is required for input
and selects the InitState branch.

Init State

The bit fields in union with the 32-bit InitFlags
have a mixture of types. The modern identification of the second byte as being wholly
spare but separate from spare bits in the first byte looks like a legacy from a
reworking for Windows 8:

Offset

Mask

Definition

Versions

0x08

0x01

UCHAR WriteOutputOnExit : 1;

6.0 and higher

0x02

UCHAR DetectManifest : 1;

6.0 and higher

0x04

UCHAR IFEOSkipDebugger : 1;

6.2 and higher

0x08

UCHAR IFEODoNotPropagateKeyState : 1;

6.2 and higher

UCHAR SpareBits1 : 6;

6.0 to 6.1

UCHAR SpareBits1 : 4;

6.2 and higher

0x09

0x03

unknown two bits

6.0 to 6.1

UCHAR SpareBits2 : 6;

6.0 to 6.1

UCHAR SpareBits2 : 8;

6.2 and higher

0x0A

0xFFFF

USHORT ProhibitedImageCharacteristics : 16;

6.2 and higher

Microsoft’s name for the second byte’s 2-bit field in the original layout is
not known. The value 3 is explicitly invalid. The cases 0, 1 and 2 were later separated
into combinations of single bits of the first byte:

Unknown

IFEOSkipDebugger

IFEODoNotPropagateKeyState

0

clear

clear

1

set

clear

2

set

set

As late as the original Windows 10, these remain the only combinations that have
known use. KERNELBASE sets both bits when the created process is to start as a debugged
process, i.e., because DEBUG_PROCESS or
DEBUG_ONLY_THIS_PROCESS is set among the process creation
flags.

Output

Three cases of failure involve the return of information that may help the user-mode
caller present the problem or recover from it.

FailSection

Offset (x86)

Offset (x64)

Definition

0x08

0x10

HANDLE FileHandle;

Exe Format

Offset (x86)

Offset (x64)

Definition

0x08

0x10

USHORT DllCharacteristics;

Exe Name

Failure as PsCreateFailExeName indicates that however
usable may be the executable as a file its execution is prevented by something about
its name, specifically as its name appears as a subkey of Image
File Execution Options. The caller is not explicitly told what prevents execution
but is returned a handle to the subkey.

There may be multiple causes as a point of design but only one is implemented.
The executable’s Image File Execution Options subkey
has a Debugger value. The kernel in effect interprets
this value’s presence as meaning that a process with this name cannot ordinarily be created. (The kernel’s error code is STATUS_OBJECT_PATH_INVALID.)
The one exception to “ordinarily” is if the IFEOSkipDebugger
bit is set on input.