This is an adaptation for Steemit of Zerosum0x0. if you want to have it in HTML format you can see it here.
In the attempt to evade AV, attackers go to great lengths to avoid the common reflective injection code execution function, CreateRemoteThread(). Alternative techniques include native API (ntdll) thread creation and user APCs (necessary for SysWow64->x64), etc.
This technique uses SetThreadContext() to change a selected thread's registers, and performs a restoration process with NtContinue(). This means the hijacked thread can keep doing whatever it was doing, which may be a critical function of the injected application.
You'll notice the PoC (x64 only, #lazy) is using the common VirtualAllocEx() and WriteVirtualMemory() functions. But instead of creating a new remote thread, we piggyback off of an existing one, and restore the original context when we're done with it. This can be done locally (current process) and remotely (target process).
Stage 0: Thread Hijack
Code can be found in hijack/hijack.c
- Select a target PID.
- Process is opened, and any thread is found.
- Thread is suspended, and thread context (CPU registers) copied.
- Memory allocated in remote process for reflective DLL.
- Memory allocated in remote process for thread context.
- Set the thread context stack pointer to a lower address.
- Change thread context with SetThreadContext().
- Resume the thread execution.
Stage 1: Reflective Restore
Code can be found in dll/ReflectiveDll.c
- Normal reflective DLL injection takes place.
- Optional: Spawn new thread locally for a primary payload.
- Optional: Thread is restored with NtContinue(), using the passed-in previous context.
You can go from x64->SysWow64 using Wow64SetThreadContext(), but not the other way around. I unfortunately did not observe possible sorcery for SysWow64->x64.
One major hiccup to overcome, in x64 mode, is that the register RCX (function param 1) is volatile even across a SetThreadContext() call. To overcome this, I stored a cave (in this case, the DOS header). Luckily, NtContinue() allows setting the volatile registers, so there's no issues in the restoration process, otherwise it would have needed a hacky code cave inserted or something.
// retrieve CONTEXT from DOS header cave
lpParameter = (LPVOID)*((PULONG_PTR)((LPBYTE)uiLibraryAddress+2));
Another issue is we could corrupt the original threads stack. I subtracted 0x2000 from RSP to find a new spot to spam up.
I've seen similar (but non-successful) techniques for code injection. I found a rare amount of similar information [1] [2]. These techniques were not interested in performing proper cleanup of the stolen thread, which is not practical in many circumstances. This is essentially the same process that RtlRemoteCall() follows. As such, there may be issues for threads in a wait state returning an incorrect status? None of these sources uses reflective restoration.
As user mode API is highly explored territory, this may not be an original technique. If so, take the example for what it is ([relatively] clean code with academic explanation) and chalk it up to multiple discovery. Leave flames, spam, and questions in the comments!
If you want to learn more about techniques like this, come to the Advanced Windows Post-Exploitation / Malware Forward Engineering DEF CON 25 workshop.
Hi! I am a robot. I just upvoted you! I found similar content that readers might be interested in:
https://zerosum0x0.blogspot.com/2017/07/threadcontinue-reflective-injection.html
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit