概述
本文记录在实际逆向分析 360 安全软件 EPSVHRule.dll 崩溃时使用的方法论、工具链和完整步骤。涵盖用户态崩溃转储和内核态转储两种场景,以及从 WSL 远程调用 Windows 调试器的桥接方案。
相关文章:BSOD Dump 分析 - EPSVHRule 系统卡死 · mcp-windbg 集成 · 手动创建蓝屏Dump
分析对象
本次分析涉及两个 dump 文件:
| 属性 | 5.dmp | MEMORY.DMP |
|---|---|---|
| 路径 | D:\Montarius\Downloads\5.dmp | D:\Montarius\Downloads\MEMORY\MEMORY.DMP |
| 类型 | 用户态崩溃转储 | Kernel Summary Dump |
| 大小 | 较小(用户态) | 205 MB |
| BugCheck | 0xC0000005 (Access Violation) | 0xE2 (MANUALLY_INITIATED_CRASH) |
| 崩溃位置 | EPSVHRule!ATL::CAtlStringMgr::GetInstance+0xd | 系统卡死后手动触发 |
| 系统 | — | Windows 7 SP1 x64 (Build 7601.24384) |
| 符号表 | D:\Montarius\Downloads\931307_EPSVHRule_unsigned-1785372423\Release | Microsoft Symbol Server |
工具链总览
┌─────────────────────────────────────────────────────┐
│ WSL (Linux) │
│ │
│ ┌─────────────┐ ┌──────────────────────────┐ │
│ │ mcporter │───►│ Python 脚本 │ │
│ │ (MCP 客户端) │ │ (CDBSession API) │ │
│ └──────┬──────┘ └──────────┬───────────────┘ │
│ │ stdio │ subprocess │
│ │ │ │
│ ┌──────┴──────────────────────┴───────────────┐ │
│ │ /mnt/c/Python312/python.exe -m mcp_windbg │ │
│ └──────────────────────┬──────────────────────┘ │
│ │ │
└─────────────────────────┼───────────────────────────┘
│
──────── Windows ─────────┼───────────────────────────
│
▼
┌──────────────┐ ┌──────────────┐
│ cdb.exe │ │ kd.exe │
│ (用户态调试) │ │ (内核态调试) │
└──────────────┘ └──────────────┘
│ │
▼ ▼
.dmp 分析 KDNET/管道/串口
工具清单
| 工具 | 用途 | 路径/来源 |
|---|---|---|
| CDB | 用户态崩溃转储分析(Console Debugger) | C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\cdb.exe |
| KD | 内核态调试 | 同目录下 kd.exe |
| mcp-windbg | MCP 服务器,桥接 WinDbg/CDB 给 AI | pip install mcp-windbg(需 mcp<2.0) |
| mcporter | WSL 侧 MCP 客户端,调用 mcp-windbg | npm install -g mcporter |
| Python 3 | 编写自动化分析脚本 | C:\Python312\python.exe |
| PowerShell | 内核 dump 分析脚本编排 | Windows 内置 |
| strings / grep | WSL 侧对 dump 文件做静态字符串扫描 | Linux 原生 |
方法一:mcp-windbg + mcporter(用户态 dump)
适用场景
从 WSL / AI 侧远程分析 Windows 用户态崩溃转储(.dmp)。
前置配置
详见 mcp-windbg 集成。核心配置:
json
// ~/.openclaw/workspace/config/mcporter.json
{
"mcpServers": {
"mcp-windbg": {
"command": "/mnt/c/Python312/python.exe -m mcp_windbg"
}
}
}分析步骤
Step 1 — 打开转储并自动分检
bash
# 通过 mcporter 调用
mcporter call mcp-windbg.open_cdb_dump \
dump_path="D:\\Montarius\\Downloads\\5.dmp" \
include_stack_trace=true返回 session_id,后续操作使用该 ID。
Step 2 — 基本信息收集
bash
# 最近事件
mcporter call mcp-windbg.run_cdb_command session_id="cdb-xxxx" command=".lastevent"
# 自动分析(如果符号可用)
mcporter call mcp-windbg.run_cdb_command session_id="cdb-xxxx" command="!analyze -v"
# 寄存器
mcporter call mcp-windbg.run_cdb_command session_id="cdb-xxxx" command="r"
# 线程列表
mcporter call mcp-windbg.run_cdb_command session_id="cdb-xxxx" command="~"Step 3 — 加载符号
windbg
// 设置符号路径:微软符号服务器 + 本地 PDB
.sympath srv*C:\Symbols*https://msdl.microsoft.com/download/symbols;D:\Montarius\Downloads\931307_EPSVHRule_unsigned-1785372423\Release
// 强制重新加载
.reload /f
.reload /f EPSVHRule.dllStep 4 — 切换到异常上下文并获取调用栈
windbg
// 切换到异常发生时的上下文
.ecxr
// 带帧号的调用栈
knf
// 带帧指针的调用栈
kvStep 5 — 符号搜索与反汇编
windbg
// 搜索相关符号
x EPSVHRule!CShimDB*
x EPSVHRule!*Uninstall*
x EPSVHRule!ATL::CAtlStringMgr*
// 完整函数反汇编
uf EPSVHRule!Uninstall
uf EPSVHRule!ATL::CAtlStringMgr::GetInstance
// 调用点反汇编
u uninst+0x117d0 L50
u uninst+0x11940 L50Step 6 — 内存与 TLS 检查
windbg
// 读取 TLS index
dd EPSVHRule!_tls_index
// 查看 DLL 加载状态
!dlls -c EPSVHRule
// 读取栈上可能的字符串
du @rsp L20
// 模块详细信息
lmvm EPSVHRule
lmvm uninstStep 7 — 特定线程栈
windbg
// 崩溃线程(thread 12)完整栈
~12 knStep 8 — 关闭会话
bash
mcporter call mcp-windbg.close_cdb_session session_id="cdb-xxxx"方法二:Python 脚本直调 CDBSession(自动化分析)
适用场景
需要批量执行命令、自动化分析流程时,直接通过 Python 调用
mcp_windbg.cdb_session.CDBSessionAPI。
脚本模板
python
"""Crash dump analysis script - direct CDBSession API."""
import sys
sys.path.insert(0, r"C:\Python312\Lib\site-packages")
from mcp_windbg.cdb_session import CDBSession
DUMP_PATH = r"D:\Montarius\Downloads\5.dmp"
SYMBOLS_PATH = r"D:\Montarius\Downloads\931307_EPSVHRule_unsigned-1785372423\Release"
def main():
session = CDBSession(
dump_path=DUMP_PATH,
symbols_path=f"srv*C:\\Symbols*https://msdl.microsoft.com/download/symbols;{SYMBOLS_PATH}",
timeout=120,
verbose=False,
)
try:
# 1. 强制加载符号
session.send_command(".reload /f EPSVHRule.dll", timeout=60)
# 2. 切换到异常上下文
session.send_command(".ecxr", timeout=10)
# 3. 调用栈
print("=== knf ===")
print("\n".join(session.send_command("knf", timeout=10)))
# 4. 符号搜索
for pattern in ["EPSVHRule!CShimDB*", "EPSVHRule!*Uninstall*", "EPSVHRule!ATL::*"]:
print(f"\n=== x {pattern} ===")
try:
print("\n".join(session.send_command(f"x {pattern}", timeout=10)))
except Exception as e:
print(f"Failed: {e}")
# 5. 函数反汇编
for func in ["EPSVHRule!Uninstall", "EPSVHRule!ATL::CAtlStringMgr::GetInstance"]:
print(f"\n=== uf {func} ===")
try:
print("\n".join(session.send_command(f"uf {func}", timeout=10)))
except Exception as e:
print(f"Failed: {e}")
# 6. 调用点反汇编
for addr in ["uninst+0x117d0", "uninst+0x11940", "uninst+0x10d80", "uninst+0x72bd0"]:
print(f"\n=== u {addr} L50 ===")
try:
print("\n".join(session.send_command(f"u {addr} L50", timeout=10)))
except Exception as e:
print(f"Failed: {e}")
# 7. TLS 和 DLL 状态
print("\n=== dd EPSVHRule!_tls_index ===")
try:
print("\n".join(session.send_command("dd EPSVHRule!_tls_index", timeout=10)))
except Exception as e:
print(f"Failed: {e}")
# 8. 崩溃线程栈
print("\n=== ~12 kn ===")
try:
print("\n".join(session.send_command("~12 kn", timeout=10)))
except Exception as e:
print(f"Failed: {e}")
finally:
session.close()
if __name__ == "__main__":
main()从 WSL 调用 Windows Python
bash
# 直接执行
/mnt/c/Python312/python.exe /tmp/analyze_dump.py
# 或通过包装脚本
cat > /tmp/run_analysis.py << 'EOF'
import subprocess
result = subprocess.run(
[r"/mnt/c/Python312/python.exe", r"\\wsl.localhost\Ubuntu\tmp\analyze_dump.py"],
capture_output=True, text=True, timeout=300
)
print(result.stdout)
if result.stderr:
print("STDERR:", result.stderr)
EOF
python3 /tmp/run_analysis.py实际使用的脚本清单
| 脚本 | 用途 |
|---|---|
/tmp/analyze_dump.py | 初始分检:.lastevent / !analyze -v / kb / r / ~ |
/tmp/analyze_dump2.py | 深入分析:反汇编 Uninstall / GetInstance,检查 vftable / TLS |
/tmp/analyze_dump3.py | 符号加载:完整 .reload /f + knf + ln + 调用链反汇编 |
/tmp/analyze_dump4.py | 完整分析:符号搜索 + 完整反汇编 + TLS 检查 + 崩溃线程栈 |
方法三:PowerShell + CDB 直接调用(内核 dump)
适用场景
分析 Kernel Summary Dump,符号不完整时手动遍历内核结构体。
脚本模板
powershell
# run_cdb.ps1 — 内核 dump 分析脚本
$cdb = 'C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\cdb.exe'
$dump = 'D:\Montarius\Downloads\MEMORY\MEMORY.DMP'
$outfile = 'C:\Users\Montarius\bsod_epsvh_full.txt'
$proc = New-Object System.Diagnostics.Process
$proc.StartInfo.FileName = $cdb
$proc.StartInfo.Arguments = "-z `"$dump`" -lines"
$proc.StartInfo.UseShellExecute = $false
$proc.StartInfo.RedirectStandardInput = $true
$proc.StartInfo.RedirectStandardOutput = $true
$proc.StartInfo.RedirectStandardError = $true
$proc.Start() | Out-Null
$cmds = @(
".symfix C:\symbols",
".reload",
# 调用栈
"kv 100",
# 列出处理器
"~",
# 已加载模块
"lm",
"lm m 360*",
# 搜索字符串(内核地址空间)
"s -a fffff800'054b0000 L6000 EPSVH",
"s -a fffff880'01600000 L100000 EPSVH",
"s -a fffff880'05440000 L200000 EPSVH",
"s -a fffff880'01600000 L100000 safemon",
"s -a fffff880'05440000 L200000 360Safe",
# 手动遍历 EPROCESS(Win7 SP1 x64 无 PDB 时)
# PsInitialSystemProcess = fffff800'04100028
"dq fffff800'04100028+0x188 L1", # ActiveProcessLinks.Flink
"dc fffff800'04100028+0x2d0 L4", # ImageFileName
"dq fffff800'04100028+0x180 L1", # UniqueProcessId
# dump 当前栈区
"dps fffff800'054b0000 L600",
"q"
)
foreach ($cmd in $cmds) {
$proc.StandardInput.WriteLine($cmd)
}
$proc.StandardInput.Close()
$output = $proc.StandardOutput.ReadToEnd()
$output | Out-File -FilePath $outfile -Encoding UTF8
$proc.WaitForExit(300000)Win7 SP1 x64 内核结构体偏移(无 PDB 手动遍历)
| 结构体 | 字段 | 偏移 | 说明 |
|---|---|---|---|
_EPROCESS | UniqueProcessId | +0x180 | 进程 PID |
_EPROCESS | ActiveProcessLinks | +0x188 | 进程链表(LIST_ENTRY) |
_EPROCESS | ImageFileName | +0x2d0 | 进程名(char[15]) |
_EPROCESS | ThreadListHead | +0x308 | 线程链表头 |
_ETHREAD | ThreadListEntry | +0x428 | 线程链表节点 |
_KTHREAD | StackBase | +0x038 | 内核栈底 |
_KTHREAD | InitialStack | +0x028 | 内核栈顶 |
_KTHREAD | TrapFrame | +0x0d8 | 陷阱帧 |
遍历方法
PsInitialSystemProcess给出 System 进程的 EPROCESS 地址。通过+0x188的 Flink 遍历ActiveProcessLinks双向链表,每个节点减去0x188即为对应 EPROCESS 基址。
符号问题与解决
| 问题 | 原因 | 解决 |
|---|---|---|
ntkrnlmp PDB 找不到 | Win7 SP1 版本未匹配 | .sympath srv*C:\symbols*https://msdl.microsoft.com/download/symbols + .reload /f nt |
!analyze -v 不可用 | kdexts.dll 不兼容 | 从 Win8.1 SDK 复制 kdexts.dll(6.3.9600.17298) |
dt nt!_EPROCESS 失败 | 无 PDB 类型信息 | 使用上表偏移手动 dq/dc 读取 |
!process 0 0 失败 | 同上 | 手动遍历 PsInitialSystemProcess 链表 |
方法四:WSL 静态字符串扫描(快速侦察)
适用场景
无需 WinDbg,直接在 WSL 中对 dump 文件做二进制字符串扫描,快速提取关键信息。
bash
# 提取所有可读字符串
strings -n 6 /mnt/d/Montarius/Downloads/MEMORY/MEMORY.DMP > /tmp/dump_strings.txt
# 搜索关键词
grep -i "EPSVHRule" /tmp/dump_strings.txt
grep -i "safemon" /tmp/dump_strings.txt
grep -i "360Safe" /tmp/dump_strings.txt
grep -i "VMwareDnD" /tmp/dump_strings.txt
# 搜索内核回调注册信息
grep -E "CmRegister|PsSetCreate|ObRegister|FltSend" /tmp/dump_strings.txt
# 搜索文件路径
grep -E "^C:\\\\|^[A-Z]:\\\\" /tmp/dump_strings.txt | sort -u
# 搜索 DLL 名称
grep -iE "\.dll$" /tmp/dump_strings.txt | sort -u本次分析中通过字符串扫描发现的关键证据
- EPSVHRule.dll 身份 — 版本 1.0.0.1005,路径
C:\Program Files (x86)\360\360Safe\safemon\EPSVHRule.dll - VMware DnD 传输记录 —
EPSVHRule_d.dll通过 VMware 拖拽从宿主机传入虚拟机 - 360 内核 Hook 清单 —
CmRegisterCallbackEx、PsSetCreateProcessNotifyRoutineEx、ZwTerminateProcess等 - 文件过滤驱动信息 —
360FsFlt.sys,Altitude 382300 - QQ 进程白名单 —
*\QQPCINS.EXE、*\QQ.EXE等
分析结论汇总
5.dmp(用户态崩溃)
| 项目 | 结论 |
|---|---|
| 崩溃类型 | Access Violation (0xC0000005) |
| 崩溃位置 | EPSVHRule!ATL::CAtlStringMgr::GetInstance+0xd |
| 调用链 | EPSVHRule!Uninstall ← uninst+0x11802 ← uninst+0x11961 ← uninst+0x10daa ← uninst+0x72bfa ← uninst+0x73285 |
| 崩溃线程 | 0:012 (thread 12) |
| 根因 | TLS 访问异常 — GetInstance 内部访问 TLS slot 时 fs:[2C] 为 NULL,发生在卸载流程中 |
MEMORY.DMP(内核转储)
| 项目 | 结论 |
|---|---|
| 崩溃类型 | 手动触发 BugCheck 0xE2 (MANUALLY_INITIATED_CRASH) |
| 触发方式 | Right Ctrl + ScrollLock × 2(通过 i8042prt 键盘驱动) |
| Dump 类型限制 | Kernel Summary Dump — 用户态内存不可用 |
| 卡死根因 | EPSVHRule_d.dll(修改版)被注入 360 safemon 目录,与内核驱动链(ZhuDongFangYu + 360FsFlt + 360qpesv)交互异常,最可能为死锁或 DPC 递归 |
| 验证命令 | !deadlock / !locks / ~* kb / !fltkd.filters(需 WinDbg + 完整符号) |
WinDbg 验证命令速查
死锁验证
windbg
!deadlock
!locks
!qlocks
!kl文件过滤驱动检查
windbg
!fltkd.filters
!fltkd.filter 360FsFlt进程和线程检查
windbg
!process 0 0 zhudongfangyu.exe
.process /r /p <EPROCESS>
~* kb模块加载验证
windbg
lmvm EPSVHRule
!dh <BaseAddress> -fEPSVHRule 加载进程查找
windbg
!for_each_process ".process /r /p @#Process; lm | findstr /i EPSVH"相关文章
- mcp-windbg 集成 — WSL 环境下通过 mcporter 集成 WinDbg MCP
- BSOD Dump 分析 - EPSVHRule 系统卡死 — 本次分析的完整结果文档
- 手动创建蓝屏Dump — 如何手动触发内核转储
- Windbg基础命令 — WinDbg 常用命令参考
- windbg 内核调试记录 — 内核调试笔记
- GS寄存器与x64系统结构访问 — x64 下通过 GS 寄存器访问 TEB/PEB
- X64内核结构体详解 — EPROCESS/ETHREAD/TEB/PEB 结构体