Mapping from NDIS OIDs to WMI classes

In which we write a PowerShell script, install the WDK, attach a kernel debugger, reverse-engineer the OS, and prove Goldbach’s conjecture

We’ve previously talked about how to rummage through all the NDIS WMI classes, but there’s one topic we haven’t fully covered.  Suppose you’re looking for the WMI class that maps to a specific OID — how do you find the right class?

There are a few ways you can do this.  The first is just to take a guess based on the name.  Suppose we want to find the WMI class that corresponds to OID_GEN_VLAN_ID.  Let’s search for any WMI class that has “VLAN” in the name:

Get-WmiObject -Namespace root\wmi -List  | Where-Object {$_.name -Match "VLAN" }

My machine has only one matching class, MSNdis_VlanIdentifier, and indeed that’s the right one.

But this technique relies somewhat on luck.  What if you don’t find any matches — should you keep searching, or does that mean there really is no WMI class for that OID?  So that takes us to the more methodical approach.

If you install the Windows Driver Kit (WDK), then it will give you several rather helpful files:

wmicore.mof Defines each of the built-in NDIS WMI classes
ndisguids.h Defines the names of the GUIDs that underlie built-in NDIS WMI classes
ndis.h and ntddndis.h Defines various OIDs, structures, and flags that will be useful in decoding some WMI classes

Let’s start in wmicore.mof.  Open it in a text editor to find a comment indicating which WMI class implements OID_GEN_VLAN_ID.  Again, we find MSNdis_VlanIdentifier.

 ///     OID_GEN_VLAN_ID:
[WMI, Dynamic, Provider("WMIProv"), guid("{765dc702-c5e8-4b67-843b-3f5a4ff2648b}"),
 Description("NDIS VLAN Identifier") : amended]
class  MSNdis_VlanIdentifier : MSNdis
{
    [ read, write, Description("The IEEE 802.1Q VLAN ID assigned to this NIC.") : amended,
        WmiDataId(1)]    uint32    NdisVlanId;
};

By searching through this file, you can find all the built-in WMI classes that NDIS provides.

Not so fast, you say.  You want to be methodical, and relying on code comments is not exactly bulletproof.  What about MSNdis_VendorID, which is in wmicore.mof, but is missing a comment mentioning which OID it is tied to?

Here’s where ndisguids.h comes in handy.  Note that each WMI class in wmicore.mof has a GUID.  For example, MSNdis_VendorID has GUID {5ec1035e-a61a-11d0-8dd4-00c04fc3358c}.  You can find that same GUID in ndisguids.h (although the numbers are presented a little differently):

 DEFINE_GUID(GUID_NDIS_GEN_VENDOR_ID,
    0x5ec1035e, 0xa61a, 0x11d0, 0x8d, 0xd4, 0x00, 0xc0, 0x4f, 0xc3, 0x35, 0x8c);

Unlike the WMI class names, the GUID names in ndisguid.h have the same naming scheme as OID names.  So GUID_NDIS_GEN_VENDOR_ID corresponds to OID_GEN_VENDOR_ID.  You can do a similar transformation for each GUID that is related to an OID.

Let’s summarize what we have gotten so far.  NDIS provides WMI classes on every miniport.  NDIS translates an OID into a GUID, and WMI translates that GUID into a WMI class.  You can download the WDK to see OIDs in ntddndis.h, the GUIDs in ndisguid.h, and the WMI classes themselves in wmicore.mof.

Is that all?  Well… not quite.  These are valid techniques to explore the NDIS-provided WMI classes… but what about miniport- or LWF-provided classes?  Some miniport drivers implement their own private WMI classes.  Is there a way to peek at those?

Yup.  But to do this, we’ll need to trot out a kernel debugger.  Run !ndiskd.miniport -wmi <miniporthandle> to see all the WMI classes the miniport provides.

 0: kd> !ndiskd.miniport ffffe000be3761a0 -wmi

WMI

    f4a8027a-23b7-11d1-9ed9-00a0c9010057   OID 0xffa0c90a
                       OID, ARRAY, CUSTOM
    GUID_NDIS_ENUMERATE_ADAPTER            [N/A]
                       READ, NOT_SETTABLE, NDIS_ONLY
    GUID_NDIS_NOTIFY_ADAPTER_REMOVAL       [N/A]
                       STATUS, EVENT_ENABLED, NOT_SETTABLE, NDIS_ONLY
    GUID_NDIS_GEN_LINK_SPEED               OID_GEN_LINK_SPEED
                       OID, READ, NOT_SETTABLE
    GUID_NDIS_GEN_VENDOR_ID                OID_GEN_VENDOR_ID
                       OID, READ, NOT_SETTABLE

In practice, a miniport will have hundreds of GUIDs; the excerpt above highlights just a few of the types of WMI classes you might find.  A class marked with the OID flag is (unsurprisingly) translated to an OID.  In the excerpt above, you can see that my miniport supports OID_GEN_VENDOR_ID, as well as a vendor-custom OID 0xffa0c80a.  (The miniport also supports the WMI event GUID_NDIS_NOTIFY_ADAPTER_REMOVAL.)

Let’s suppose we want to find the corresponding WMI class for that vendor-private GUID/OID.  It’s little surprise that PowerShell can do it in a hurry.  Just drop in the GUID that you got from !ndiskd.  (If !ndiskd hid the GUID behind its friendly name, as it did for GUID_NDIS_GEN_VENDOR_ID, you can unmask it by running !ndiskd.help GUID_NDIS_GEN_VENDOR_ID.)

Get-WmiObject -Namespace root\wmi -List  |

    Where-Object {$_.Qualifiers['guid'].Value -eq '{f4a8027a-23b7-11d1-9ed9-00a0c9010057}' }

If you happen to have that vendor’s NIC driver installed, you'll see their WMI class pop up.  If not, well, try again with one of the system-provided GUIDs.

Now we’ve seen three ways to map WMI classes to OIDs:

  • Guess a likely class name and search for it with PowerShell
  • Search for the OID in ndisguid.h, then find the matching GUID in wmicore.mof
  • Use !ndiskd.miniport -wmi to find all the GUIDs that are available on a particular miniport

I’m out of space for today, so I’ll have to save my proof of Goldbach’s conjecture for next week.