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 implemeted 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 infomration 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 man. 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