概述

本文记录在实际逆向分析 360 安全软件 EPSVHRule.dll 崩溃时使用的方法论、工具链和完整步骤。涵盖用户态崩溃转储内核态转储两种场景,以及从 WSL 远程调用 Windows 调试器的桥接方案。

相关文章:BSOD Dump 分析 - EPSVHRule 系统卡死 · mcp-windbg 集成 · 手动创建蓝屏Dump

分析对象

本次分析涉及两个 dump 文件:

属性5.dmpMEMORY.DMP
路径D:\Montarius\Downloads\5.dmpD:\Montarius\Downloads\MEMORY\MEMORY.DMP
类型用户态崩溃转储Kernel Summary Dump
大小较小(用户态)205 MB
BugCheck0xC0000005 (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\ReleaseMicrosoft 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-windbgMCP 服务器,桥接 WinDbg/CDB 给 AIpip install mcp-windbg(需 mcp<2.0
mcporterWSL 侧 MCP 客户端,调用 mcp-windbgnpm install -g mcporter
Python 3编写自动化分析脚本C:\Python312\python.exe
PowerShell内核 dump 分析脚本编排Windows 内置
strings / grepWSL 侧对 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.dll

Step 4 — 切换到异常上下文并获取调用栈

windbg
// 切换到异常发生时的上下文
.ecxr
 
// 带帧号的调用栈
knf
 
// 带帧指针的调用栈
kv

Step 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 L50

Step 6 — 内存与 TLS 检查

windbg
// 读取 TLS index
dd EPSVHRule!_tls_index
 
// 查看 DLL 加载状态
!dlls -c EPSVHRule
 
// 读取栈上可能的字符串
du @rsp L20
 
// 模块详细信息
lmvm EPSVHRule
lmvm uninst

Step 7 — 特定线程栈

windbg
// 崩溃线程(thread 12)完整栈
~12 kn

Step 8 — 关闭会话

bash
mcporter call mcp-windbg.close_cdb_session session_id="cdb-xxxx"

方法二:Python 脚本直调 CDBSession(自动化分析)

适用场景

需要批量执行命令、自动化分析流程时,直接通过 Python 调用 mcp_windbg.cdb_session.CDBSession API。

脚本模板

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 手动遍历)

结构体字段偏移说明
_EPROCESSUniqueProcessId+0x180进程 PID
_EPROCESSActiveProcessLinks+0x188进程链表(LIST_ENTRY)
_EPROCESSImageFileName+0x2d0进程名(char[15])
_EPROCESSThreadListHead+0x308线程链表头
_ETHREADThreadListEntry+0x428线程链表节点
_KTHREADStackBase+0x038内核栈底
_KTHREADInitialStack+0x028内核栈顶
_KTHREADTrapFrame+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

本次分析中通过字符串扫描发现的关键证据

  1. EPSVHRule.dll 身份 — 版本 1.0.0.1005,路径 C:\Program Files (x86)\360\360Safe\safemon\EPSVHRule.dll
  2. VMware DnD 传输记录EPSVHRule_d.dll 通过 VMware 拖拽从宿主机传入虚拟机
  3. 360 内核 Hook 清单CmRegisterCallbackExPsSetCreateProcessNotifyRoutineExZwTerminateProcess
  4. 文件过滤驱动信息360FsFlt.sys,Altitude 382300
  5. QQ 进程白名单*\QQPCINS.EXE*\QQ.EXE

分析结论汇总

5.dmp(用户态崩溃)

项目结论
崩溃类型Access Violation (0xC0000005)
崩溃位置EPSVHRule!ATL::CAtlStringMgr::GetInstance+0xd
调用链EPSVHRule!Uninstalluninst+0x11802uninst+0x11961uninst+0x10daauninst+0x72bfauninst+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> -f

EPSVHRule 加载进程查找

windbg
!for_each_process ".process /r /p @#Process; lm | findstr /i EPSVH"

相关文章