简介:这份《process monitor使用说明.docx》面向需要排查Windows系统异常、软件冲突、权限受限与终端软件运行问题的IT运维人员及安全分析初学者,聚焦Process Monitor在文件系统、注册表、进程和线程活动监控中的实际应用。文档结合IPGuard、ip-guard等终端管理软件的问题定位场景,帮助读者理解监控范围、过滤思路、数据留存与结果研判之间的关系,适合作为日常故障诊断、软件兼容性核查和性能瓶颈初筛的参考材料。资源包共1个docx文档,大小约84KB,篇幅紧凑,便于随查随用,也方便团队内部传阅或用作培训辅助材料。已有696人学习下载,说明其在系统监控与终端软件排障场景中具有一定实用价值。读者可从中获得从现象复现到关键事件筛选、再到根因判断的整体框架,并借助文档中的分析视角提升系统问题定位效率,减少无效排查时间。
1. 现场跑三趟,不如开一次 procmon
现场同事说"点一下就闪退",你抱上笔记本跑过去,重启三次没复现;回到工位刚坐下,电话又响。往返几趟之后,真正缺的不是经验,而是一次完整的事件记录。Process Monitor(下面统一叫 procmon)干的就是这件事:它把文件、注册表、进程线程、网络四类系统调用按时间轴摊开,谁在什么时刻碰了哪个路径、返回码是什么,一条不落地落到日志里。这份《process monitor 使用说明.docx》解决的是最前端的纪律问题——什么时候停采集、什么时候清空、什么时候按放大镜。它适合需要给第三方软件定性的人:业务系统打不开、配置写不进去、和 ip-guard 这类终端管控客户端共存时行为异常,都能靠它把口供录下来。流程不复杂,但顺序错一步,抓回来的就是一锅噪声。
2. 事件流的底层账本:procmon 到底在录什么、录多少
2.1 四类事件源与各自的排查入口
procmon 在用户态挂界面,在内核里挂一个文件系统过滤驱动,再加注册表回调与进程、线程通知。这个层级决定了它的强项:凡是最终落到文件路径、注册表键、进程创建、网络端口的动作,都能被记下来,哪怕调用方一行日志都不打。代价是量级——一次"打开软件"的动作,背后可能是十几万条事件。
常见做法是先把四类事件源的量级和典型 Operation 摆出来,按"先小后大"的顺序看:
| 事件类别 | 典型 Operation | 什么情况下先看它 | 单次复现的典型条数 |
|---|---|---|---|
| 进程与线程 | Process Create、Thread Create、Process Exit | 软件压根起不来、启动链断在中间 | 百级 |
| 网络 | TCP Connect、TCP Send、UDP Send | 连不上服务端、解析异常、握手超时 | 千级 |
| 注册表 | RegOpenKey、RegQueryValue、RegSetValue | 配置读不到默认值、策略不生效 | 万级 |
| 文件系统 | CreateFile、ReadFile、WriteFile、CloseFile | DLL 加载失败、配置写不进、文件被占用 | 十万级 |
顺序很重要。很多"软件打不开"其实是进程创建那一步就失败了,压根没跑到读配置;反过来"能打开但功能不对",才需要下沉到文件和注册表。一上来就盯着文件系统看,等于在十万行里找一个本来不存在的答案。
另外记住一点:procmon 记录的是系统调用返回之后的结果,不是应用层的意图。它告诉你"这个路径打开失败了,返回 ACCESS DENIED",但不会告诉你"为什么拒绝"。后半截要靠 Stack 页签和进程树去补,这是第 4 章的内容。
2.2 事件风暴与缓冲区:为什么"先停再清"不是仪式
采集是来一条记一条,界面批量刷新渲染。默认事件堆在内存里,条数上去之后内存占用直线上升,到顶就开始丢最早的事件。Options 菜单里的 Set Backing File 可以把事件落到磁盘上的临时文件,同时能设一个体积上限,超过之后按最旧的丢。这两件事合起来,解释了说明书为什么反复强调"先停止采集、再清空"。
第一,停止是为了切断噪声源。目标软件只要还在跑,后台定时任务、心跳、日志轮转就持续产生事件。你在这个背景下按清空,清掉的只是屏幕上已经渲染的行,采集通道并没有归零,一秒之后又是几万条压上来,前面做的全白费。正确顺序是:点放大镜停止(图标带红叉)→ 点橡皮擦清空 → 再点放大镜开始。
第二,清空是为了让时间轴对齐。分析时大家习惯从"复现动作开始那一刻"往后数,如果日志开头还压着上一次的残留,起止时间就得靠肉眼找,交接给别人时更容易错位。
第三,Backing File 是后悔药。现场机器内存本来就不宽裕,几百万条事件足以让 procmon 自己变成问题的一部分。我一般会在开抓之前先设好落盘路径和上限(比如 2 GB),把"丢事件"变成一个可预期的行为,而不是等界面卡死了才发现前半段没了。
提示:Backing File 生成的不一定只有一个文件,超出上限后会滚动出编号文件,回传时别只拷第一个。
2.3 一次最小可用捕获的按钮状态判读
工具栏上真正要认的只有四个:漏斗(Filter)、放大镜(Capture)、向下箭头(Autoscroll)、橡皮擦(Clear)。放大镜没有红叉表示正在采集,带红叉表示已停止。这个图标状态是最容易翻车的地方——以为自己停了其实还在抓,或者以为在抓结果是停的,复现动作从头到尾一条没录上。
按说明书走一遍,落地成可执行的顺序:
- 关闭要采集的目标软件,包括托盘进程和它拉起的后台服务,任务管理器里确认没有残留进程名;
- 打开 procmon,首次运行接受 EULA,确认标题栏显示管理员权限,否则大量系统级事件根本看不到;
- 看放大镜,若无红叉先点一下停止;
- 点橡皮擦清空当前列表,确认事件计数归零;
- 配过滤条件(第 3 章展开),把目标进程和相关路径圈进来;
- 点放大镜开始采集;
- 让复现问题的同事按原始步骤操作,操作完成立刻点停止;
- File > Save 落盘,命名规范见第 6 章。
第 3 步和第 4 步是最容易被跳过的两步,也是后面所有分析能不能成立的前提。
3. 照着说明书走一遍:从关掉目标软件到落盘一个日志文件
3.1 启动、停止、清空的三步顺序与状态判读
说明书里那句"若出现如下图显示则按照操作;重置确定后,再按照步骤 1 操作",说的其实是同一个坑:上一次采集没结束,或者采集通道还开着,你按清空只是清了显示。判断依据是三处——放大镜图标状态、状态栏的 Events 计数、以及窗口标题里是否还挂着正在写入的日志文件名。
把这三处做成一张判读表,现场对着看就不会错:
| 现象 | 含义 | 下一步动作 |
|---|---|---|
| 放大镜无红叉,Events 计数持续上涨 | 正在采集 | 点放大镜停止,等计数不再变化 |
| 放大镜带红叉,Events 计数不为 0 | 已停止但有残留事件 | 点橡皮擦清空,确认计数归零 |
| Events 计数归零后仍快速上涨 | 清空后采集又被触发 | 先停止,再清空,确认红叉状态 |
| 标题栏无管理员标识 | 权限不足,事件残缺 | 以管理员身份重启 procmon |
顺序上只有一条铁律:停止在前,清空在中,开始在后。反过来做(先清空再停止),你会看到计数刚归零又跳回几千,然后误以为软件有问题。
还有个小细节:目标软件的"关闭"要关干净。很多客户端关掉主窗口后托盘还在,服务还在跑,进程名还在进程列表里。抓之前用任务管理器按名称排序确认一遍,比事后在日志里排除噪声省事得多。
3.2 过滤器怎么加:三列组合的实操
Filter 对话框就四个字段:Column、Relation、Value、Action。看起来简单,真正的坑在规则顺序——procmon 从上往下逐条判断,命中第一条就停止,后面的规则不再参与。这意味着 Exclude 写在 Include 后面等于没写。
举个真实会遇到的例子。你想只看 app.exe,同时想排掉对 pagefile.sys 的访问。如果这么写:
| 顺序 | Column | Relation | Value | Action |
|---|---|---|---|---|
| 1 | Process Name | is | app.exe | Include |
| 2 | Path | contains | pagefile | Exclude |
pagefile 的访问会先命中第 1 条被 Include 进来,第 2 条永远轮不到。正确写法是把排除往前放:
| 顺序 | Column | Relation | Value | Action |
|---|---|---|---|---|
| 1 | Path | contains | pagefile | Exclude |
| 2 | Process Name | is | app.exe | Include |
一套常用的起步配置,可以直接抄:
| 目的 | Column | Relation | Value | Action |
|---|---|---|---|---|
| 排除 procmon 自身噪声 | Process Name | is | Procmon64.exe | Exclude |
| 排除分页与休眠文件 | Path | contains | pagefile | Exclude |
| 只看目标进程 | Process Name | is | app.exe | Include |
| 盯住配置目录 | Path | begins with | C:\ProgramData\Vendor\ | Include |
| 只看失败结果 | Result | is | ACCESS DENIED | Include |
注意 Include 和 Exclude 混用时,规则顺序决定了最终留下的集合。最稳的习惯是:Exclude 全部排在前面,Include 排在后面,从上往下读一遍能说通再点 OK。
3.3 保存格式与回传:PML、CSV、XML 的取舍
File > Save 里有三件事要选:格式、保存范围、目标文件。
格式上,PML 是 procmon 的原生日志格式,保留全部列、Stack 页签和进程树,只有它能被 procmon 完整回放。CSV 和 XML 是给脚本吃的,CSV 会丢 Stack 信息,中文系统下还可能把列名本地化成"进程名称""操作"这类,写解析脚本时要先打印表头确认。有些资料说保存成 .EVtx,那其实是 Windows 事件日志的格式,和 procmon 不是一套东西,别混。
保存范围上,有两个选项:All events 和 Events displayed using current filter。前者是全部,后者只存当前过滤后可见的。现场回传我一般选 All events,因为对方可能有不同的分析角度,过滤后的日志等于把别人的视线也一起裁掉了。
需要批量处理时,用命令行起停更省事,也能避免"忘了点开始"这种低级失误:
# 1) 无界面启动,事件直接落到 backing file,避免内存被吃满 $procmon = "C:\Tools\Procmon\Procmon64.exe" Start-Process $procmon -ArgumentList "/Quiet","/Minimized","/AcceptEula","/BackingFile","C:\Trace\base.pml" # 2) 复现人操作完成后收尾:让后台实例把缓冲刷到磁盘并退出 Start-Process $procmon -ArgumentList "/Terminate" -Wait # 3) 确认产物,注意可能有 base-1.pml 这类滚动文件 Get-ChildItem C:\Trace\base*.pml | Select-Object Name, Length, LastWriteTime参数说明:/Quiet跳过启动时的过滤设置弹窗,/Minimized最小化到任务栏避免挡住操作界面,/AcceptEula静默接受协议(只在受控机器上用),/BackingFile指定落盘文件,/Terminate通知正在运行的实例停止采集并落盘退出。第 3 步的base*.pml通配是为了把滚动文件一起收走,只拷base.pml会丢后面的半段。
拿到 CSV 之后,先用一个小脚本看分布,比在界面里翻页快得多:
import csv from collections import Counter csv_path = r"C:\Trace\base.csv" with open(csv_path, newline="", encoding="utf-8-sig") as f: reader = csv.reader(f) header = next(reader) # 先确认列名,中文系统可能是本地化表头 print("列名:", header) i_op = header.index("Operation") i_res = header.index("Result") i_path = header.index("Path") i_proc = header.index("Process Name") stat, samples = Counter(), [] for row in reader: if len(row) <= max(i_op, i_res, i_path, i_proc): continue # 字段缺失的行直接跳过,避免 IndexError stat[(row[i_op], row[i_res])] += 1 if row[i_res] in ("ACCESS DENIED", "NAME NOT FOUND") and len(samples) < 200: samples.append((row[i_proc], row[i_op], row[i_path], row[i_res])) for (op, res), n in stat.most_common(10): print(f"{op:<28}{res:<20}{n}") for s in samples[:20]: print(s)逻辑说明:先用csv.reader读表头并打印,避免DictReader在本地化列名下取不到键;再按 (Operation, Result) 组合计数,看清分布之后再决定加什么过滤条件;最后把失败样本截前 200 条,防止刷屏。参数上,encoding="utf-8-sig"处理 BOM 头,max(...)那行做长度保护,样本上限按需调大。
4. 过滤与堆栈:让 ip-guard 类客户端的行为浮出水面
4.1 过滤器的布尔组合与常见误配
第 3 章讲的是单条规则和顺序,这一节讲组合。Filter 对话框里没有括号,没有 AND/OR 优先级,全靠"从头往下、命中即停"这一条规则决定语义。想表达"A 且 B",只能靠 Exclude 先砍掉所有非 A 和非 B 的事件,剩下的自然就是交集。
比如要盯住 app.exe 对某个目录的访问失败。写法是三层:先 Exclude 所有非 app.exe 的进程,再 Exclude 所有不在该目录下的路径,最后 Include 全部。听起来绕,但它是唯一能表达交集的方式。
常见误配有三种。第一种是把 Exclude 放在 Include 后面,前面演示过,等于没写。第二种是 Value 用了contains却忘了大小写,路径在 Windows 上不区分大小写,但过滤器里的匹配行为和你写的字符串形态有关,路径写全比写片段稳。第三种是对 Result 列过早做 Include——结果列里 NAME NOT FOUND 在正常启动过程中大量出现,先 Include 进来只会把真正的问题淹掉,正确做法是先不限制 Result,看完分布再说。
还有一个容易被忽略的点:过滤只影响显示,不影响采集。也就是说过滤条件是事后可以改的,只要你保存了 All events,回放时重新配一遍过滤就行。这也是为什么保存时建议选 All events。
4.2 Result 列才是重点:ACCESS DENIED 与 NAME NOT FOUND 的语义差别
事件列表里最该看的是 Result 列,不是 Path 列。常见的几个值含义差别很大:
| Result | 含义 | 通常是不是问题 |
|---|---|---|
| SUCCESS | 调用成功 | 不是 |
| ACCESS DENIED | 权限不足或被过滤驱动拒绝 | 视上下文,最值得追 |
| NAME NOT FOUND | 路径不存在 | 大面积出现通常是正常探测 |
| REPARSE | 发生了重定向(如目录联接) | 一般不是,但会误导路径判断 |
| FILE LOCKED WITH ONLY READERS | 有其他进程持读锁 | 是,常见于更新失败 |
| BUFFER OVERFLOW | 查询缓冲区不够,返回所需长度 | 多数是正常枚举行为 |
判断标准不是"有没有失败",而是同一路径上的对比。同样一个配置文件,进程 A 读是 SUCCESS,进程 B 读是 ACCESS DENIED,这就是要追的线索。反过来,一个启动过程里出现几千条 NAME NOT FOUND,多数是程序在按顺序试探多个可选路径——先试 System32,再试 SysWOW64,再试当前目录,找不到就换下一个,属于正常设计。
真正值得停下来看一眼的组合是:关键配置路径 NAME NOT FOUND 之后,进程紧接着退出了;或者某个 DLL 的 CreateFile 返回 ACCESS DENIED,随后主进程退出。
4.3 Stack 页签与进程树:从一次拒绝追到一个模块
procmon 只能告诉你"谁被拒了",告诉不了你"谁拒的"。这个信息在 Stack 页签里:选中一条事件,右键 Properties,切到 Stack。默认情况下多半只能看到模块名加偏移,比如xxx.sys + 0x1a2b这种。想看函数名,得在 Options > Configure Symbols 里配符号路径,让调试符号能从符号服务器拉下来。现场机器常常不能随便连外网,这时候就退回"看模块名"这一层,也够用——你要找的是"这次调用链路上有没有第三方驱动"。
具体怎么用:选一条 ACCESS DENIED 的 CreateFile 事件,打开 Stack,从上往下看调用来源。如果栈里出现的是目标软件自己的模块,说明是它自己权限配置的问题;如果栈中间夹着一个第三方内核驱动模块,那就是这个驱动在这条链路上做了拦截或改写。ip-guard 这类终端管控客户端会挂文件、注册表、进程过滤驱动,出现在栈里是正常的,关键是看它出现的那几层对应什么动作。
进程树(Tools > Process Tree)解决的是另一个问题:某个进程是谁拉起来的、什么时候退的、命令行参数是什么。软件"启动后马上消失"这类现象,在事件列表里表现为 Process Create 紧跟一个 Process Exit,把这两条对上,再看 Exit Status,往往比翻文件事件快十倍。
5. 避坑清单:五条踩过的坑和解法
第一条:抓了半天,日志里全是 procmon 自己。现象是打开日志一看,排在前面的全是 Procmon64.exe 的 CreateFile 和 RegQueryValue,真正想看的进程被挤到几千行之后。 原因有两层:procmon 界面刷新本身会产生事件;另外未加任何过滤时,系统里所有进程的活动都在往里灌。 解决是开抓之前先配一条Process Name is Procmon64.exe → Exclude,再配一条目标进程的 Include,然后才点放大镜。顺序别反。
第二条:保存下来打不开,或者对方说"你这日志不全"。现象是文件发过去,对方用 procmon 打开报格式错误;或者打开之后只有几百条,明显比现场看到的少。 原因通常是保存时格式选了 CSV 却用 procmon 去开,或者保存范围选了 Events displayed using current filter,把过滤掉的那部分直接丢了。 解决是回传一律用 PML,保存范围选 All events;真要给脚本用,另存一份 CSV,两份一起发。
第三条:复现完了才发现前半段事件没了。现象是日志的时间戳从中间某处开始,前面的复现动作一条没有。 原因是内存缓冲到顶开始丢最旧的事件,或者 Backing File 的上限设得太小,滚动文件早被覆盖。 解决是开抓前进 Options > Set Backing File,指定一个剩余空间充足的盘,上限给到 2 GB;流程超过十几分钟的,中途停一次另存,分段抓。
第四条:时间对不上,两条日志拼不起来。现象是 A 机器抓的日志和 B 机器抓的日志放在一起,同一个动作的时间差了十几秒,没法对齐。 原因有两个:跨天时 CSV 里的 Time of Day 只有时分秒,日期要看另一列;两台机器的系统时钟本身没校准。 解决是对比时统一用完整的 Date & Time 列,抓取前把两台机器对一次时,另外在日志里手动记一笔开始和结束的墙钟时间,写在文件名里。
第五条:换台机器回放,过滤条件全没了。现象是现场配好的那套过滤,回到工位打开 procmon 发现是默认状态,得重新配一遍,还容易配错。 原因是过滤器跟着当前会话走,不跟着日志文件走。 解决是配好之后在 Filter 菜单里把当前过滤另存成配置文件,回放时先加载过滤再加载日志。这一步花十秒,能省掉后面反复调参的半小时。
6. 把一次抓取变成可复用证据:命名、打点与回放
6.1 三个让日志"能被人接住"的习惯
第一是命名。日志文件名里至少要有日期、机器标识、目标进程、复现动作、记录人代号,用下划线连起来,别用空格。比如20240612_WKS0231_app_exe_login_crash_A.pml。这个习惯看着琐碎,但当你要在十几份日志里找"上周那台机器上登录闪退那次",命名规范就是唯一的索引。
第二是打点。开抓之后、正式复现之前,让操作的人先做一个无害且唯一的动作,比如点开目标软件的"关于"对话框再关掉。这个动作在时间轴上会留下一个非常干净的特征事件,后面所有分析都从这个锚点往后数,比对着绝对时间戳找起点可靠得多。血泪经验:没有锚点的日志,回看时你永远不确定"复现到底是从第 37 秒还是第 41 秒开始的"。
第三是回放。拿到 PML 之后不要急着翻事件列表,先按第 5 章第五条把过滤配置加载回来,再 Tools > Process Tree 看一遍进程的生灭顺序,最后才进事件列表按 Result 列排序。这个顺序能保证你先看到骨架,再填血肉,不至于一上来就陷进十万条 CreateFile 里。
| 回放步骤 | 做什么 | 看什么 |
|---|---|---|
| 1 | 加载过滤配置 | 确认规则顺序没被改 |
| 2 | 打开 Process Tree | 进程生灭、命令行、退出码 |
| 3 | 事件列表按 Result 排序 | 非 SUCCESS 的集中区域 |
| 4 | 对可疑事件开 Stack | 调用链上有没有第三方模块 |
| 5 | 导出关键片段另存 | 时间窗口 + 当前过滤 |
一个具体技巧:找到可疑时间窗口之后,用 Filter 里的Time of Day列加两条边界规则,把窗口前后各留 2 秒,其余全部 Exclude,然后另存一份小日志。这份几百条的"切片"发给同事或厂商,对方看得懂,也不会被体量吓退。
从那以后我每次开抓之前,都会强制走一遍"停、清、配、锚、抓"五步,哪怕只是查一个很小的权限问题。跳过任何一步,最后都得用两倍时间补回来。希望帮到你。
本文还有配套的精品资源,点击获取