SilentisVox/Reverse-Shell-with-Indirect-Syscalls

GitHub: SilentisVox/Reverse-Shell-with-Indirect-Syscalls

一份通过间接系统调用在 Windows 上实现反向 shell 的技术实现与教学文档,核心目标是绕过 EDR 的用户态钩子检测机制。

Stars: 1 | Forks: 0

# 通过间接系统调用实现反向 Shell 在最底层,建立连接和创建进程是非常困难的。 利用对系统行为的了解,可以通过这些底层调用来构建反向 shell。 这是一个使用间接系统调用建立反向 shell 的具体实现。 **免责声明**:此内容仅用于教育和测试目的。 请勿在未获授权的机器上使用。 请勿利用此技术在未获授权的情况下连接或控制机器。 ## 创建反向 shell。 ###### 结合 `examples/simple` 目录阅读 Windows 提供了许多具有不同用途的函数,但关键的函数将用于**建立连接**和**创建进程**。 `CreateProcessA` 函数可以创建一个进程; 创建进程时涉及的一个结构是 `STARTUPINFO`。 该结构中包含用于标准句柄的字段。 这意味着进程的标准输入输出可以通过网络套接字进行重定向。 创建网络套接字的关键函数有:`WSAStartup`、`WSASocketA` 和 `connect`。 要获取网络套接字的句柄(附带所有 Windows 功能),您必须首先通过 `WSAStartup` 加载必要的库。 `WSASocketA` 会检查 `WSAStartup` 是否已执行,并创建一个用于连接的句柄。 有了句柄后,您就可以使用 `connect` 连接到远程主机。 ## 深入底层。 观察反向 shell 的执行流程时,我们可以看到哪些底层函数被高层函数所调用。 我们至少需要一个套接字以及用于建立连接的机制。 观察 `WSASocketA` 的调用堆栈。 ``` → WS2_32!WSASocketA → WS2_32!WSASocketW → mswsock!WSPSocket → mswsock!SockSocket → ndll!NtCreateFile ``` 在最底层,`NtCreateFile` 会根据参数传入的端点创建一个句柄。 与之通信的驱动程序是 `afd.sys`,我们通过扩展属性中看到的 `AfdOpenPacketXX` 从中创建一个句柄。 这个句柄是一个套接字,但它与文件句柄没有任何区别。 可以以描述网络连接的方式来控制该套接字。 在建立连接之前,套接字需要绑定地址和端口。 然后就可以连接到远程地址和端口了。 观察 `connect` 的调用堆栈。 ``` → WS2_32!connect → mswsock!NSPStartup → ndll!NtDeviceIoControlFile ``` 句柄的所有行为都可以通过 `NtDeviceIoControlFile` 进行修改。 根据给定的控制代码,内核将按照调用者的描述更改与该句柄相关的信息。 我们需要关注的 2 个控制行为是绑定和连接。通过控制代码 `0x12003 && 0x12007` 执行了 2 次独特的调用。 传入的结构并不大; 在操作句柄的同时,我们可以进行绑定和连接。 最后,是创建进程。 正确传递标准句柄非常重要,这样进程才能继承/使用它们。 观察 `CreateProcessA` 的调用堆栈。 ``` → kernel32!CreateProcessA → kernelbase!CreateProcessA → kernelbase!CreateProcessInternalA → kernelbase!CreateProcessInternalW → ndll!NtCreateUserProcess ``` 在最底层创建进程可能是最难以复现的事情。 最重要的结构是 `UserProcessParams`。 当该结构被正确初始化时,它会包含窗口和标准句柄信息。 ## 复现相关行为。 要创建句柄,需使用 `NtCreateFile`。 尝试重现完全相同的调用非常重要,这样所有的行为才能保持一致。 我们需要的特定项是 `ObjectAttributes.Attributes = 0x42`; 该值表示创建的句柄是“可传递/可继承的”。 ``` NTSTATUS NTAPI NtCreateFile( PHANDLE File, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID Buffer, ULONG BufferLength ); ``` 对于这 2 次句柄修改,使用了 `NtDeviceIoControlFile`。 我特别想要复现的调用是“bind”(绑定)。 为了获得最自然的行为,最好自动绑定地址和端口。 需要保留的特定项是 `AfdBindSocket.dwFlags = 0x2`; 该值表示自动绑定地址/端口。 ``` NTSTATUS NTAPI NtDeviceIoControlFile( HANDLE File, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG IoControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength ); ``` 对于进程创建,使用了 `NtCreateUserProcess`。 一个重要的因素是确保标准句柄正确放置在 `UserProcessParamters` 结构中。 ``` NTSTATUS NTAPI NtCreateUserProcess( PHANDLE hProcess, PHANDLE hThread, ACCESS_MASK ProcessDesiredAccess, ACCESS_MASK ThreadDesiredAccess, POBJECT_ATTRIBUTES ProcessObjectAttributes, POBJECT_ATTRIBUTES ThreadObjectAttributes, ULONG dwProcessFlags, ULONG dwThreadFlags, PRTL_USER_PROCESS_PARAMETERS pProcessParameters, PPS_CREATE_INFO pCreateInfo, PPS_ATTRIBUTE_LIST pAttributeList ); ``` ## 间接执行系统调用。 ###### 结合 `examples/advanced` 目录阅读 ![assets/img.jpg](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/59540b4c39144825.jpg) 在 `ntdll.dll` 中,有多个包含 `syscall` 的函数。 这些函数是从用户态切换到内核态的交接点。 大多数 EDR 会挂钩这些函数,并在执行前检查参数。 规避这种方法非常简单:在我们的代码中,包含一个调用该指令地址的函数。 假设 `ntdll.dll` 已经被解析。 我们获得了所需特定函数的地址,现在需要知道 `syscall` 发生的位置。 指令 `0x0F05` 标记了一个 `syscall`。 读取该函数处的字节,找到 syscall,并将其存储在任意位置; 将该指令存储在寄存器中,然后将该寄存器作为函数进行调用。
标签:Awesome列表, C2通信, Chrome扩展, EDR绕过, Hpfeeds, Windows API, 中高交互蜜罐, 云资产清单, 免杀技术, 反向Shell, 安全技术, 恶意软件开发, 数据展示, 暴力破解检测, 系统调用, 红蓝对抗, 红队, 网络套接字, 网络安全, 逆向工程, 间接系统调用, 隐私保护, 高交互蜜罐