Windows Wireless Networking


Mario Contestabile
marioc@computer.org
http://bubbler.net/outlaw/blog

The high level, general objective is to determine if the existing WiFi connection is secure.

The conclusion I've reached so far is that there are two methods of determining this, using NDIS and using WMI.


1- The NDIS way
2- The WMI way

1- NDIS/WRAPI


My initial web searches resulted in the discovery of this WRAPI API.

http://sysnet.ucsd.edu/pawn/wrapi

This library uses NDIS: some IOCTLs are
IOCTL_NDISUIO_OPEN_DEVICE,
IOCTL_NDISUIO_QUERY_OID_VALUE

Seems old and outdated, Release v2.0 -- September 30, 2002
Tried running an application using the online DLL with vc6, vc7, and vc8 compilers but obtained same results, a crash on load.

Web searches indicated this is a common issue so I subscribed to mailing list for feedback, and the general consensus is that the online DLL was built with an older DDK, and this will not work on newer versions of Windows.

So I decided to use the latest DDK to bypass wrapi.dll altogether and build my own NDIS DLL.

After downloading and installing the Windows Server 2003 SP1 DDK from

http://www.microsoft.com/whdc/DevTools/ddk/default.mspx

I was ready for my version of WRAPI, so using wrapi.cpp as the starting point I've re-written it.

Some key changes are:

1- Include paths showing the ddk install path:
#include <winioctl.h>
#include <C:/WINDDK/3790.1830/inc/w2k/ntddndis.h>
#include <C:/winddk/3790.1830/src/network/ndis/ndisprot/sys/nuiouser.h>

2- I removed the needless MFC dependency, the code is a straightforward win32 console.

3- Replaced IOCTLs with  
IOCTL_NDISUIO_BIND_WAIT is replaced with IOCTL_NDISPROT_BIND_WAIT
IOCTL_NDISUIO_QUERY_BINDING is replaced with IOCTL_NDISPROT_QUERY_BINDING IOCTL_NDISUIO_OPEN_DEVICE is replaced with IOCTL_NDISPROT_OPEN_DEVICE IOCTL_NDISUIO_QUERY_OID_VALUE is replaced with IOCTL_NDISPROT_QUERY_OID_VALUE and its NDISUIO_QUERY_OID structure replaced with NDISPROT_QUERY_OID structure

Of all the IOCTLs, the only successful one was IOCTL_NDISPROT_QUERY_BINDING

As errors from the other I received the three following:

Could not IOCTL_NDISPROT_OPEN_DEVICE, error 170 from OpenNdisDevice()

Could not IOCTL_NDISPROT_QUERY_OID_VALUE with OID_802_11_SSID, error 1167 from GetSSId()

Could not IOCTL_NDISPROT_QUERY_OID_VALUE with OID_802_11_WEP_STATUS, error 1167 from GetWEPStatus()

See the NewWrapi.zip for the CPP file of this Win32 code. 

Now, instead of  using this seemingly limited "\\\\.\\\\Ndisuio" device, I digged deeper and using simple NDIS IOCTLs 
quickly found that this was powerful, and easy. That's probably why this WRAPI lib no longer needs to be maintained,
the NDIS functionality is straightforward.

So, re-writing GetWEPStatus() as follows, we have the result we've been trying to obtain:

HANDLE m_hFileHandle2 = CreateFile("\\\\.\\{DCA933C6-03CC-4A3B-B189-37394675F552}",
   GENERIC_READ|GENERIC_WRITE,
   0,
   NULL,
   OPEN_EXISTING,
   FILE_ATTRIBUTE_NORMAL,
   (HANDLE) INVALID_HANDLE_VALUE); 

   DWORD f = OID_802_11_WEP_STATUS;

   if (DeviceIoControl(m_hFileHandle2,
      IOCTL_NDIS_QUERY_GLOBAL_STATS,
      (LPVOID) &f,
      sizeof(OID_802_11_WEP_STATUS),
      (LPVOID) &QueryBuffer[0],
      sizeof(QueryBuffer),
      &dwBytesReturned,
      NULL))
   {
   // QueryBuffer[0] contains our value indicating the security in-place
   }


Doing a search for OID_802_11_WEP_STATUS on MSDN will only return Windows CE hits. Rather take a look at the available 802.11 OIDs here:

(Notice some are Required, others are recommended or optional.)

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/NetXP_r/hh/NetXP_r/217wirelessoid_bca9862e-feea-406f-b11d-ea01859bfbd3.xml.asp


You'll notice a comment on that web page to the effect that, "The WLAN OIDs are available through Windows Management Instrumentation (WMI)".

This is interesting, if you lookup IOCTL_NDIS_QUERY_GLOBAL_STATS on MSDN it states, "This IOCTL will be deprecated in later operating system releases. You should use WMI interfaces to query miniport driver information".

This leads us the next method of retrieving this information...


2- WMI


Through continuing research, the next most obvious technology which seemed poised to solve our problem was WMI.


Here's an image of the WMI architecture:




CIM, Common Information Model (CIM), is simply a way to model information in a class-like structure. I've seen the term "object-oriented" to describe CIM, but coming from a C++ background, inheritance and properties does not OO make.

The CIM is continually updated, and the 2 in root\cimv2 means the schema represented is CIM Version 2.

The absolute form of an object path is \\machine\namespace:class

For example: \\evil-inside\root\cimv2:Win32_NetworkAdapter

See
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/win32_networkadapter.asp
for WMI information.

The Win32_NetworkAdapter class looked promising, so I first used that in a C# project.

To do so, create a typical C# project (I used Visual Studio 2005).
Right-click the project and select "Add Reference..."
Choose the System.Management assembly.
Failing to do will make the line

using System.Management;

fail during compilation with error CS0234, namespace not found.


I used the following code to dump various properties to see if this class is of any help.

SelectQuery query = new SelectQuery("Win32_NetworkAdapter");

ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);

foreach (ManagementBaseObject envVar in searcher.Get())
{
   Console.WriteLine("Name {0} Status {1} Type {2} MAC {3} caption {4} desc {5} NetConnectionID {6}  ProductName {7} SystemName {8}", envVar["Name"], envVar["Status"], envVar["AdapterType"], envVar["MACAddress"], envVar["Caption"], envVar["Description"], envVar["NetConnectionID"], envVar["ProductName"], envVar["SystemName"]);
}


Of the 21 results obtained , the wireless one for my laptop displays:

Name Broadcom 802.11b/g WLAN Status Type Ethernet 802.3 MAC 00:90:4B:F8:9B:E2 caption [00000011] Broadcom 802.11b/g WLAN desc Broadcom 802.11b/g WLAN NetConnectionID Wireless Network Connection ProductName Broadcom 802.11b/g WLAN SystemName EVIL-INSIDE

Nothing here seems helpful for out global objective.

Note that the "root\cimv2" namespace is the default scope for the System.Management API.

There's another way of iterating through the WMI classes which I found to be pretty cool, and useful.

Run wbemtest.exe

First, change the namespace to "root\cimv2".

For a good explanation, see
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnclinic/html/scripting06112002.asp

This namespace, "Provides information about the computer, disks, peripheral devices, files, folders, file systems, networking components, operating system, printers, processes, security, services, shares, SAM users and groups, and more."

a- Simply click the "Connect..." button, enter "root\cimv2" and the "Connect" button. 

See and snapshots.

b- Click the "Query" button, and in the "Enter Query" field, type "SELECT * FROM Win32_NetworkAdapter" then hit "Apply".

c- Boom. Guess what, 21 results!

So now instead of recompiling my .NET app above to try and obtain these values, I can use this tool to actually see them all. Again, nothing jumps out from all the properties which seems helpful for our overall objective.

To see all available classes, we can use the MSDN documentation, or using the WMI Tester tool, click the "Enum Classes..." button, and select the "Recursive" radio button.

On my desktop machine I have 908 objects in the "root\cimv2" namespace; you can double click on a class and see all properties and methods for each class.

There is no .NET implementation of WMI, simply managed API's to access the existing WMI framework through COM.

If we go back to the Windows Management Tester, wbemtest.exe, and switch to the "root\wmi" namespace and enumerate all classes, we can see several MSNDis_XXXXX classes; can this finally be the breakthrough we've been looking for?

One of the interesting classes I found was:  (In the root\wmi namespace!)

MSNdis_80211_WEPStatus 

When performing this query over various test connections this class seems to contain precisely what we are looking for, specifically:


- Ndis80211WEPStatus is 0 when WEP is in use
- Ndis80211WEPStatus is 2 when over an unsecured connection
- Ndis80211WEPStatus is 4 when WPA-PSK is in use
- Ndis80211WEPStatus is 6 when WPA is in use
- Ndis80211WEPStatus is 7 when disconnected

To see the SSID,
Use the MSNdis_80211_ServiceSetIdentifier class.
The Ndis80211Ssid property has the id

Ok, so what we have so far seems very good and easy to use.

I'll show the C++ and C# code to do the WQL Query, but first you're probably wondering, "How can I get this information for an active connection?"

We can probably use another query to check for the connection state, and only look at the WiFi security setting for those connections, but a much better way to do is to simply append

"WHERE Active = True"

to the query in the first place!


There is also another cool tool called CIM Studio for exploring schemas.
(Clearly many people have WMI perusal needs)

You can get it from: WMI Administrative Tools
http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=6430F853-1120-48DB-8CC5-F2ABDC3ED314

One disadvantage I've found with CIM Studio is that unlike wbemtest, when a returned property is itself a class, you can't double click it and continue on interrogating each embedded object.

Here is a WiFi link I found interesting too..

Christian Huitema, Director, Wireless Networking - Wireless Vision Longhorn And Beyond
Gary Huber, DELL - Wireless Directions
Tim Towell, Intel - Wireless Directions
http://download.microsoft.com/download/9/8/f/98f3fe47-dfc3-4e74-92a3-088782200fe7/TWNE05003_WinHEC05.ppt


Here's the C# way:



// NEED to CHANGE Namespace to WMI!!
// Remember "root\cimv2 is the default namespace for System.Management"

SelectQuery query = new SelectQuery("MSNdis_80211_WEPStatus");
char [] foo = { 'r', 'o', 'o', 't', '\\', 'w', 'm', 'i' };
String rightNameSpase = new String(foo);
ManagementScope bar = new ManagementScope(rightNameSpase);
ManagementObjectSearcher searcher = new ManagementObjectSearcher(bar, query);
foreach (ManagementBaseObject envVar in searcher.Get())
{
   Console.WriteLine(" WEP Status {0}", envVar["Ndis80211WEPStatus"]);
}

and for

the C++ way

go to http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/example__getting_wmi_data_from_the_local_computer.asp, make the following code changes:

hres = pLoc->ConnectServer( _bstr_t(L"ROOT\\wmi"), ....

hres = pSvc->ExecQuery( bstr_t("WQL"), bstr_t("SELECT * FROM MSNdis_80211_WEPStatus"), ....

 hr = pclsObj->Get(L"Ndis80211WEPStatus", 0, &vtProp, 0, 0);
wcout << "Ndis80211WEPStatus : " << vtProp.iVal << endl;


In Summary



- Forget WRAPI altogether
- NDIS can be used
- But WMI offers conveniency and flexibilty


Email me bugs and comments
marioc@computer.org