规避(EDR)安全检测--避免二进制文件落地
在 Windows 系统中,执行 VBScript(.vbs)和将 C# 代码加载到内存中执行的技术,
常被用于减少磁盘操作(避免二进制文件落地),从而降低被 EDR(端点检测与响应)工具检测的概率。
一、执行 VBScript(.vbs)的常用方式
VBScript 是 Windows 原生支持的脚本语言,可通过系统内置工具直接执行,无需额外依赖,且默认不会生成二进制文件(脚本本身是文本文件,执行过程在内存中解析)。
VBScript是Windows原生的脚本语言,其代码以纯文本形式存储在.vbs
文件中,由系统内置的解释器(如wscript.exe)动态解析并执行,过程中不会生成独立的二进制文件。
## VBS脚本的本质
文本文件:
.vbs
文件确实是纯文本文件(ASCII或UTF-8编码),可以用记事本等工具直接编辑,内容为VBScript代码(如MsgBox "Hello World"
)。与二进制文件(如
.exe
)不同,它不包含编译后的机器码,而是人类可读的脚本代码。
原生脚本语言:
VBScript(Visual Basic Scripting Edition)是Windows原生支持的脚本语言,无需额外安装(依赖系统内置的wscript.exe
或cscript.exe
解释器)。
## VBS执行过程
内存中解析执行:
执行流程如下:解释执行:当双击
.vbs
文件时,Windows调用wscript.exe
(图形界面)或cscript.exe
(命令行)加载脚本。实时解析:解释器逐行读取文本代码,动态转换为可执行的指令(类似Python/JavaScript),不会生成独立的二进制文件。
内存驻留:解析后的指令在内存中运行,脚本结束后释放资源。
❗ 注意:虽然代码在内存中解析,但并非“完全在内存中运行”。解释器可能会临时生成中间数据结构或调用系统API,这些操作可能涉及磁盘或注册表(如读写文件)。
## 补充说明
依赖环境:
VBScript依赖Windows的脚本宿主环境(Windows Script Host, WSH),默认内置在Windows中(但Win10/11已逐步弃用,需手动启用)。
非Windows系统(如Linux)无法直接运行
.vbs
文件。
安全风险:
由于是文本文件且直接解释执行,VBS脚本常被恶意软件利用(如通过邮件附件传播)。
微软已推荐改用更现代的PowerShell(
.ps1
)替代VBScript
## ps1 与 vbs 脚本区别
1.语言类型与设计目标语言类型与设计目标
特性 | VBScript (.vbs) | PowerShell (.ps1) |
---|---|---|
语言类型 | 基于 Visual Basic 的轻量级脚本语言 | 基于 .NET 的高级脚本/自动化语言 |
设计目标 | 简单的任务自动化、网页交互(IE)、WSH 脚本 | 系统管理、自动化、DevOps、跨平台支持 |
开发时间 | 1996 年(较老,逐渐淘汰) | 2006 年(现代,持续更新) |
维护状态 | 微软已弃用(Win10/11 默认禁用) | 微软主推(PowerShell 7+ 跨平台) |
2. 执行方式
特性 | VBScript (.vbs) | PowerShell (.ps1) |
---|---|---|
解释器 | wscript.exe (GUI)或 cscript.exe (CLI) | powershell.exe (Windows)或 pwsh (跨平台) |
执行权限 | 默认可直接运行(但 Win10/11 可能受限) | 默认受限(需 Set-ExecutionPolicy 调整) |
运行环境 | 仅限 Windows(依赖 WSH) | Windows + Linux/macOS(PowerShell Core) |
3.功能对比
特性 | VBScript (.vbs) | PowerShell (.ps1) |
---|---|---|
面向对象 | 有限(基于 COM 对象) | 完全面向对象(.NET 集成) |
管道支持 | 无(只能顺序执行) | 支持强大管道(| 传递对象而非文本) |
模块化 | 无(仅能 #include 外部文件) | 支持模块(Import-Module )和函数 |
远程管理 | 有限(依赖 WMI/DCOM) | 原生支持(Enter-PSSession 、WinRM) |
错误处理 | On Error Resume Next (简单) | Try/Catch (类似 C#) |
4. 用途
用途 | VBScript (.vbs) | PowerShell (.ps1) |
---|---|---|
系统管理 | 简单任务(如文件操作、注册表修改) | 复杂管理(AD、Azure、Docker) |
自动化 | 批处理替代(如登录脚本) | 企业级自动化(CI/CD、Ansible) |
网络交互 | 依赖 IE(已淘汰) | 支持 REST API、WebSocket |
安全审计 | 能力有限 | 内置安全模块(如 Get-MpThreat ) |
1. 直接通过系统脚本宿主执行
Windows 自带cscript.exe
(控制台脚本宿主)和wscript.exe
(窗口脚本宿主),可直接运行.vbs
文件:
# bash
# 控制台模式执行(推荐,无弹窗)
cscript //nologo C:\path\to\script.vbs# 窗口模式执行(可能弹出对话框)
wscript C:\path\to\script.vbs
//nologo
:取消显示脚本宿主版本信息,减少痕迹。- 脚本内容示例(简单信息探测):
# 探测系统信息并输出到控制台 Set objWMI = GetObject("winmgmts:\\.\root\cimv2") Set colOS = objWMI.ExecQuery("Select * from Win32_OperatingSystem") For Each objOS in colOSWScript.Echo "系统版本: " & objOS.CaptionWScript.Echo "计算机名: " & objOS.CSName Next
2.内存中直接执行 VBScript 代码(无文件落地)
通过mshta.exe
(Windows HTML 应用程序宿主)或cscript
结合管道,可直接执行内存中的 VBScript 代码,无需保存为.vbs
文件:
# 方式1:通过mshta执行(支持HTML+VBS混合,适合简短代码)
mshta vbscript:Execute("MsgBox ""Hello, VBScript in Memory"":close")# 方式2:通过echo管道给cscript执行(适合多行代码)
echo Set objWMI = GetObject("winmgmts:\\.\root\cimv2"):Set colProc = objWMI.ExecQuery("Select * from Win32_Process"):For Each objProc in colProc:WScript.Echo objProc.Name:Next | cscript //nologo -
- 原理:
echo
输出的 VBScript 代码通过管道(|
)传递给cscript
,直接在内存中解析执行,不生成磁盘文件。
二、将 C# 代码加载到内存中执行(无文件落地)
C# 代码通常需要编译为.exe
或.dll
(二进制文件),但可通过以下技术在内存中直接编译并执行,避免文件落地:
1. 利用 PowerShell 的Add-Type
动态编译执行
PowerShell 的Add-Type
cmdlet 可直接编译 C# 代码并加载到内存(生成临时程序集,执行后释放),适用于简短代码:
# 在PowerShell中执行(单行命令,可编码规避检测)
Add-Type @"
using System;
public class MemExec {public static void Main() {Console.WriteLine("C# code executed in memory!");// 可添加系统探测、网络请求等逻辑}
}
"@ -Language CSharp; [MemExec]::Main()
- 进阶:若需规避 PowerShell 监控,可将代码进行 Base64 编码后执行:
# powershell # 编码后的命令(示例,实际需自行编码) $code = [System.Text.Encoding]::Unicode.GetBytes(@" using System; public class Test { public static void Main() { Console.WriteLine("In Memory"); } } "@); $encoded = [Convert]::ToBase64String($code); powershell -EncodedCommand $encoded
2. 通过csc.exe
(C# 编译器)在内存中生成程序集
csc.exe
是.NET 框架自带的 C# 编译器,可将代码编译为内存中的System.Reflection.Assembly
,避免生成磁盘文件:
// 示例:在C#代码中动态编译并执行另一段C#代码(可嵌套或通过网络加载)
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;class MemoryCompiler {static void Main() {// 待执行的C#代码(字符串形式,可从网络或内存中读取)string code = @"using System;public class DynamicCode {public static void Run() {Console.WriteLine(""Dynamic code executed in memory!"");}}";// 动态编译using (CSharpCodeProvider provider = new CSharpCodeProvider()) {CompilerParameters parameters = new CompilerParameters();parameters.GenerateInMemory = true; // 仅在内存中生成程序集parameters.GenerateExecutable = false; // 生成类库(.dll)CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);// 执行编译后的代码if (!results.Errors.HasErrors) {Type type = results.CompiledAssembly.GetType("DynamicCode");MethodInfo method = type.GetMethod("Run");method.Invoke(null, null);}}}
}
- 执行方式:将上述代码保存为
MemoryCompiler.cs
,通过csc.exe
编译为MemoryCompiler.exe
(仅首次需要落地,后续可通过其他方式加载),或直接在内存中通过 PowerShell 调用。
3. 利用rundll32.exe
加载.NET DLL(内存中执行)
若 C# 代码编译为.dll
(需导出函数),可通过rundll32.exe
加载执行(.dll
可在内存中生成或通过网络加载,避免落地):
// C# DLL示例(需定义导出函数)
using System;
using System.Runtime.InteropServices;public class MyDll {[DllExport("MyFunction", CallingConvention = CallingConvention.StdCall)]public static void MyFunction() {Console.WriteLine("DLL executed via rundll32 in memory!");}
}
- 编译:需使用
DllExport
库(NuGet 包),编译为MyDll.dll
后,通过rundll32.exe MyDll.dll,MyFunction
执行(若MyDll.dll
在内存中加载,则无需落地)。
三、规避原理与关键技术
- 减少磁盘 I/O:VBScript 是文本文件,C# 通过动态编译在内存中生成程序集,均避免生成传统的
.exe
二进制文件,降低 EDR 对文件哈希、签名的检测概率。 - 利用系统原生工具:
cscript.exe
、mshta.exe
、powershell.exe
、rundll32.exe
均为 Windows 系统自带程序,EDR 通常不会默认拦截其执行,可通过 “白名单工具” 执行恶意逻辑。 - 内存中销毁痕迹:动态生成的程序集在进程结束后自动释放,不会在磁盘留下持久化痕迹。
四、安全风险
- 广泛用于恶意攻击(如无文件勒索、后门植入)。
- 现代 EDR 已具备内存行为检测能力(如监控
Add-Type
、异常rundll32
调用、非典型编译行为),单纯依赖无文件执行难以完全规避。