.NET Inside Out Part 15 — Starting process on different desktop

This is the fifteenth part of the .NET Inside Out series. For your convenience you can find other parts in the table of contents in Part 1 – Virtual and non-virtual calls in C#

Windows supports multiple desktops for a long time. We may want to run an application on different one than the current but it isn’t easy in C#. There is an lpDesktop property but it isn’t directly exposed to the C# wrapper. Let’s see what we can do about that.

First, let’s see if it works. I’m using Sysinternals Desktops application to create new desktop. It is named “Sysinternals Desktop 1”. To start a process there I can use the following C++ code:

Now, we somehow need to get an access to STARTUPINFO record in C#. Let’s take this code:

When we debug Start method internals we get to the following:

It’s quite a lot of code which we don’t want to duplicate. Crucial line is:

Let’s go deeper:

Okay, so we have a class with two interesting fields. One of them is the size of the object — standard WinAPI approach. The other one is lpDesktop which is initialized to null pointer and cannot be directly modified. What can we do?

Well, as usual — let’s hijack the constructor and do some magic.

We start with creating a string for desktop name (lines 13-15). We need to pin it to make sure it is not moved by the GC. Next, we get the type used by .NET (line 19) and its constructor (line 20). Finally, we have a method for new constructor (39-43) which we get through reflection in line 21.

So what do we do? We use reflection to get method handles for old constructor and a new dummy method which we want to inject in the old place. We then do the trick with jump to hijack the constructor and execute NewConstructor method in place of the old one.

Now, in NewConstructor we use reflection to modify both fields. First, we set the size in line 41. Finally, we just get the pointer to the byte array and put it in the field (line 42).

Rest of the code should be pretty familiar, we just unlock the page, calculate relative address and do the jump. Nothing special here.

One important thing to note is line 14: we get normal bytes of the desktop name and then encode it in UTF 16 because application runs as a Unicode. So if we have string like abcde we need to introduce zeroes after each letter to get something like a 0 b 0 c 0 d 0 e 0. Obviously, letters must be replaced with ASCII codes. At the very end we need to add two more zeroes to represent the zero terminating the string (to get the null-terminated string).

That’s it. Changes for x64 or Ansii application should be pretty obvious now.