如何在NDIS 6过滤器驱动程序中启用802.11监控模式(DOT11_OPERATION_MODE_NETWORK_MONITOR)?

时间:2015-12-29 09:03:42

标签: c windows winpcap ndis

我已将 WinPcap 移植到 NDIS 6过滤器驱动程序https://github.com/nmap/npcap。但它仍然不支持捕获所有802.11本机数据包(如未捕获控制和管理帧)。

我注意到有一种方法可以使用 WlanSetInterface 功能为无线适配器设置 DOT11_OPERATION_MODE_NETWORK_MONITOR 。但是这个调用成功(返回值正常,我的wi-fi网络在此调用后断开连接)。但问题是我无法使用Wireshark在Wi-Fi接口上看到任何数据包,甚至连虚假以太网形式的802.11数据也看不到。所以一定有问题。

我知道从NDIS 6和vista,启用此功能可能(至少微软自己的网络监视器3.4 支持此功能)。

所以我想知道如何为NDIS 6版WinPcap启用监控模式?感谢。

我的代码如下所示:

// 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;
}

更新

Guy让我清楚了解WinPcap的高级库方面应该对监控模式做些什么,本质上是设置/获取OID值。但是WinPcap驱动程序应该做什么,我是否需要更改驱动程序?我认为 WlanSetInterface 调用实际上与使用OID请求设置 DOT11_OPERATION_MODE_NETWORK_MONITOR 做同样的事情?它不起作用的事实意味着npf驱动程序还需要某种更改吗?

1 个答案:

答案 0 :(得分:1)

(针对问题更新和后续评论更新了答案。)

使用pcap_oid_set_request_win32()(主分支中libpcap版本中的pcap-win32.c)进行OID设置/获取操作。如果在p->opt.rfmon中设置了pcap_activate_win32(),请将OID OID_DOT11_CURRENT_OPERATION_MODE设置为DOT11_CURRENT_OPERATION_MODE structure,并将uCurrentOpMode设置为DOT11_OPERATION_MODE_NETWORK_MONITOR

对于pcap_can_set_rfmon_win32(),尝试获取设备的句柄(请注意,这是在激活调用之前完成的),如果成功,请使用pcap_oid_get_request_win32()尝试获取该OID的值;如果成功,你可以设置它,否则你要么不能设置它,要么你有错误。

驱动程序已经支持通用的get / set OID操作 - 这是PacketRequest()使用的内容,而pcap_oid_get_request_win32() / pcap_oid_set_request_win32()是在PacketRequest()上面实现的,所以它们使用的是

作为wireshark-dev列表中的线程I indicated中的消息中的you started,处理来自NDIS的接收指示的代码必须能够处理“原始数据包”接收指示,并且您可能必须将这些添加到NDIS数据包过滤器。 (And you'll have to hack dumpcap, if you're going to use Wireshark to test the changes;您将无法更改NPcap,以便人们可以将其删除,现有版本的Wireshark将支持监控模式。)

我还指出how to query a device to find out whether it supports monitor mode

关于关闭监视器模式,这将需要驱动程序,packet.dll和libpcap工作。在司机:

    NDIS 6驱动程序中的
  • ,对于每个接口,都有一个“监控模式实例”和一个保存的操作模式的计数,并且对于每个打开的接口的NPF实例,都有一个“监控模式”标志;
  • 在Windows 9x和NDIS 4/5驱动程序中,添加“开启监控模式”BIOC来电,ERROR_NOT_SUPPORTED始终失败;
  • 在NDIS 6驱动程序中,添加相同的BIOC调用,如果未设置实例的“监控模式”标志,则尝试将操作模式设置为监控模式,如果成功,则保存如果接口的监控模式计数为零,则为旧操作模式,增加接口的监控模式计数并设置实例的“监控模式”标志(它还可以将适当的值添加到数据包过滤器);
  • 具有关闭打开的NPF实例的例程检查实例的“监控模式”标志,如果设置,则递减“监控模式实例”计数,如果计数达到零,则恢复旧的操作模式。

在packet.dll中,添加一个PacketSetMonitorMode()例程,它是有问题的BIOC ioctl的包装器。

pcap-win32.c中,如果请求监控模式,请致电PacketSetMonitorMode(),而不是直接设置操作模式。

要在驱动程序中设置OID,请参阅BIOCQUERYOID中的BIOCSETOIDNPF_IoControl()的代码路径 - 新的BIOC ioctl将在NPF_IoControl()中处理。

(当然,请进行适当的MP锁定。)

如果您可以枚举接口的所有NPF实例,则可能不需要监控模式计数 - 计数只是设置了监控模式标志的实例数。

在驱动程序中执行此操作意味着如果执行监控模式捕获的程序突然终止,以便没有用户模式代码进行任何清理,模式仍然可以重置。