Libpcap 作用 安裝 socket 原始套接字 pcap_lookupdev pcap_open_live pcap_lookupnet pcap_compile pcap_loop

                                      粉絲不過W

socket 原始套接字回顧

   原始套接字

      開發者可發送任意的數據包到網上

      開發網絡攻擊等特殊軟件

      需要開發者手動組織數據、各種協議包頭、校驗和計算

   創建方法:

//創建數據鏈路層的原始套接字
int sock_raw_fd = 0;
sock_raw_fd = socket(PF_PACKET, SOCK_RAM, htons(ETH_P_ALL));

 Libpcap 概念

      一個網絡數據捕獲開發包

     平臺獨立具有強大功能

    一套高層的編程接口的集合;隱藏了操作系統的細節,可捕獲網上的所有,包括到達其他主機的數據包

      使用非常廣泛,幾乎只要涉及到網絡數據包的捕獲的功能,都可以用它開發,如 wireshark

      開發語言爲 C 語言,也有基於其它語言的開發包,如 Python 語言的開發包 pycap

  Libpcap 主要的作用

        捕獲各種數據包

          如:網絡流量統計

       過濾網絡數據包

         如:過濾掉本地上的一些數據,類似防火牆

      分析網絡數據包

         如:分析網絡協議,數據的採集

     存儲網絡數據包

        如:保存捕獲的數據以爲將來進行分析

Libpcap 的安裝:

sudo apt-get install libpcap-dev

libpcap 開發實例

   libpcap 函數庫開發應用程序基本步驟:

      打開網絡設備

     設置過濾規則

     捕獲數據

     關閉網絡設備

  捕獲網絡數據包常用函數:

     pcap_lookupdev( )

    pcap_open_live()

    pcap_lookupnet( )

    pcap_compile( )

    pcap_setfilter( )

     pcap_next( )

     pcap_loop( )

      pcap_close( )

pcap_lookupdev

/*
 *function:
 *  得到可用的網絡設備名指針
 *parameter:
 *  errbuf: 存放相關的錯誤信息
 *return:
 *  成功:設備名指針
 *  失敗: NULL
 */
char *pcap_lookupdev(char *errbuf)

  如: 

    char *dev = NULL;
    char err_buf[100] = "";
    dev = pcap_lookupdev(err_buf);
    if(NULL == dev)
    {
        perror("pcap_lookupdev");
        exit(-1);
    } 

 pcap_open_live

/*
 *function:
 *  打開一個用於捕獲數據的網絡接口
 *parameter:
 *  device:  網絡接口的名字
 *  snaplen:捕獲數據包的長度
 *  promise:1 代表混雜模式, 其它非混雜模式
 *  to_ms:  等待時間
 *  ebuf:   存儲錯誤信息
 *return:
 *  返回一個 Libpcap 句柄
 */
pcap_t *pcap_open_live(const char *device,int snaplen,int promisc,int to_ms,har *ebuf);

  如:

char error_content[PCAP_ERRBUF_SIZE] = "";
pcap_t *pcap_handle = NULL;
pcap_handle = pcap_open_live("eth0", 1024, 1, 0, error_content);

 pcap_lookupnet

/*
 *function:
 *  獲得指定網絡設備的網絡號和掩碼
 *parameter:
 *  device:網絡設備名
 *  netp:存放網絡號的指針
 *  maskp:存放掩碼的指針
 *  errbuf:存放出錯信息
 *retrun:
 *  成功: 0
 *  失敗: -1
 */
int pcap_lookupnet(char *device, buf_u_int32 *netp, bpf_u_int32 *maskp, char *errbuf);
unsigned int net_ip;    //網絡地址
unsigned int net_mask;    //子網掩碼

res = pcap_lookupnet(dev, &net_ip, &net_mask, error_content);
if(res == -1)
{
    perror("pcap_lookupnet");
    exit(-1);
}

 pcap_compile

/*
 *function:
 *  編譯 BPF 過濾規則
 *parameter:
 *  p:       Libpcap 句柄
 *  program: bpf 過濾規則
 *  buf:     過濾規則字符串
 *  optimize:優化
 *  mask:    掩碼
 *retrun:
 *  成功: 0
 *  失敗: -1
 */
int pcap_compile(pcap_t *p, struct bpf_program *program, char *buf, int optimize, bpf_u_int32 mask);

  如:

struct buf_program buf_filter;
char *bpf_filter_string = "arp or ip";

//編譯BPF過濾規則
if(pcap_compile(pcap_handle, &bpf_filter, bpf_filter_string, 0, 0xffffff00) < 0)
{
    perror("pcap_compile");
}

 pcap_setfilter

/*
 *function:
 *  設置 BPF 過濾規則
 *parameter:
 *  p:Libpcap 句柄
 *  fp:BPF 過濾規則
 *return:
 *  成功: 0
 *  失敗: -1
 */
int pcap_setfilter(pcap *p, struct bpf_program *fp);
//設置過濾規則
if(pcap_setfilter(pcap_handle, &bpf_filter) < 0)
{
    perror("pcap_setfilter");
}

 pcap_next

/*
 *function:
 *  捕獲一個網絡數據包
 *parameter:
 *  p:Libpcap 句柄
 *  h:數據包頭
 *return:
 *  捕獲的數據包的地址
 */
const u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h);
struct pcap_pkthdr protocol_header;
unsigned char *p_packet_content = NULL;
p_packet_content = pcap_next(pcap_handle, &protocol_header);

 pcap_loop

/*
 *function:
 *  循環捕獲網絡數據包,直到遇到錯誤或者滿足退出條件;
 *每次捕獲一個數據包就會調用 callback 指示的回調函數,所以可在回調函數中進行數據包的處理操作
 *parameter:
 *  p:       Libpcap 句柄
 *  cnt:     指定捕獲數據包的個數,如 -1,會永無休止的捕獲
 *  callback:回調函數
 *  user:    向回調函數中傳遞的參數
 */
int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user);
if(pcap_loop(pcap_handle, -1, ethernet_protocol_callback, NULL) < 0)
{
    perror("pcap_loop");
}

 pcap_close

/*
 *function:
 *  關閉 Libpcap 操作,並銷燬相應的資源
 *parameter:
 *  p:關閉的 Libpcap 句柄
 */
void pcap_close(pcap_t *p);
pcap_close(pcap_handle);

捕獲第一個網絡數據包

/*
 *function:
 *  通過捕獲一個網絡數據包,然後對其進行數據的解析分析
 */
#include <stdio.h>
#include <stdlib.h>
#include <pcap.h>
#include <arpa/inet.h>
#include <time.h>

#define BUFSIZE 1514

struct ether_header
{
    unsigned char ether_dhost[6];	//目的mac
    unsigned char ether_shost[6];	//源mac
    unsigned short ether_type;		//以太網類型
};

/*
 *function:
 *  通過使用libpcap接收一個數據包,然後對數據包進行解析
 */
int main(int argc,char *argv[])
{
    pcap_t * pcap_handle = NULL;
    char error_content[100] = "";	                // 出錯信息
    unsigned char *p_packet_content = NULL;		// 保存接收到的數據包的起始地址
    unsigned char *p_mac_string = NULL;			// 保存mac的地址,臨時變量
    unsigned short ethernet_type = 0;			// 以太網類型
    char *p_net_interface_name = NULL;	    	// 接口名字
    struct pcap_pkthdr protocol_header;
    struct ether_header *ethernet_protocol;

    //獲得接口名
    p_net_interface_name = pcap_lookupdev(error_content);
    if(NULL == p_net_interface_name)
    {
        perror("pcap_lookupdev");
        exit(-1);
    }

    //打開網絡接口
    pcap_handle = pcap_open_live(p_net_interface_name, BUFSIZE, 1, 0, error_content);
    p_packet_content = pcap_next(pcap_handle, &protocol_header);

    printf("----------------------------------------------------\n");
    printf("capture a Packet from p_net_interface_name :%s\n", p_net_interface_name);
    printf("Capture Time is :%s", ctime((const time_t *)&protocol_header.ts.tv_sec));
    printf("Packet Lenght is :%d\n",protocol_header.len);

    //分析以太網中的 源mac、目的mac
    ethernet_protocol = (struct ether_header *)p_packet_content;

    p_mac_string = (unsigned char *)ethernet_protocol -> ether_shost;//獲取源mac
    printf("Mac Source Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(p_mac_string + 0),
                                                                   *(p_mac_string + 1),
                                                                   *(p_mac_string + 2),
                                                                   *(p_mac_string + 3),
                                                                   *(p_mac_string + 4),
                                                                   *(p_mac_string + 5));

    p_mac_string = (unsigned char *)ethernet_protocol->ether_dhost;//獲取目的mac
    printf("Mac Destination Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(p_mac_string + 0),    
                                                                        *(p_mac_string + 1),
                                                                        *(p_mac_string + 2),
                                                                        *(p_mac_string + 3),
                                                                        *(p_mac_string + 4),
                                                                        *(p_mac_string + 5));

    //獲得以太網的數據包的地址,然後分析出上層網絡協議的類型
    ethernet_type = ntohs(ethernet_protocol->ether_type);
    printf("Ethernet type is :%04x\t",ethernet_type);
    switch(ethernet_type)
    {
        case 0x0800 : 
            printf("The network layer is IP protocol\n");    //ip
            break;    
        case 0x0806 :
            printf("The network layer is ARP protocol\n");    //arp
            break;
        case 0x0835 :
            printf("The network layer is RARP protocol\n");    //rarp
            break;
        default : 
            printf("The network layer unknow!\n");
            break;
    }

    pcap_close(pcap_handle);
    return 0;
}

      gcc 編譯時需要加上-lpcap
       運行時需要使用超級權限
 

 捕獲指定類型數據包,如 ARP

/*
 *function:
 *  安排規則,接收一個數據包
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <arpa/inet.h>
#include <pcap.h>

#define BUFSIZE 1514

struct ether_header
{
    unsigned char ether_dhost[6];	//目的mac
    unsigned char ether_shost[6];	//源mac
    unsigned short ether_type;		//以太網類型
};

int main(int argc,char *argv[])
{
    pcap_t * pcap_handle;
    int ret = 0;
    char error_content[512] = "";	// 出錯信息
    const unsigned char *p_packet_content = NULL; // 保存接收到的數據包的起始地址
    unsigned char *p_mac_string = NULL;			// 保存mac的地址,臨時變量
    unsigned short ethernet_type = 0;			// 以太網類型
    char *p_net_interface_name = "eth0";		// 接口名字
    struct pcap_pkthdr protocol_header;
    struct ether_header *ethernet_protocol = NULL;
    struct bpf_program bpf_filter;
    char *bpf_filter_string = "arp or ip";
    bpf_u_int32 netp = 0, maskp = 0;

    //打開網絡接口
    p_net_interface_name = pcap_lookupdev(error_content);
    if(NULL == p_net_interface_name)
    {
        perror("pcap_lookupdev");
		exit(-1);
    }

    pcap_handle = pcap_open_live(p_net_interface_name, 1024, 1, 0, error_content);

    //獲得網絡號和掩碼
    ret = pcap_lookupnet(p_net_interface_name, &netp, &maskp, error_content);
    if(ret == -1)
    {
        perror("pcap_lookupnet");
        exit(-1);
    }

    //編譯BPF過濾規則
    if(pcap_compile(pcap_handle, &bpf_filter, bpf_filter_string, 0, maskp) < 0)
    {
        perror("pcap_compile");
    }

    //設置過濾規則
    if(pcap_setfilter(pcap_handle,&bpf_filter) < 0)
    {
        perror("pcap_setfilter");
    }

    while(1)
    {
        //所捕獲的數據包的地址
        p_packet_content = pcap_next(pcap_handle, &protocol_header);
        printf("-----------------------------------------------\n");
        printf("capture a Packet from p_net_interface_name :%s\n",p_net_interface_name);
        printf("Capture Time is :%s",ctime((const time_t *)&protocol_header.ts.tv_sec));
        printf("Packet Lenght is :%d\n",protocol_header.len);
        //分析以太網中的 源mac、目的mac
        ethernet_protocol = (struct ether_header *)p_packet_content;  //以太網幀頭部
        p_mac_string = (unsigned char *)ethernet_protocol->ether_shost;//獲取源mac
        printf("Mac Source Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(p_mac_string + 0),
                                                                        *(p_mac_string + 1),
                                                                        *(p_mac_string + 2),
                                                                        *(p_mac_string + 3),
                                                                        *(p_mac_string + 4),
                                                                        *(p_mac_string + 5));
        p_mac_string = (unsigned char *)ethernet_protocol->ether_dhost;//獲取目的mac
        printf("Mac Destination Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(p_mac_string + 0),
                                                                            *(p_mac_string + 1),
                                                                            *(p_mac_string + 2),
                                                                            *(p_mac_string + 3),
                                                                            *(p_mac_string + 4),
                                                                            *(p_mac_string + 5));

        //獲得以太網的數據包的地址,然後分析出上層網絡協議的類型
        ethernet_type = ntohs(ethernet_protocol->ether_type);
        printf("Ethernet type is :%04x\n",ethernet_type);
        switch(ethernet_type)
        {
            case 0x0800:
                printf("The network layer is IP protocol\n");    //ip
                break;
            case 0x0806:
                printf("The network layer is ARP protocol\n");    //arp
                break;
            case 0x0835:
                printf("The network layer is RARP protocol\n");    //rarp
                break;
            default:
                printf("The network layer unknow!!!\n");
                break;
        }
    }
    pcap_close(pcap_handle);
    return 0;
}

捕獲多個網絡數據包

/*
 *function:
 *  通過回調函數的方式,來抓取多個網絡數據包
 */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pcap.h>
#include <arpa/inet.h>
#include <unistd.h>

#define BUFSIZE 1514

struct ether_header
{
    unsigned char ether_dhost[6];	//目的mac
    unsigned char ether_shost[6];	//源mac
    unsigned short ether_type;		//以太網類型
};

void ethernet_protocol_callback(unsigned char *argument,
                                const struct pcap_pkthdr *packet_heaher,
                                const unsigned char *packet_content);

int main(int argc, char *argv[])
{
    char error_content[100];	//出錯信息
    pcap_t * pcap_handle;
    unsigned char *mac_string;
    unsigned short ethernet_type;    //以太網類型
    unsigned int net_ip;			//網絡地址
    unsigned int net_mask;			//子網掩碼
    char *net_interface = NULL;	    //接口名字
    int res = 0;
    struct pcap_pkthdr protocol_header;
    struct ether_header *ethernet_protocol;
    struct bpf_program bpf_filter;
    char bpf_filter_string[] = "ip";

    //獲取網絡接口
    net_interface = pcap_lookupdev(error_content);
    if(NULL == net_interface)
    {
        perror("pcap_lookupdev");
        exit(-1);
    }

    res = pcap_lookupnet(net_interface, &net_ip, &net_mask, error_content);
    if(-1 == res)
    {
        perror("pcap_loopupnet");
        exit(-1);
    }

    //打開網絡接口
    pcap_handle = pcap_open_live(net_interface, BUFSIZE, 1, 0, error_content);
    //編譯BPF過濾規則
    pcap_compile(pcap_handle, &bpf_filter, bpf_filter_string, 0, net_mask);
    pcap_setfilter(pcap_handle, &bpf_filter);//設置過濾規則

    if(pcap_loop(pcap_handle,-1,ethernet_protocol_callback,NULL) < 0)
    {
        perror("pcap_loop");
    }

    pcap_close(pcap_handle);
	return 0;
}

// 回調函數
void ethernet_protocol_callback(unsigned char *argument,
                                const struct pcap_pkthdr *packet_heaher,
                                const unsigned char *packet_content)
{
    unsigned char *mac_string;
    struct ether_header *ethernet_protocol;
    unsigned short ethernet_type;	//以太網類型

    printf("----------------------------------------------------\n");
    printf("%s\n", ctime((time_t *)&(packet_heaher->ts.tv_sec))); //轉換時間
    ethernet_protocol = (struct ether_header *)packet_content;

    mac_string = (unsigned char *)ethernet_protocol->ether_shost;   //獲取源mac地址
    printf("Mac Source Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac_string + 0),
                                                                   *(mac_string + 1),
                                                                   *(mac_string + 2),
                                                                   *(mac_string + 3),
                                                                   *(mac_string + 4),
                                                                   *(mac_string + 5));

    mac_string = (unsigned char *)ethernet_protocol->ether_dhost;  //獲取目的mac
    printf("Mac Destination Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac_string + 0),
                                                                        *(mac_string + 1),
                                                                        *(mac_string + 2),
                                                                        *(mac_string + 3),
                                                                        *(mac_string + 4),
                                                                        *(mac_string + 5));

    ethernet_type = ntohs(ethernet_protocol->ether_type);    //獲得以太網的類型
    printf("Ethernet type is :%04x\n",ethernet_type);
    switch(ethernet_type)
    {
        case 0x0800:
            printf("The network layer is IP protocol\n");    //ip
            break;
        case 0x0806:
            printf("The network layer is ARP protocol\n");    //arp
            break;
        case 0x0835:
            printf("The network layer is RARP protocol\n");    //rarp
            break;
        default:
            break;
    }

    usleep(800*1000);
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章