一、pcap文件格式
https://www.cnblogs.com/Chary/articles/15716063.html
接口协议(四):以太网(Ethernet)学习(一):协议_以太网协议_QNee的博客-CSDN博客
二、代码
pcapParser.h
#ifndef _PCAP_PARSER_H_
#define _PCAP_PARSER_H_
#include <stdint.h>
#include <iostream>
#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 当地标准事件 一般全0*/
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 temp; //20230725 临时添加2字节,正常不需要此位
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; //头部校验和
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*128]; // 4k缓存
uint32_t 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
#include "pcapParser.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 = htons(tcpHeader->srcPort);//大小端转换
uint16_t dstPort = htons(tcpHeader->dstPort);
// 用户数据长度
uint16_t dataLen = len - sizeof(TCPHeader_t);//TCP包总长-TCP包头
if (0 != tcpFilter(srcPort, dstPort, dataLen))
{
// tcp过滤
return;
}
printf("srcPort[%d]->dstPort[%d]\n", srcPort, dstPort);
//存到缓存,用来做粘包,半包处理
memcpy(mTcpData, buf + offset, dataLen);
mTcpLen += dataLen;
std::cout << "=======================================begin=============================================\n";
std::cout << "data["<< dataLen <<"]: " << mTcpData <<std::endl;
std::cout << "========================================end==============================================\n";
//用户数据
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, 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("地址:srcIp[%s]->dstIp[%s] 协议类型:%#x(6为tcp,17为udp) ip包总长=%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("mac地址:[%02x:%02x:%02x:%02x:%02x:%02x]->[%02x:%02x:%02x:%02x:%02x:%02x] 协议类型:%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 0x0800代表IP协议( 网际协议) 、 0x0806代表ARP协议(地址解析协议)
{
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
#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;
}