Pcap 数据报解析

最近看了一下网络的书,信息系统也有实验任务,所以就学习了一下pcap包的解析。

主要是对内部以太网帧头,ip头部,tcp头部或者udp头部的解析。我因为用访问google.cn作为的样例,没有udp包就还没加udp的头部,不过大同小异了。
要注意的就是以太网是大端传输,而上层的协议都是小端传输,所以要转换字节序,可以使用ntohs() 和 ntohl() 两个函数,windows环境的话需要#include <WinSock2.h>和Ws2_32.lib库文件。

我这里使用了二维链表,只记录的每条链表的头部,每次插入时维护tcp序列号递增的顺序。

参考

pcap文件格式解析
ip头和tcp头结构
udp头部

我贴一下自己的代码

//pcap.h
#include <cstdint>
#include <list>
#include <map>
#include <algorithm>
#define max_len			200005
#define len_ip_hdr		20
#define len_tcp_hdr		20
#define len_ether_hdr	14
#define len_pkt_hdr		16
#define len_file_hdr	24 
#define len_udp_hdr		8
#define num_proto_tcp	0x06
#define num_proto_udp	0x11

//pcap文件报头
struct pcap_file_hdr{
	//char v[24];
	uint32_t magic;					 //标识位    32bit
	uint16_t version_major;          //主版本号  16bits
	uint16_t version_minor;          //副版本号  16bits
	uint32_t thiszone;               //区域时间  32bits
	uint32_t sigfigs;                //时间戳    32bits
	uint32_t snaplen;                //数据包最大长度   32bits(所抓获的数据包的最大长度)
	uint32_t linktype;               //链路层类型 32bits*/
};

//数据报报头
struct pcap_pkt_hdr{
	uint32_t ts_sec;                //时间戳秒
	uint32_t ts_usec;               //时间戳微秒
	uint32_t caplen;                //数据包长度 32bits
	uint32_t len;                   //实际长度 数据不完整时小于前值 32bits
};

//以太网帧头
#define ether_addr_len 6
struct ether_hdr{
	uint8_t ether_dst[ether_addr_len];
	uint8_t ether_src[ether_addr_len];
	uint16_t ether_type;
};

//IP报头
struct ip_hdr{
	uint8_t 	ip_hdr_len : 4;		// The header length.
	uint8_t 	ip_version : 4;		//The IP version.
	uint8_t 	ip_tos;				//Type of Service.
	uint16_t 	ip_len;				//IP packet length(both data and header).
	uint16_t 	ip_id;				//Identification.
	uint16_t 	ip_off;				//Fragment offset. //前3位标志,后13位片偏移
#define IP_RF 0x8000            /* reserved fragment flag */
#define IP_DF 0x4000            /* dont fragment flag */
#define IP_MF 0x2000            /* more fragments flag */
#define IP_OFFMASK 0x1fff       /* mask for fragmenting bits */
	uint8_t 	ip_ttl;				//Time To Live.
	uint8_t 	ip_proto;			//The type of the upper - level protocol.
	uint16_t 	ip_chk;				// IP header checksum.
	uint32_t 	ip_src;				//IP source address(in network format).
	uint32_t 	ip_dst;				//IP destination address(in network format).
};

//TCP报头
typedef uint32_t tcp_seq;
struct tcp_hdr{
	uint16_t th_port_src;
	uint16_t th_port_dst;
	tcp_seq th_seq;                 /* sequence number */
	tcp_seq th_ack;                 /* acknowledgement number */
	uint8_t  th_offx2;               /* data offset, rsvd */
#define TH_OFF(th)      (((th)->th_offx2 & 0xf0) >> 4)
	uint8_t  th_flags;
#define TH_FIN  0x01
#define TH_SYN  0x02
#define TH_RST  0x04
#define TH_PUSH 0x08
#define TH_ACK  0x10
#define TH_URG  0x20
#define TH_ECE  0x40
#define TH_CWR  0x80
#define TH_FLAGS        (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
	uint16_t th_win;                 /* window */
	uint16_t th_chk;                 /* checksum */
	uint16_t th_urp;                 /* urgent pointer */
};

struct udp_hdr{
	uint16_t uh_port_src;		/* source port */
	uint16_t uh_port_dst;		/* destination port */
	uint16_t uh_ulen;		/* datagram length */
	uint16_t uh_chk;			/* datagram checksum */
};

struct unit{
	uint32_t addr1, addr2;
	uint16_t port1, port2;
	uint8_t proto;
	bool operator <(const unit &rhs)const {
		if (proto != rhs.proto) return proto < rhs.proto;
		if (addr1 != rhs.addr1) return addr1 < rhs.addr1;
		if (addr2 != rhs.addr2) return addr2 < rhs.addr2;
		if (port1 != rhs.port1) return port1 < rhs.port1;
		if (port2 != rhs.port2) return port2 < rhs.port2;
		return false;
	}
};

struct node{
	unit *conn;
	pcap_pkt_hdr *pkt;
	ether_hdr *ether;
	ip_hdr *ip;
	uint8_t *ip_opts;
	tcp_hdr *tcp;
	uint8_t *tcp_opts;
	udp_hdr* udp;
	uint8_t *load;
	uint32_t len_load;

	node(unit *conn = NULL, pcap_pkt_hdr *pkt = NULL, ether_hdr *ether = NULL, ip_hdr *ip = NULL, 
		uint8_t *ip_opts = NULL, tcp_hdr *tcp = NULL, uint8_t *tcp_opts = NULL, udp_hdr *udp = NULL, uint8_t *load = NULL, 
		uint32_t len_load = 0) :conn(conn), pkt(pkt), ether(ether), ip(ip), ip_opts(ip_opts), 
		tcp(tcp), tcp_opts(tcp_opts), udp(udp), load(load), len_load(len_load){
		if (conn->addr1 > conn->addr2){
			std::swap(conn->addr1, conn->addr2);
			std::swap(conn->port1, conn->port2);
			if (tcp != NULL){
				std::swap(tcp->th_seq, tcp->th_ack);
			}
		}
	};

	bool operator>=(const node &rhs)const{
		if (tcp == NULL) return true;
		return tcp->th_seq > rhs.tcp->th_seq || (tcp->th_seq == rhs.tcp->th_seq && tcp->th_ack >= rhs.tcp->th_ack);
	}
};

typedef std::list<node> List;

class Pcap{
private:
	char *dirDst;
	CFile *fileSrc;
	pcap_file_hdr file_hdr;
	std::list<List*> result;
	std::map<unit, List*> index;
	
public:
	void Analyse();
	void Modify(ip_hdr *ip);
	void Modify(tcp_hdr *tcp);
	void Modify(udp_hdr *udp);
	void PrintInfo();
	void PrintPcap();
	void PrintLoad();
	void TransferIP(uint32_t addr, char *dst);

	char * DirDst() const { return dirDst; }
	void DirDst(char * val) { dirDst = val; }

	CFile * FileSrc() const { return fileSrc; }
	void FileSrc(CFile * val) { fileSrc = val; }
};
#include "stdafx.h"
#include "Pcap.h"




void Pcap::Analyse(){
	index.clear();
	uint32_t len_load = 0;		//负载数据长度
	//fread(&file_hdr, len_file_hdr, 1, fileSrc);
	fileSrc->Read(&file_hdr, len_file_hdr);
	while (true){
		pcap_pkt_hdr *pkt = new pcap_pkt_hdr();
		if (fileSrc->Read(pkt, len_pkt_hdr) == 0){
			break;
		}

		ether_hdr *ether = new ether_hdr();
		//fread(ether, len_ether_hdr, 1, fileSrc);
		fileSrc->Read(ether, len_ether_hdr);

		ip_hdr *ip = new ip_hdr();
		//fread(ip, len_ip_hdr, 1, fileSrc);
		fileSrc->Read(ip, len_ip_hdr);
		Modify(ip);
		//读ip选项
		uint8_t *ip_opts = NULL;
		if ((ip->ip_hdr_len << 2) - len_ip_hdr != 0){
			ip_opts = (uint8_t*)malloc((ip->ip_len << 2) - len_ip_hdr);
			//fread(ip_opts, (ip->ip_hdr_len << 2) - len_ip_hdr, 1, fileSrc);
			fileSrc->Read(ip_opts, (ip->ip_hdr_len << 2) - len_ip_hdr);
		}
		uint8_t *load = NULL, *tcp_opts = NULL;
		tcp_hdr *tcp = NULL;
		udp_hdr *udp = NULL;
		if (ip->ip_proto == num_proto_tcp){
			tcp = new tcp_hdr();
			//TCP包
			//fread(tcp, len_tcp_hdr, 1, fileSrc);
			fileSrc->Read(tcp, len_tcp_hdr);
			Modify(tcp);

			//选项长度备用
			uint8_t opts = 4 * TH_OFF(tcp);

			if (opts - len_tcp_hdr != 0){
				tcp_opts = (uint8_t*)malloc(opts - len_tcp_hdr);
				//fread(tcp_opts, opts - len_tcp_hdr, 1, fileSrc);
				fileSrc->Read(tcp_opts, opts - len_tcp_hdr);
			}
			len_load = ip->ip_len - len_ip_hdr - opts;
			load = (uint8_t*)malloc(len_load);
			//fread(load, len_load, 1, fileSrc);
			fileSrc->Read(load, len_load);
		}
		else if (ip->ip_proto == num_proto_udp){
			//UDP包
			udp = new udp_hdr();
			//fread(udp, len_udp_hdr, 1, fileSrc);
			fileSrc->Read(udp, len_udp_hdr);
			Modify(udp);

			len_load = ip->ip_len - len_ip_hdr - len_udp_hdr;
			load = (uint8_t*)malloc(len_load);
			//fread(load, len_load, 1, fileSrc);
			fileSrc->Read(load, len_load);
		}
		else{
			//其他协议包。跳过
			//fseek(fileSrc, ip->ip_len - len_ip_hdr, SEEK_CUR);
			fileSrc->Seek(ip->ip_len - len_ip_hdr, CFile::current);
			continue;
		}

		//五元组信息
		unit *conn = new unit();
		conn->proto = ip->ip_proto;
		conn->addr1 = ip->ip_src;
		conn->addr2 = ip->ip_dst;
		if (ip->ip_proto == num_proto_udp){
			conn->port1 = udp->uh_port_src;
			conn->port2 = udp->uh_port_dst;
		}
		else{
			conn->port1 = tcp->th_port_src;
			conn->port2 = tcp->th_port_dst;
		}

		//包信息
		node temp = node(conn, pkt, ether, ip, ip_opts, tcp, tcp_opts, udp, load, len_load);
		
		//五元组链表索引
		List *val = index[*conn];
		if (val == NULL){
			val = new List();
			index[*conn] = val;
			val->push_back(temp);
			result.push_back(val);
		}
		else{
			//顺序插入
			auto it = val->end();
			--it;
			if (temp >= *it){
				val->push_back(temp);
			}
			else{
				for (; it != val->begin(); --it){
					if (temp >= *it){
						++it;
						val->insert(it, temp);
						break;
					}
				}
			}
		}
	}
}

void Pcap::Modify(ip_hdr *ip)
{
	ip->ip_len = ntohs(ip->ip_len);
	ip->ip_off = ntohs(ip->ip_off);
	ip->ip_chk = ntohs(ip->ip_chk);
	ip->ip_src = ntohl(ip->ip_src);
	ip->ip_dst = ntohl(ip->ip_dst);
}

void Pcap::Modify(udp_hdr *udp)
{
	udp->uh_port_src = ntohs(udp->uh_port_src);
	udp->uh_port_dst = ntohs(udp->uh_port_dst);
	udp->uh_ulen = ntohs(udp->uh_ulen);
	udp->uh_chk = ntohs(udp->uh_chk);
}

void Pcap::Modify(tcp_hdr *tcp)
{
	tcp->th_port_src = ntohs(tcp->th_port_src);
	tcp->th_port_dst = ntohs(tcp->th_port_dst);
	tcp->th_seq = ntohl(tcp->th_seq);
	tcp->th_ack = ntohl(tcp->th_ack);
	tcp->th_win = ntohs(tcp->th_win);
	tcp->th_chk = ntohs(tcp->th_chk);
	tcp->th_urp = ntohl(tcp->th_urp);
}

//输出控制信息
void Pcap::PrintInfo()
{
	char path[MAX_PATH];
	strcpy(path, dirDst);
	strcpy(path + strlen(path), "\\");
	//CreateDirectory((LPCWSTR)path, NULL);
	for (auto it = result.begin(); it != result.end(); ++it){
		List *cur = *it;
		unit *conn = (cur->begin())->conn;
		char addr1[MAX_PATH], addr2[MAX_PATH];
		memset(addr1, 0, sizeof(addr1));
		memset(addr2, 0, sizeof(addr2));
		TransferIP(conn->addr1, addr1);
		TransferIP(conn->addr2, addr2);
		char title[MAX_PATH];
		strcpy(title, path);
		if (conn->proto == num_proto_udp){
			sprintf(title + strlen(title), "UDP");
		}
		else{
			sprintf(title + strlen(title), "TCP");
		}
		sprintf(title + strlen(title), "[%s][%d][%s][%d].txt", addr1, conn->port1, addr2, conn->port2);
		FILE *fp = fopen(title, "w");
		fprintf(fp, "Src IP:		%s\n", addr1);
		fprintf(fp, "Src Port:	%d\n", conn->port1);
		fprintf(fp, "Dst IP:		%s\n", addr2);
		fprintf(fp, "Dst Port:	%d\n", conn->port2);
		fprintf(fp, "Protocal:	%d", conn->proto);
		for (auto it2 = cur->begin(); it2 != cur->end(); ++it2){
			fprintf(fp, "%c%c%c%c", 0x0a0d, 0x0a0d, 0x0a0d, 0x0a0d);
			fprintf(fp, "{\n");
			if (conn->proto == num_proto_tcp){
				fprintf(fp, "	seq number:		%lld\n", (__int64)it2->tcp->th_seq);
				fprintf(fp, "	ack number:		%lld\n", (__int64)it2->tcp->th_ack);
				fprintf(fp, "	window size:	%lld\n", (__int64)it2->tcp->th_win);
			}
			else{
				fprintf(fp, "	udp length:		%d\n", (int)it2->udp->uh_ulen);
				fprintf(fp, "	check sum:		%d\n", (int)it2->udp->uh_chk);
			}
			fprintf(fp, "}");
		}
		fclose(fp);
	}
}

//输出Pcap包
void Pcap::PrintPcap()
{
	char path[MAX_PATH];
	strcpy(path, dirDst);
	strcpy(path + strlen(path), "\\");
	//CreateDirectory((LPCWSTR)path, NULL);
	for (auto it = result.begin(); it != result.end(); ++it){
		List *cur = *it;
		unit *conn = (cur->begin())->conn;
		char addr1[MAX_PATH], addr2[MAX_PATH];
		memset(addr1, 0, sizeof(addr1));
		memset(addr2, 0, sizeof(addr2));
		TransferIP(conn->addr1, addr1);
		TransferIP(conn->addr2, addr2);
		char title[MAX_PATH];
		strcpy(title, path);
		if (conn->proto == num_proto_udp){
			sprintf(title + strlen(title), "UDP");
		}
		else{
			sprintf(title + strlen(title), "TCP");
		}
		sprintf(title + strlen(title), "[%s][%d][%s][%d].pcap", addr1, conn->port1, addr2, conn->port2);
		FILE *fp = fopen(title, "wb");
		fwrite(&file_hdr, len_file_hdr, 1, fp);
		for (auto it2 = cur->begin(); it2 != cur->end(); ++it2){
			fwrite(it2->pkt, len_pkt_hdr, 1, fp);
			fwrite(it2->ether, len_ether_hdr, 1, fp);

			Modify(it2->ip);
			fwrite(it2->ip, len_ip_hdr, 1, fp);
			int x = (it2->ip->ip_hdr_len << 2) - len_ip_hdr;
			fwrite(it2->ip_opts, x, 1, fp);

			if (it2->ip->ip_proto == num_proto_tcp){
				Modify(it2->tcp);
				fwrite(it2->tcp, len_tcp_hdr, 1, fp);
				x = (TH_OFF(it2->tcp) << 2) - len_tcp_hdr;
				fwrite(it2->tcp_opts, x, 1, fp);
				Modify(it2->tcp);
			}
			else{
				Modify(it2->udp);
				fwrite(it2->udp, len_udp_hdr, 1, fp);
				Modify(it2->udp);
			}
			Modify(it2->ip);

			fwrite(it2->load, it2->len_load, 1, fp);
			
		}
		fclose(fp);
	}
}

//输出负载数据
void Pcap::PrintLoad()
{
	char path[MAX_PATH];
	strcpy(path, dirDst);
	strcpy(path + strlen(path), "\\");
	//CreateDirectory((LPCWSTR)path, NULL);
	for (auto it = result.begin(); it != result.end(); ++it){
		List *cur = *it;
		unit *conn = (cur->begin())->conn;
		char addr1[MAX_PATH], addr2[MAX_PATH];
		memset(addr1, 0, sizeof(addr1));
		memset(addr2, 0, sizeof(addr2));
		TransferIP(conn->addr1, addr1);
		TransferIP(conn->addr2, addr2);
		char title[MAX_PATH];
		strcpy(title, path);
		if (conn->proto == num_proto_udp){
			sprintf(title + strlen(title), "UDP");
			//continue;
		}
		else{
			sprintf(title + strlen(title), "TCP");	
		}
		sprintf(title + strlen(title), "[%s][%d][%s][%d]-.txt", addr1, conn->port1, addr2, conn->port2);
		FILE *fp = fopen(title, "w");
		uint32_t last_seq = 0, last_len = 0;
		bool is_first = true;
		for (auto it2 = cur->begin(); it2 != cur->end(); ++it2){
			if (conn->proto == num_proto_tcp){
				if (!is_first && last_seq + last_len != it2->tcp->th_seq){
					fprintf(fp, "%c%c%c%c", 0x0a0d, 0x0a0d, 0x0a0d, 0x0a0d);
				}
				int x = strlen((char*)(it2->load));
				//fprintf(fp, "%s", it2->load);
				if (it2->len_load != 0){
					fwrite(it2->load, it2->len_load, 1, fp);
					is_first = false;
				}
				last_seq = it2->tcp->th_seq;
				last_len = it2->len_load;
			}
			else{
				if (it2->len_load != 0){
					fwrite(it2->load, it2->len_load, 1, fp);
					fprintf(fp, "%c%c%c%c", 0x0a0d, 0x0a0d, 0x0a0d, 0x0a0d);
				}
			}
		}
		fclose(fp);
	}
}


void Pcap::TransferIP(uint32_t addr, char *dst)
{
	uint16_t a = addr >> 24;
	uint16_t b = (addr & 0x00ff0000) >> 16;
	uint16_t c = (addr & 0x0000ff00) >> 8;
	uint16_t d = (addr & 0x000000ff);
	sprintf(dst, "%d.%d.%d.%d", a, b, c, d);
}

posted on 2016-05-12 14:21  张济  阅读(975)  评论(0编辑  收藏  举报

导航