找出一个进程是否是一个系统进程

时间:2018-11-17 19:15:13

标签: c# process

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

现在,我要过滤掉一些系统进程(至少在任务管理器中显示为“ Windows进程”的那些进程)。有什么方法可以执行此操作,还是我必须维护所有Windows进程的进程名称(或文件目录)列表?

3 个答案:

答案 0 :(得分:2)

简短答案:

根据以下列表(摘自Windows 10版本),对taskmanager中的解决方案进行了硬编码:

%windir%\explorer.exe
%windir%\system32\ntoskrnl.exe
%windir%\system32\WerFault.exe
%windir%\system32\backgroundTaskHost.exe
%windir%\system32\backgroundTransferHost.exe
%windir%\system32\winlogon.exe
%windir%\system32\wininit.exe
%windir%\system32\csrss.exe
%windir%\system32\lsass.exe
%windir%\system32\smss.exe
%windir%\system32\services.exe
%windir%\system32\taskeng.exe
%windir%\system32\taskhost.exe
%windir%\system32\dwm.exe
%windir%\system32\conhost.exe
%windir%\system32\svchost.exe
%windir%\system32\sihost.exe



长答案:

花了一些时间才能到达该列表-以下是开悟的路径;-)



原始答案:

要回答您的问题找出一个进程是否为系统进程并不像看起来那样容易。为了获得此信息,您必须获得Windows系统通常被实现为Security identifiers的进程的所有者

  

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

您肯定会看到其中之一,就像 S-1-5-18 S-1-5-21-2557247 ...- .. .-...- 1001

这里有WellKnown SIDs的完整列表,其中还包括一堆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_TYPESECURITY_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,例如 System 本地服务网络服务,等等。

这是一个小示例,它对本地计算机上的所有进程执行此操作。 当然,您将需要以正确的权限运行它,否则,将出现访问被拒绝的错误。

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);
        }
    }





更新

如果将(原始答案的)结果与任务管理器中的进程列表进行比较,则仍然存在差异。当我进一步调查此问题时,我遇到了一个article,该状态指出,标记为 critical 的进程将显示在 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%\system32\ntoskrnl.exe
%windir%\system32\WerFault.exe
%windir%\system32\backgroundTaskHost.exe
%windir%\system32\backgroundTransferHost.exe
%windir%\system32\winlogon.exe
%windir%\system32\wininit.exe
%windir%\system32\csrss.exe
%windir%\system32\lsass.exe
%windir%\system32\smss.exe
%windir%\system32\services.exe
%windir%\system32\taskeng.exe
%windir%\system32\taskhost.exe
%windir%\system32\dwm.exe
%windir%\system32\conhost.exe
%windir%\system32\svchost.exe
%windir%\system32\sihost.exe


所以我的结论是:
基于上面的列表(摘自Windows 10版本),对taskmanager中的解决方案进行了硬编码。

答案 1 :(得分:1)

一种方法是过滤掉所有路径以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
  }
}

答案 2 :(得分:0)

仅过滤结果:

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

可以忽略路径检查