Exploring NDIS’s WMI classes

Getting fancy with PowerShell and WMI

Last time we got our feet wet with a simple PowerShell script to query Ethernet MAC addresses.  It looked easy, but of course, it requires you to know the magic WMI class name "MSNdis_EthernetCurrentAddress".  How do you go about discovering other interesting WMI classes?  Once again, PowerShell to the rescue:

PS > Get-WmiObject -Namespace root\wmi -List  | Where-Object {$_.name -Match "MSNdis" } | Sort-Object

This command will list all of the NDIS WMI classes available on your system.  (Try it out!)  There are quite a few, but you can bring order to chaos by classifying them into several main categories:

Class name prefix Description
MSNdis_80211 WiFi wireless miniports
MSNdis_Ethernet Ethernet (or Ethernet-emulating) miniports
MSNdis_AtmMSNdis_FddiMSNdis_TokenRing Very old media types (ATM, FDDI, and Token Ring)
MSNdis_Co CoNDIS drivers
MSNdis_NotifyMSNdis_Status WMI Events (another great feature, which we'll talk about later)
MSNdis_Wmi Data structures used as parameters to methods (which we'll talk about right now)

 

Let's pick one of the classes at random, and see what we can do with it.  How about MSNdis_ReceiveScaleCapabilities?  From its name, we infer that it reads the adapter's RSS capabilities.  Recall that many of NDIS's WMI classes are translated directly from the underlying OID — knowing this will help us quite a bit as we start to decode the WMI class.  A quick search on MSDN turns up OID_GEN_RECEIVE_SCALE_CAPABILITIES, which uses an NDIS_RECEIVE_SCALE_CAPABILITIES structure.  Keep that documentation in mind as we explore the WMI class.

Now let's get our hands dirty:

PS > $Adapters = Get-WmiObject -Namespace root\wmi -Class MSNdis_ReceiveScaleCapabilities
PS > $Adapters[6]
Active           : True
InstanceName     : BitBlaster 2000 Ethernet Adapter

Well that's… disappointing.  We got back an array of instances (one for each network adapter), but the only real properties are the name a boolean Active flag.  I know that this network adapter supports RSS, so where is all the RSS info hiding?

It turns out that many NDIS classes don’t expose their information directly.  Instead, they require you to invoke a WMI method on the class to retrieve the information.  In order to find the method, let's use the PowerShell Get-Member cmdlet:

PS > Get-WmiObject -Namespace root\wmi -Class MSNdis_ReceiveScaleCapabilities | Get-Member
TypeName: System.Management.ManagementObject#root\wmi\MSNdis_ReceiveScaleCapabilities
Name                             MemberType   Definition
----                             ----------   ----------
WmiQueryReceiveScaleCapabilities Method       System.Management.ManagementBaseObject WmiQueryReceiveScaleCapabilitie...
Active                           Property     System.Boolean Active {get;set;}
InstanceName                     Property     System.String InstanceName {get;set;}
__CLASS                          Property     System.String __CLASS {get;set;}
__DERIVATION                     Property     System.String[] __DERIVATION {get;set;}
__DYNASTY                        Property     System.String __DYNASTY {get;set;}
__GENUS                          Property     System.Int32 __GENUS {get;set;}
__NAMESPACE                      Property     System.String __NAMESPACE {get;set;}
__PATH                           Property     System.String __PATH {get;set;}
__PROPERTY_COUNT                 Property     System.Int32 __PROPERTY_COUNT {get;set;}
__RELPATH                        Property     System.String __RELPATH {get;set;}
__SERVER                         Property     System.String __SERVER {get;set;}
__SUPERCLASS                     Property     System.String __SUPERCLASS {get;set;}
ConvertFromDateTime              ScriptMethod System.Object ConvertFromDateTime();
ConvertToDateTime                ScriptMethod System.Object ConvertToDateTime();

There's a lot of stuff in there, but you can see that one member is a method named WmiQueryReceiveScaleCapabilities.  Let's dig into it:

PS > Get-WmiObject -Namespace root\wmi -Class MSNdis_ReceiveScaleCapabilities | Get-Member -Name WmiQueryReceiveScaleCapabilities | Format-List
TypeName   : System.Management.ManagementObject#root\wmi\MSNdis_ReceiveScaleCapabilities
Name       : WmiQueryReceiveScaleCapabilities
MemberType : Method
Definition : System.Management.ManagementBaseObject
WmiQueryReceiveScaleCapabilities(System.Management.ManagementObject#MSNdis_WmiMethodHeadr Header)

So it's a function that takes an MSNdis_WmiMethodHeader as input, and returns another WMI class as output.  You'll find that this is the general pattern for NDIS's WMI methods — fortunately, the MSNdis_WmiMethodHeader is basically boilerplate; you don't have to fret over how to create each one.  Let's create a little helper function to do that boilerplate for us:

$NDIS_WMI_METHOD_HEADER_REVISION_1             = 1
$NDIS_WMI_OBJECT_TYPE_METHOD                   = 0x02
$NDIS_SIZEOF_WMI_METHOD_HEADER_REVISION_1      = 0xffff
function Get-NdisObjectHeader
{
param(
$revision = $NDIS_WMI_METHOD_HEADER_REVISION_1,
$type     = $NDIS_WMI_OBJECT_TYPE_METHOD,
$size     = $NDIS_SIZEOF_WMI_METHOD_HEADER_REVISION_1
)
$hdr = ([wmiclass]'root\wmi:MSNdis_ObjectHeader').CreateInstance()
$hdr.Revision      = $revision
$hdr.Type          = $type
$hdr.Size          = $size
return $hdr
}
function Get-NdisWmiHeader
{
param($timeout = 5)
$whdr = ([wmiclass]'root\wmi:MSNdis_WmiMethodHeader').CreateInstance()
$whdr.Header       = Get-NdisObjectHeader
$whdr.PortNumber   = 0
$whdr.NetLuid      = 0
$whdr.Padding      = 0
$whdr.RequestId    = 0
$whdr.Timeout      = $timeout
return $whdr
}

Now that we can conjure up the input parameter, we can return to the task of executing our WMI method.  This part should be easy:

PS > $whdr = Get-NdisWmiHeader
PS > $RSS = $Adapters[6].WmiQueryReceiveScaleCapabilities($whdr)
PS > $RSS
RssCaps          : System.Management.ManagementBaseObject
PS > $RSS.RssCaps
CapabilitiesFlags         : 184550145
Header                    : System.Management.ManagementBaseObject
NumberOfInterruptMessages : 1
NumberOfReceiveQueues     : 2

Ah-hah!  Now that looks like a promising result!  (You didn't really have doubts that we'd figure it out eventually, did you?)

The last mystery remaining is the number 184550145.  But if you remember what we said earlier — WMI classes are based on OID requests — you'll check the documentation for NDIS_RECEIVE_SCALE_CAPABILITIES.  And indeed, each field listed above corresponds to a field in that driver structure.  So you can conclude that you should use the flags documented there to decode the CapabilitiesFlags.  In this case, decimal 184550145 is hex 0x0B000301, which corresponds to NDIS_RSS_CAPS_MESSAGE_SIGNALED_INTERRUPTS | NDIS_RSS_CAPS_CLASSIFICATION_AT_ISR | NDIS_RSS_CAPS_USING_MSI_X | NDIS_RSS_CAPS_HASH_TYPE_TCP_IPV4 | NDIS_RSS_CAPS_HASH_TYPE_TCP_IPV6 | NdisHashFunctionToeplitz.

Now that you're armed with the techniques above, you have the chops to explore the remaining NDIS WMI classes.  Next time we'll talk about one more cool trick you can do with WMI.

Comments

  • Anonymous
    February 07, 2012
    This rocks my socks.

  • Anonymous
    February 19, 2013
    and simply get the ndis driver version through wmi ?

  • Anonymous
    March 08, 2013
    For NDIS contract version, you can use gwmi -namespace rootwmi -class MSNdis_DriverVersion The result is an integer packed as per OID_GEN_DRIVER_VERSION, with 0xFF00 masking out the major version, and 0x00FF masking out the minor version. For the driver-reported version number, look at MSNdis_VendorDriverVersion. Note that if you're targeting Windows 8 / Windows Server 2012, you can alternatively use Get-NetAdapter and pick out MSFT_NetAdapter.DriverMajorNdisVersion , DriverVersionString, and MajorDriverVersion.  (These are a little easier to work with.)

  • Anonymous
    June 23, 2014
    This driver does not seem to be installed on windows 7 all request to MSNdis are answered with Error.. Can you Advise ? 10x.

  • Anonymous
    June 24, 2014
    I'm quite sure that Windows 7 includes MSNdis support, as I wrote this blog post using Windows 7.  What error are you getting?  Can you try on a different PC?

  • Anonymous
    March 13, 2015
    Hello! Could you briefly translate this simple query (WmiQueryReceiveScaleCapabilities) from PowerShell to C# ? I'm trying to use GetMethodParameters/InvokeMethod but I'm stuck.. (I'm not a master in C#..), probably for you it will be easy peasy Appreciate your help. Bob

  • Anonymous
    March 14, 2015
    The comment has been removed

  • Anonymous
    March 14, 2015
    @ Bob Unfortunately, porting the code to C# is both outside my expertise and the scope of this blog.  I can only offer you a guess. I believe that "GetMethodParameters" is a red herring -- that lets you reflect on the methods.  But in this case, you already know the method parameters, so you don't need to do reflection. Instead, take a look at ManagementClass.CreateInstance.  The example at the bottom of this page shows how ot create an instance of a class and set its member fields:  msdn.microsoft.com/.../system.management.managementclass.createinstance(v=vs.110).aspx  .  Except you won't need to call Put on it as in the example.  Instead, use the object instance as an input to ManagementClass.InvokeMethod.

  • Anonymous
    March 15, 2015
    Okay, looks like you got the answer on stackoverflow: stackoverflow.com/.../powershell-c-sharp-wmi-correct-filling-input-object-for-invokemethod

  • Anonymous
    March 15, 2015
    @Jeffery I just wanted to share link to stackoverflow, but you were first :] ps Thanks for this NDIS blog [this is the best source of NDIS knowledge], and waiting for more!