【笔记】libpcap安装与使用
Ubuntu 20.04。
文章目录
起步
一、下载libpcap 库
前往http://www.tcpdump.org/release/ ,下载格式为xxxx.tar.gz的文件。
二、安装
- 解压文件到你的当前目录
tar zxvf xxxx.tar.gz
- 进入刚才解开的libpcap目录,生成Makefile文件
sudo ./configure
配置过程如果出现错误,
请查看你是否安装了所有的依赖包bison, m4, flex以及libpcap-dev(安装方法 sudo apt install ****)
尝试运行 sudo apt install bison m4 flex libpcap-dev -y
- 将生成的库安装到系统默认目录中。此目录为 /usr/lib ,如果需要修改,可以修改文件Makefile 的 prefix。
sudo make install
三、使用
- 编译选项:
gcc 源文件.c -lpcap -o 输出文件名
-
Warning不用管:
-
测试程序:
#include <pcap.h>
#include <stdio.h>
int main()
{
char errBuf[PCAP_ERRBUF_SIZE], * device;
//look for the net device
device = pcap_lookupdev(errBuf);
if(device)
{
printf("success: device: %s\n", device);
}
else
{
printf("error: %s\n", errBuf);
}
return 0;
}
- 测试程序运行结果:
完整程序 (们)
注意,
解析报文的时候,
结构体很可能因为字节对齐出现问题!
如下:
struct CAN_layer{
u_int16_t pkg_type;
u_int16_t layer_addr_type;
u_int16_t layer_addr_len;
u_int64_t unused; //。。。字节对齐
u_int16_t protocol;
};
应改写成:
struct CAN_layer{
u_int16_t pkg_type;
u_int16_t layer_addr_type;
u_int16_t layer_addr_len;
u_char unused[8]; //。。。字节对齐
u_int16_t protocol;
};
1. 头文件集锦
myheader.h
/* ethernet headers are always exactly 14 bytes [1] */
#define SIZE_ETHERNET 14
/* Ethernet addresses are 6 bytes */
#define ETHER_ADDR_LEN 6
#define PACKET_LEN 1500
/* Ethernet header */
struct ethheader {
u_char ether_dhost[ETHER_ADDR_LEN]; /* destination host address */
u_char ether_shost[ETHER_ADDR_LEN]; /* source host address */
u_short ether_type; /* IP? ARP? RARP? etc */
};
/* IP Header */
struct ipheader {
unsigned char iph_ihl:4, iph_ver:4; //IP Header length & Version.
unsigned char iph_tos; //Type of service
unsigned short int iph_len; //IP Packet length (Both data and header)
unsigned short int iph_ident; //Identification
unsigned short int iph_flag:3, iph_offset:13; //Flags and Fragmentation offset
unsigned char iph_ttl; //Time to Live
unsigned char iph_protocol; //Type of the upper-level protocol
unsigned short int iph_chksum; //IP datagram checksum
struct in_addr iph_sourceip; //IP Source address (In network byte order)
struct in_addr iph_destip;//IP Destination address (In network byte order)
};
/* ICMP Header */
struct icmpheader {
unsigned char icmp_type; //ICMP message type
unsigned char icmp_code; //Error code
unsigned short int icmp_chksum; //Checksum for ICMP Header and data
unsigned short int icmp_id; //Used in echo request/reply to identify request
unsigned short int icmp_seq;//Identifies the sequence of echo messages,
//if more than one is sent.
};
/* TCP Header */
struct tcpheader {
u_short tcp_sport; /* source port */
u_short tcp_dport; /* destination port */
u_int tcp_seq; /* sequence number */
u_int tcp_ack; /* acknowledgement number */
u_char tcp_offx2; /* data offset, rsvd */
#define TH_OFF(th) (((th)->tcp_offx2 & 0xf0) >> 4)
u_char tcp_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)
u_short tcp_win; /* window */
u_short tcp_sum; /* checksum */
u_short tcp_urp; /* urgent pointer */
};
/* UDP Header */
struct udpheader
{
u_int16_t udp_sport; /* source port */
u_int16_t udp_dport; /* destination port */
u_int16_t udp_ulen; /* udp length */
u_int16_t udp_sum; /* udp checksum */
};
struct pseudo_tcp
{
unsigned saddr, daddr;
unsigned char mbz;
unsigned char ptcl;
unsigned short tcpl;
struct tcpheader tcp;
char payload[PACKET_LEN];
};
// DNS layer header's structure
struct dnsheader {
unsigned short int query_id;
unsigned short int flags;
unsigned short int QDCOUNT;
unsigned short int ANCOUNT;
unsigned short int NSCOUNT;
unsigned short int ARCOUNT;
};
2. 打印报文内容
sniff.c
#include <pcap.h>
#include <stdio.h>
#include <arpa/inet.h>
#include "myheader.h"
#include <ctype.h>
/* This function will be invoked by pcap for each captured packet.
We can process each packet inside the function. */
void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
int i=0;
int size_data=0;
printf("\nGot a packet\n");
struct ethheader *eth=(struct ethheader *)packet;
if(ntohs(eth->ether_type) == 0x800)
{
struct ipheader *ip = (struct ipheader *)(packet + sizeof(struct ethheader));
printf(" From: %s\n",inet_ntoa(ip->iph_sourceip));
printf(" To: %s\n",inet_ntoa(ip->iph_destip));
struct tcpheader *tcp = (struct tcpheader *)(packet + sizeof(struct ethheader) + sizeof(struct ipheader));
printf(" Source Port: %d\n",ntohs(tcp->tcp_sport));
printf(" Destination Port: %d\n",ntohs(tcp->tcp_dport));
switch(ip->iph_protocol) {
case IPPROTO_TCP:
printf(" Protocol: TCP\n");
break;
case IPPROTO_UDP:
printf(" Protocol: UDP\n");
break;
case IPPROTO_ICMP:
printf(" Protocol: ICMP\n");
break;
default:
printf(" Protocol: Others\n");
break;
}
char *data = (u_char *)packet + sizeof(struct ethheader) + sizeof(struct ipheader) + sizeof(struct tcpheader);
size_data = ntohs(ip->iph_len) - (sizeof(struct ipheader) + sizeof(struct tcpheader));
if (size_data > 0) {
printf(" Payload (%d bytes):\n", size_data);
for(i = 0; i < size_data; i++) {
if (isprint(*data))
printf("%c", *data);
else
printf(".");
data++;
}
}
}
return;
}
int main()
{
pcap_t *handle;
char errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program fp;
char filter_exp[] = "proto TCP and dst portrange 10-100";
bpf_u_int32 net;
// Step 1: Open live pcap session on NIC with interface name
handle = pcap_open_live("enp0s3", BUFSIZ, 1, 1000, errbuf);
// Step 2: Compile filter_exp into BPF psuedo-code
pcap_compile(handle, &fp, filter_exp, 0, net);
pcap_setfilter(handle, &fp);
// Step 3: Capture packets
pcap_loop(handle, -1, got_packet, NULL);
pcap_close(handle); //Close the handle
return 0;
}
3. 嗅探与伪造
icmpspoof.c
#include <pcap.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include "myheader.h"
char DEST_IP[]="172.17.0.2"; //被欺骗机docker的IP地址
unsigned short in_cksum(unsigned short *buf,int length);
void send_raw_ip_packet(struct ipheader* ip);
void send_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);
int main(){
pcap_t *handle;
char errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program fp;
//设置报文过滤条件:来源于docker的icmp请求报文
char* filter_exp = "(icmp[0] == 8) && (src 172.17.0.2)";
printf("filter: %s\n",filter_exp);
bpf_u_int32 net;
send_packet();
// Step 1: 用网卡名字docker0打开pcap会话
handle = pcap_open_live("docker0", BUFSIZ, 1, 1000, errbuf);
// Step 2: 为侦听进程(bpf_program)fp添加过滤条件
pcap_compile(handle, &fp, filter_exp, 0, net);
pcap_setfilter(handle, &fp);
// Step 3: 抓取报文,回调函数send_packet,功能是抓取到请求报文时发欺骗答复包
pcap_loop(handle, -1, send_packet, NULL);
pcap_close(handle); //关闭句柄
return 0;
}
/******************************************************************
抓取到请求报文时,伪造并发送欺骗答复包
*******************************************************************/
void send_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet){
//获得真实请求报文的IP头和ICMP头
struct ipheader *ip_true=(struct ipheader *)(packet+sizeof(struct ethheader));
struct icmpheader *icmp_true=(struct icmpheader *)(packet+sizeof(struct ethheader)+sizeof(struct ipheader));
printf("Get a icmp request packet from %s,to %s\n",DEST_IP,inet_ntoa(ip_true->iph_destip));
printf("Send a cheat reply.\n");
char buffer[PACKET_LEN];
memset(buffer, 0, PACKET_LEN);
// 构造ICMP头
struct icmpheader *icmp;
icmp = (struct icmpheader *)(buffer + sizeof(struct ipheader));
icmp->icmp_type = 0; //设置类型为答复
// 计算icmp的数据校验位(调用in_cksum函数)
icmp->icmp_chksum = 0;
icmp->icmp_chksum = in_cksum((unsigned short *)icmp,
sizeof(struct icmpheader));
// 将真实的ICMP头的id和seq填入伪造的ICMP头中,显得更真实一些
icmp->icmp_id = icmp_true->icmp_id;
icmp->icmp_seq = icmp_true->icmp_seq;
// 构造IP头,填入常规参数
struct ipheader *ip = (struct ipheader *) buffer;
ip->iph_ver = 4;
ip->iph_ihl = 5;
ip->iph_tos = 16;
ip->iph_ident = htons(54321);
ip->iph_ttl = 128;
// 设置源地址为真实地址的目的地址,目的地址为被欺骗主机的IP地址
ip->iph_sourceip.s_addr = inet_addr(inet_ntoa(ip_true->iph_destip));
ip->iph_destip.s_addr = inet_addr(DEST_IP);
ip->iph_protocol = IPPROTO_ICMP; // The value is 1, representing ICMP.
ip->iph_len = htons(sizeof(struct ipheader) + sizeof(struct icmpheader));
// ip->iph_chksum 可由系统补全,不需要手动设置
// 最后,结合IP头和ICMP头发送报文
send_raw_ip_packet (ip);
}
/*******************************************************************************
用 raw socket 发 IP报文
*******************************************************************************/
void send_raw_ip_packet(struct ipheader* ip)
{
struct sockaddr_in dest_info;
int enable = 1;
// 创建一个原始网络套接字sock, and 设置相关参数
int sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &enable, sizeof(enable));
// 提供必要的信息和地址
dest_info.sin_family = AF_INET;
dest_info.sin_addr = ip->iph_destip;
// 把数据包发出去
printf("Sending spoofed IP packet...\n");
if(sendto(sock,ip,ntohs(ip->iph_len),0,(struct sockaddr *)&dest_info,sizeof(dest_info)) < 0)
{//要是发送失败
perror("PACKET NOT SENT\n");
return;
}
else {//发送成功时,打印发送的报文的源IP和目标IP
printf("\n---------------------------------------------------\n");
printf(" From: %s\n",inet_ntoa(ip->iph_sourceip));
printf(" To: %s\n",inet_ntoa(ip->iph_destip));
printf("\n---------------------------------------------------\n");
}
close(sock);//关闭sock
}
/*******************************************************************************
生成ICMP头的数据校验码,其内在逻辑是既定的,无需多加注释
*******************************************************************************/
unsigned short in_cksum(unsigned short *buf,int length)
{
unsigned short *w = buf;
int nleft = length;
int sum = 0;
unsigned short temp=0;
/*
* The algorithm uses a 32 bit accumulator (sum), adds
* sequential 16 bit words to it, and at the end, folds back all the
* carry bits from the top 16 bits into the lower 16 bits.
*/
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
/* treat the odd byte at the end, if any */
if (nleft == 1) {
*(u_char *)(&temp) = *(u_char *)w ;
sum += temp;
}
/* add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); // add hi 16 to low 16
sum += (sum >> 16); // add carry
return (unsigned short)(~sum);
}
附加内容
unmask(0)
原文链接
linux中的 umask 函数主要用于:在创建新文件或目录时 屏蔽掉新文件或目录不应有的访问允许权限。文件的访问允许权限共有9种,分别是:r w x r w x r w x(它们分别代表:用户读 用户写 用户执行 组读 组写 组执行 其它读 其它写 其它执行)。
其实这个函数的作用,就是设置允许当前进程创建文件或者目录最大可操作的权限,比如这里设置为0,它的意思就是0取反再创建文件时权限相与,也就是:(~0) & mode 等于八进制的值0777 & mode了,这样就是给后面的代码调用函数mkdir给出最大的权限,避免了创建目录或文件的权限不确定性。
共享内存
参考文章:
Linux 进程间通信(IPC)—大总结
共享内存无锁队列的实现
写得实在太好了。
报文时间戳处理
时间戳相减函数
报文的时间戳存储在回调函数的const struct pcap_pkthdr *
中,结构如下:
struct pcap_pkthdr {
struct timeval ts; /* time stamp */
bpf_u_int32 caplen; /* length of portion present */
bpf_u_int32 len; /* length this packet (off wire) */
};
其中struct timeval
结构如下:
struct timeval
{
__time_t tv_sec; /* Seconds. */
__suseconds_t tv_usec; /* Microseconds. */
};
根据两个结构的特点,报文相对时间戳的计算函数如下:
void diffstamp(struct timeval* stamp1, struct timeval stamp2)
{
stamp1->tv_sec = stamp1->tv_sec - stamp2.tv_sec;
stamp1->tv_usec = stamp1->tv_usec - stamp2.tv_usec;
if(stamp1->tv_usec<0)
{
stamp1->tv_sec -=1;
stamp1->tv_usec += 1000000;
}
}
时间戳处理结果: