Windows Sysinternals
Windows Sysinternals是一组Windows工具套件,它包含超过70种适用于Windows平台的高级诊断和排错工具。
下载地址:
Sysinternals 实用工具 - Sysinternals | Microsoft Learn
Procmon是Windows Sysinternals中的一个用于监控注册表、文件、进程/线程,以及网络活动的工具。
Procmon
Procmon需要加载内核驱动,所以需要以管理员运行。
Procmon分x86和x64版本,分别可以监控32位程序和64位程序。
主界面如下
可捕获的事件列表
图标 | 事件 | 描述 |
| 注册表 | 注册表操作,如键和值的创建、枚举、查询、删除 |
| 文件系统 | 针对本地存储和远程文件系统 |
| 网络 | UDP和TCP网络活动,包括源和目标地址(不含传输或接收的实际数据)。Procmon可配置为将网络地址解析为网络名称,或只显示IP地址。为此可使用【Options】菜单中的【 Show Resolved Network Addresses 】显示解析后的网络地址)选项,或通过Ctrl+N切换 |
| 进程 | 进程和线程事件,如父进程创建子进程、进程启动、线程创建、线程退出、进程退出,以及将可执行映像载入进程的地址空间(注意:Procmon不会记录此类映像的卸载操作) |
| Profiling | 为系统中运行的每个进程和线程生成并记录一条事件,记录自上一次Profiling事件后内核与用户时间总量、内存用量、上下文切换次数。进程Profiling事件始终会被捕获,默认情况下不捕获线程Profiling事件。 |
基础使用
工具栏按钮
打开监控日志文件
保存当前列表到文件
开始/停止监控
自动滚动
清空列表
过滤对话框
强调显示对话框
从窗口选择要监控的进程
进程树
事件属性
查找
跳转至对象
显示/隐藏注册表活动开关
显示/隐藏网络系统活动开关
显示/隐藏网络活动开关
显示/隐藏进程和线程活动开关
显示/隐藏Profiling事件开关
配置显示列
Sequence Number(序号) 为当前显示结果显示从“0”开始的行号。
Event Class(事件类) 可显示为Registry(注册表)、File System(文件系统)、Network(网络)、Process(进程)或Profiling。
Category(类别) 对于适用的文件和注册表操作,事件可分为Read(读取)、Write(写入)、Read Metadata(读取元数据)或Write Metadata(写入元数据)类别。
Relative Time(相对时间) 操作执行时间与Procmon启动时间,或Procmon显示的内容上一次清空时间之间的相对时间。
Duration(持续时间) 以秒为单位显示操作的执行时间。对于线程Profiling事件,该时间是指自上一次线程Profiling事件之后该线程消耗的内核与用户时间总和;对于进程Profiling事件,该值将显示为“0”。
Completion Time(完成时间) 事件执行完毕的时间。该时间的格式与上文提到的Time of Day列格式一致。对于尚未完成的操作,该列将不显示任何内容。进程管理列可显示有关进程的运行时信息,具体如下。
User Name(用户名) 执行该进程的安全主体。
Session ID(会话ID)运行该进程的终端服务会话。服务始终运行在Session 0(内核层)中。
Integrity(完整性) 执行该操作的进程的完整性级别。
Thread ID(线程ID) 执行该操作的线程的ID,也叫做TID,该列的列头也会显示为“TID”的字样。
Virtualized(虚拟化) 代表执行该操作的进程是否启用了UAC虚拟化。另外请注意,该技术与应用程序虚拟化或计算机虚拟化技术无关。
事件属性对话框
Event Tab选项卡
该页可显示每个事件的下列信息:日期和时间、TID、事件类、操作、结果路径和持续时间。
Process选项卡
Process(进程)选项卡显示了事件发生那一刻,所选事件背后的进程相关细节信息。
Stack选项卡
Stack(栈)选项卡可用于确定事件发生的原因以及导致该事件的组件显示了该事件被记录那一刻的线程调用栈。栈可用于确定事件发生的原因以及导致该事件的组件
Frame(帧) 显示帧编号,K代表内核模式帧,U代表用户模式帧
Module(模块) 包含该帧中所执行代码的文件名称。
Location(位置) 模块中实际执行代码的具体位置。
Address(地址) 执行中进程的代码指令在虚拟地址空间内的地址。
Path(路径) Module列显示的文件完整路径。
配置事件筛选器
单击工具栏
或按Ctrl+L打开筛选器对话框
可首先从下拉菜单选择一个属性,
并从第二个下拉菜单选择所要执行的测试类型,
随后从第三个下拉复选框菜单中选择要对比的值。所有文本对比都是大小写不敏感的。
在从第一个下拉菜单选中一个属性后,第三个下拉复选框菜单会列出当前数据集所有可用的值。
例如选择Process Name(进程名)后,第三个下拉复选框菜单会列出已生成事件的所有进程的名称。此外我们也可以直接编辑该下拉复选框菜单中的值。
随后通过第一行的第四个下拉菜单选择是要包含或排除匹配的事件,
单击【Add】(添加)按钮即可将新的筛选器条件加入现有筛选器。筛选器列表修改完成后,单击【OK】或【Apply】。
保存Procmon记录
要保存Procmon的追踪记录,按Ctrl+S或单击任务栏上
图标
进程树
按下Ctrl+T或单击工具栏上的【Process Tree】(进程树)按钮可显示如下所示的Process Tree对话框。
Process Tree对话框可用具备层次结构的形式显示已加载记录中所有被引用的进程,并可反映进程之间的父子关系,这一点与Procexp的树状视图较为类似。
高级输出
默认情况下,Procmon会自动隐藏与应用程序排错工作无关的事件,
其中包括:
1、Procmon自身活动产生的事件。
2、Procexp或Autoruns产生的事件。
3、System进程内部产生的事件。
4、Profiling事件,包括每一秒均会生成的进程Profiling事件。
5、名称以“IRPMJ”开头的底层操作(I/O请求包,Windows驱动使用此类操作实现文件或设备I/O、PnP、电源管理,以及其他与I/O有关的功能)。
6、名称以“FASTIO_”开头的底层操作。例如I/O系统之外其他组件产生的I/O请求包(IRP),以及使用文件系统驱动或缓存管理器执行的I/O请求。
7、以“FAST IO”开头的结果,如“FAST IO DISALLOWED”。
8、涉及系统分页文件的活动。
9、NTFS和MFT(主文件分配表)的内部管理。
从【Filter】菜单选择【Enable Advanced Output】(启用高级输出)即可取消上述所有排除(Profiling事件除外),并会为文件系统操作显示驱动级的名称。
例如,Basic模式的CreateFile操作会在高级模式下显示为IRP_MJ_CREATE(IRP是IO request packet的缩写,即IO请求包。在内核中,如需对某个设备进行功能请求,大部分情况下是通过发送IRP实现的.)。
反选【Enable Advanced Output】选项将重新应用上文列出的排除,并恢复为显示Basic模式的操作名称。
进阶使用
使用Procmon记录启动过程
从【Options】菜单中启用【Enable Boot Logging】(启用启动记录)选项后,会弹出一个确认对话框
可按需选择每秒生成一条或十条。
此外也可以使用/EnableBootLogging命令行选项启动Procmon并直接启用启动记录功能。
启用启动记录后,Procmon会通过配置驱动的方式作为引导启动驱动程序(Boot start driver),在系统下次启动过程中的早期阶段先于大部分其他驱动自动加载。
Procmon的驱动会将操作信息记录至%windir%\Procmon.PMB,并在系统关机或再次运行Procmon之前持续记录。
启动完成后,打开Procmon会有如下的提示对话框
我们可以将开机产生的事件信息保存。
不管是否保存,Procmon都会自动加载上次开机产生的事件
让Procmon在账户注销后继续运行
若要启动注销后依然可以继续运行Procmon,可参照下列命令行示例:
1 PsExec -s -d Procmon.exe /AcceptEula /Quiet /BackingFile C:\Procmon.pml
若要停止追踪可运行下列命令行命令:
1 PsExec -s -d Procmon.exe /AcceptEula /Terminate
Procmon提供的分析工具
这些工具位于【Tools】(工具)菜单下:
Process Activity Summary(进程活动摘要)
File Summary(文件摘要)
Registry Summary(注册表摘要)
Stack Summary(栈摘要)
Network Summary(网络摘要)
Cross Reference Summary(交叉引用摘要)
Count Occurrences(出现次数)
Process Activity Summary
Process Activity Summary(进程活动摘要)对话框通过表格列出了在应用当前筛选器的前提下,捕获有数据的每个进程。
表中每一行会显示进程名称和PID,CPU用量图表,文件、注册表和网络事件的数量,提交峰值和工作集峰值,并会通过图表显示进程生命周期内这些数据的变化情况。
File Summary
SummaryFile Summary(文件摘要)对话框,列出了经过当前筛选器筛选后所有文件和目录操作的汇总信息,并可按照路径、文件夹、文件扩展,将结果分组显示在不同选项卡中。
对于每个唯一的文件系统路径,该对话框会显示针对该文件执行I/O操作使用的时间总量;打开、关闭、读取、写入、Get ACL、Set ACL,以及其他操作的执行次数;执行的操作总数;以及文件读取和写入的字节总数。
Registry Summary
Registry Summary(注册表摘要)对话框用表格显示了注册表操作所涉及的每个注册表路径;针对这些键执行I/O操作所耗费的总时间;执行打开、关闭、读取、写入和其他操作的数量;以及所有操作的总数。
单击列头可按照对应列的数据对所有内容进行排序,拖拽列头可调整列的显示顺序。
双击行可以在当前筛选器中为该行对应的注册表路径添加一条路径规则。
Stack Summary
SummaryStack Summary(栈摘要)对话框可分析能够被Procmon追踪的事件全部的栈追踪信息,确定信息之间的共性和差异,并用可展开的树形图将结果呈现出来。
对于一个调用栈内的每一帧,均可看到在Procmon可追踪事件中的执行次数,Procmon可捕获操作所耗费的时间总量,模块的名称和路径,以及其内部的绝对偏移量。
如果有可用的符号信息,Stack Summary还会显示每个栈帧的函数名称、源文件路径,以及位于源文件中的行号。
Network Summary
SummaryNetwork Summary(网络摘要)对话框列出了筛选后的追踪记录中每个TCP和UDP端点及端口信息,对应的连接、断开连接、发送、接收操作的数量,这些操作的总数,以及接收和发送的字节总数。
单击列头可针对该列数据对所有信息排序,拖拽列头可调整列顺序。
双击行可在筛选器中为对应的端点和端口设置路径规则。
高级使用
写入自定义调试信息
在前面我写的几篇Windows内核开发入门的教程里,使用DbgPrint函数,可以写入调试信息,并在DebugView工具中看到。
这里也是类似一样的原理,我们可以通过写入自己的调试信息,并在Procmon中进行查看。
不过这里并没有提供相关的Api函数,需要使用DeviceIoControl与内核进行通信。
需要注意的是,默认情况下所有Profiling事件均会被筛选掉。若要查看调试输出事件,需要在工具栏上启用【Show Profiling Events】按钮。随后可能还需要强调显示Debug Output Profiling操作并排除进程Profiling操作。
实现方式如下:
1 #include<tchar.h> 2 #include<Windows.h> 3 4 const ULONG FILE_DEVICE_PROCMON_LOG = 0x00009535; 5 const ULONG IOCTL_EXTERNAL_LOG_DEBUGOUT = (ULONG)CTL_CODE(FILE_DEVICE_PROCMON_LOG, 0x81, METHOD_BUFFERED, FILE_WRITE_ACCESS); 6 7 BOOL WriteProcmonDebugOutput(const LPTSTR szDebugOutput) 8 { 9 if (!szDebugOutput) 10 return FALSE; 11 12 HANDLE hDevice = CreateFile(L"\\\\.\\Global\\ProcmonDebugLogger", 13 GENERIC_READ | GENERIC_WRITE, 14 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 15 NULL, 16 OPEN_EXISTING, 17 FILE_ATTRIBUTE_NORMAL, 18 NULL); 19 20 if (hDevice == INVALID_HANDLE_VALUE) 21 return FALSE; 22 23 DWORD nLen = wcslen(szDebugOutput) * sizeof(TCHAR); 24 DWORD unused = 0; 25 26 BOOL bRet = DeviceIoControl(hDevice, 27 IOCTL_EXTERNAL_LOG_DEBUGOUT, 28 (LPVOID)szDebugOutput, 29 nLen, 30 NULL, 31 0, 32 &unused, 33 NULL); 34 35 CloseHandle(hDevice); 36 hDevice = NULL; 37 return bRet; 38 } 39 40 void main() 41 { 42 const LPTSTR szOutput = _tcsdup(L"HelloWorld"); 43 WriteProcmonDebugOutput(szOutput); 44 delete szOutput; 45 }
运行效果如下:
调试技术的大师John Robbins创建了一系列辅助类,你可以将其用于自己的原生或托管应用程序中。
下载地址为:http://github.com/Wintellect/ProcMonDebugOutput