首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

基於 libpcap/WinPcap 的簡易網路封包擷取器 (Sniffer) C 實作

     編輯:關於C++

專案架構

  • 開發工具:libpcap (Linux/Unix) / WinPcap (Windows)
  • 核心流程:列舉介面 → 開啟介面 → 設定混雜模式/緩衝/超時 → 分配封包結構 → 迴圈擷取 → 統計/釋放

完整原始碼 (Windows WinPcap 版)


/* 簡易 Sniffer - 畢業專題範例 */
#include 
#include 
#include "..\\..\\Include\\packet32.h"
#include "..\\..\\Include\\ntddndis.h"

#define MAX_ADAPTER 10

void PrintPackets(LPPACKET lpPacket); // 自訂封包解析列印

char AdapterList[MAX_ADAPTER][1024];

int main() {
    LPADAPTER lpAdapter = 0;
    LPPACKET  lpPacket;
    int i, Open, AdapterNum = 0;
    DWORD dwErrorCode, dwVersion, dwWinMajorVer;
    ULONG AdapterLength = 4096;
    char buffer[256000];
    struct bpf_stat stat;

    // 版本資訊
    printf("Packet.dll version: %s\n", PacketGetVersion());
    printf("Adapters installed:\n");

    // 取得作業系統版本區分 Unicode/ANSI
    dwVersion = GetVersion();
    dwWinMajorVer = (DWORD)(LOBYTE(LOWORD(dwVersion)));

    // 列舉網卡
    if (!(dwVersion >= 0x80000000 && dwWinMajorVer >= 4)) { // Windows NT 系列
        WCHAR AdapterName[8192], *temp, *temp1;
        if (!PacketGetAdapterNames(AdapterName, &AdapterLength)) {
            printf("Unable to retrieve adapter list!\n"); return -1;
        }
        temp = temp1 = AdapterName;
        while ((*temp != L'\0') || (*(temp-1) != L'\0')) {
            if (*temp == L'\0') {
                memcpy(AdapterList[AdapterNum], temp1, (temp-temp1)*2);
                temp1 = temp + 1;
                AdapterNum++;
            }
            temp++;
        }
        for (i=0; i AdapterNum) printf("Number must be <= %d\n", AdapterNum);
    } while (Open > AdapterNum);

    // 開啟介面
    lpAdapter = PacketOpenAdapter(AdapterList[Open-1]);
    if (!lpAdapter || lpAdapter->hFile == INVALID_HANDLE_VALUE) {
        dwErrorCode = GetLastError();
        printf("Open adapter failed, Error: %lx\n", dwErrorCode); return -1;
    }

    // 設定混雜模式
    if (!PacketSetHwFilter(lpAdapter, NDIS_PACKET_TYPE_PROMISCUOUS))
        printf("Warning: cannot set promiscuous mode!\n");

    // 設定 512KB 核心緩衝
    if (!PacketSetBuff(lpAdapter, 512000)) {
        printf("Cannot set kernel buffer!\n"); return -1;
    }

    // 設定 1 秒讀取逾時
    if (!PacketSetReadTimeout(lpAdapter, 1000))
        printf("Warning: cannot set read timeout!\n");

    // 分配封包結構
    if ((lpPacket = PacketAllocatePacket()) == NULL) {
        printf("Failed to allocate LPPACKET\n"); return -1;
    }
    PacketInitPacket(lpPacket, buffer, 256000);

    // 主擷取迴圈
    while (!kbhit()) {
        if (!PacketReceivePacket(lpAdapter, lpPacket, TRUE)) {
            printf("PacketReceivePacket failed\n"); break;
        }
        PrintPackets(lpPacket); // 解析並列印
    }

    // 統計
    if (PacketGetStats(lpAdapter, &stat))
        printf("\n\n%d packets received.\n%d packets dropped\n", stat.bs_recv, stat.bs_drop);
    else
        printf("Warning: cannot get stats!\n");

    // 清理
    PacketFreePacket(lpPacket);
    PacketCloseAdapter(lpAdapter);
    return 0;
}

關鍵 API 對照表

函數用途關鍵參數
PacketGetAdapterNames列舉網卡名稱緩衝區、長度指標
PacketOpenAdapter開啟介面介面名稱
PacketSetHwFilter硬體過濾器NDIS_PACKET_TYPE_PROMISCUOUS 等
PacketSetBuff核心緩衝區大小位元組數 (建議 512KB+)
PacketSetReadTimeout讀取逾時毫秒 (0=無限等待)
PacketAllocatePacket分配封包結構無
PacketInitPacket綁定使用者緩衝區封包結構、緩衝區、長度
PacketReceivePacket同步擷取封包介面、封包結構、TRUE
PacketGetStats取得統計 (收到/丟棄)介面、bpf_stat 結構
PacketFreePacket釋放封包結構封包結構
PacketCloseAdapter關閉介面介面

硬體過濾器旗標 (ntddndis.h)

  • NDIS_PACKET_TYPE_PROMISCUOUS:混雜模式 (全部封包)
  • NDIS_PACKET_TYPE_DIRECTED:導向本機
  • NDIS_PACKET_TYPE_BROADCAST:廣播
  • NDIS_PACKET_TYPE_MULTICAST:多播
  • NDIS_PACKET_TYPE_ALL_MULTICAST:所有多播

現代替代方案

  • WinPcap 已停止維護,Windows 建議改用 Npcap (相容 WinPcap API、支援迴送介面、效能更好)。
  • 跨平台首選 libpcap (Linux/macOS/Windows 配合 Npcap)。
  • 高層封裝:pcap_loop, pcap_next_ex, pcap_compile/pcap_setfilter (BPF 過濾)。

安全與法律提醒

封包擷取涉及網路隱私與資安法規,僅限於自有網路環境、授權滲透測試、教學研究用途。未經授權監聽他人流量可能違法。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved