下载源代码
http://www.tcpdump.org/release/
通过 tar zxvf libpcap-1.0.0.tar.gz 解压文件。
进入文件目录后依次执行
./configure
make && make install
配置过程中缺少什么就使用 sudo apt install xx 安装
示例代码
代码的作用是读取一个 .pcap 文件,并解析其中的TCP报文。
.pcap 文件可以通过wireshark 进行保存获得。
#include <stdio.h>
#include <pcap.h>
#include <time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
typedef struct eth_hdr{
u_char dst_mac[6]; // 目标mac 硬件地址
u_char src_mac[6]; // 源mac 硬件地址
u_short eth_type; // 以太帧数据类型
}__attribute__((packed)) eth_hdr;
eth_hdr *ethernet;
typedef struct ip_hdr{
int version:4; // 版本
int header_len:4; // 头长度
u_char tos:8;
int total_len:16;
int ident:16;
int flags:16;
u_char ttl:8; // 跳转生命期
u_char protocol:8; // 协议
int checksum:16; // 校验和
u_char sourceIP[4]; // 源地址
u_char destIP[4]; // 目标地址
}__attribute__((packed)) ip_hdr;
ip_hdr *ip;
typedef struct tcp_hdr{
u_short sport:16; // 源端口号
u_short dport:16; // 目标端口号
u_int seq:32; // 序列值
u_int ack:32; // 确认
u_char head_len:4; // 头部长度
u_char reserved:4; // 保留字段
u_char flags:8;
u_short wind_size:16; // 窗口大小
u_short check_sum:16; // 校验和
u_short urg_ptr:16; // 紧急指针
}__attribute__((packed)) tcp_hdr;
tcp_hdr *tcp;
typedef struct udp_hdr{
u_short sport; // 源端口号
u_short dport; // 目标端口号
u_short tot_len; // 总长度
u_short check_sum; // 校验和
}__attribute__((packed)) udp_hdr;
udp_hdr *udp;
// aaaaaaaa bbbbbbbb cccccccc dddddddd
// dddddddd cccccccc bbbbbbbb aaaaaaaa
u_int32_t swap32(u_int32_t a){
return (((a>>24)&0xFF)<<0) | (((a>>16)&0xFF)<<8) | (((a>>8)&0xFF)<<16) | (((a>>0)&0xFF)<<24);
}
void pcap_callback(unsigned char * arg,const struct pcap_pkthdr *packet_header,const unsigned char *packet_content){
static int id=1;
printf("id=%d\n",id++);
printf("Packet length : %d\n",packet_header->len);
printf("Number of bytes : %d\n",packet_header->caplen);
printf("Received time : %s\n",ctime((const time_t*)&packet_header->ts.tv_sec));
int i;
for(i=0;i<packet_header->caplen;i++){
printf(" %02x",packet_content[i]);
if((i+1)%16==0){
printf("\n");
}
}
printf("\n\n");
u_int eth_len = sizeof(struct eth_hdr);
u_int ip_len = sizeof(struct ip_hdr);
u_int tcp_len = sizeof(struct tcp_hdr);
u_int udp_len = sizeof(struct udp_hdr);
printf("analyse information:\n\n");
printf("ethernet header information:\n");
ethernet=(eth_hdr *)packet_content;
printf("src_mac : %02x-%02x-%02x-%02x-%02x-%02x\n",ethernet->src_mac[0],ethernet->src_mac[1],ethernet->src_mac[2],ethernet->src_mac[3],ethernet->src_mac[4],ethernet->src_mac[5]);
printf("dst_mac : %02x-%02x-%02x-%02x-%02x-%02x\n",ethernet->dst_mac[0],ethernet->dst_mac[1],ethernet->dst_mac[2],ethernet->dst_mac[3],ethernet->dst_mac[4],ethernet->dst_mac[5]);
printf("ethernet type : %u\n",ethernet->eth_type);
if(ntohs(ethernet->eth_type)==0x0800){
printf("IPV4 is used\n");
printf("IPV4 header information:\n");
// 偏移获得 ip 数据包头
ip=(ip_hdr*)(packet_content+eth_len);
printf("source ip : %d.%d.%d.%d\n",ip->sourceIP[0],ip->sourceIP[1],ip->sourceIP[2],ip->sourceIP[3]);
printf("dest ip : %d.%d.%d.%d\n",ip->destIP[0],ip->destIP[1],ip->destIP[2],ip->destIP[3]);
if(ip->protocol==6){
printf("tcp is used:\n");
tcp=(tcp_hdr*)(packet_content+eth_len+ip_len);
// 网络字节序到主机字节序
printf("tcp source port : %u\n",ntohs(tcp->sport));
printf("tcp dest port : %u\n",ntohs(tcp->dport));
printf("tcp seq : %u\n",swap32(tcp->seq));
printf("tcp ack : %u\n",swap32(tcp->ack));
printf("head_len : %u\n",ntohs(tcp->head_len));
printf("windoes size : %u\n",ntohs(tcp->wind_size));
printf("check_sum : %u\n",ntohs(tcp->check_sum));
printf("urg_ptr : %u\n",ntohs(tcp->urg_ptr));
}
else if(ip->protocol==17){
printf("udp is used:\n");
udp=(udp_hdr*)(packet_content+eth_len+ip_len);
printf("udp source port : %u\n",ntohs(udp->sport));
printf("udp dest port : %u\n",ntohs(udp->dport));
}
else {
printf("other transport protocol is used\n");
}
}
else {
printf("ipv6 is used\n");
}
printf("------------------done-------------------\n");
printf("\n\n");
}
int main(int argc, char *argv[]){
char errBuf[PCAP_ERRBUF_SIZE], * devStr;
pcap_t *device = pcap_open_offline("ssss.pcapng",errBuf);
if(!device){
printf("error: pcap_open_offline(): %s\n", errBuf);
exit(1);
}
/* wait loop forever */
pcap_loop(device, -1, pcap_callback, NULL);
pcap_close(device);
return 0;
}
编译过程
g++ pcap.cpp -o test -lpcap
需要连接 libpcap.so 动态库。
如果刚配置完成会没有更新linux的动态库路径,使用 sudo ldconfig 更新路径后再链接。
运行结果
踩坑的地方
执行后我发现tcp的seq/ack字段与wireshark显示的字段不同,后来查找资料发现只是wireshark对这个数值进行了转化显示,实际的数值结果入上图所示。
附赠程序解析的 .pcap 文件
链接:https://pan.baidu.com/s/1-iYnLbBgAoAkt5VuQnps9g
提取码:xxvj