2

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

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

我知道从 NDIS 6 和 vista 开始,启用此功能是可能的(至少 Microsoft 自己的Network Monitor 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 驱动程序也需要进行某种更改?

4

1 回答 1

2

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

使用master 分支的 libpcap 版本中的 来进行 OID 设置/获取操作pcap_oid_set_request_win32()pcap-win32.c如果p->opt.rfmon设置为,则使用设置为的结构设置pcap_activate_win32()OID 。OID_DOT11_CURRENT_OPERATION_MODEDOT11_CURRENT_OPERATION_MODEuCurrentOpModeDOT11_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 列表中启动的线程中的消息中指出的那样,处理来自 NDIS 的接收指示的代码必须能够处理“原始数据包”接收指示,并且您可能必须将它们添加到 NDIS 数据包过滤器也是。(如果您要使用 Wireshark 来测试更改,您将不得不破解 dumpcap;您将无法更改 NPcap,以便人们可以将其放入并且现有版本的 Wireshark 将支持监控模式。 )

我还在那里指出了如何查询设备以了解它是否支持监控模式

至于关闭监控模式,则需要驱动程序、packet.dll 和 libpcap 工作。在驱动程序中:

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

在 packet.dll 中,添加一个PacketSetMonitorMode()例程,它是相关BIOCioctl 的包装器。

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

要在驱动程序中设置 OID,请参阅BIOCQUERYOIDBIOCSETOIDin的代码路径NPF_IoControl()- 新的BIOCioctl 将在NPF_IoControl().

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

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

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

于 2015-12-29T19:23:38.897 回答