Search code examples
c++windowswinapicode-injectiondll-injection

Change context of an alertable / waitable thread


I want to inject a piece of code into a running module using thread suspension method.

  1. SuspendThread
  2. GetThreadContext
  3. DoSomething
  4. ResumeThread

My question is what would happen if the thread I'm currently injecting is in alertable / waitable mode(WaitForSingleObject, GetMessage). what would happen once i hit the ResumeThread command.


Solution

  • The same thing that would have happened otherwise, I assume.


    Lets say the target thread is currently in user mode. You save all the registers for later, set RIP to point to your code and call ResumeThread(). At some point your code start to execute, does whatever it does, restores all the registers the injection code saved, and lets the program resume its normal operation.

    Now lets say the target thread is waiting. Waiting means the thread performs a system call that tells the scheduler not to schedule the thread for execution until something happens (an event is signaled, etc.). You save the registers of the user mode context (the way they were when sysenter was called), set RIP to point to you code and call ResumeThread(). That all well and nice, but the scheduler still won't schedule it for execution until the terms of the wait are satisfied.

    When the wait finally ends, the thread does finished its business in kernel-mode, returns to user mode, and instead of executing the ret command following the sysenter goes on to perform your code. Finally your code restores all the registers and jumps to the saved RIP (from ntdll!ZwWaitForSingleObject or whatever) and everything continues as normal.

    Finally, lets say you were performing an alertable wait. The story goes on pretty much as in the previous two paragraphs (you don't really need me to repeat that a third time, do you? :)), except that before the wait function returns it executes all the user APCs queued for the thread - exactly as it would have happened without your intervention - and then goes on to execute your code etc.


    So basically what happens is what you should have expected to happen:

    • If you called SetThreadContext() the user-mode context is changed and the computer behaves accordingly, regardless of whether the thread was waiting or not.
    • If the thread was waiting for something it continues waiting for the same thing, regardless of whether you called 'SetThreadContext()' or not.
    • If the thread was in an alertable wait, before the system call returns it makes sure the user APC queue is empty (either because there were user APCs and it called them or because the queue was empty and the 'regular' wait condition finally happened). This, again, regardless of whether you called SetThreadContext() or not.