Introduction
I spent some time with WLAN, and I wondered how NetStumbler performed this nice scan, but the source is not free. I programmed a lot of stuff but nothing worked. I tried to code my own driver and used WinPcap, but nothing worked very well. Then I read an article, "Scanning for Wireless Networks" in 29A magazine 8 from GriYo. My work is mainly based on this article and you can find a lot of sources on the internet that do the same thing.
Background
It's possible via NDIS Miniport (from userspace) to access a lot of functions of the WLANcard. To get access to all these nice functions, you need the Windows DDK.
Take a look at ntddndis.h and you will find a lot of actions you can perform. For example, you can get or set the WEP key. I only implemented the scanning function, but you can improve it. To build your own application, you can take my article class or write our own.
Using the Code
Well, I put a lot of comments in the code so you will be able to understand it easily. All the functions you need are in the airctl
class. Take a look at the header file.
class airctl
{
HANDLE m_handle;
deviceInfo* m_devices;
public:
airctl(void);
void freeScanList(void);
NDIS_802_11_BSSID_LIST* scan(void);
bool open( char *device_name);
BOOL list_devices( void);
inline deviceInfo* getDevList( ){return m_devices;};
public:
~airctl(void);
private:
void AddDevice(char * desc, char * name);
void clearDeviceList(void);
NDIS_802_11_BSSID_LIST* m_pBSSIDList;
BOOL get_device_info( int Index,
char *key_name,
char *device_info,
char *device_description);
};
First, we need a list of all devices. Call list_devices()
and it will list everything in an single linked list. Then the demo program shows the user a list of all available devices. The structure for the list is as follows:
struct deviceInfo
{
char *description; char *name; deviceInfo* next;};
Look at the following piece of code. It is from the demo program.
deviceInfo* dv = win->m_airctl.getDevList();
if (dv == NULL)
{
List.AddString("No available interface found !");
}else{
while (dv != NULL) {
List.AddString(dv->description); dv = dv->next;
}
}
After the user selects a device, it will be opened.
To open the device with the aitctl
class, we only need to call open()
with the device name in it.
if(m_airctl.open(dv->name)!= true)
{
MessageBox("Cant open selected device","fehler",0);
}else
{
::AfxBeginThread( threadFunc , (LPVOID) this);
}
But we are curious and will take a small look into that function.
m_handle = CreateFileA( device_file,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL) ;
if( m_handle == INVALID_HANDLE_VALUE)
{
return false;
}
else
{
return true;
}
Now it's time to search the WLANs. We will call scan()
from the aitctl
class.
NDIS_802_11_BSSID_LIST * pBSSIDList = pDlg->m_airctl.scan();
How is this performed? It's done via DeviceIoControl
. This is for direct input and output, or for retrieving information from a physical device. For more information, read this article. It's a really nice series of articles.
First we force the WLAN device to scan for WLANs; we wait for a moment and then we ask the device again to tell us an answer. The source code for this looks like this:
oidcode = OID_802_11_BSSID_LIST_SCAN ;
DeviceIoControl( m_handle,
IOCTL_NDIS_QUERY_GLOBAL_STATS,
&oidcode,
sizeof( oidcode),
( ULONG *) NULL,
0,
&bytesreturned,
NULL) ;
Sleep( 2000) ;
memset( m_pBSSIDList, 0, sizeof( NDIS_802_11_BSSID_LIST) *
NUMBEROF_BSSIDS) ;
oidcode = OID_802_11_BSSID_LIST ;
if( DeviceIoControl( m_handle, IOCTL_NDIS_QUERY_GLOBAL_STATS, &oidcode,
sizeof( oidcode), ( ULONG *) m_pBSSIDList, sizeof( NDIS_802_11_BSSID_LIST) *
NUMBEROF_BSSIDS,
&bytesreturned, NULL) == 0) {
return NULL;
}
else {
return m_pBSSIDList;
}
Now we have a pointer to the NDIS_802_11_BSSID_LIST
take a look in ntddndis.h where it's defined.
typedef struct _NDIS_802_11_BSSID_LIST
{
ULONG NumberOfItems; NDIS_WLAN_BSSID Bssid[1];
}
So far so good, but what does NDIS_WLAN_BSSID
look like?
typedef struct _NDIS_WLAN_BSSID
{
ULONG Length; NDIS_802_11_MAC_ADDRESS MacAddress; UCHAR Reserved[2];
NDIS_802_11_SSID Ssid; ULONG Privacy; NDIS_802_11_RSSI Rssi; NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
NDIS_802_11_CONFIGURATION Configuration;
NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
NDIS_802_11_RATES SupportedRates;
} NDIS_WLAN_BSSID, *PNDIS_WLAN_BSSID;
This looks much better. This is the information we would like to obtain. Now we can show them to the user.
But, we have one thing left to discuss: how to get the next item. For this, we will use another pointer which points to NDIS_WLAN_BSSID
. Now the variable ULONG Length
tells us how long this entry is. After this, a certain number of bytes the next entry "lies."
Well, the following is not a good solution, but it shows you what I mean and what it could look like.
for(unsigned int i =0 ;i < pBSSIDList->NumberOfItems;i++){
int temp=i; PNDIS_WLAN_BSSID cpSsid=pBSSIDList->Bssid;
while(temp!=0 ){
cpSsid=(PNDIS_WLAN_BSSID)((char*)cpSsid+ cpSsid->Length);
temp--;
}
}
That's it! Thank you for reading, now it's up to you.
Points of Interest
You can perform a lot of more functions to the WLAN device. Just take a look into ntddndis.h under 802.11 OIDs.
History
- First release version 0.1
License
This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.
A list of licenses authors might use can be found here.