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` 目录阅读

在 `ntdll.dll` 中,有多个包含 `syscall` 的函数。
这些函数是从用户态切换到内核态的交接点。
大多数 EDR 会挂钩这些函数,并在执行前检查参数。
规避这种方法非常简单:在我们的代码中,包含一个调用该指令地址的函数。
假设 `ntdll.dll` 已经被解析。
我们获得了所需特定函数的地址,现在需要知道 `syscall` 发生的位置。
指令 `0x0F05` 标记了一个 `syscall`。
读取该函数处的字节,找到 syscall,并将其存储在任意位置;
将该指令存储在寄存器中,然后将该寄存器作为函数进行调用。
标签:Awesome列表, C2通信, Chrome扩展, EDR绕过, Hpfeeds, Windows API, 中高交互蜜罐, 云资产清单, 免杀技术, 反向Shell, 安全技术, 恶意软件开发, 数据展示, 暴力破解检测, 系统调用, 红蓝对抗, 红队, 网络套接字, 网络安全, 逆向工程, 间接系统调用, 隐私保护, 高交互蜜罐