使用qt控制台抓取tcp数据包
前提条件
- 安装
npcap: 链接
项目准备
-
创建控制台程序
-
在项目文件中添加包含目录、附加库目录、附加库
rubyINCLUDEPATH += D:/softwares/Libs/NpcapSDK/Include LIBS += -LD:/softwares/Libs/NpcapSDK/Lib/x64 LIBS += -lwpcap LIBS += -lole32 LIBS += -lPacket LIBS += -lIphlpapi
检查是否已经安装npcap
c++
bool checkNpcapExist()
{
WCHAR path[512];
uint len = GetSystemDirectory(path, 480);
QString systemPath = QString::fromWCharArray(path, len);
QString npcapFolder = QDir(systemPath).filePath("Npcap");
return QDir(npcapFolder).exists();
}
- 也就是判断目录:
C:\Windows\System32\Npcap存不存在
获取网络适配器列表
- 我们需要选择一个网络适配器去抓取tcp数据包,所以需要获取本地机器的所有网络适配器,当然
npcap提供了pcap_findalldevsapi,但是如果我们要想获取它的可读化的FriendName,需要经过一些操作
c++
QString getDeviceFriendName(const char *devName)
{
QString friendName(devName);
// extract guid string by regex
QRegularExpression regex(R"(({[A-F0-9\-]+}))");
auto match = regex.match(friendName);
if (!match.hasMatch()) return friendName;
// translate guid string to GUID
GUID guid;
HRESULT hr = CLSIDFromString(match.captured(1).toStdWString().c_str(), &guid);
if (!SUCCEEDED(hr)) return friendName;
// GUID => LUID => Alias(FriendName)
NET_LUID luid;
if (0 == ConvertInterfaceGuidToLuid(&guid, &luid))
{
WCHAR buffer[256] = {0};
if (0 == ConvertInterfaceLuidToAlias(&luid, buffer, 256))
{
friendName = QString::fromWCharArray(buffer);
}
}
return friendName;
}
int main()
{
char errbuf[PCAP_ERRBUF_SIZE];
pcap_if_t *alldevs;
// pcap_t *handle;
if (pcap_findalldevs(&alldevs, errbuf) == -1) {
qCritical() << "Error in pcap_findalldevs:" << errbuf;
return -1;
}
QStringList devNames;
pcap_if_t *dev = alldevs;
char* lastName = nullptr;
while (dev)
{
QString devName = getDeviceFriendName(dev->name);
devNames.append(devName);
lastName = dev->name;
dev = dev->next;
}
return 0;
}
打开网络适配器并设置筛选条件
- 一个网络适配器抓到的tcp数据包有很多,但是我们只想关注我们想要的tcp数据包,所以需要设置筛选条件
c++
// open the adapter
pcap_t* adapter = pcap_open_live(lastName, 65536, 1, 1000, errbuf);
if (adapter == nullptr)
{
pcap_freealldevs(alldevs);
qDebug() << "fail to open the net adapter: " << lastName;
return -1;
}
pcap_freealldevs(alldevs);
// set filter
struct bpf_program fcode;
int res = 0;
if ((res = pcap_compile(adapter, &fcode, "tcp port 3000", 1, PCAP_NETMASK_UNKNOWN)) < 0)
{
qDebug() << "fail to compile:" << pcap_statustostr(res);
pcap_close(adapter);
return -1;
}
if ((res = pcap_setfilter(adapter, &fcode) < 0))
{
qDebug() << "fail to set filter:" << pcap_statustostr(res);
pcap_close(adapter);
return -1;
}
- 这里我们设置的条件是只筛选 端口号
3000的tcp数据包,筛选字符串为:tcp port 3000
开始抓包,设置处理函数
c++
void main()
{
// start capture loop
pcap_loop(adapter, 0, packet_handler, nullptr);
pcap_close(adapter);
}
void packet_handler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data)
{
Q_UNUSED(param);
Q_UNUSED(pkt_data);
uint len = header->len;
QString str = QString::asprintf("%ld:%ld (%ld)", header->ts.tv_sec, header->ts.tv_usec, len);
qDebug() << str;
}
运行效果

完整代码
c++
#include <QCoreApplication>
#include <winsock2.h>
#include <QDebug>
#include <windows.h>
#include <QDir>
#include <pcap.h>
#include <QRegularExpression>
#include <objbase.h>
#include <netioapi.h>
bool checkNpcapExist()
{
WCHAR path[512];
uint len = GetSystemDirectory(path, 480);
QString systemPath = QString::fromWCharArray(path, len);
QString npcapFolder = QDir(systemPath).filePath("Npcap");
return QDir(npcapFolder).exists();
}
QString getDeviceFriendName(const char *devName)
{
QString friendName(devName);
// extract guid string by regex
QRegularExpression regex(R"(({[A-F0-9\-]+}))");
auto match = regex.match(friendName);
if (!match.hasMatch()) return friendName;
// translate guid string to GUID
GUID guid;
HRESULT hr = CLSIDFromString(match.captured(1).toStdWString().c_str(), &guid);
if (!SUCCEEDED(hr)) return friendName;
// GUID => LUID => Alias(FriendName)
NET_LUID luid;
if (0 == ConvertInterfaceGuidToLuid(&guid, &luid))
{
WCHAR buffer[256] = {0};
if (0 == ConvertInterfaceLuidToAlias(&luid, buffer, 256))
{
friendName = QString::fromWCharArray(buffer);
}
}
return friendName;
}
void packet_handler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data)
{
Q_UNUSED(param);
Q_UNUSED(pkt_data);
// struct tm* ltime;
// char timestr[16];
// time_t local_tv_sec;
uint len = header->len;
QString str = QString::asprintf("%ld:%ld (%ld)", header->ts.tv_sec, header->ts.tv_usec, len);
qDebug() << str;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 判断npcap是否存在
if (!checkNpcapExist())
{
qDebug() << "Npcap not exists";
return -1;
}
// 检索网卡列表
char errbuf[PCAP_ERRBUF_SIZE];
pcap_if_t *alldevs;
// pcap_t *handle;
if (pcap_findalldevs(&alldevs, errbuf) == -1) {
qCritical() << "Error in pcap_findalldevs:" << errbuf;
return -1;
}
QStringList devNames;
pcap_if_t *dev = alldevs;
char* lastName = nullptr;
while (dev)
{
QString devName = getDeviceFriendName(dev->name);
devNames.append(devName);
lastName = dev->name;
dev = dev->next;
}
// open the adapter
pcap_t* adapter = pcap_open_live(lastName, 65536, 1, 1000, errbuf);
if (adapter == nullptr)
{
pcap_freealldevs(alldevs);
qDebug() << "fail to open the net adapter: " << lastName;
return -1;
}
pcap_freealldevs(alldevs);
// set filter
struct bpf_program fcode;
int res = 0;
if ((res = pcap_compile(adapter, &fcode, "tcp port 3000", 1, PCAP_NETMASK_UNKNOWN)) < 0)
{
qDebug() << "fail to compile:" << pcap_statustostr(res);
pcap_close(adapter);
return -1;
}
if ((res = pcap_setfilter(adapter, &fcode) < 0))
{
qDebug() << "fail to set filter:" << pcap_statustostr(res);
pcap_close(adapter);
return -1;
}
// start capture loop
pcap_loop(adapter, 0, packet_handler, nullptr);
pcap_close(adapter);
qDebug() << "hello";
return a.exec();
}