zoukankan      html  css  js  c++  java
  • 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
    ", addr1);
    		fprintf(fp, "Src Port:	%d
    ", conn->port1);
    		fprintf(fp, "Dst IP:		%s
    ", addr2);
    		fprintf(fp, "Dst Port:	%d
    ", 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, "{
    ");
    			if (conn->proto == num_proto_tcp){
    				fprintf(fp, "	seq number:		%lld
    ", (__int64)it2->tcp->th_seq);
    				fprintf(fp, "	ack number:		%lld
    ", (__int64)it2->tcp->th_ack);
    				fprintf(fp, "	window size:	%lld
    ", (__int64)it2->tcp->th_win);
    			}
    			else{
    				fprintf(fp, "	udp length:		%d
    ", (int)it2->udp->uh_ulen);
    				fprintf(fp, "	check sum:		%d
    ", (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);
    }
    
  • 相关阅读:
    Python易忽略要点记录二
    Python使用的几个小技巧
    MySQL内容整理二
    C++面向对象知识总结
    AssemblyInfo 的作用
    中国新年第一篇
    function函数的运用
    C语言1博客作业03
    C语言1博客作业02
    .C# 拷贝一个图片到指定文件夹下(IO文件操作实例)
  • 原文地址:https://www.cnblogs.com/macinchang/p/5485535.html
Copyright © 2011-2022 走看看