libpcap安裝與使用


Ubuntu 20.04。

起步

一、下載libpcap 庫

前往http://www.tcpdump.org/release/ ,下載格式為xxxx.tar.gz的文件。

二、安裝

  1. 解壓文件到你的當前目錄
tar zxvf xxxx.tar.gz 
  1. 進入剛才解開的libpcap目錄,生成Makefile文件
sudo ./configure 

配置過程如果出現錯誤,
請查看你是否安裝了所有的依賴包bison, m4, flex以及libpcap-dev(安裝方法 sudo apt install ****)
嘗試運行 sudo apt install bison m4 flex libpcap-dev -y

  1. 將生成的庫安裝到系統默認目錄中。此目錄為 /usr/lib ,如果需要修改,可以修改文件Makefile 的 prefix。
sudo make install

三、使用

  1. 編譯選項:
gcc 源文件.c -lpcap -o 輸出文件名
  1. Warning不用管:
    在這里插入圖片描述

  2. 測試程序:

#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;  
} 
  1. 測試程序運行結果:
    在這里插入圖片描述

完整程序 (們)

注意,
解析報文的時候,
結構體很可能因為字節對齊出現問題!
如下:

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;
  }
}

時間戳處理結果:
在這里插入圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM