CreateRemoteThread, Vista and separate sessions

Recently I’ve hit a wall during development. I had written a nice workaround for a problem, based on code injection. In fact the code wasn’t injected by loading a DLL but instead by loading relocatable (32bit) code of less than 250 byte size. However, once I started testing it on Vista, the topstack method to retrieve the address of kernel32.dll inside the target process didn’t work anymore, so I had to resort to the PEB method. No big deal.

However, once I got that sorted out, the whole thing worked when run from the same (terminal) session, but failed as soon as the program (in simulation of the later real-world conditions) was started as SYSTEM by the task scheduler (i.e. from the session in which services run). Obviously the task scheduler isn’t all too talkative about the reasons of failure of a scheduled program, so my assumption was, that it must have to do with the stricter session separation on Vista and the documentation of CreateRemoteThread() confirms this:

Terminal Services isolates each terminal session by design. Therefore, CreateRemoteThread fails if the target process is in a different session than the calling process.

Only later I found out, that the task scheduler has its own window station and desktop to separate programs, but the problem was in fact based on the restrictions in CreateRemoteThread() - i.e. the fact it didn’t work with task scheduler was a false alarm wrt. to my problem. After asking Nico, a friend and fellow system-level developer, it turned out he had already hit the same wall. He pointed me to this thread in the madshi.net forum (if you’re a Delphi/BCB programmer, try out madshi’s excellent components!). In this thread Nico describes how to overcome the limitation by using an APC. A quite clever method indeed. Here’s how it works in a nutshell. The APC which is being queued for the thread will get executed once the thread gets signaled. Normally a thread gets signaled when it exits, so setting the thread start address to kernel32!ExitThread as suggested by Nico will achieve exactly this. To create the suspended thread using ntdll!RtlCreateUserThread at kernel32!ExitThread is only the “framework” for what’s actually going on. Whatever code needs to be executed will be defined by the “APC routine” and the parameter is the one to the call of ntdll!NtQueueApcThread. It has to be tested whether ntdll!NtQueueApcThread can be replaced by a call to kernel32!QueueUserAPC.

Searching the web will also reveal that some people claim that NtCreateThreadEx() can be used to overcome the limitation in a similar way. Perhaps it can, given that RtlCreateUserThread() calls NtCreateThreadEx() almost directly - the only change is a “cast” of the third parameter (a BOOLEAN) to a 32bit type.

But that still doesn’t even touch the actual issue. Windows uses LPCs in order to register threads as belonging to the Win32 subsystem (csrss.exe is the server process). People have reportedly destroyed hardware by sending the wrong LPC message to the wrong port on older Windows versions. The point is, there is no easy way to “emulate” this behavior and those messages as well as their structure differ by OS version. So to make a long story short: there is no easy way to register a thread as being a Win32-thread yourself without risking some major damage.

Meet native threads. The mystery that was left, was why LoadLibrary() would seemingly work with the above APC-method, yet a call of CreateProcess() from within my relocatable code would fail at all times. The reason is simple: the thread not being a Win32-thread is limited in what it can do, as there is no proper “connection” to the subsystem. Consequently those calls that rely on respective features will fail, while others may succeed. Sad, but that is how it is.

CreateProcessAsUser() came to the rescue on those systems, although - of course - creating the new process in the right desktop inside the target session is not possible by any documented means, thanks to the inability to retrieve the information which is “right” …

// Oliver

11 Responses to “CreateRemoteThread, Vista and separate sessions”


  1. 1 carlos

    “creating the new process in the right desktop inside the target session is not possible by any documented means” so you cant get the session id form a process/thread running in the right desktop?
    I mean gettokeninformation(targetthread… sessionid…) then settokeninformation (newtoken, sessionid) CreateProcessAsUser(newtoken) if you have problems with drawing in the desktop you can always add the logonsid to newtoken

  2. 2 carlos

    Also have you tried elicz libs?

  3. 3 Oliver

    Carlos, the problem is, to find out in which exact desktop the other process is in the target session. If it is the default desktop, everything will be fine, otherwise there really doesn’t seem to be any documented way.

    To elaborate on the scenario: I wanted to start a GUI program under the session of which I knew that it ran some other program already. So injection was a natural choice.

    And no, haven’t tried libs from EliCZ.

  4. 4 Oliver

    GetTokenInformation tells you only the session (and there are other functions to retrieve this as well), however, getting the desktop/winsta, requires to run code in the remote process, so you’re back at step one.

  5. 5 carlos

    Ahh i see i missunderstod you, but are you sure that getting the desktop/winsta, requires to run code in the remote process?

  6. 6 Oliver

    Ahh i see i missunderstod you, but are you sure that getting the desktop/winsta, requires to run code in the remote process?

    Frankly, I didn’t find a documented way. Obviously the flaw must be in me or the documentation then ;)

    If you found a method, go ahead and tell. Although the problem has been solved, it would be nice to refine it in this point.

  7. 7 carlos

    well there is no documented way but i don’t think it requires to run code in the remote process, maybe you can get the winsta\desktop name from peb->ProcessParameters->DesktopInfo

  8. 8 Oliver

    Wasn’t that at a random address now in Vista? So I’d still have to check the fs:0 address within the context of the remote thread/process?!

  9. 9 carlos

    I was thinking NtQueryInformationProcess(…ProcessBasicInformation…).

  10. 10 Oliver

    Aaah, indeed. That might work.

  11. 11 Chris

    If this process resets its winsta or desktop, you have no chance to find it without creating a process and let it look in every available winstas and desktops for the window you are looking for.

Leave a Reply