VS下c++解析pcap文件

一、pcap文件格式

https://www.cnblogs.com/Chary/articles/15716063.html

二、代码

pcapParser.h

cpp 复制代码
#ifndef _PCAP_PARSER_H_
#define _PCAP_PARSER_H_

#include <stdint.h>

#pragma pack(1)

//pacp文件头结构体
struct pcap_file_header //24字节
{
	uint32_t magic;       /* 0xa1b2c3d4 */
	uint16_t version_major;   /* magjor Version 2 */
	uint16_t version_minor;   /* magjor Version 4 */
	uint32_t thiszone;      /* gmt to local correction */
	uint32_t sigfigs;     /* accuracy of timestamps */
	uint32_t snaplen;     /* max length saved portion of each pkt */
	uint32_t linktype;    /* data link type (LINKTYPE_*) */
};

//时间戳
struct time_val
{
	int tv_sec;         /* seconds 含义同 time_t 对象的值 */
	int tv_usec;        /* and microseconds */
};

//package数据包头结构体
struct package_pkthdr //8+4+4=16
{
	struct time_val ts;  /* time stamp */
	uint32_t caplen; /* length of portion present 32位 ,标识所抓获的数据包保存在pcap文件中的实际长度,以字节为单位*/
	uint32_t len;    /* length this packet (off wire) 抓获的数据包的真实长度,如果文件中保存不是完整的数据包,那么这个值可能要比前面的数据包长度的值大*/
};

// ethnet协议头
struct EthnetHeader_t//14字节
{
	unsigned char srcMac[6];
	unsigned char dstMac[6];
	uint16_t protoType;//连接类型
};

//IP数据报头 20字节
struct IPHeader_t
{
	uint8_t Ver_HLen;       //版本+报头长度
	uint8_t TOS;            //服务类型
	uint16_t TotalLen;       //总长度
	uint16_t ID; //标识
	uint16_t Flag_Segment;   //标志+片偏移
	uint8_t TTL;            //生存周期
	uint8_t Protocol;       //协议类型
	uint16_t Checksum;       //头部校验和
	uint16_t test; //20230725 add lijie
	uint32_t SrcIP; //源IP地址
	uint32_t DstIP; //目的IP地址

};

// UDP头 (8字节)
struct UDPHeader_t
{
	uint16_t SrcPort;    // 源端口号16bit
	uint16_t DstPort;    // 目的端口号16bit
	uint16_t Length;     // 长度
	uint16_t CheckSum;   // 校验码
};

// TCP头 (20字节)
struct TCPHeader_t
{
	uint16_t srcPort;          // 源端口
	uint16_t dstPort;          // 目的端口
	uint32_t SeqNo;            // 序列号
	uint32_t AckNo;            // 确认号
	uint16_t headAndFlags;     // 首部长度即标志位
	uint16_t WinSize;          // 窗口大小
	uint16_t CheckSum;         // 校验和
	uint16_t UrgPtr;           // 紧急指针
};

#pragma pack()

class PcapParser
{
private:
	char mUdpData[4096];             // 4k缓存
	uint32_t mUdpLen;
	char mTcpData[4096*4096];             // 4k缓存
	uint32_t mTcpLen;
	//uint64_t mTcpLen;
	//long long unsigned int mTcpLen;

	uint32_t mPackIndex;


	void ipDecode(const char* buf);
	void udpDecode(const char* buf, int len);
	void tcpDecode(const char* buf, int len);

public:
	PcapParser() : mUdpLen(0), mTcpLen(0), mPackIndex(0){ }
	~PcapParser() {}
public:
	// 过滤Ip
	virtual int ipFilter(const char* srcIp, const char* dstIp) { return 0; }
	// 过滤端口
	virtual int tcpFilter(const uint16_t srcPort, const uint16_t dstPort, const uint32_t msgLen) { return 0; }
	virtual int udpFilter(const uint16_t srcPort, const uint16_t dstPort, const uint32_t msgLen) { return 0; }
	// udp消息回调
	virtual int onUdpMsg(const char* buf, int len) { return 0; }
	// tcp消息回调
	virtual int onTcpMsg(const char* buf, int len) { return 0; }

	// pcap文件解析
	void parse(const char* filename);
};


#endif

pcapParser.cpp

cpp 复制代码
#include "pcapParser.h"
//#include <windows.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <io.h>
#include <process.h>
#include <stdlib.h>
#include <fcntl.h>
#include <WS2tcpip.h>

#pragma comment(lib, "ws2_32.lib")

// tcp协议解析
void PcapParser::tcpDecode(const char* buf, int len)
{
	int offset = 0;
	TCPHeader_t* tcpHeader = (TCPHeader_t*)(buf + offset);
	offset += sizeof(TCPHeader_t);//20

	uint16_t srcPort = tcpHeader->srcPort;
	uint16_t dstPort = tcpHeader->dstPort;
	// 用户数据长度
	uint16_t dataLen = len - sizeof(TCPHeader_t);

	if (0 != tcpFilter(srcPort, dstPort, dataLen))
	{
		// tcp过滤
		return;
	}

	printf("srcPort[%d]->dstPort[%d] len:%d\n", srcPort, dstPort, dataLen);

	//存到缓存,用来做粘包,半包处理
	memcpy(mTcpData + mTcpLen, buf + offset, dataLen);
	mTcpLen += dataLen;

	// 用户数据
	int usedLen = onTcpMsg(mTcpData, mTcpLen);
	if (usedLen > 0)
	{
		memcpy(mTcpData, mTcpData + usedLen, usedLen);
		mTcpLen -= usedLen;
	}
}

// udp协议解析
void PcapParser::udpDecode(const char* buf, int len)
{
	int offset = 0;
	UDPHeader_t* udpHeader = (UDPHeader_t*)(buf + offset);
	offset += sizeof(UDPHeader_t);

	uint16_t srcPort = ntohs(udpHeader->SrcPort);
	uint16_t dstPort = ntohs(udpHeader->DstPort);
	uint16_t packLen = ntohs(udpHeader->Length);

	// 用户数据长度
	uint16_t dataLen = packLen - sizeof(UDPHeader_t);

	if (0 != udpFilter(srcPort, dstPort, dataLen))
	{
		// udp过滤
		return;
	}

	// 存到缓存,用来做粘包,半包处理
	memcpy(mUdpData + mUdpLen, buf + offset, dataLen);
	mUdpLen += dataLen;

	// 用户数据
	int usedLen = onUdpMsg(mUdpData, mUdpLen);
	if (usedLen > 0)
	{
		memcpy(mUdpData, mUdpData + usedLen, usedLen);
		mUdpLen -= usedLen;
	}
}

// IP 协议解析
void PcapParser::ipDecode(const char* buf)
{
	int offset = 0;
	IPHeader_t* ipHeader = (IPHeader_t*)(buf + offset);
	offset += sizeof(IPHeader_t);//20

	char srcIp[32] = { 0 };
	char dstIp[32] = { 0 };

	inet_ntop(AF_INET, &ipHeader->SrcIP, srcIp, sizeof(srcIp));
	inet_ntop(AF_INET, &ipHeader->DstIP, dstIp, sizeof(dstIp));

	uint16_t ipPackLen = ntohs(ipHeader->TotalLen);

	printf("[%s]->[%s] 协议类型proto:%#x  ipPackLen=%d  packIdx=%d\n", srcIp, dstIp, ipHeader->Protocol, ipPackLen, mPackIndex);

	if (0 != ipFilter(srcIp, dstIp))
	{
		return;
	}

	switch (ipHeader->Protocol)
	{
	case 17:// UDP协议
		udpDecode(buf + offset, ipPackLen - sizeof(IPHeader_t));
		break;
	case 6: // TCP协议
		tcpDecode(buf + offset, ipPackLen - sizeof(IPHeader_t));
		break;
	default:
		printf("[%s:%d]unsupported protocol %#x\n", __FILE__, __LINE__,
			ipHeader->Protocol);
		break;
	}
}

//解析pcap文件
void PcapParser::parse(const char* filename)
{
	struct stat st;
	if (stat(filename, &st))
	{
		printf("stat file %s failed, errno=%d errmsg=%s\n", filename, errno, strerror(errno));
		return;
	}

	size_t fileSize = st.st_size;

	if (!fileSize)
	{
		printf("file is empty!\n");
		return;
	}

	char *buf = (char*)malloc(fileSize + 1);

	FILE* fp = fopen(filename, "r");
	if (!fp)
	{
		printf("open file %s failed, errno=%d errmsg=%s\n", filename, errno, strerror(errno));
		return;
	}
	fread(buf, sizeof(char), fileSize, fp);
	fclose(fp);


	size_t offset = 0;
	// pcap 文件头
	pcap_file_header* pcapHeader = (pcap_file_header*)(buf + offset);//24字节
	offset += sizeof(pcap_file_header);
	//标记文件开始,并用来识别文件和字节顺序。值可以为0xa1b2c3d4或者0xd4c3b2a1,如果是0xa1b2c3d4表示是大端模式
	printf("如果magic=0xa1b2c3d4,则为大端;magic=0xd4c3b2a1为小端\n");
	printf("pcap file head -> magic:%#x   version:%d.%d\n", pcapHeader->magic, pcapHeader->version_major, pcapHeader->version_minor);

	size_t proto_offset = 0;//初始偏移
	//mPackIndex = 0;


	while (offset < fileSize)
	{
		// package数据包头
		package_pkthdr* packageHeader = (package_pkthdr*)(buf + offset);//16字节
		proto_offset = offset + sizeof(package_pkthdr);

		// arp协议头
		//是根据IP地址获取物理地址的一个TCP/IP协议
		EthnetHeader_t* ethHeader = (EthnetHeader_t*)(buf + proto_offset);//14字节
		proto_offset += sizeof(EthnetHeader_t);

		uint16_t protocol = ntohs(ethHeader->protoType);//LinkType

		
		printf("[%02x:%02x:%02x:%02x:%02x:%02x]->[%02x:%02x:%02x:%02x:%02x:%02x] proto:%04x\n",
		ethHeader->srcMac[0], ethHeader->srcMac[1], ethHeader->srcMac[2], ethHeader->srcMac[3], ethHeader->srcMac[4], ethHeader->srcMac[5],
		ethHeader->dstMac[0], ethHeader->dstMac[1], ethHeader->dstMac[2], ethHeader->dstMac[3], ethHeader->dstMac[4], ethHeader->dstMac[5],
		protocol);
		
		// ip 协议
		if (protocol == 0x0800)//2^11
		{
			ipDecode(buf + proto_offset);//移到地址后面,取地址后面的数据
		}
		else
		{
			printf("[%s:%d]unsupported protocol %#x\n", __FILE__, __LINE__,
				protocol);
		}

		offset += (packageHeader->caplen + sizeof(package_pkthdr));//移到下一个数据包位置
		mPackIndex++;
	}

	printf("total package count:%d\n", mPackIndex);

	if (buf)
	{
		free(buf);
		buf = NULL;
	}
}

main.cpp

cpp 复制代码
#include "pcapParser.h"
#include <time.h>
#include<stdio.h>
#include<iostream>

class MsgParser : public PcapParser
{
private:
	int mCount;
public:
	MsgParser()
	{
		mCount = 0;
	}

public:
	int getCount() { return mCount; }
	int onUdpMsg(const char* buf, int len)
	{
		// do something
		return len;
	}
};


int main(int argc, char* argv[])
{
	if (2 != argc)
	{
		printf("usage: %s [PCAP_FILE]\n", argv[0]);
		return 0;
	}

	MsgParser parser;
	parser.parse(argv[1]);
	//printf("total quote count:%d\n", parser.getCount());
	system("pause");
	return 0;
}
相关推荐
java1234_小锋15 分钟前
Elasticsearch是如何实现Master选举的?
大数据·elasticsearch·搜索引擎
Java 第一深情4 小时前
零基础入门Flink,掌握基本使用方法
大数据·flink·实时计算
MXsoft6184 小时前
华为服务器(iBMC)硬件监控指标解读
大数据·运维·数据库
PersistJiao5 小时前
Spark 分布式计算中网络传输和序列化的关系(二)
大数据·网络·spark·序列化·分布式计算
九河云5 小时前
如何对AWS进行节省
大数据·云计算·aws
FreeIPCC6 小时前
谈一下开源生态对 AI人工智能大模型的促进作用
大数据·人工智能·机器人·开源
梦幻通灵6 小时前
ES分词环境实战
大数据·elasticsearch·搜索引擎
Elastic 中国社区官方博客6 小时前
Elasticsearch 中的热点以及如何使用 AutoOps 解决它们
大数据·运维·elasticsearch·搜索引擎·全文检索
天冬忘忧7 小时前
Kafka 工作流程解析:从 Broker 工作原理、节点的服役、退役、副本的生成到数据存储与读写优化
大数据·分布式·kafka
sevevty-seven8 小时前
幻读是什么?用什么隔离级别可以防止幻读
大数据·sql