How do I filter a WMI search on what I think is a

2019-07-07 19:42发布

问题:

I am using the following to get a list of loaded dependents or modules via WMI, but am having issues geting my search string correct, basically I need to target a specific dependent process via its handle, and the handle property seems to be nested inside a ManagementObject.

var wmiQueryString = string.Format("select * from CIM_ProcessExecutable WHERE Dependent.Handle=\"{0}\"",procID);
using (var searcher = new ManagementObjectSearcher(string.Format(wmiQueryString)))
using (var results = searcher.Get())
{
    foreach (var item in results.Cast<ManagementObject>())
    {
        try
        {
            var dependent = new ManagementObject((string)item["Dependent"]);
            Console.WriteLine(new FileInfo((string)dependent["Name"]).FullName);
        }
        catch (System.Management.ManagementException ex)
        {
            // Process does not exist anymore
        }
    }
}

Using just "Dependent.Handle" does not seem to work, every variation I have tried has resulted in invalid query string exceptions. I assume because the searcher has no understanding of the object structure?

I could load and filter through the data in my C#, but for performance reasons I would like to do the filter in the WMI query.

Updated code based on answer below:

        var wmiQueryString = string.Format("ASSOCIATORS OF {{Win32_Process.Handle=\"{0}\" }} WHERE ResultClass = CIM_ProcessExecutable", procID);
        using (var searcher = new ManagementObjectSearcher(wmiQueryString))
        using (var results = searcher.Get())
        {
            foreach (ManagementObject item in results) // This throws System.Management.ManagementException: 'Invalid object path '
            {
                foreach (PropertyData prop in item.Properties) // At this point this is just here for testing, but this is never reached anyway as the exception occurs prior to the iteration.
                {
                    Console.WriteLine("{0}: {1}", prop.Name, prop.Value);
                }

                //var dependent = item["Dependent"] as ManagementObject;
                //Console.WriteLine(new FileInfo((string)dependent["Name"]).FullName);
            }
        }

This however throws System.Management.ManagementException: 'Invalid object path ' on the indicated line. But this may just be from the previous line, which may still be indicative of a bad query string.

回答1:

Turns out, the secret sauce is references of not associators of.

var wmiQueryString = string.Format( "references of {{win32_process.Handle={0}}}", handle );
using ( var searcher = new ManagementObjectSearcher( wmiQueryString ) )
using ( var results = searcher.Get( ) )
{
  foreach ( ManagementObject item in results )
  {
    Console.WriteLine( item.ClassPath ); //--> turns out this is the cim_processexecutalbe

    //--> and these are it's properties...with references to cim_datafile...  
    foreach ( PropertyData prop in item.Properties )
    {
      Console.WriteLine( "{0}: {1}", prop.Name, prop.Value );
    }
  }
}

This gets you the CIM_ProcessExecutables properties:

\\CLAYDEV\root\cimv2:Win32_SessionProcess
Antecedent: \\.\root\cimv2:Win32_LogonSession.LogonId="999"
Dependent: \\.\root\cimv2:Win32_Process.Handle="628"
\\CLAYDEV\root\cimv2:Win32_SystemProcesses
GroupComponent: \\CLAYDEV\root\cimv2:Win32_ComputerSystem.Name="CLAYDEV"
PartComponent: \\CLAYDEV\root\cimv2:Win32_Process.Handle="628"
\\CLAYDEV\root\cimv2:CIM_ProcessExecutable
Antecedent: \\CLAYDEV\root\cimv2:CIM_DataFile.Name="C:\\WINDOWS\\system32\\winlogon.exe"
BaseAddress: 140696226496512
Dependent: \\CLAYDEV\root\cimv2:Win32_Process.Handle="628"
GlobalProcessCount:
ModuleInstance: 1687814144
ProcessCount: 0
....

It also turns out - as Mateo points out in the comments, that references of and associators of are kinda finicky about formatting. Can't have extra spaces inside the {}. I didn't know that.

You can use associators of, too. If you get the syntax just so...and you refer to the associated type (not the associating type). What I mean is CIM_ProcessExecutables associates CIM_Process to CIM_DataFile. So, to get only the CIM_DataFiles properties...you can do this:

  var wmiQueryString = string.Format( "associators of {{win32_process.Handle={0}}} where resultclass=cim_datafile", handle );

...which gets you directly to the CIM_DataFile properties...

\\CLAYDEV\root\cimv2:CIM_DataFile
AccessMask: 17957033
Archive: True
Caption: c:\windows\system32\winlogon.exe
Compressed: False
CompressionMethod:
CreationClassName: CIM_LogicalFile
CreationDate: 20170510121417.106825-240
CSCreationClassName: Win32_ComputerSystem
CSName: CLAYDEV
Description: c:\windows\system32\winlogon.exe
Drive: c:
EightDotThreeFileName: c:\windows\system32\winlogon.exe
Encrypted: False
EncryptionMethod:
Extension: exe
FileName: winlogon
FileSize: 707072
FileType: Application
FSCreationClassName: Win32_FileSystem
FSName: NTFS
Hidden: False
InstallDate: 20170510121417.106825-240
InUseCount:
LastAccessed: 20170510121417.106825-240
LastModified: 20170419020715.554583-240
Manufacturer: Microsoft Corporation
Name: c:\windows\system32\winlogon.exe
Path: \windows\system32\
Readable: True
Status: OK
System: False
Version: 10.0.15063.250
Writeable: True
...

Getting only the interesting properties:

I can't see where there's anyway to select a subset from an associators of or references of statement...but as suggested in the comments below, you can do a SelectMany to get just the properties you want:

  var wmiQueryString = string.Format( "associators of {{win32_process.Handle={0}}} where resultclass=cim_datafile", handle );
  using ( var searcher = new ManagementObjectSearcher( wmiQueryString ) )
  {
    var results =
      searcher
      .Get( )
      .OfType<ManagementBaseObject>( )
      .SelectMany
      ( df => df.Properties.OfType<PropertyData>( ).Where( pd => pd.Name == "Caption" ) );

    foreach ( PropertyData item in results )
    {
      Console.WriteLine( item.Value );
    }
  }

The problem with that is it still iterates the entire property set to get the target property. It seems to run faster to just go straight at the property you want:

  var wmiQueryString = string.Format( "associators of {{win32_process.Handle={0}}} where resultclass=cim_datafile", handle );
  using ( var searcher = new ManagementObjectSearcher( wmiQueryString ) )
  using ( var results = searcher.Get( ) )
  {
    foreach ( ManagementObject item in results )
    {
      Console.WriteLine( item[ "Caption" ] );
    }
  }

...but it's not super fast either way, I'm afraid.



标签: c# wmi