I promised I’d blog about this little bugger, so here it is. It’s an amazing little API that’s pretty useful for various purposes. Let’s look at the definion first:
IN HANDLE Process,
IN HANDLE Thread,
IN PVOID CallSite,
IN ULONG ArgumentCount,
IN PULONG Arguments,
IN BOOLEAN PassContext,
IN BOOLEAN AlreadySuspended
You can already guess what this function does! Basically, it hijacks any thread of your choice into any process that you have access to, and sends the thread to a new “CallSite” by updating its EIP there. It also allocates a new ESP, and pushes up to 4 parameters at most. That’s somewhat similar to CreateRemoteThread, but notice that in this case we’re hijacking an existing thread, not creating a new one. Which means that once the CallSite has been hit, the thread will simply die.
So this is where the PassContext argument comes into play. If set to TRUE, then the function will also push the thread’s context before being supended and modified, on the new ESP. Therefore, if you control the routine on the other side, you can have it issue a NtSetContextThread(&Context), and the hijacked thread will return where it was executing. Be careful with waits though, since this will restart a wait.
Finally, the AlreadySuspended parameter is useful if your thread is already suspended, and you want to resume it yourself at a later time. Otherwise, the remote call is instant (as soon as the OS switches to the other thread).
So what’s all this good for? Turns out one great use is to build a sweet IPC (Inter-Process Communication) framework without resorting to LPC/RPC. I built a sample project that I’ll release soon, but the basic premise is that two threads, client and server, in different processes, are running. The server thread is always suspended, and loops around a NtSuspendThread call (for the case when it gets waken up). The server process has a special routine for incoming requests. The client process has a special routine for incoming replies. The client issues, at various times, InterAPI calls. These calls have 4 parameters, which is the maximum. The first is the actual Thread ID of the caller, the second is an API Number, and the last 2 parameters are specific to whichever API call is being done. The handler then calls the respective handler (based on a table, the API Numbers are indexes). To reply, the handler then issues a second RtlRemoteCall back to the client (to its reply routine). But how do we know the caller? That’s where the Thread ID comes in, since we can get a handle to it and its process by using NtQueryInformationThread. And how do we know the pointer to the callback routine? Let’s leave that as a surprise for the code.
This callback routine only takes one parameter, which is the return code. The callback then saves this return code in the TEB (guess where?), and issues an NtResumeThread, which will cause the caller thread to return. The caller thread can then read the return code from the TEB and return it to the caller of the InterAPI.
The code will be shown soon