stdin handle from PEB->ProcessParameters is invalid (ObReferenceObjectByHandle) on process creation

I’m trying to check out the StandardInput (and Output and Error) handle from a CreateProcessNotifyRoutine, but observing a few processes, I keep getting invalid handles (or non-FILE_OBJECT ones, but the invalid ones are the real problem, I think).

The problematic line is line 79, but I’m giving the full function for context.

https://pastebin.com/WucUy8PU

PhantomR wrote:

I’m trying to check out the StandardInput (and Output and Error) handle from a CreateProcessNotifyRoutine, but observing a few processes, I keep getting invalid handles (or non-FILE_OBJECT ones, but the invalid ones are the real problem, I think).

You are blithely assuming that standard file handles are already created
by the time the notification runs.  I don’t know for sure, but I
wouldn’t be at all surprised to learn that those handles aren’t opened
until much later in the creation process, after control has passed to
the initialization code.

Then, of course, there’s the fact that none of those fields in the
RTL_USER_PROCESS_PARAMETERS structure are documented.  It’s a dangerous
game.

I don’t know for sure, but I wouldn’t be at all surprised to learn that those handles aren’t opened
until much later in the creation process, after control has passed to the initialization code.

Well, you don’t really need to"learn" it - this part can be logically deduced

A call to CreateProcess() involves few system calls behind the scenes. First of all, the system has to create the address space of the newly-created process. This is done by ZwCreateProcess() system call. A process does not yet have any threads in it. Then the primary thread of the new process has to get created in the suspended state. Finally, crss.exe has to get notified about the new process. Only at this point the primary thread of the new process may be allowed to run. However, it has to do some initialization before actually transferring control to the target app’s main() function. This is the earliest possible moment when the above mentioned handles may get opened by the newly-created process - it cannot get done before its primary thread starts running, can it.

Although the above mentioned sequence makes a perfect sense from CreateProcess() 's perspective, it is done as a sequence of the system calls that are technically unrelated to one another, at least as far as the kernel is concerned. This, in turn, leads us to the logical conclusion that PsCreateProcessNotifyRoutine() has to get invoked somewhere on ZwCreateProcess() 's path, i. e. before ZwCreateProcess() returns control. Otherwise, the kernel would have to keep the track of the system calls made by the process in order to make it work.

Therefore, we can logically conclude that no handles are yet opened by the newly-created process at the time PsCreateProcessNotifyRoutine()
gets invoked.

Ironically, it has absolutely nothing to do with the OP’s problem. His problem lies with the fact that ParentId and ProcessId “handles”
that PsCreateProcessNotifyRoutine() receives as parameters are, in actuality, not really the ones. In its conventional meaning, the term “handle” refers to the logical index that allows you to locate a pointer to the actual object in the object table of a given process, However, ParentId and ProcessId are just system-wide process identifiers that have absolutely nothing to do with the object table of a process, and, hence, point to the middle of nowhere as far as the above mentioned table is concerned . This is why the OP gets the invalid results.

Anton Bassov

I’m really grateful to both of you for your answers. I’m quite confused about the ParentId/ProcessId thing… Is there any solution?

However, ParentId and ProcessId are just system-wide process identifiers that have absolutely nothing to do with

They’re called”handles” (due to a detail of implementation) because they are, actually, handles. They’re just not handles to either a process-specific or the kernel handle table. They come from a separate table that is maintained using the Ex handle management code. It’s just a convenient way to create/maintain a list of unique 32-bit values.

Peter

Well, I would expect the Std handles being opened (as a result of inheritance) when handle table of the new process gets created. I would expect that happening before the process notification callback is invoked, however, values of these handles might not be written in its PEB yet.

Well, I would expect the Std handles being opened (as a result of inheritance) when handle table of the new process gets created.

Which system are you speaking about??? What you are saying above very obviously applies to the systems that create processes by means of fork() system cal. In this case a newly-created process, indeed, inherits the address space and descriptors of its parent. Although ZwCreateProcess() allows an option of making a child inherit handles of its parent (this is how one can implement fork() call for POSIX subsystem), this feature is of no use to “regular” Windows processes that are created by a call to CreateProcess().

Anton Bassov

On May 25, 2019, at 2:31 PM, anton_bassov wrote:
>
> Although ZwCreateProcess() allows an option of making a child inherit handles of its parent (this is how one can implement fork() call for POSIX subsystem), this feature is of no use to “regular” Windows processes that are created by a call to CreateProcess().

I think you’re dismissing this too handily. The STARTUPINFO structure that one passes to CreateProcess literally contains the stdin, stdout, and stderr handles that are to be used by the new process. This is how one implements pipes, which are definitely a key Win32 feature, and not just a Posix feature.

That’s partly why I couched my answer in some doubt. In the case of a process in a pipeline, I expect these handles would be valid as soon as the process space was created.

Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

The STARTUPINFO structure that one passes to CreateProcess literally contains the stdin, stdout, and stderr handles that are to be used by >the new process.

Even more, CreateProcess() also has " bInheritHandles" parameter as well, so that above mentioned std handles are not the only ones that may potentially be inherited by the child. However, ZwCreateProcess() will inherit only those handles that are explicitly marked as inheritable ones with HANDLE_FLAG_INHERIT. This flag is not set by default, so that you have to call SetHandleInformation() on your target handles before calling CreateProcess(). Concerning std handles in STARTUPINFO, STARTF_USESTDHANDLES flag has additionally be specified in order to make the child inherit these handles. Otherwise, the console window’s buffer is going to be used for stdin/stdout/stderr.

My point is that inheriting handles “rather untypical” feature that you should not expect by default. OTOH, I should not have brushed off this possibility too quickly either

In the case of a process in a pipeline, I expect these handles would be valid as soon as the process space was created.

This is out the question - once inheriting handles is a feature that is implemented by ZwCreateProcess() , inherited handles are going to be valid immediately after ZwCreateProcess() returns control…

Anton Bassov

I am probably missing something but I consider handle inheritance as a key concept in creating child processes with redirected standard input/output/error streams. The approach is quite straightforward:

  1. create objects for handling standard input/output/error streams (e.g. pipes, regular opened files etc),
  2. set their handles inheritable as needed (you need to inherit “write” handle for stdout and stderr, “read” handle for stdin),
  3. specify the handles in the STARTUPINFO structure,
  4. call CreateProcess.

The handles are inherited, placing their values into STARTUPINFO ensures the child process knows their values. I do not think the handles are automatically duplicated to the child process just because they are present in the STARTUPINFO structure, their inheritance does just that. No need to make things more complicated than they already are.

Here is some sample code that does hits.
https://docs.microsoft.com/en-us/windows/desktop/procthread/creating-a-child-process-with-redirected-input-and-output

On *NIX systems, the advantage of fork() is that the child process sort of knows values of all handles (file descriptors) inherited from the parent. But if the call is followed by exec() you pretty much get the same result as CreateProcess does on Windows (including that file descriptor marked as “noninheritable” are closed).

I am probably missing something but I consider handle inheritance as a key concept in creating child processes with redirected standard >input/output/error streams.

Well, this is what the option of inheriting handles is for, in the first place. However, unlike UNIX -like systems, this not not the kind of thing that you usually encounter in the Windows world, right…

I do not think the handles are automatically duplicated to the child process just because they are present in the STARTUPINFO structure, >their inheritance does just that.

According to the following link, std handles in the STARTUPINFO structure are ignored if STARTF_USESTDHANDLES flag is not set.

https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/ns-processthreadsapi-_startupinfoa

If this is the case, then setting STARTF_USESTDHANDLES flag in the STARTUPINFO structure, apparently, makes CreateProcess() call SetHandleInformation() on the target handles behind the scenes, effectively making the target handles inheritable.

At the same time, the link below indirectly suggests otherwise

https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessa

[begin quote]

Important The caller is responsible for ensuring that the standard handle fields in STARTUPINFO contain valid handle values. These fields are copied unchanged to the child process without validation, even when the dwFlags member specifies STARTF_USESTDHANDLES. Incorrect values can cause the child process to misbehave or crash. Use the Application Verifier runtime verification tool to detect invalid handles.

[end quote]

  1. create objects for handling standard input/output/error streams (e.g. pipes, regular opened files etc),
  2. set their handles inheritable as needed (you need to inherit “write” handle for stdout and stderr, “read” handle for stdin),
  3. specify the handles in the STARTUPINFO structure,
  4. call CreateProcess.

Taking into consideration yet another example of contradictions in the official documentation, I think this is,indeed, the most reasonable option

On *NIX systems, the advantage of fork() is that the child process sort of knows values of all handles (file descriptors)
inherited from the parent. But if the call is followed by exec() you pretty much get the same result as CreateProcess does
on Windows (including that file descriptor marked as “noninheritable” are closed).

Well, making a child immediately call exec() seems to defeat the very purpose of splitting the process creation in two stages, don’t you think…

Anton Bassov

Well, making a child immediately call exec() seems to defeat the very purpose of splitting the process creation in two stages, don’t you think…

I think so. IIRC some systems did not support process creation “in one call”, so the fork-exec sequence is still in use.

Thank you very much for the links.