了解流程是否为系统流程



我试图找出用户在程序运行时运行的程序,并将它们输出到文件中。现在我面临的情况是,当使用Process.GetProcesses()检索所有进程时,我会看到大约269个进程的列表,这相当于任务管理器显示的所有进程,包括像77个svchost进程这样的Windows进程。

现在我想过滤掉一些系统进程(至少那些在任务管理器中显示为"Windows进程"的进程)。有什么方法可以做到这一点吗?或者我必须维护所有Windows进程的进程名称(或文件目录)列表吗?

简短回答:

任务管理器中的解决方案基于以下列表(取自Windows 10版本)进行硬编码:

%windir%explorer.exe
%windir%system32ntoskrnl.exe
%windir%system32WerFault.exe
%windir%system32backgroundTaskHost.exe
%windir%system32backgroundTransferHost.exe
%windir%system32winlogon.exe
%windir%system32wininit.exe
%windir%system32csrss.exe
%windir%system32lsass.exe
%windir%system32smss.exe
%windir%system32services.exe
%windir%system32taskeng.exe
%windir%system32taskhost.exe
%windir%system32dwm.exe
%windir%system32conhost.exe
%windir%system32svchost.exe
%windir%system32sihost.exe



长答案:

我花了一些时间才找到这个列表——下面是通往启蒙的道路;-)



原始答案:

要回答您的问题了解流程是否是系统流程并不像看上去那么容易。为了获得这些信息,您必须获得进程的所有者,在windows系统上,该所有者通常被分配给安全标识符。

安全标识符(SID)是用于标识受信任方的可变长度的唯一值。每个帐户都有一个由权威机构(如Windows域控制器)颁发的唯一SID,并存储在安全数据库中。每次用户登录时,系统都会从数据库中检索该用户的SID,并将其放入该用户的访问令牌中。系统使用访问令牌中的SID来识别与Windows安全性的所有后续交互中的用户。当SID被用作用户或组的唯一标识符时,就不能再使用它来标识另一个用户或组。

你肯定会看到其中一个,它有点像S-1-5-18S-1-5-21-2557247…-…-…-1001

有一个完整的已知SID列表,其中还包括一组您可能都认为与系统过程相关的SID。

如果我的假设是正确的,那么您希望获得在本地系统帐户下运行的所有进程,该帐户将是S-1-5-18

别说话,让我们编码:

首先,我们(就是你,我已经测试过了;-))需要从advapi32.dll导入GetSecurityInfo,如下所示:

[DllImport("advapi32.dll", SetLastError = true)]
private static extern uint GetSecurityInfo(IntPtr handle,
SE_OBJECT_TYPE objectType,
SECURITY_INFORMATION securityInfo,
out IntPtr sidOwner,
out IntPtr sidGroup,
out IntPtr dacl,
out IntPtr sacl,
out IntPtr securityDescriptor);

它需要像这样定义SE_OBJECT_TYPE和SECURITY_INFORMATION的两个枚举:

private enum SE_OBJECT_TYPE
{
SE_UNKNOWN_OBJECT_TYPE,
SE_FILE_OBJECT,
SE_SERVICE,
SE_PRINTER,
SE_REGISTRY_KEY,
SE_LMSHARE,
SE_KERNEL_OBJECT,
SE_WINDOW_OBJECT,
SE_DS_OBJECT,
SE_DS_OBJECT_ALL,
SE_PROVIDER_DEFINED_OBJECT,
SE_WMIGUID_OBJECT,
SE_REGISTRY_WOW64_32KEY
}
private enum SECURITY_INFORMATION
{
OWNER_SECURITY_INFORMATION = 1,
GROUP_SECURITY_INFORMATION = 2,
DACL_SECURITY_INFORMATION = 4,
SACL_SECURITY_INFORMATION = 8,
}

现在我们差不多到了。如果以以下方式调用GetSecurityInfo。。。

uint returnValue = GetSecurityInfo(process.Handle,
SE_OBJECT_TYPE.SE_KERNEL_OBJECT,
SECURITY_INFORMATION.OWNER_SECURITY_INFORMATION,
out IntPtr ownerSid,
out IntPtr groupSid,
out IntPtr dacl,
out IntPtr sacl,
out IntPtr securityDescriptor);

如果结果是ERROR_SUCESS(即0),则可以使用SecurityIdentifier类的实例来检查检索到的SID是否为本地系统帐户,如下所示:

SecurityIdentifier securityIdentifier = new SecurityIdentifier(ownerSid);
if (securityIdentifier.IsWellKnown(WellKnownSidType.LocalSystemSid))
{
// The process is running unter the local system account.
}

就是这样。要获得最终结果,您必须检查多个SID,如系统本地服务

网络服务这里有一个小示例,它为本地机器上的所有进程执行此操作。当然,您需要使用正确的特权来运行此操作,否则将出现访问被拒绝的错误。

private static void Main(string[] args)
{
const uint ERROR_SUCCESS = 0;
Process[] processes = Process.GetProcesses();
foreach (Process process in processes)
{
try
{
uint returnValue = GetSecurityInfo(process.Handle,
SE_OBJECT_TYPE.SE_KERNEL_OBJECT,
SECURITY_INFORMATION.OWNER_SECURITY_INFORMATION,
out IntPtr ownerSid,
out IntPtr groupSid,
out IntPtr dacl,
out IntPtr sacl,
out IntPtr securityDescriptor);
if (returnValue != ERROR_SUCCESS)
{
// If the function succeeds, the return value is ERROR_SUCCESS.
// If the function fails, the return value is a nonzero error code defined in WinError.h.
continue;
}
SecurityIdentifier securityIdentifier = new SecurityIdentifier(ownerSid);
Console.WriteLine("Owner of process {0} is {1}", process.ProcessName, securityIdentifier);
if (securityIdentifier.IsWellKnown(WellKnownSidType.LocalSystemSid))
{
Console.WriteLine("Running under System Account");
}
}
catch (Exception e)
{
Console.WriteLine("Unable to retrieve owner for process {0}: {1}", process.ProcessName, e.Message);
}
}





更新:

如果将(原始答案的)结果与任务管理器中的进程列表进行比较,仍然存在差异。当我进一步调查这个问题时,我看到了一篇文章,其中指出,标记为关键的进程将显示在windows进程下。

如果流程有一个可见的窗口,则任务管理器将其称为"应用程序"。

如果进程被标记为关键进程,则任务管理器将其称为"Windows进程">

否则,任务管理器将其称为"后台进程"。

这可以通过简单地调用IsProcessCritical来评估。因此需要DllImport。。。

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool IsProcessCritical(IntPtr hProcess, ref bool Critical);

之后可以这样称呼:

bool criticalProcess = false;
if (!IsProcessCritical(process.Handle, ref criticalProcess))
{
// Could not retrieve process information
}
if (criticalProcess)
{
// This is a critical process, it should be listed
// in the "Windows processes" section.
}

尽管这听起来很有希望,但事实并非如此——它仍然会导致不正确的结果


因此,在安装API Monitor(顺便说一句,这是一款令人难以置信的软件)并过滤和搜索500多万个(已经预先过滤的)API调用后,我注意到Taskmgr.exe多次使用参数调用ExpandEnvironmentString,这些参数在调用之前似乎没有检索到。


经过进一步的调查(并得出合乎逻辑的结论),我注意到Taskmgr.exe中嵌入了一个硬编码列表。只需使用Process explorer即可找到:

  1. 启动流程资源管理器
  2. 右键单击Taskmgr.exe
  3. 导航到字符串选项卡
  4. 向下滚动
  5. 感到失望

有以下条目:

%windir%explorer.exe
%windir%system32ntoskrnl.exe
%windir%system32WerFault.exe
%windir%system32backgroundTaskHost.exe
%windir%system32backgroundTransferHost.exe
%windir%system32winlogon.exe
%windir%system32wininit.exe
%windir%system32csrss.exe
%windir%system32lsass.exe
%windir%system32smss.exe
%windir%system32services.exe
%windir%system32taskeng.exe
%windir%system32taskhost.exe
%windir%system32dwm.exe
%windir%system32conhost.exe
%windir%system32svchost.exe
%windir%system32sihost.exe


因此,我的结论是:
任务管理器中的解决方案是基于上面的列表(取自Windows 10版本)进行硬编码的。

实现这一点的一种方法是过滤掉所有路径以windows目录路径开头的进程。

您可以通过调用Environment.GetFolderPath来获取windows目录的路径与Environment.SpecialFolder.Windows类似:

var windowsPath = Environment.GetFolderPath(Environment.SpecialFolder.Windows);

然后你可以过滤掉所有图像位于该文件夹中的进程:

var processes = Process.GetProcesses();
foreach (var process in processes) {
if (!process.MainModule.FileName.StartsWith(windowsPath)) {
// Do something with process
}
}

只过滤结果:

Process.GetProcesses().Where(x => x.MainWindowHandle != IntPtr.Zero)

检查路径,可以绕过

最新更新