DITTOWARE: How I Built an EDR Evasion PoC That Bypassed 63/72 AV Vendors Including Microsoft Defender

⚠️ Research only. This is a documented proof-of-concept built for defensive security research and responsible disclosure. The payload executes calc.exe — nothing more. All findings were reported to Microsoft MSRC before publication. This post exists to help defenders understand what modern AV misses — not to help attackers.


I submitted a file to VirusTotal. The result came back 9/72 detected. Sixty-three vendors missed it. Microsoft Defender missed it. Kaspersky missed it. BitDefender missed it. McAfee missed it.

That file was something I built from scratch — a research PoC I named DITTOWARE.

After seeing those results I wrote a formal disclosure email to Microsoft with a PoC zip attached (generated directly from the GitHub Actions workflow). Microsoft updated their Defender signatures after the report. The case was filed with MSRC in February 2026 under coordinated disclosure.

This is the full story of how I built it, what techniques it used, why they worked, and what defenders should do about it.


The Name

I’ll be honest — I’m both proud and slightly embarrassed by this one.

The whole point of this PoC was to build a final payload that could mimic any signature, transform into anything, slip past any pattern matcher — a file with no fixed identity, no detectable fingerprint, something that could become whatever it needed to be to stay undetected.

In Pokémon, there’s exactly one creature that does this: Ditto. It transforms into any Pokémon it faces. It copies your form, your moves, your presence. It has no real identity of its own — it just becomes whatever it needs to be.

Ditto + malware = DITTOWARE.

I named it at midnight and I’m keeping it. Yes, it’s a terrible name. Yes, I’m aware a 9-year-old with a Pokédex could have come up with it. But if you think about it — a shapeshifting PoC that 63/72 AV vendors couldn’t pin down, named after the one Pokémon that literally cannot be identified by its own form? That’s either the worst name or the most accurate name in the history of security research. I choose the latter and I will not be taking questions.

(Ditto does them all. So does DITTOWARE.)


The Result First

DITTOWARE VirusTotal Scan — 9/72 detection Figure 1: VirusTotal scan results — installer.exe (252 KB), SHA256: 01b5351d2ba6e8ad2d84d6385197490efc4ee9efc60af174e66914bd7d544957. Scanned February 2026.

Metric Value
Vendors scanned 72
Detected 9 (12.5%)
Bypassed 63 (87.5%)
Microsoft Defender ✅ BYPASSED
CrowdStrike Falcon ❌ Detected (behavioral)
Kaspersky ✅ BYPASSED
BitDefender ✅ BYPASSED
McAfee ✅ BYPASSED

The 9 that caught it were: Bkav Pro, CrowdStrike Falcon, Cynet, DeepInstinct, Elastic, Google, Ikarus, SecureAge, and Symantec. Of those, 6 are ML-based behavioral EDRs that flagged it on behavior heuristics — not signatures. Traditional signature-based AV: 3/55 detection rate (5%).


Why I Built This

I’m an Ethical Hacker and VP of DDC at CBIT. Understanding what AV misses is fundamental to real defensive work. You can’t write good detection rules if you don’t know how evasion works. You can’t advise your organization on EDR choices if you don’t know which categories of techniques different vendors cover.

DITTOWARE started as a research question: can I combine four known-but-individually-detected techniques in a way that the sum of the parts defeats everything?

The answer was yes — and 63 vendors confirmed it.


The Architecture

flowchart TD subgraph ATK["Attacker Side — Python"] PG["payload_generator.py\n267-byte x64 shellcode\nlaunches calc.exe"] EE["entropy_encoder.py\nLSB Steganography\nEntropy: 0.29"] AS["attacker_server.py\nHTTP listener — /pwned"] PG --> EE end subgraph VIC["Victim Execution — C++ / MASM"] EXE["installer.exe — 252 KB"] UN["unhook.cpp\nRestore hooked ntdll\nbytes from disk"] DE["DecodeFromBMP\nExtract shellcode\nfrom pixel LSBs"] SB["SendBeacon\nHTTP callback"] MS["module_stomper.cpp\nOverwrite wintrust.dll .text\n→ execute shellcode"] EXE --> UN --> DE --> SB --> MS end EE -- "payload.bmp" --> EXE AS -. "receives beacon" .-> SB

Four independent techniques. Each one alone is detected by some vendors. Combined and layered, 63/72 missed the entire chain.


Technique 1: Bitmap Steganography (Entropy: 0.29)

The first problem any AV faces when it sees a PE is: does this look suspicious? One of the most reliable signals is Shannon entropy. Shellcode and encrypted payloads look almost random — entropy near 7.8 out of 8. ML classifiers flag anything above ~7.0 as high risk.

LSB steganography completely defeats this.

I wrote entropy_encoder.py to embed the 267-byte shellcode into the Least Significant Bits of a carrier BMP file’s pixel data:

def encode_payload(carrier_bmp, payload_bytes):
    """Embed payload in LSB of each pixel byte."""
    bits = []
    for byte in payload_bytes:
        for i in range(7, -1, -1):
            bits.append((byte >> i) & 1)
    
    for i, bit in enumerate(bits):
        pixels[i] = (pixels[i] & 0xFE) | bit
    
    return modified_bmp

Result:

Sample Entropy ML Detection Risk
Raw shellcode 7.82 HIGH
AES-encrypted payload 7.95 HIGH
payload.bmp (DITTOWARE) 0.29 LOW

The payload.bmp looks like a perfectly normal near-white bitmap to every entropy scanner. No AV vendor performs steganographic LSB analysis on BMP files in their scan pipeline.


Technique 2: Direct Syscalls (syscalls.asm)

Modern EDRs hook into ntdll.dll — the lowest-level Windows userspace library — by patching the first bytes of functions like NtCreateThreadEx and NtAllocateVirtualMemory with JMP instructions that redirect into the EDR’s monitoring DLL. This is how they intercept every API call your process makes.

Direct syscalls skip ntdll.dll entirely. Instead of calling NtAllocateVirtualMemory, you issue the raw syscall CPU instruction directly, with the correct System Service Number (SSN) loaded into eax:

; syscalls.asm — simplified
NtAllocateVirtualMemory PROC
    mov r10, rcx
    mov eax, SSN_ALLOCATE   ; Dynamic SSN — resolved at runtime
    syscall
    ret
NtAllocateVirtualMemory ENDP

The SSN is resolved dynamically at runtime by parsing the actual ntdll.dll on disk (not the in-memory hooked version). The EDR’s JMP patches never get hit. The process goes directly to the kernel — the EDR loses all visibility.


Technique 3: Module Stomping (module_stomper.cpp)

Most AV/EDR detects shellcode execution by looking for the classic RWX (Read-Write-Execute) memory regions. If you VirtualAlloc a block of memory and flip it to executable, that’s a bright red flag.

Module stomping avoids this entirely. Instead of creating new executable memory, I loaded a legitimate signed DLL (wintrust.dll — a Microsoft-signed digital signature library), mapped it into memory, and wrote the shellcode into its existing .text section.

The .text section is already RX (Read-Execute). No RWX allocation ever happens. The shellcode runs from memory that looks exactly like legitimate Microsoft code.

// module_stomper.cpp — simplified
HANDLE hFile = CreateFileW(L"C:\\Windows\\System32\\wintrust.dll", ...);
HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
LPVOID pDll = MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0);

// Write shellcode into .text section
memcpy((BYTE*)pDll + textSectionOffset, shellcode, shellcodeSize);

// Execute — appears as wintrust.dll code
((void(*)())((BYTE*)pDll + textSectionOffset))();

From a memory forensics perspective the execution appears to originate from a signed Microsoft DLL. No unsigned memory pages. No RWX regions.


Technique 4: Blind EDR — unhook.cpp

This runs first, before everything else. Modern EDRs patch the first 5–10 bytes of ntdll.dll functions in memory with JMP hooks. unhook.cpp restores those bytes by:

  1. Opening ntdll.dll from disk (the clean, unpatched version)
  2. Reading the original bytes from the .text section
  3. Writing them back over the hooked in-memory version with NtProtectVirtualMemory

After this runs, the EDR’s hooks are gone. It has no in-process visibility. It’s blind.

The real trick: this unhoking step itself uses direct syscalls (Technique 2) — so even the unhooking doesn’t trigger any EDR hooks. It’s self-referentially clean.


Why Each Defense Failed

Defense Layer Bypass Technique Root Cause
Signature DB Unique hash per build (GitHub Actions) No prior sample, no signature
ML Entropy Classifier Steganography (entropy 0.29) Below detection threshold of ~7.0
AMSI Native C++ binary only AMSI only hooks scripting runtimes (PowerShell, .NET)
User-mode EDR hooks Direct syscalls Kernel transition skips ntdll entirely
RWX memory detection Module stomping Uses existing RX section from signed DLL
Behavioral heuristics Clean API sequence + unhooking No suspicious call patterns visible

The Microsoft MSRC Disclosure

After seeing the results I wrote a formal disclosure email to Microsoft Security Response Center (MSRC). The email:

  • Documented all four techniques with proof
  • Attached the PoC zip (built by the GitHub Actions workflow — the same installer.exe that was scanned on VirusTotal)
  • Proposed four concrete mitigations (LSB analysis for images, kernel-level ETW syscall monitoring, .text section hash verification, driver-level telemetry)

Microsoft updated Defender’s signatures after receiving the report. The MSRC case was filed February 9, 2026, under coordinated disclosure with a 90-day timeline.

This is the correct way to do this work. You find the gap, you document it rigorously, you give the vendor the chance to fix it before you publish.


The GitHub Actions Build Pipeline

One detail I’m proud of: the entire build is automated. Push to main on GitHub → Actions compiles the Windows binary on a windows-latest runner → downloads installer.exe as a build artifact. No local Windows build environment needed.

This also means every build produces a unique binary (different compilation timestamp, slightly different object layout) — unique SHA256, no prior signature. The AV scanner has never seen it before the moment it’s submitted.

# .github/workflows/build.yml (excerpt)
jobs:
  build:
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v3
      - name: Build DITTOWARE
        run: |
          cd phase2_poc\native_loader\build
          .\build.bat
      - uses: actions/upload-artifact@v3
        with:
          name: dittoware-installer
          path: phase2_poc\native_loader\build\installer.exe

What This Means for Defenders

The key finding: traditional signature-based AV is completely ineffective against this technique combination. The 5% detection rate from traditional AV (3/55) was essentially random.

ML-based behavioral EDRs did much better — 6/8 detected it (75%). CrowdStrike and DeepInstinct flagged the behavioral pattern even when they couldn’t see the individual techniques. That’s the right architectural direction.

Recommended mitigations:

Gap Fix
LSB steganography Add BMP/PNG steganographic channel entropy analysis
Direct syscalls Kernel-level ETW provider for raw syscall monitoring
Module stomping Runtime hash verification of .text sections in mapped DLLs
Hook restoration Driver-level PPL telemetry collection (not userspace)

The YARA rules are in the research repo:

rule DITTOWARE_DirectSyscall {
    meta:
        description = "Detects direct syscall stub patterns"
        author = "Vasanthadithya Mundrathi"
    strings:
        $syscall_stub = { 4C 8B D1 B8 ?? ?? 00 00 0F 05 C3 }
    condition:
        $syscall_stub
}

What’s Still a PoC

DITTOWARE is deliberately minimal:

  • Payload: calc.exe only. No persistence, no lateral movement, no destructive capability.
  • Privilege level: User-space only. No kernel exploitation, no UAC bypass.
  • Scope: Detection evasion only. This is not a weaponized tool.

The research demonstrates the detection gap. The job of turning awareness into better AV is Microsoft’s and the security community’s.


Bypassing 63 of 72 AV vendors — including Microsoft Defender — as a student project, then responsibly disclosing it with a formal PoC and proposed mitigations, and watching Microsoft actually update their signatures in response: that’s what real security research looks like from the outside.

It’s not a script kiddie win. It’s a research contribution. The gap was real. The disclosure was handled correctly. The signatures are better now.

— Vasanth