WinPcap应用小记

10 篇文章 2 订阅

引言

项目中根据需求要增加抓包模块,win平台下自然就用到了winpcap。
同时提供了按大小存或按时间存储的接口,并在外层控制存储数量。效果如下:
抓包功能

配置

  1. 在官网下载安装包开发包
  2. 配置VS2012:
    • 项目→属性→配置属性→C/C++→常规,右侧的“附加包含库目录”中添加开发包解压后的WpdPack的Include目录;
    • 项目→属性→配置属性→C/C++→预处理器,右侧的“预处理器定义”加上WPCAP,HAVE_REMOTE,WIN32;
    • 项目→属性→配置属性→链接器→常规,右侧的“附加库目录”中添加开发包解压后的WpdPack的lib目录;
    • 项目→属性→配置属性→链接器→输入,右侧的“附加依赖项”中添加wpcap.lib和Packet.lib,ws2_32.lib。

使用

  1. 线程中实现基础的抓包并保存功能,见代码
  2. 其他高级应用,如过滤等,参考引用文章
#ifndef THREADPCAP_H
#define THREADPCAP_H

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

typedef struct _DEVInfo{
    QString name;
    QString description;
    QString familyName;         //协议族,
    QString address;            //主机ip
    QString netmask;            //子网掩码
}DEVInfo;

// 将字节序ip地址转为点分十进制的字符串地址,并获取
inline char *my_iptos(u_long in);

/* 定义了QThread 的子类 PcapThread */
class ThreadPcap:public QThread
{
    Q_OBJECT

public:
    ThreadPcap(QObject *parent=0);
    ~ThreadPcap();

    u_char hexStr2UChar(QString hexS);
    // Windows Socket 初始化
    void winSocketInit();
    // 扫描本机所有的适配器,并获取每个适配器的信息
    QVector<DEVInfo> findAllDev();
    // 打开一个适配器
    void openLiveDev(const char *);
    // 打开一个堆文件
    void openDump(QString);
    // 捕获数据包函数
    void run();
    // 停止抓包
    void stop();
    // 设置保存模式
    void setSaveMode(int,int);
    // 重新开始按时间存文件
    void reStartSavePeriod();
signals:
    void signal_creatPcapFile(QString);//各个变量的变化发送到GUI线程    
protected:
    pcap_t * m_handle;
    pcap_if_t *alldevs;
    pcap_dumper_t *dumpfp;

private:
    pcap_pkthdr* pHeader_;
    const u_char * pPktdata_;                           //数据包
    int res;
    bool scanIsFinished;
    bool m_bSaveTimeup;
    int m_nSaveMode;//保存模式,0按大小KB保存,1按大小MB保存,2按时间s保存
    int m_nSaveSizeNum;//按大小保存时,单包的大小
};

#endif // THREADPCAP_H
#include "threadpcap.h"
#include "myhelper.h"

ThreadPcap::ThreadPcap(QObject *parent)
    : QThread(parent),scanIsFinished(false),m_bSaveTimeup(false)
{
    m_handle = NULL;
}

ThreadPcap::~ThreadPcap()
{
    pcap_freealldevs(alldevs);
    WSACleanup();
    if(m_handle != NULL)pcap_close(m_handle);

}
u_char ThreadPcap::hexStr2UChar(QString hexS)
{
    QByteArray array = hexS.toLocal8Bit();
    char *data = array.data();
    char *str;
    u_char ret = (u_char)strtol(data,&str,16);
    //printf("%d %02x\n",ret,ret);
    return ret;
}

// Windows Socket 初始化
void ThreadPcap::winSocketInit()
{
    WSADATA wsaData;
    // 调用Windows Sockets DLL
    if (WSAStartup(MAKEWORD(2,1),&wsaData))
    {
        qDebug() << tr("Winsock无法初始化!");
        WSACleanup();
        return ;
    }
}

// 扫描本机所有的适配器,并获取每个适配器的信息
QVector<DEVInfo> ThreadPcap::findAllDev()
{
    QVector<DEVInfo> allDev;
    DEVInfo tempDevInfo;

    pcap_if_t *p;

    char errbuf[PCAP_ERRBUF_SIZE];

    // 获取本地机器设备列表
    if(pcap_findalldevs(&alldevs,errbuf) == -1)
    {
        qDebug()<<"Find all devices is error"<<errbuf;
        exit(1);
    }

    for(p = alldevs;p!= NULL;p = p->next)
    {
        tempDevInfo.name = p->name;
        //printf("\tIS loopback address : %s\n",(p->flags & PCAP_IF_LOOPBACK)?"yes":"no");
        if(p->description){            
            tempDevInfo.description = p->description;
        }
        else{
            tempDevInfo.description = "(No description available)";
        }

        pcap_addr_t *a;
        tempDevInfo.address.clear();
        for(a = p->addresses;a;a = a->next)
        {
            switch(a->addr->sa_family)
            {
            case AF_INET:                    
                tempDevInfo.familyName = "AF_INET";
                if (a->addr)
                {                        
                    tempDevInfo.address += my_iptos(((struct sockaddr_in *)a->addr)->sin_addr.s_addr);
                    //qDebug()<<"tempDevInfo.address"<<tempDevInfo.address;
                }
                if (a->netmask)
                {                        
                    tempDevInfo.netmask = my_iptos(((struct sockaddr_in *)a->netmask)->sin_addr.s_addr);
                    //qDebug()<<"tempDevInfo.netmask"<<tempDevInfo.netmask;
                }
                if (a->broadaddr)
                    //printf("\tBroadcast Address: %s\n",my_iptos(((struct sockaddr_in *)a->broadaddr)->sin_addr.s_addr));
                if (a->dstaddr)
                    //printf("\tDestination Address: %s\n",my_iptos(((struct sockaddr_in *)a->dstaddr)->sin_addr.s_addr));
                break;
            case AF_INET6:                    
                if (a->addr)
                    //printf("\tAddress: %d\n", inet_ntop(a->addr, ip6str, sizeof(ip6str)));
                break;
            default:
                //printf("\tAddress Family Name: Unknown\n");
                break;
            }
        }
        allDev.append(tempDevInfo);        
    }

    return allDev;
}

// 打开一个适配器
void ThreadPcap::openLiveDev(const char *dev)
{
    char errBuf[PCAP_ERRBUF_SIZE] = {0};

    //混杂模式
    m_handle = pcap_open_live(dev,  //设备名
                            65535,  //65535保证能捕获到不同数据链路层上的每个数据包的全部内容
                            1,      //混杂模式
                            0,      //读取超时时间,0意味着没有超时,那么如果没有数据到达的话,读操作就永远不会返回,如果设为-1,不论有没有读到数据都会立即返回
                            errBuf  //错误缓冲池
                            );
    qDebug()<<"Open live dev is ok,handle:"<<m_handle;
    if(!m_handle)
    {
        qDebug()<<"Open live dev is error:"<<errBuf;
        exit(1);
    }
}

// 打开一个堆文件
void ThreadPcap::openDump(QString filename)
{   
    emit signal_creatPcapFile(filename);
    QByteArray fileByteArray = filename.toLocal8Bit();
    /* 开始堆过程 */
    dumpfp = pcap_dump_open(m_handle, fileByteArray.data());
    if(dumpfp==NULL)
    {
        qDebug("Error on opening output file\n");
        exit(-1);
    }
}


#define IPTOSBUFFERS    12
char *my_iptos(u_long in)
{
    static char output[IPTOSBUFFERS][3*4+3+1];
    static short which;
    u_char *p;

    p = (u_char *)&in;
    which = (which + 1 == IPTOSBUFFERS ? 0 : which + 1);
    sprintf(output[which], "%d.%d.%d.%d; ", p[0], p[1], p[2], p[3]);
    return output[which];
}

///* 捕获数据包函数*/
void ThreadPcap::run()
{
    long Pkt_size;
    scanIsFinished=false;
    while(!scanIsFinished)
    {
        if ((res = pcap_next_ex(m_handle,&pHeader_,&pPktdata_))>=0)
        {
            if(res==0)
            {
                /* 超时时间到 */
                continue;
            }
            pcap_dump((unsigned char *)dumpfp, pHeader_, pPktdata_);
        }

        if (m_nSaveMode==0)//如果按大小KB保存
        {
            Pkt_size=pcap_dump_ftell(dumpfp);//获取存取的包文件的大小,单位:字节
            if (Pkt_size>m_nSaveSizeNum*PCAPSIZE_KB)
            {
                pcap_dump_close(dumpfp);
                /* 重新开始堆过程 */
                QString sCurrentFileName = "./Output/Pcap/"+QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss_zzz")+".pcap";
                openDump(sCurrentFileName);
            }
        }
        else if (m_nSaveMode==1)//如果按大小MB保存
        {
            Pkt_size=pcap_dump_ftell(dumpfp);//获取存取的包文件的大小,单位:字节
            if (Pkt_size>m_nSaveSizeNum*PCAPSIZE_MB)
            {
                pcap_dump_close(dumpfp);
                /* 重新开始堆过程 */
                QString sCurrentFileName = "./Output/Pcap/"+QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss_zzz")+".pcap";
                openDump(sCurrentFileName);
            }
        }
        else if (m_nSaveMode==2)//如果按时间s保存
        {
            if (m_bSaveTimeup)//如果保存时间到
            {
                //qDebug()<<QDateTime::currentDateTime().toString("hh:mm:ss.zzz")<<QStringLiteral("重新开始按时间抓包——重新存文件");
                pcap_dump_close(dumpfp);
                /* 重新开始堆过程 */
                QString sCurrentFileName = "./Output/Pcap/"+QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss_zzz")+".pcap";
                openDump(sCurrentFileName);
                m_bSaveTimeup=false;
            }
        }       
    }
    //pcap_close(m_handle);
    pcap_dump_close(dumpfp);

}

//停止抓包
void ThreadPcap::stop()
{
    scanIsFinished=true;
}

//设置保存模式
void ThreadPcap::setSaveMode(int nSaveMode,int nSaveSizeNum)
{
    m_nSaveMode=nSaveMode;
    if (m_nSaveMode==0)//1按大小KB保存
    {
        m_nSaveSizeNum=nSaveSizeNum;
    } 
    else if (m_nSaveMode==1)//按大小MB保存
    {
        m_nSaveSizeNum=nSaveSizeNum;
    }
}

//重新开始按时间存文件
void ThreadPcap::reStartSavePeriod()
{
    m_bSaveTimeup=true;
}

问题解决

  • 编译错误:fatal error C1189: #error : The C++ Standard Library forbids macroizing keywords. Enable warning
    属性–配置属性-c/c++-预处理器 添加:_XKEYCHECK_H
  • 编译错误:warning C4005]ws2def.h(91): warning C4005: “AF_IPX”: 宏重定义 winsock.h(460) : 参见“AF_IPX”的前一个定义
    属性->配置属性->c/c++ ->预处理器->预处理器定义:添加WIN32_LEAN_AND_MEAN
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值