如何判断进程类型?Windows内核层区分系统与用户级进程并获取应用列表
关于Windows进程区分与任务管理器应用列表的内核层实现
刚好之前折腾过Windows 7内核驱动里的进程枚举和区分工作,来给你详细拆解这两个问题:
一、如何判断一个进程是用户交互程序还是系统/后台进程?
首先得澄清:Windows官方并没有严格的「用户程序/普通进程」划分,我们常说的用户程序一般指有可视化窗口、和用户直接交互的进程,而系统/后台进程包括系统服务、无窗口的后台工具等。判断方式分用户态和内核态两种:
用户态快速判断
- 检查进程是否有顶层可见窗口:可以用
EnumWindows遍历系统中所有窗口,再通过GetWindowThreadProcessId关联到进程ID,有可见顶层窗口的基本就是用户能看到的程序。 - 查看进程会话ID:调用
ProcessIdToSessionId获取会话ID,系统核心进程(比如smss.exe、csrss.exe)的会话ID是0,用户登录后启动的程序会话ID通常≥1(不过部分服务也会跑到用户会话,这点要注意)。
内核态精准判断
- 查看
EPROCESS结构体的SessionId字段:会话ID为0的基本是系统级进程,会话ID≥1的属于用户会话进程。 - 解析进程令牌:系统进程的令牌通常携带
NT AUTHORITY\SYSTEM这类高权限SID,而用户程序的令牌对应的是当前登录用户的SID。可以用SeQueryInformationToken函数获取令牌的用户信息来区分。
二、Windows 7内核驱动中区分系统服务进程与用户级应用,及获取任务管理器「应用程序」栏的进程列表
先明确:任务管理器的「应用程序」栏不是所有用户进程的集合,它只展示有顶层可见窗口、且运行在用户交互桌面的进程,服务创建的窗口(哪怕有)也不会出现在这里。要在驱动里实现这个,分两步走:
1. 内核层区分系统服务进程与用户级应用
系统服务进程是由服务控制管理器(services.exe)启动的,有几个核心特征可以用来区分:
- 父进程追溯:查看
EPROCESS的InheritedFromUniqueProcessId字段,父进程是services.exe的基本都是服务进程(别硬编码PID,services.exe的PID每次启动可能不一样)。 - 会话ID:大部分服务默认运行在会话0(Windows Vista之后的会话隔离机制),而用户交互程序在用户会话(ID≥1)。
- 令牌权限:服务进程通常使用
SYSTEM令牌或专用服务令牌,用户应用则使用当前登录用户的令牌,通过PsReferencePrimaryToken获取令牌后解析SID就能区分。 - 桌面关联:服务进程一般关联的是
WinSta0\Service-0x0-xxxx$这类专用桌面,而用户应用关联的是WinSta0\Default交互桌面。
2. 获取和任务管理器「应用程序」栏一致的进程列表
要复刻这个列表,核心是筛选出有顶层可见窗口且在用户交互桌面的进程,驱动里可以这么做:
- 检查进程的桌面关联:通过
EPROCESS的WindowStation和Desktop字段,判断是否关联了WinSta0\Default桌面(这是用户交互的默认桌面)。 - 验证窗口可见性:需要获取进程的顶层窗口信息,检查是否带有
WS_VISIBLE样式。内核里可以通过附加到目标进程,调用win32k的内部窗口枚举函数(不过要注意,Windows 7的会话隔离意味着会话0的驱动需要切换到目标会话才能访问用户窗口)。 - 排除系统级窗口:有些系统进程也会在用户会话创建窗口,但这类窗口通常不会被任务管理器归类到「应用程序」栏,比如
explorer.exe的系统托盘窗口,需要通过窗口类名或进程名称进一步过滤。
驱动实现的注意事项
- 操作
EPROCESS时,一定要用PsLookupProcessByProcessId安全获取进程对象,别直接硬读内存,容易导致系统崩溃。 - Windows 7的会话隔离机制会限制会话0的驱动访问用户会话资源,需要用
KeStackAttachProcess附加到目标会话的进程后再操作窗口相关结构。 - 尽量避免在驱动里直接操作注册表(比如查服务注册表项),不仅效率低,还容易触发系统安全机制。
内容的提问来源于stack exchange,提问作者MrLister




