Monday, June 16, 2008

Simple WMI client

Let's say you need to acquire couple of statistics from remote Windows server. You're not using a fancy utility like HP OVO or something like that, you just need an ad hoc solution, which can't have any component running on the server.

In my case I needed to check on free space, number of processes and available memory on couple of servers. The easiest way to do it, is to use the WMI - Windows Management Instrumentation. It provides SQL-like access to Windows system information. It allows you to inquire on all sorts of various information, for full list of available classes check this MSDN article.

I decided to write a simple command line client in C#, because it would most probably take quite a lot of effort in C and I should broaden the range of languages I use on a daily basis anyway.

using System;
using System.Management;
using System.Globalization;
using System.Net;
using Microsoft.Win32;

class DisplayInfo
{
 public static int Main(string[] args)
 {
  ManagementScope scope;
  ConnectionOptions options = new ConnectionOptions();
  if ((args.Length == 0) || (args[0] == "--help") || (args[0] == "-h") || (args[0] == "/?"))
  {
   Console.WriteLine("wmi - select information from WMI\n\n" + 
       "Usage: wmi WMI_CLASS [servername username password]\n\n" +
       "Most usefull classes are\n" + 
       "\tWin32_OperatingSystem\n" +
       "\tWin32_Share\n" +
       "\tWin32_DiskDrive\n" +
       "\tWin32_LogicalDisk\n" +
       "\tWin32_Process\n");
   return 0;
  }
  else if (args.Length == 1)
  {
   scope = new ManagementScope();
  }
  else
  {
   string server = args[1];

   options.Username = args[2];
   options.Password = args[3];
   options.Authority = "NTLMDOMAIN:"+server;

   scope = new ManagementScope("\\\\"+server+"\\root\\cimv2", options);
  }

  scope.Connect();

  SelectQuery query = new SelectQuery("SELECT * FROM "+args[0]);

  ManagementObjectSearcher s = new ManagementObjectSearcher(scope, query);
  foreach (ManagementObject o in s.Get())
  {
   PropertyDataCollection properties = o.Properties;
   foreach (PropertyData property in properties)
   {
    Console.WriteLine("{0}: {1}", property.Name, o[property.Name]);
   }
   Console.WriteLine();
  }
  Console.WriteLine();
  return 0;
 }
}

It doesn't do any error checking whatsoever, because it assumes you know what you're doing.

Also note the actual query line


SelectQuery query = new SelectQuery("SELECT * FROM "+args[0]);

which allows you to do a nice SQL injection like this


wmi "Win32_LogicalDisk WHERE Name = 'C:'" 

It requires minimum .NET 1.1 and can be easily compiled on any computer with .NET (you don't need any SDK or anything). For convenience binary version can be downloaded - wmi.zip (3kB)

Usage: wmi WMI_CLASS [servername user password]

So if you want for example to inquiry on all the drives and their free space on a remote server, you need to do


wmi Win32_LogicalDisk remote_server remote_user remote_password | grep -i \\\(^FreeSpace\\\)\\\|\\\(^Caption\\\) 

Caption: C:
FreeSpace: 3357395456
Caption: D:
FreeSpace:
Caption: F:
FreeSpace: 3357395456
Caption: H:
FreeSpace: 88749633536
Caption: O:
FreeSpace: 33317158912
Caption: P:
FreeSpace: 33317158912
Caption: Q:
FreeSpace: 2288992256
Caption: S:
FreeSpace: 396537856
Caption: T:
FreeSpace: 2888278016

4 comments:

Anonymous said...

could you please advise how to obtain grep ?

ondrew said...

You can get it from various places

http://gnuwin32.sourceforge.net

http://mingw.org

http://www.cygwin.com

All of them have advantages and disadvantages. I personally prefer mingw.

Anonymous said...

Thanks for sharing. For alternate/enhanced usage, might also be nice to modify the SELECT * FROM with another SQL injection, replacing * with another CLI argument so that one could selectively pick the fields or data of relevance from the query rather than get it all and filter that during the result processing phase.

dluu said...

Do you know if one can perform complex SQL queries that involve joins or unions for WMI? Something like

SELECT WPP.IDProcess, WPP.PercentProcessorTime, WPP.PrivateBytes, WPP.HandleCount, WNC.NumberBytesinallHeaps FROM Win32_PerfFormattedData_PerfProc_Process AS WPP INNER JOIN Win32_PerfFormattedData_NETFramework_NETCLRMemory AS WNC ON WPP.IDProcess = WNC.IDProcess WHERE WPP.Name LIKE 'MyAppName%' AND WNC.Name LIKE 'MyAppName%'