引言
项目中根据需求要增加抓包模块,win平台下自然就用到了winpcap。
同时提供了按大小存或按时间存储的接口,并在外层控制存储数量。效果如下:
配置
- 在官网下载安装包和开发包
- 配置VS2012:
- 项目→属性→配置属性→C/C++→常规,右侧的“附加包含库目录”中添加开发包解压后的WpdPack的Include目录;
- 项目→属性→配置属性→C/C++→预处理器,右侧的“预处理器定义”加上WPCAP,HAVE_REMOTE,WIN32;
- 项目→属性→配置属性→链接器→常规,右侧的“附加库目录”中添加开发包解压后的WpdPack的lib目录;
- 项目→属性→配置属性→链接器→输入,右侧的“附加依赖项”中添加wpcap.lib和Packet.lib,ws2_32.lib。
使用
- 线程中实现基础的抓包并保存功能,见代码
- 其他高级应用,如过滤等,参考引用文章
#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 *)∈
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