nmap / npcap

Nmap Project's Windows packet capture and transmission library
https://npcap.com
Other
3.03k stars 519 forks source link

Couldn't capture 802.11 radio beacon packets using Npcap SDK in Windows C++ #741

Open kmramn opened 4 months ago

kmramn commented 4 months ago

Describe the bug I wrote a C++ program using Npcap SDK in Windows environment where I have enabled Wi-Fi in monitor mode. But the program is unable to capture Wi-Fi 802.11 Beacon packets. Using Wireshark application, I am able to capture the Wi-Fi 802.11 Beacon packets.

To Reproduce Steps to reproduce the behavior:

#include <pcap.h>
#pragma comment(lib, "wpcap.lib")

#include <Windows.h>
#include <winerror.h>

#include <stdio.h>

#include <iostream>
using namespace std;

#include <tchar.h>
#include <strsafe.h>

#include <time.h>

BOOL LoadNpcapDlls()
{
    _TCHAR npcap_dir[512];
    UINT len;
    len = GetSystemDirectory(npcap_dir, 480);
    if (!len) {
        fprintf(stderr, "Error in GetSystemDirectory: %x", GetLastError());
        return FALSE;
    }
    _tcscat_s(npcap_dir, 512, _T("\\Npcap"));
    if (SetDllDirectory(npcap_dir) == 0) {
        fprintf(stderr, "Error in SetDllDirectory: %x", GetLastError());
        return FALSE;
    }
    return TRUE;
}

void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
    struct tm ltime;
    char timestr[16];
    time_t local_tv_sec;

    (VOID)(param);
    (VOID)(pkt_data);

    /* convert the timestamp to readable format */
    local_tv_sec = header->ts.tv_sec;
    localtime_s(&ltime, &local_tv_sec);
    strftime(timestr, sizeof timestr, "%H:%M:%S", &ltime);

    printf("%s,%.6d len:%d\n",
        timestr, header->ts.tv_usec, header->len);
}

/* Main Decleration */
void wmain()
{
    pcap_if_t *alldevs;
    pcap_if_t *d;
    int inum;
    int i = 0;
    pcap_t *adhandle;
    char errbuf[PCAP_ERRBUF_SIZE];

    /* Load Npcap and its functions. */
    if (!LoadNpcapDlls())
    {
        fprintf(stderr, "Couldn't load Npcap\n");
        exit(1);
    }

    /* Retrieve the device list on the local machine */
    if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING,
        NULL, &alldevs, errbuf) == -1)
    {
        fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
        exit(1);
    }

    /* Print the list */
    for (d = alldevs; d; d = d->next)
    {
        printf("%d. %s", ++i, d->name);
        if (d->description)
            printf(" (%s)\n", d->description);
        else
            printf(" (No description available)\n");
    }

    if (i == 0)
    {
        printf("\nNo interfaces found! Make sure Npcap is installed.\n");
        return;
    }

    printf("Enter the interface number (1-%d):", i);
    scanf_s("%d", &inum);

    if (inum < 1 || inum > i)
    {
        printf("\nInterface number out of range.\n");
        /* Free the device list */
        pcap_freealldevs(alldevs);
        return;
    }

    /* Jump to the selected adapter */
    for (d = alldevs, i = 0; i < inum - 1; d = d->next, i++);

    /* Open the device */
    if ((adhandle = pcap_open(d->name, // name of the device
        2048, // portion of the packet to capture
               // 65536 guarantees that the whole packet will
               // be captured on all the link layers
        PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
        1000, // read timeout
        NULL, // authentication on the remote machine
        errbuf // error buffer
    )) == NULL)
    {
        fprintf(stderr,
            "\nUnable to open the adapter. %s is not supported by Npcap\n",
            d->name);
        /* Free the device list */
        pcap_freealldevs(alldevs);
        return;
    }
    //Try setting monitor mode and error check, trimmed down to the error I'm facing 
    int mm_set = pcap_can_set_rfmon(adhandle);
    if (mm_set == 0)
    {
        fprintf(stderr, "Error setting monitor mode: Device doesn't have MM capability\n");
    }
    else
    {
        if (!pcap_set_rfmon(adhandle, 1))
        {
            cout << "Monitor Mode Enabled, pcap_set_rfmon(...) == 0" << endl;
        }
    }
    cout << endl;

    //Using pcap_set_rfmon() here to illustrate issue, this will output a 0 
    //indicating success but the pcap_activate() error check contradicts this
    cout << pcap_set_rfmon(adhandle, 1) << endl;

    //int status = pcap_activate(adhandle);
    //Activate the interface for sniffing
    if (pcap_activate(adhandle))
    {
        cout << endl;
        pcap_perror(adhandle, "Error");
        cout << endl;
        pcap_set_rfmon(adhandle, 0);
        pcap_activate(adhandle);
    }

    printf("\nlistening on %s...\n", d->description);

    /* At this point, we don't need any more the device list. Free it */
    //pcap_freealldevs(alldevs);

    /* start the capture */
    pcap_loop(adhandle, 0, packet_handler, NULL);
}

Expected behavior A clear and concise description of what you expected to happen.

Screenshots Capturing the radio 802.11 Beacon using C++ is not working. image

Capturing the radio 802.11 Beacon using Wireshark is working. image

Is it possible to capture the radio 802.11 Beacon packets using C++ and Npcap SDK? If possible, can you please suggest me what correction U have to make? Thank you.

guyharris commented 4 months ago

(Single quotes do not serve as "make this fixed-width text" brackets; you must use three backquotes, ```. Here's a properly-fixed-widthified, and better-indented, version of the code, which should be easier to read.)

#include <pcap.h>
#pragma comment(lib, "wpcap.lib")

#include <Windows.h>
#include <winerror.h>

#include <stdio.h>

#include 
using namespace std;

#include <tchar.h>
#include <strsafe.h>

#include <time.h>

BOOL LoadNpcapDlls()
{
    _TCHAR npcap_dir[512];
    UINT len;
    len = GetSystemDirectory(npcap_dir, 480);
    if (!len) {
        fprintf(stderr, "Error in GetSystemDirectory: %x", GetLastError());
        return FALSE;
    }
    _tcscat_s(npcap_dir, 512, _T("\Npcap"));
    if (SetDllDirectory(npcap_dir) == 0) {
        fprintf(stderr, "Error in SetDllDirectory: %x", GetLastError());
        return FALSE;
    }
    return TRUE;
}

void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
    struct tm ltime;
    char timestr[16];
    time_t local_tv_sec;

    (VOID)(param);
    (VOID)(pkt_data);

    /* convert the timestamp to readable format */
    local_tv_sec = header->ts.tv_sec;
    localtime_s(&ltime, &local_tv_sec);
    strftime(timestr, sizeof timestr, "%H:%M:%S", &ltime);

    printf("%s,%.6d len:%d\n",
        timestr, header->ts.tv_usec, header->len);
}

/* Main Decleration */
void wmain()
{
    pcap_if_t *alldevs;
    pcap_if_t *d;
    int inum;
    int i = 0;
    pcap_t *adhandle;
    char errbuf[PCAP_ERRBUF_SIZE];

    /* Load Npcap and its functions. */
    if (!LoadNpcapDlls())
    {
        fprintf(stderr, "Couldn't load Npcap\n");
        exit(1);
    }

    /* Retrieve the device list on the local machine */
    if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING,
        NULL, &alldevs, errbuf) == -1)
    {
        fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
        exit(1);
    }

    /* Print the list */
    for (d = alldevs; d; d = d->next)
    {
        printf("%d. %s", ++i, d->name);
        if (d->description)
            printf(" (%s)\n", d->description);
        else
            printf(" (No description available)\n");
    }

    if (i == 0)
    {
        printf("\nNo interfaces found! Make sure Npcap is installed.\n");
        return;
    }

    printf("Enter the interface number (1-%d):", i);
    scanf_s("%d", &inum);

    if (inum < 1 || inum > i)
    {
        printf("\nInterface number out of range.\n");
        /* Free the device list */
        pcap_freealldevs(alldevs);
        return;
    }

    /* Jump to the selected adapter */
    for (d = alldevs, i = 0; i < inum - 1; d = d->next, i++);

    /* Open the device */
    if ((adhandle = pcap_open(d->name, // name of the device
        2048, // portion of the packet to capture
              // 65536 guarantees that the whole packet will
              // be captured on all the link layers
        PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
        1000, // read timeout
        NULL, // authentication on the remote machine
        errbuf // error buffer
    )) == NULL)
    {
        fprintf(stderr,
            "\nUnable to open the adapter. %s is not supported by Npcap\n",
            d->name);
        /* Free the device list */
        pcap_freealldevs(alldevs);
        return;
    }
    //Try setting monitor mode and error check, trimmed down to the error I'm facing 
    int mm_set = pcap_can_set_rfmon(adhandle);
    if (mm_set == 0)
    {
        fprintf(stderr, "Error setting monitor mode: Device doesn't have MM capability\n");
    }
    else
    {
        if (!pcap_set_rfmon(adhandle, 1))
        {
            cout << "Monitor Mode Enabled, pcap_set_rfmon(...) == 0" << endl;
        }
    }
    cout << endl;

    //Using pcap_set_rfmon() here to illustrate issue, this will output a 0 
    //indicating success but the pcap_activate() error check contradicts this
    cout << pcap_set_rfmon(adhandle, 1) << endl;

    //int status = pcap_activate(adhandle);
    //Activate the interface for sniffing
    if (pcap_activate(adhandle))
    {
        cout << endl;
        pcap_perror(adhandle, "Error");
        cout << endl;
        pcap_set_rfmon(adhandle, 0);
        pcap_activate(adhandle);
    }

    printf("\nlistening on %s...\n", d->description);

    /* At this point, we don't need any more the device list. Free it */
    //pcap_freealldevs(alldevs);

    /* start the capture */
    pcap_loop(adhandle, 0, packet_handler, NULL);
}
guyharris commented 4 months ago
        if ((adhandle = pcap_open(d->name, // name of the device
...
        //Try setting monitor mode and error check, trimmed down to the error I'm facing 

pcap_open() does all the work to open a device; you can't set monitor mode after a device is opened.

You need to use pcap_create() and pcap_activate(), as well as:

You do not need to set promiscuous mode if you're setting monitor mode.

Those should be done between pcap_create() and pcap_activate(), along with...

        //Using pcap_set_rfmon() here to illustrate issue, this will output a 0 
        //indicating success but the pcap_activate() error check contradicts this
        cout << pcap_set_rfmon(adhandle, 1) << endl;

...the pcap_set_rfmon() mode.

Older versions of libpcap would not catch attempts to set monitor mode after a pcap_t has been activated, so that call would do nothing and then return 0. Newer versions of libpcap - including the version that the current version of Npcap uses - will check whether the pcap_t is activated and return an error. (pcap_open_live() and pcap_open() return a pointer to an already-activated pcap_t; pcap_create() returns a pointer to an unactivated pcap_t.)

What version of Npcap are you using?

kmramn commented 4 months ago

Npcap version in Control Panel

image

Earlier while installing through Wireshark, it was having the version 1.78. So, I have to uninstall manually and install 1.79

using pcap_lib_version()

image

I will definitely try your above suggestion.

kmramn commented 4 months ago

This is the entire code that I wrote.

#include <pcap.h>
#pragma comment(lib, "wpcap.lib")
#include <Windows.h>
#include <winerror.h>
#include <stdio.h>
#include <iostream>
using namespace std;
#include <tchar.h>
#include <strsafe.h>
#include <time.h>

BOOL LoadNpcapDlls()
{
    _TCHAR npcap_dir[512];
    UINT len;
    len = GetSystemDirectory(npcap_dir, 480);
    if (!len) {
        fprintf(stderr, "Error in GetSystemDirectory: %x", GetLastError());
        return FALSE;
    }
    _tcscat_s(npcap_dir, 512, _T("\\Npcap"));
    if (SetDllDirectory(npcap_dir) == 0) {
        fprintf(stderr, "Error in SetDllDirectory: %x", GetLastError());
        return FALSE;
    }
    return TRUE;
}

void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
    struct tm ltime;
    char timestr[16];
    time_t local_tv_sec;

    // unused variables
    (VOID)(param);
    (VOID)(pkt_data);

    // convert the timestamp to readable format
    local_tv_sec = header->ts.tv_sec;
    localtime_s(&ltime, &local_tv_sec);
    strftime(timestr, sizeof timestr, "%H:%M:%S", &ltime);

    printf("%s,%.6d len:%d\n",
        timestr, header->ts.tv_usec, header->len);
}

void wmain()
{
    const char *pcap_version = pcap_lib_version();
    printf("%s\n", pcap_version);

    pcap_if_t *alldevs;
    pcap_if_t *d;
    int inum;
    int i = 0;
    pcap_t *adhandle;
    char errbuf[PCAP_ERRBUF_SIZE];

    // Load Npcap and its functions.
    if (!LoadNpcapDlls())
    {
        fprintf(stderr, "Couldn't load Npcap\n");
        exit(1);
    }

    // Retrieve the device list on the local machine
    if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING,
        NULL, &alldevs, errbuf) == -1)
    {
        fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
        exit(1);
    }

    // Print the list
    for (d = alldevs; d; d = d->next)
    {
        printf("%d. %s", ++i, d->name);
        if (d->description)
            printf(" (%s)\n", d->description);
        else
            printf(" (No description available)\n");
    }

    if (i == 0)
    {
        printf("\nNo interfaces found! Make sure Npcap is installed.\n");
        return;
    }

    printf("Enter the interface number (1-%d):", i);
    scanf_s("%d", &inum);

    if (inum < 1 || inum > i)
    {
        printf("\nInterface number out of range.\n");
        // Free the device list
        pcap_freealldevs(alldevs);
        return;
    }

    // Jump to the selected adapter
    for (d = alldevs, i = 0; i < inum - 1; d = d->next, i++);

    for (int j = 0; d->name[j]; j++)
        cout << d->name[j] << endl;

    // Create pcap handle
    if ((adhandle = pcap_create(d->name, errbuf)) == NULL)
    //if ((adhandle = pcap_create((const char*)L"rpcap://Device/NPF_{A0C40D7F-C59D-4ECA-86A3-C4A30B549BE6}\0\0", errbuf)) == NULL)
    //if ((adhandle = pcap_create("any", errbuf)) == NULL)
    //if ((adhandle = pcap_create(NULL, errbuf)) == NULL)
    {
        printf("pcap_create() failed: %s\n", errbuf);
        exit(EXIT_FAILURE);
    }

    // Check if device can be put into monitor mode
    if (pcap_can_set_rfmon(adhandle) == 0)
    {
        printf("Monitor mode can not be set.\n");
        exit(EXIT_FAILURE);
    }

    // Attempt to put the card into monitor mode
    if (pcap_set_rfmon(adhandle, 1) != 0)
    {
        printf("Failed to set monitor mode.\n");
        exit(EXIT_FAILURE);
    }

    if (pcap_set_snaplen(adhandle, 2048) != 0)  // Set the snapshot length to 2048
    {
        printf("Failed to set snaplen.\n");
        exit(EXIT_FAILURE);
    }

    //if (pcap_set_promisc(adhandle, 0) != 0) // Turn promiscuous mode off
    //{
    //    printf("Failed to set pcap_set_promisc.\n");
    //    exit(EXIT_FAILURE);
    //}

    if (pcap_set_timeout(adhandle, 1000) != 0) // Set the timeout to 512 milliseconds
    {
        printf("Failed to set timeout.\n");
        exit(EXIT_FAILURE);
    }

    if (pcap_activate(adhandle) != 0)
    {
        printf("pcap_activate() failed\n");
        exit(EXIT_FAILURE);
    }

    //int status = pcap_activate(handler);
    //Try setting monitor mode and error check, trimmed down to the error I'm facing 
    int mm_set = pcap_can_set_rfmon(adhandle);
    if (mm_set == 0)
    {
        fprintf(stderr, "Error setting monitor mode: Device doesn't have MM capability\n");
    }
    else
    {
        if (!pcap_set_rfmon(adhandle, 1))
        {
            cout << "Monitor Mode Enabled, pcap_set_rfmon(...) == 0" << endl;
        }
    }
    cout << endl;

    //Using pcap_set_rfmon() here to illustrate issue, this will output a 0 
    //indicating success but the pcap_activate() error check contradicts this
    cout << pcap_set_rfmon(adhandle, 1) << endl;

    //int status = pcap_activate(adhandle);
    //Activate the interface for sniffing
    if (pcap_activate(adhandle))
    {
        cout << endl;
        pcap_perror(adhandle, "Error");
        cout << endl;
        pcap_set_rfmon(adhandle, 0);
        pcap_activate(adhandle);
    }

    printf("\nlistening on %s...\n", d->description);

    // At this point, we don't need any more the device list. Free it
    //pcap_freealldevs(alldevs);

    // start the capture
    pcap_loop(adhandle, 0, packet_handler, NULL);
}

But while calling pcap_create() it is throwing the following error.

pcap_create() failed: Error opening adapter: The filename, directory name, or volume label syntax is incorrect.  (123)

I also tried to hardcode the string in C++ as given below, but still I got the same error.

rpcap://\\Device\\NPF_{A0C40D7F-C59D-4ECA-86A3-C4A30B549BE6}
rpcap:\\\\Device\\NPF_{A0C40D7F-C59D-4ECA-86A3-C4A30B549BE6}
rpcap://Device\\NPF_{A0C40D7F-C59D-4ECA-86A3-C4A30B549BE6}
rpcap://\\\\Device\\NPF_{A0C40D7F-C59D-4ECA-86A3-C4A30B549BE6}

Is there any format that I need to follow? Thank you.

guyharris commented 4 months ago

Is there any format that I need to follow?

Yes. Try just \Device\NPF_{A0C40D7F-C59D-4ECA-86A3-C4A30B549BE6} (with double backslashes if necessary). pcap_create() currently doesn't support URL syntax, but, as you're not doing a remote capture, the rpcap:// is unnecessary and can be omitted.

guyharris commented 4 months ago

And just use pcap_findalldevs().

kmramn commented 4 months ago

Thank you so much, now it is capturing.

image

I make habit of researching first and then post my doubts. But since this is helpful and working, so I will post my doubt directly in this thread. Can I reply to this 802.11 radio frames, provided, with my valid key properly? Thank you again.