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