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 rootwmi -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_Atm
MSNdis_Fddi
MSNdis_TokenRing
Very old media types (ATM, FDDI, and Token Ring)
MSNdis_Co CoNDIS drivers
MSNdis_Notify
MSNdis_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 rootwmi -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 rootwmi -Class MSNdis_ReceiveScaleCapabilities | Get-Member

   TypeName: System.Management.ManagementObject#rootwmiMSNdis_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 rootwmi -Class MSNdis_ReceiveScaleCapabilities | Get-Member -Name WmiQueryReceiveScaleCapabilities | Format-List

TypeName   : System.Management.ManagementObject#rootwmiMSNdis_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]’rootwmi:MSNdis_ObjectHeader’).CreateInstance()
    $hdr.Revision      = $revision
    $hdr.Type          = $type
    $hdr.Size          = $size

    return $hdr
}

function Get-NdisWmiHeader
{
    param($timeout = 5)

    $whdr = ([wmiclass]’rootwmi: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 (10)

  1. A Guy says:

    This rocks my socks.

  2. juuhuu says:

    and simply get the ndis driver version through wmi ?

  3. 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.)

  4. Goose says:

    This driver does not seem to be installed on windows 7

    all request to MSNdis are answered with Error..

    Can you Advise ?

    10x.

  5. 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?

  6. Bob says:

    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

  7. Bob says:

    My code:

    // Obtain in-parameters for the method

    ManagementBaseObject inParams =

     classInstance.GetMethodParameters("WmiQueryReceiveScaleCapabilities");

    //here, how to fill correctly inParams with NdisWmiHeader(->NdisObjectHeader) structure/object ??

    // Execute the method and obtain the return values.

    ManagementBaseObject outParams =

     classInstance.InvokeMethod("WmiQueryReceiveScaleCapabilities", inParams, null);

  8. @ 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.

  9. Bob says:

    @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!