I have ported WinPcap to a NDIS 6 filter driver: https://github.com/nmap/npcap. But it still doesn't support capturing all 802.11 native packets (like control and management frames are not captured).
I noticed there is a way setting DOT11_OPERATION_MODE_NETWORK_MONITOR for the wireless adapter using WlanSetInterface function. But this call succeeds (the return value is OK, and my wi-fi network disconnects after this call). But the problem is I can't see any packets on the Wi-Fi interface using Wireshark, not even the 802.11 data in fake ethernet form. So there must be something wrong with it.
I know that from NDIS 6 and vista, enabing this feature is possible (at least Microsoft's own Network Monitor 3.4 supports this).
So I want to know how to enable monitor mode for the NDIS 6 version WinPcap? Thanks.
My code is shown as below:
// WlanTest.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <wlanapi.h>
#define WLAN_CLIENT_VERSION_VISTA 2
void SetInterface(WLAN_INTF_OPCODE opcode, PVOID* pData, GUID* InterfaceGuid)
{
DWORD dwResult = 0;
HANDLE hClient = NULL;
DWORD dwCurVersion = 0;
DWORD outsize = 0;
// Open Handle for the set operation
dwResult = WlanOpenHandle(WLAN_CLIENT_VERSION_VISTA, NULL, &dwCurVersion, &hClient);
dwResult = WlanSetInterface(hClient, InterfaceGuid, opcode, sizeof(ULONG), pData, NULL);
WlanCloseHandle(hClient, NULL);
}
// enumerate wireless interfaces
UINT EnumInterface(HANDLE hClient, WLAN_INTERFACE_INFO sInfo[64])
{
DWORD dwError = ERROR_SUCCESS;
PWLAN_INTERFACE_INFO_LIST pIntfList = NULL;
UINT i = 0;
__try
{
// enumerate wireless interfaces
if ((dwError = WlanEnumInterfaces(
hClient,
NULL, // reserved
&pIntfList
)) != ERROR_SUCCESS)
{
__leave;
}
// print out interface information
for (i = 0; i < pIntfList->dwNumberOfItems; i++)
{
memcpy(&sInfo[i], &pIntfList->InterfaceInfo[i], sizeof(WLAN_INTERFACE_INFO));
}
return pIntfList->dwNumberOfItems;
}
__finally
{
// clean up
if (pIntfList != NULL)
{
WlanFreeMemory(pIntfList);
}
}
return 0;
}
// open a WLAN client handle and check version
DWORD
OpenHandleAndCheckVersion(
PHANDLE phClient
)
{
DWORD dwError = ERROR_SUCCESS;
DWORD dwServiceVersion;
HANDLE hClient = NULL;
__try
{
*phClient = NULL;
// open a handle to the service
if ((dwError = WlanOpenHandle(
WLAN_API_VERSION,
NULL, // reserved
&dwServiceVersion,
&hClient
)) != ERROR_SUCCESS)
{
__leave;
}
// check service version
if (WLAN_API_VERSION_MAJOR(dwServiceVersion) < WLAN_API_VERSION_MAJOR(WLAN_API_VERSION_2_0))
{
// No-op, because the version check is for demonstration purpose only.
// You can add your own logic here.
}
*phClient = hClient;
// set hClient to NULL so it will not be closed
hClient = NULL;
}
__finally
{
if (hClient != NULL)
{
// clean up
WlanCloseHandle(
hClient,
NULL // reserved
);
}
}
return dwError;
}
// get interface state string
LPWSTR
GetInterfaceStateString(__in WLAN_INTERFACE_STATE wlanInterfaceState)
{
LPWSTR strRetCode;
switch (wlanInterfaceState)
{
case wlan_interface_state_not_ready:
strRetCode = L"\"not ready\"";
break;
case wlan_interface_state_connected:
strRetCode = L"\"connected\"";
break;
case wlan_interface_state_ad_hoc_network_formed:
strRetCode = L"\"ad hoc network formed\"";
break;
case wlan_interface_state_disconnecting:
strRetCode = L"\"disconnecting\"";
break;
case wlan_interface_state_disconnected:
strRetCode = L"\"disconnected\"";
break;
case wlan_interface_state_associating:
strRetCode = L"\"associating\"";
break;
case wlan_interface_state_discovering:
strRetCode = L"\"discovering\"";
break;
case wlan_interface_state_authenticating:
strRetCode = L"\"authenticating\"";
break;
default:
strRetCode = L"\"invalid interface state\"";
}
return strRetCode;
}
int main()
{
HANDLE hClient = NULL;
WLAN_INTERFACE_INFO sInfo[64];
RPC_CSTR strGuid = NULL;
TCHAR szBuffer[256];
DWORD dwRead;
if (OpenHandleAndCheckVersion(&hClient) != ERROR_SUCCESS)
return -1;
UINT nCount = EnumInterface(hClient, sInfo);
for (UINT i = 0; i < nCount; ++i)
{
if (UuidToStringA(&sInfo[i].InterfaceGuid, &strGuid) == RPC_S_OK)
{
printf(("%d. %s\n\tDescription: %S\n\tState: %S\n"),
i,
strGuid,
sInfo[i].strInterfaceDescription,
GetInterfaceStateString(sInfo[i].isState));
RpcStringFreeA(&strGuid);
}
}
UINT nChoice = 0;
// printf("for choice wireless card:");
//
// if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE), szBuffer, _countof(szBuffer), &dwRead, NULL) == FALSE)
// {
// puts("error input");
// return -1;
// }
// szBuffer[dwRead] = 0;
// nChoice = _ttoi(szBuffer);
//
// if (nChoice > nCount)
// {
// puts("error input.");
// return -1;
// }
//ULONG targetOperationMode = DOT11_OPERATION_MODE_EXTENSIBLE_STATION;
ULONG targetOperationMode = DOT11_OPERATION_MODE_NETWORK_MONITOR;
SetInterface(wlan_intf_opcode_current_operation_mode, (PVOID*)&targetOperationMode, &sInfo[nChoice].InterfaceGuid);
return 0;
}
Update:
Guy has made me clear about what should the high-level library side of WinPcap do about the monitor mode, in nature is setting/getting OID values. But what should the WinPcap driver do, do I need to change the driver? I think the WlanSetInterface call is actually doing the same thing as setting the DOT11_OPERATION_MODE_NETWORK_MONITOR using OID request? Does the fact that it doesn't work mean that the npf driver also needs some kind of changes?