Shellcode

Introduction to Shellcode

Shellcode is machine code that executes specific operations when injected into a target process's memory. It typically serves as the payload in exploits, performing actions like opening a command shell, establishing network connections, or executing arbitrary commands.

Basic Shellcode Concepts

Shellcode Execution Pattern

  • Shellcode injection follows the "Allocate-Write-Execute" pattern:

    1. Allocate RWX memory with size of shellcode

    2. Write shellcode to allocated memory

    3. Execute the content of allocated memory

  • Avoid direct PAGE_EXECUTE_READWRITE allocation as it's easily flagged. Better to:

    1. Allocate with PAGE_READWRITE

    2. Write payload

    3. Change protection to executable

Position-Independent Code Techniques

Shellcode Development Process

API Resolution for Windows Shellcode

identifying kernel32.dll:

  • get PEB->Ldr.InMemoryOrderModuleList

  • iterate through PEB->Ldr.InMemoryOrderModuleList; the first entry is the exe, followed by ntdll.dll, and then kernel32.dll (Win10/11 +). Hash-compare names to locate it

  • parse export address table

  • loop over export address table to find GetProcAddress name

  • get function ordinal and then address ( you have access to GetProcAddress now)

  • ensure GetProcAddress is available before resolving LoadLibraryA; both can be fetched in the same enumeration loop

  • resolve LoadLibraryA using GetProcAddress and call it on WS2_32.dll

  • get and call WSAStartup

  • lookup WSASocketA and create a socket with it

  • lookup WSAConnect and connect to the socket

  • find the address for CreateProcessA from kernel32.dll again

  • push cmd.exe to stack, fill STARTUPINFOA and call CreateProcessA

  • use a nc -nvv -p 443 -l on your own machine and run the shellcode to connect back to it

  • p.s: don't forget to fix the IP and port

Shellcode Execution Flow (Windows)

Shellcode Loaders

Shellcode loaders typically follow this pattern:

Loader Implementation

Meet Shellcode Loader

  • Takes shellcode as input, verifies environment, injects it if all is good

  • Responsible for evading sandboxes, proxies, AV, EDR, and safely landing in a machine

  • Environment verification/keying, careful shellcode decryption

  • Ends its duties after injecting shellcode

  • Injection targets:

    • Self process

    • Other process

  • Vulnerable to automated-detonation/sandboxes

  • Loader might reside in:

    • Process tree

    • Process modules list

  • Recommended languages:

    • Rust (secure, but larger binaries can stand out)

    • Nim

    • Go (runtime signature easily flagged)

    • Zig (small, no runtime, clean FFI)

    • Carbon preview (experimental, minimal footprint)

Allocation Phase

  • Linear, big enough R+X memory region options:

    • New memory region/section

    • Code caves

    • Stack, heap (after adjusting page permissions + disabling DEP)

    • Keep memory regions small/separate

  • Typical allocation techniques:

    • VirtualAllocEx / NtAllocateVirtualMemory

    • ZwCreateSection + NtMapViewOfSection

  • Best practice - avoid RWX allocations:

    1. Allocate RW

    2. Write shellcode

    3. Use VirtualProtectEx to switch to RX

Write Phase

  • Typical write techniques:

    • WriteProcessMemory / NtWriteVirtualMemory

    • memcpy to mapped section

    • Atom bombing (loud and unreliable)

  • Evasion strategies:

    • Prepend shellcode with dummy machine opcodes

    • Split shellcode into chunks and write in randomized order

    • Apply delays between consecutive writes

Execute Phase

  • Most fragile step in the process:

    • New thread introduced, so EDR closely inspects starting point

    • EDR might check if starting function is in image-backed memory (e.g., ntdll.dll)

  • Typical execution techniques:

    • CreateRemoteThread / ZwCreateThreadEx

    • NtSetContextThread

    • NtQueueApcThreadEx

    • API function hooks / trampolines

  • Some EDRs might spoof Thread Execution Block or ntdll.dll to detect if the process is attempting to modify them

Exec/DLL to Shellcode Conversion

Cross‑Platform Considerations

Windows on ARM64 (WoA)

  • Snapdragon X Elite and Surface Pro 10 ship with WoA. Syscalls use SVC 0 and the ARM64 table in ntdll!KiServiceTableArm64.

  • Pointer Authentication (PAC) signs the LR value; avoid stack pivots or re‑sign the new target with PACIASP.

  • Example loader: examples/woa_loader.s.

Linux 6.9+: eBPF Arena & BPF Tokens

  • Kernel 6.9 introduces BPF tokens and a shared BPF arena (BPF_MAP_TYPE_ARENA) that can hold executable memory.

  • Hide shellcode chunks in an arena map and call them via bpf_prog_run_pin_on_cpu.

  • Demo: examples/ebpf_arena_loader.c.

macOS Signed System Volume (SSV)

  • macOS 12+ seals the system partition; unsigned payloads can't live there.

  • Persistence flow:

    1. Create a sealed‑system snapshot (bless --mount … --create-snapshot).

    2. Mount snapshot read‑write, inject payload, resign with kmutil, and bless the new snapshot.

  • User space: use launch agents or dylib hijacks in /Library/Apple/System/Library/Dyld/.

Shellcode Storage & Hiding Techniques

Storage Options

  • Hardcoded:

    • Shellcode stored in code requires recompilation before use

    • Typically stored in RW/RO section

  • PE Resources:

    • Can use RCDATA binary blobs

    • Most extensively scanned by AV

  • Additional PE Section:

    • Stored in additional R+X section

    • Use second-to-last / N-to-last section

  • Acquired from internet:

  • Certificate Table (highly recommended):

    • Patch the PE file by padding Certificate Table with shellcode bytes and update PE headers

    • Example: Embed shellcode in teams.exe, backdoor ffmpeg.dll with shellcode-loader

    • Shellcode loader finds & loads shellcode inside memory

    • This keeps teams.exe signature intact and only breaks ffmpeg.dll signature

    • When teams.exe starts, it loads ffmpeg.dll, which in turn loads the shellcode from teams.exe's certificate table

  • Legitimate PE backdoored with shellcode & loader

Protection Methods

Shellcode should be protected using:

  • Compression (LZMA)

  • Encryption/encoding (XOR32/RC4/AES)

Evasion Techniques

Progressive Evasion Techniques

For shellcode execution with increasing evasion capability:

  1. Begin with basic shellcode execution

  2. Add encryption/obfuscation

  3. Implement direct syscalls to bypass monitoring

  4. Move to remote process injection when needed

Windows Defender Evasion

Shellcode Evasion Tools

  • Available at github.com/hackmosphere/DefenderBypassarrow-up-right:

    • myEncoder3.py - Transforms binary files to hex and applies XOR encryption

    • InjectBasic.cpp - Basic shellcode injector in C++

    • InjectCryptXOR.cpp - Adds XOR decryption for encrypted shellcode

    • InjectSyscall-LocalProcess.cpp - Uses direct syscalls to bypass userland hooks, removes suspicious functions from IAT

    • InjectSyscall-RemoteProcess.cpp - Injects shellcode into remote processes

    • Windows 11 version 24H2 introduces AMSI heap scanning; allocate with PAGE_NOACCESS, decrypt in place, then switch to PAGE_EXECUTE_READ to avoid live-heap scans

Local vs Remote Injection

  • Remote injection faces multiple technical challenges:

    • Enforced defenses (CFG, CIG)

    • Event tracing in Windows (ETW)

    • More closely monitored steps: open process handle, allocate, write, execute

  • Remote injection detection points:

    • ETW Ti feeds

    • EDR hooks and sensors

    • Back-tracing thread's call stack (was NtOpenProcess invoked from a trusted thread?)

Advanced Implementations

DripLoader Technique

  • Implementation details:

    • Reserves enough 64KB big chunks with NO_ACCESS

    • Allocates chunks of Read+Write memory in that reserved pool, 4KB each

    • Writes shellcode in chunks in randomized order

    • Re-protects into Read+Exec

    • Overwrites prologue of ntdll!RtlpWow64CtxFromAmd64 with a JMP trampoline to shellcode

    • Uses direct syscalls to call out to NtAllocateVirtualMemory, NtWriteVirtualMemory, NtCreateThreadEx

Practical Examples

Reverse Shell Shellcode Implementation

[!TIP] Windows 11 23H2 blocks outbound TCP 443/4444 to local subnets (only when Smart App Control policy is set to Block). Choose a non‑standard port or use a named‑pipe payload.

Last updated