libpcap抓取數據包


libpcap是數據包捕獲函數庫。該庫提供的C函數接口可用於需要捕獲經過網絡接口數據包的系統開發上。libpcap提供的接口函數主要實現和封裝了與數據包截獲有關的過程。這個庫為不同的平台提供了一致的編程接口,在安裝了libpcap的平台上,以libpcap為接口寫的程序,能夠自由的跨平台使用。

linux下libpcap的安裝:sudo apt-get install libpcap-dev

linux下gcc編譯程序:gcc my_pcap.c -lpcap

執行程序的時候如果報錯:no suitable device found,以管理員權限運行程序即可,sudo ./my_pcap

 

libpcap的抓包框架:

頭文件: #include <pcap.h> 在/usr/local/include/pcap目錄下

1.查找網絡設備

char *pcap_lookupdev(char *errbuf) 

該函數用於返回可被pcap_open_live()或pcap_lookupnet()函數調用的網絡設備名(一個字符串指針)。如果函數出錯,則返回NULL,同時errbuf中存放相關的錯誤消息。

2.獲得指定網絡設備的網絡號和掩碼

int pcap_lookupnet(char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, char *errbuf) 

netp參數和maskp參數都是bpf_u_int32指針。如果函數出錯,則返回-1,同時errbuf中存放相關的錯誤消息。

Bpf_u_int32:32位無符號數

Struct in_addr

{

   unsigned long s_addr;

}

inet_ntoa();以a.b.c.d的形式顯示地址。

3.打開網絡設備 

 pcap_t *pcap_open_live(char *device,  int snaplen,  int promisc, int to_ms, char *ebuf) 

獲得用於捕獲網絡數據包的數據包捕獲描述字。device參數為指定打開的網絡設備名。snaplen參數定義捕獲數據的最大字節數,65535是最大值。promisc指定 是否將網絡接口置於混雜模式,設置為1表示混雜模式。to_ms參數指定超時時間(毫秒),設置為0表示超時時間無限大。ebuf參數則僅在pcap_open_live()函數出錯返回NULL時用於傳遞 錯誤消息。

typedef struct pcap pcap_t;

pcap結構在libpcap源碼的pcap-int.h定義,使用時一般都是使用其指針類型)。

4.打開已有的網絡數據包 //如果是抓取數據包,這個過程不需要

pcap_t *pcap_open_offline(char *fname, char *errbuf)

fname參數指定打開的文件名。該文件中的數據格式與tcpdump兼容。errbuf參數則僅在pcap_open_offline()函數出錯返回NULL時用於傳遞錯誤消息。

pcap_t *pcap_fopen_offline(FILE *fp, char *errbuf)打開文件指針。

5.編譯和設置過濾條件

int pcap_compile(pcap_t *p, struct bpf_program *fp, char *str,  int optimize, bpf_u_int32 netmask) 

設置過濾條件,舉一些例子:

  • src host 192.168.1.1:只接收源ip地址是192.168.1.1的數據包
  • dst port 80:只接收tcp、udp的目的端口是80的數據包
  • not tcp:只接收不使用tcp協議的數據包
  • tcp[13] == 0x02 and (dst port 22 or dst port 23) :只接收 SYN 標志位置位且目標端口是 22 或 23 的數據包( tcp 首部開始的第 13 個字節)
  • icmp[icmptype] == icmp-echoreply or icmp[icmptype] == icmp-echo:只接收 icmp 的 ping 請求和 ping 響應的數據包
  • ehter dst 00:e0:09:c1:0e:82:只接收以太網 mac 地址是 00:e0:09:c1:0e:82 的數據包
  • ip[8] == 5:只接收 ip 的 ttl=5 的數據包(ip首部開始的第8個字節)

str參數指定的字符串編譯到過濾程序中。fp是一個bpf_program結構的指針,在pcap_compile()函數中被賦值。optimize參數控制結果代碼的優化。netmask參數指定本地網絡的網絡掩碼,當不知道的時候可以設為0。出錯時返回-1.

int pcap_setfilter(pcap_t *p, struct bpf_program *fp) 

指定一個過濾程序。fp參數是bpf_program結構指針,通常取自pcap_compile()函數調用。出錯時返回-1。

6.抓取和讀取數據包

int pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback,  u_char *user) 

捕獲並處理數據包。cnt參數指定函數返回前所處理數據包的最大值。cnt=-1表示在一個緩沖區中處理所有的數據包。callback參數指定一個帶有三個參數的回調函數,這三個參數為:一個從 pcap_dispatch()函數傳遞過來的u_char指針,一個pcap_pkthdr結構的指針,和指向caplen大小的數據包的u_char指針。

struct pcap_pkthdr { 

     struct tim ts;   // ts是一個結構struct timeval,它有兩個部分,第一部分是1900開始以來的秒數,第二部分是當前秒之后的毫秒數

     bpf_u_int32 caplen;    //表示抓到的數據長度

     bpf_u_int32 len;    //表示數據包的實際長度

}; 

user參數是留給用戶使用的,當callback被調用的時候這個值會傳遞給callback的第一個參數(也叫user)。

成功 則返回讀到的數據包數。返回0沒有抓到數據包。出錯時則返回-1,此時可調用pcap_perror()或pcap_geterr()函數獲取錯誤消息。返回-2表示調用了pcap_breakloop(). 

int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)  

功能基本與pcap_dispatch()函數類似,只不過此函數在cnt個數據包被處理或出現錯誤時才返回,但讀取超時不會返回。

u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)  

讀取下一個數據包,類似於pcap_dispatch()中cnt參數設為1,返回指向讀到的數據包的指針,但是不返回這個包的pcap_pkthdr結構的參數。

7.關閉文件釋放資源

void pcap_close(pcap_t *p)

關閉P指針。

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <pcap.h>
  4 
  5 #define PCAP_DATABUF_MAX 65535
  6 
  7 #define ETHERTYPE_IPV4 0x0800
  8 #define ETHERTYPE_IPV6 0x86DD
  9 
 10 typedef unsigned char   u_int8;
 11 typedef unsigned short  u_int16;
 12 typedef unsigned int    u_int32;
 13 typedef unsigned long   u_int64;
 14 
 15 /*MAC頭,總長度14字節 */
 16 typedef struct _eth_hdr{
 17     u_int8 dst_mac[6];
 18     u_int8 src_mac[6];
 19     u_int16 eth_type;
 20 }eth_hdr;
 21 eth_hdr *ethernet;
 22 
 23 /*IP頭*/
 24 typedef struct _ip_hdr{
 25     u_int8 ver_hl;    //版本和頭長
 26     u_int8 serv_type; //服務類型
 27     u_int16 pkt_len;  //包總長
 28     u_int16 re_mark;  //重組標志
 29     u_int16 flag_seg; //標志位和段偏移量
 30     u_int8 surv_tm;    //生存時間
 31     u_int8 protocol;  //協議碼(判斷傳輸層是哪一個協議)
 32     u_int16 h_check;  //頭檢驗和
 33     u_int32 src_ip;   //源ip
 34     u_int32 dst_ip;   //目的ip
 35     u_int32 option;   //可選選項
 36 }ip_hdr;
 37 ip_hdr *ip;
 38 
 39 /*TCP頭,總長度20字節,不包括可選選項*/
 40 typedef struct _tcp_hdr{
 41     u_int16 sport;     //源端口
 42     u_int16 dport;     //目的端口
 43     u_int32 seq;       //序列號
 44     u_int32 ack;       //確認序號
 45     u_int8  head_len;  //頭長度
 46     u_int8  flags;     //保留和標記位
 47     u_int16 wind_size; //窗口大小
 48     u_int16 check_sum; //校驗和
 49     u_int16 urgent_p;  //緊急指針
 50 }tcp_hdr;
 51 tcp_hdr *tcp;
 52 
 53 /*UDP頭,總長度8個字節*/
 54 typedef struct _udp_hdr{
 55     u_int16 sport;     //源端口
 56     u_int16 dport;     //目的端口
 57     u_int16 pktlen;    //UDP頭和數據的總長度
 58     u_int16 check_sum; //校驗和
 59 }udp_hdr;
 60 udp_hdr *udp;
 61 
 62 //ip整型轉換點分十進制
 63 char *InttoIpv4str(u_int32 num){
 64     char* ipstr = (char*)calloc(128, sizeof(char*)); 
 65   
 66     if (ipstr)
 67         sprintf(ipstr, "%d.%d.%d.%d", num >> 24 & 255, num >> 16 & 255, num >> 8 & 255, num & 255);
 68     else
 69         printf("failed to Allocate memory...");
 70     
 71     return ipstr;
 72 }
 73 
 74 void pcap_callback(u_char *useless,const struct pcap_pkthdr *pkthdr, const u_char *packet)
 75 {
 76     printf("data len:%u\n", pkthdr->caplen); //抓到時的數據長度
 77     printf("packet size:%u\n", pkthdr->len); //數據包實際的長度
 78 
 79     /*解析數據鏈路層 以太網頭*/
 80     ethernet = (struct _eth_hdr*)packet;
 81     u_int64 src_mac = ntohs( ethernet->src_mac );
 82     u_int64 dst_mac = ntohs( ethernet->dst_mac );
 83    
 84     printf("src_mac:%lu\n",src_mac);
 85     printf("dst_mac:%lu\n",dst_mac);
 86     printf("eth_type:%u\n",ethernet->eth_type);
 87     
 88     u_int32 eth_len = sizeof(struct _eth_hdr);  //以太網頭的長度
 89     u_int32 ip_len; //ip頭的長度
 90     u_int32 tcp_len = sizeof(struct _tcp_hdr);  //tcp頭的長度
 91     u_int32 udp_len = sizeof(struct _udp_hdr);  //udp頭的長度
 92     
 93     /*解析網絡層  IP頭*/
 94     if(ntohs(ethernet->eth_type) == ETHERTYPE_IPV4){  //IPV4
 95         printf("It's IPv4!\n");
 96         
 97         ip = (struct _ip_hdr*)(packet + eth_len);
 98         ip_len = (ip->ver_hl & 0x0f)*4;            //ip頭的長度
 99         u_int32 saddr = (u_int32)ntohl(ip->src_ip); //網絡字節序轉換成主機字節序
100         u_int32 daddr = (u_int32)ntohl(ip->dst_ip);
101         
102         printf("eth_len:%u  ip_len:%u  tcp_len:%u  udp_len:%u\n", eth_len, ip_len, tcp_len, udp_len);
103         
104         printf("src_ip:%s\n", InttoIpv4str(saddr));  //源IP地址
105         printf("dst_ip:%s\n", InttoIpv4str(daddr));  //目的IP地址
106         
107         printf("ip->proto:%u\n", ip->protocol);      //傳輸層用的哪一個協議
108         
109         /*解析傳輸層  TCP、UDP、ICMP*/
110         if(ip->protocol == 6){         //TCP
111             tcp = (struct _tcp_hdr*)(packet + eth_len + ip_len);
112             printf("tcp_sport = %u\n", tcp->sport);
113             printf("tcp_dport = %u\n", tcp->dport);
114             
115 /**********(pcaket + eth_len + ip_len + tcp_len)就是TCP協議傳輸的正文數據了***********/
116 
117         }else if(ip->protocol == 17){  //UDP
118             udp = (struct _udp_hdr*)(packet + eth_len + ip_len);
119             printf("udp_sport = %u\n", udp->sport);
120             printf("udp_dport = %u\n", udp->dport);
121             
122 /**********(pcaket + eth_len + ip_len + udp_len)就是UDP協議傳輸的正文數據了***********/
123             
124         }else if(ip->protocol == 1){   //ICMP
125             
126         }
127         
128     }else if(ntohs(ethernet->eth_type) == ETHERTYPE_IPV6){ //IPV6
129         printf("It's IPv6!\n");
130     }
131     
132     printf("============================================\n");
133 }
134 
135 int main()
136 {
137     char *dev;  //設備名
138     char errbuf[PCAP_ERRBUF_SIZE] = {}; //PCAP_ERRBUF_SIZE在pcap.h中已經定義
139     bpf_u_int32 netp, maskp;  //網絡號和掩碼
140     pcap_t *handler;          //數據包捕獲描述字
141     struct bpf_program *fp;   
142     char *filter_str = "port 9000";  //過濾條件
143     
144     /*Find network devices*/
145     if((dev = pcap_lookupdev(errbuf)) == NULL){
146         printf("lookupdev failed:%s\n", errbuf);
147         exit(1);
148     }else{
149         printf("Device:%s\n", dev);
150     }
151     
152     /*Get the network number and mask of the network device*/
153     if(pcap_lookupnet(dev, &netp, &maskp, errbuf) == -1){
154         printf("%s\n", errbuf);
155         exit(1);
156     }
157     
158     /*Open network device*/
159     if((handler = pcap_open_live(dev, PCAP_DATABUF_MAX, 1, 0, errbuf)) == NULL){
160         printf("%s\n", errbuf);
161         exit(1);
162     }
163     
164     /*Compiling and setting filtering conditions*/
165     if(pcap_compile(handler, fp, filter_str, 0, maskp) == -1){
166         printf("pcap_compile error...\n");
167         exit(1);
168     }
169     if(pcap_setfilter(handler, fp) == -1){
170         printf("pcap_setfilter error...\n");
171         exit(1);
172     }
173     
174     /*Capturing and processing data packets*/
175     if(pcap_loop(handler, -1, pcap_callback, NULL) == -1){
176         printf("pcap_loop error...\n");
177         pcap_close(handler);
178     }
179     
180     return 0;    
181 }   

 


免責聲明!

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



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