In short, the sensor will log any user-mode APCs queued to a remote thread, be it from user-mode or kernel-mode. The most important check is implemented in the kernel function : EtwTiLogQueueApcThread as shown below.
|(Click to zoom)
So queuing a user-mode APC to a thread in a process other than ours is considered suspicious and will be logged. However, when having code execution in kernel-mode we can queue a kernel-mode APC that will run in the context of the target process and from there we can queue a user-mode APC. This way, the check when KeInsertQueueApc is called from the kernel-mode APC will always yield (UserApc->Thread->Process == CurrentThread->Process).
I have written a simple driver to test this out : https://github.com/SouhailHammou/Drivers/tree/master/Apc-Injection-ATP-Bypass
- The driver registers a CreateThreadNotifyRoutine in its DriverEntry.
- CreateThreadNotifyRoutine queues a kernel-mode APC to a newly created thread.
- The kernel-mode APC is delivered as soon as the IRQL drops below APC_LEVEL in the target thread in which we allocate executable memory in user-space, copy the shellcode, then queue the user-mode APC.
- The user-mode APC is delivered in user-mode.
Thanks for your time :)
Follow me on Twitter : here