粉絲不過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);
}