I. INTRODUCTION
During red teaming or penetration testing, we often need to perform stealth
injection between processes. To do so, we must avoid detection and
intervention by Endpoint Detection and Response (EDR) solutions. That makes
bypass EDR essential when carrying out stealth injection.
In this article, I’ll introduce a new EDR evasion technique and release a
proof of concept (PoC) to demonstrate it.
I tested this bypass EDR technique against a high-end EDR, and it worked
smoothly, flying under the radar.
Additionally, this new EDR evasion technique uses a novel method to write the
payload into the target process without relying on the well-known
WriteProcessMemory API.
I’ve also previously published research on stealth injection without using
WriteProcessMemory; you can read it
here.
Find me on X to get the latest pentest and red team tricks that I’ve been researching: Two Seven One Three (@TwoSevenOneT) / X
II. MAIN SECTION
1. An overview of stealth injection for EDR evasion
As the name suggests, when performing process injection, we need to stay out
of the EDR’s watchful eye. But there aren’t many ways to bypass EDR, since the
process always involves several core APIs:
- OpenProcess to interact with the target process
-
Remote allocation/protection APIs to create a memory region for the
payload - Writing the payload into the remote process’s memory
- Executing the payload in the target process
EDR bypass techniques therefore tend to focus on these major steps:
either eliminating them or finding new variations on how they’re used.
Successfully performing stealth injection means we’ve successfully managed to
bypass EDR.
2. A new EDR evasion technique using Windows messages
You’ve probably heard of the Windows API
SendMessageW. This function sends the specified message to one or more windows. When used
with the WM_SETTEXT message, the target window calls its default window
procedure to update its internal caption, title bar, or control contents, such
as those of an Edit box or Button.
What if, instead of sending control contents, we sent our payload to the
target process?
If that’s possible, it would be an effective way to
bypass EDR without using WriteProcessMemory.
The contents of Edit controls are stored somewhere in the process’s memory. If
we can determine the address of that memory region, we can locate the payload
that was sent.
The main steps for sending the payload are:
-
Enumerate GUI processes that have a suitable control; in this article, I’ll
use an Edit control. Alternatively, create a new process as needed
(Notepad.exe on Windows 10 or Regedit.exe on Windows 11). - Get the control handle.
-
Send the payload using the WM_SETTEXT message through the
SendMessageW API. -
Search the target process’s memory for the sent message using the
ReadProcessMemory function.
This allows us to send our payload to the target process without using
WriteProcessMemory; a key step in the overall process of
bypass EDR.
3. EDR evasion with PhantomBuffer
To demonstrate this EDR evasion technique, I created a PoC suite called
PhantomBuffer. It consists of two small tools.
FindTargetWindows:
This tool searches for processes with suitable GUI controls for injecting a
payload. If it doesn’t find any, it can also spawn a process of your choice.
PhantomBuffer
This tool performs the following main steps:
- Reads and parses the payload from a file (Base64).
-
Sends the payload to the target by calling SendMessage with
WM_SETTEXT. -
Locates the payload in the target’s memory and grants that memory region
execute permission. - Redirects one of the target process’s threads to the payload.
The challenge of using stealth injection to bypass EDR when injecting a
payload into a target process:
This is addressed by using SendMessage with WM_SETTEXT.
During testing, I found that payloads containing two null bytes (0x00) weren’t written correctly to the target process. So, 0x00 is a bad character.
The challenge of redirecting a target process’s thread:
Here we run into another major issue: using the
SetThreadContext/NtSetContextThread APIs to hijack execution of a
remote thread. These two APIs are very well known for their use in EDR bypass
techniques.
As a result, Endpoint Detection and Response (EDR) systems closely monitor
their use. From indirect syscalls to directly calling NtSetContextThread or
using call stack spoofing, it’s an arms race between malware and EDRs.
I won’t go into detail in this article about EDR evasion using
SetThreadContext/NtSetContextThread. Instead, I’ll take a different approach.
Have you ever wondered what Microsoft created the
SetThreadContext/NtSetContextThread API pair for?
One possible answer is that they’re intended for monitoring programs such as
antimalware and EDR tools, as well as debugger programs.
So, what if I let a legitimate debugger perform the injection on
PhantomBuffer’s behalf? Which debugger could serve this purpose? Remember, we
don’t have a graphical interface, and we can’t manually adjust the debugger.
The answer to these questions is the cdb.exe debugger included in the
Windows Debugger Tools.
cdb.exe will perform the following specific tasks:
- Attach by PID
- Select thread by system TID
- Set RIP
- Detach
cdb.exe -p
qd"
-
-p
: Attaches to the process with the given decimal PID. -
-c “…”: Runs the quoted command string immediately after the initial
attach break. -
~~[
]s : Selects the thread whose system thread ID is
. The ID must be in hexadecimal, without a 0x prefix (e.g.,
~~[1a4]s). -
r rip=0x
: Sets the RIP register to the specified address.
The 0x prefix is required for hex values. -
qd: Quit and Detach. This ends the debugger session but leaves the target
process running.
The result will be as follows:
Once everything is properly set up, even high-end EDRs won’t have anything to say when you perform stealth injection.
We can make cdb.exe portable using only the DLLs bundled with it:
Some Endpoint Detection and Response may also flag cdb.exe if it is executed from a path other than the default:
“C:Program Files (x86)Windows Kits10Debuggersx64cdb.exe”
So, if possible, it’s safest to set it up so that cdb.exe is located at that path.
4. Defensive measures
Since this EDR evasion method based on stealth injection relies on the SendMessage API, monitoring calls to this API is a good first step.
The second bottleneck, of course, is the SetThreadContext/NtSetContextThread pair. In this article, I use a small trick involving CDB.exe, but detecting CDB.exe running from a path other than the default can also be a sign.
5. EDR Evasion Demonstration
III. SUMMARY
During EDR evasion, figuring out how to perform stealth injection into other processes without triggering alerts from Endpoint Detection and Response systems is part of the ongoing arms race between attackers and defenders.
EDR bypass techniques typically focus on hijacking a target process or thread so that it runs a payload or shellcode prepared in advance.
PhantomBuff is an EDR evasion technique that takes a different approach. It uses the SendMessage function with the WM_SETTEXT message to send a payload into a target process without using the well-known WriteProcessMemory API.
By combining SendMessage with a trick involving the Windows debugger CDB.exe, we can successfully stealth-inject code into another process, even under the close scrutiny of high-end Endpoint Detection and Response systems.
To prevent this EDR bypass method, we can also monitor the SendMessage API when used with the WM_SETTEXT message, as well as watch for CBD.exe running from an unusual location.
Author of the article: Two Seven One Three

