c語言實現linux抓包
驗證安裝libpca
引用pcap.h,確認是否能正常用libpcap開發
#include <stdio.h>
#include <pcap.h>
int main(int argc, char *argv[])
{
char *dev = argv[1];
printf("Device: %s\n", dev);
return(0);
}
然後用-lpcap選項看能不能連線上
g++ -o a a.cc a.h -lpcap
用pcap_lookupdev獲取預設裝置
int main(int argc, char *argv[])
{
char *dev, errbuf[PCAP_ERRBUF_SIZE];
dev = pcap_lookupdev(errbuf);
if (dev == NULL) {
fprintf(stderr, "%s\n", errbuf);
return(2);
}
printf("Device: %s\n", dev);
return(0);
}
開啟裝置,返回一個pcap_t型別的“handle”。呼叫這個函式叫做“開啟一個嗅探會話(sniffing session)”
pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
引數
device:pcap_lookupdev返回的裝置名,類似ethx的字串
snaplen:抓包的buffer位元組數
promisc:是否開啟混雜模式
to_ms:超時,0表述無超時
ebuf:如果出錯,會存入錯誤資訊
errbuf[PCAP_ERRBUF_SIZE];
char* dev = pcap_lookupdev(errbuf);
pcap_t* handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
if (handle == NULL) {
fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
return(2);
}
過濾器
先要呼叫pcap_compile來編譯過濾器
int pcap_compile(
pcap_t *p, 由pcap_open_live返回的“handle”
struct bpf_program *fp, 存放編譯好的過濾器的資料結構
char *str, 過濾器表示式,如“port 23”
int optimize, 表示式是否需要被“最佳化”, 取值0或1
bpf_u_int32 netmask 有pcap_lookupnet函式獲取的netmask
)
過濾器編譯好之後就可以設定到pcap_t裡
int pcap_setfilter(pcap_t *p, struct bpf_program *fp)
pcap_t *handle; /* Session handle */
char dev[] = "rl0"; /* Device to sniff on */
char errbuf[PCAP_ERRBUF_SIZE]; /* Error string */
struct bpf_program fp; /* The compiled filter expression */
char filter_exp[] = "port 23"; /* The filter expression */
bpf_u_int32 mask; /* The netmask of our sniffing device */
bpf_u_int32 net; /* The IP of our sniffing device */
if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
fprintf(stderr, "Can't get netmask for device %s\n", dev);
net = 0;
mask = 0;
}
handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
if (handle == NULL) {
fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
return(2);
}
if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
return(2);
}
if (pcap_setfilter(handle, &fp) == -1) {
fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
return(2);
}
實際抓包
struct pcap_pkthdr header; /* The header that pcap gives us */
handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
packet = pcap_next(handle, &header);
pcap_close(handle);
呼叫上面的pcap_next函式可以得到header和packet
但是pcap_next只能抓一個包,libpcap提供了迴圈抓包的函式
int pcap_loop(
pcap_t *p, //由pcap_open_live返回的“handle”
int cnt, //抓多少個包
pcap_handler callback, //抓到包後的回撥函式
u_char *user // 這個引數是使用者自定義的,libpcap會自動將這個值傳給回撥函式
)
回撥函式的形式
重點是header和packet兩個引數
void got_packet(
u_char *args, // 這個就是pcap_loop的最後一個引數u_char *user
const struct pcap_pkthdr *header,
const u_char *packet
);
header的內容
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) */
};
packet是一個u_char*,執行一塊資料,這塊資料可以被轉換為下面幾個結構體
#define SIZE_ETHERNET 14
const struct sniff_ethernet /* The ethernet header */
const struct sniff_ip /* The IP header */
const struct sniff_tcp /* The TCP header */
以及
const char *payload; /* Packet payload */
這些資料結構介紹如下
packet其實就是包頭的二進位制內容,因此結構體需要按包頭的格式編排各變數的大小和順序,然後強制型別轉換即可。
下面的結構體來自tcpdump官網的文章。
packet其實就是包頭的二進位制內容,因此結構體需要按包頭的格式編排各變數的大小和順序,然後強制型別轉換即可。
下面的結構體來自tcpdump官網的文章。
/* Ethernet addresses are 6 bytes */
#define ETHER_ADDR_LEN 6
/* Ethernet header */
struct sniff_ethernet {
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 sniff_ip {
u_char ip_vhl; /* version << 4 | header length >> 2 */
u_char ip_tos; /* type of service */
u_short ip_len; /* total length */
u_short ip_id; /* identification */
u_short ip_off; /* fragment offset field */
#define IP_RF 0x8000 /* reserved fragment flag */
#define IP_DF 0x4000 /* dont fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
u_char ip_ttl; /* time to live */
u_char ip_p; /* protocol */
u_short ip_sum; /* checksum */
struct in_addr ip_src,ip_dst; /* source and dest address */
};
#define IP_HL(ip) (((ip)->ip_vhl) & 0x0f)
#define IP_V(ip) (((ip)->ip_vhl) >> 4)
/* TCP header */
typedef u_int tcp_seq;
struct sniff_tcp {
u_short th_sport; /* source port */
u_short th_dport; /* destination port */
tcp_seq th_seq; /* sequence number */
tcp_seq th_ack; /* acknowledgement number */
u_char th_offx2; /* data offset, rsvd */
#define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4)
u_char th_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 th_win; /* window */
u_short th_sum; /* checksum */
u_short th_urp; /* urgent pointer */
};
將u_char *packet轉換成這些結構體的方法:
#define SIZE_ETHERNET 14
const struct sniff_ethernet *ethernet; /* The ethernet header */
const struct sniff_ip *ip; /* The IP header */
const struct sniff_tcp *tcp; /* The TCP header */
const char *payload; /* Packet payload */
u_int size_ip;
u_int size_tcp;
ethernet = (struct sniff_ethernet*)(packet); // 先是鏈路層的頭
ip = (struct sniff_ip*)(packet + SIZE_ETHERNET); // 鏈路層頭的後面是變長的ip包頭
size_ip = IP_HL(ip)*4; // IP_HL這個宏的作用是去ip頭第一個位元組的後4位,這4位表示IP頭的長度,單位是4byte
if (size_ip < 20) {
printf(" * Invalid IP header length: %u bytes\n", size_ip);
return;
}
tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip); // 再後面是tcp包頭
size_tcp = TH_OFF(tcp)*4;
if (size_tcp < 20) {
printf(" * Invalid TCP header length: %u bytes\n", size_tcp);
return;
}
payload = (u_char *)(packet + SIZE_ETHERNET + size_ip + size_tcp);
Variable | Location (in bytes) |
sniff_ethernet | X |
sniff_ip | X + SIZE_ETHERNET |
sniff_tcp | X + SIZE_ETHERNET + {IP header length} |
payload | X + SIZE_ETHERNET + {IP header length} + {TCP header length} |
把前面介紹的組合起來,jius簡單的抓包程式
[root@localhost cc]# cat cap.c
#include "a.h"
#define SIZE_ETHERNET 14
char errbuf[PCAP_ERRBUF_SIZE];
int addr_ntoa(int i_addr, char* str_addr)
{
if(str_addr == NULL)
{
return -1;
}
struct in_addr addr;
addr.s_addr = i_addr;
char* tmp = inet_ntoa(addr);
strcpy(str_addr, tmp);
return 0;
}
int errstr(int ret, char* str)
{
if(str != NULL) fprintf(stderr, "%s\n", str);
exit(ret);
}
int err(int ret)
{
errstr(ret, errbuf);
}
void got_packet(u_char* args, const struct pcap_pkthdr* header, const u_char* packet)
{
u_int size_ip;
u_int size_tcp;
const struct sniff_ethernet *ethernet;
const struct sniff_ip *ip;
const struct sniff_tcp *tcp;
const char *payload;
ethernet = (struct sniff_ethernet*)(packet);
ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
if((size_ip = IP_HL(ip)*4) < 20) return;
tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
if((size_tcp = TH_OFF(tcp)*4) < 20) return;
char ip_src[16];
char ip_dst[16];
addr_ntoa(ip->ip_src.s_addr, ip_src);
addr_ntoa(ip->ip_dst.s_addr, ip_dst);
printf("%s:%d>> %s:%d\n", ip_src, ntohs(tcp->th_sport), ip_dst, ntohs(tcp->th_dport));
struct ether_header *eptr = (struct ether_header *) packet;
u_int8_t* ptr;
ptr = eptr->ether_shost;
int i = ETHER_ADDR_LEN;
printf("src MAC: [");
do
{
printf("%s%x",(i == ETHER_ADDR_LEN) ? " " : ":",*ptr++);
}
while(--i>0);
printf("]\n");
ptr = eptr->ether_dhost;
i = ETHER_ADDR_LEN;
printf("dest MAC: [");
do
{
printf("%s%x",(i == ETHER_ADDR_LEN) ? " " : ":",*ptr++);
}
while(--i>0);
printf("]\n");
struct tm* local = localtime(&(header->ts));
printf("time: %d:%d:%d\n",local->tm_hour,local->tm_min,local->tm_sec);
printf("package length: %d\n", header->len);
printf("TTL: %d\n", ip->ip_ttl);
printf("\n");
}
int main()
{
char* dev;
bpf_u_int32 mask;
bpf_u_int32 net;
pcap_t *handle;
if ((dev = pcap_lookupdev(errbuf)) == NULL) err(-1);
if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) err(-1);
char strnet[16];
char strmask[16];
addr_ntoa(net, strnet);
addr_ntoa(mask, strmask);
printf("\nMonitoring: %s/%s/%s\n\n", dev, strnet, strmask);
if((handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf)) == NULL) err(-1);
if((pcap_loop(handle, 10, got_packet, "test1")) < 0) err(-1);
return 0;
}
標頭檔案裡的結構體來自tcpdump官網的文章,其實就是按照包頭的格式設定變數大小和順序。
[root@localhost cc]# cat cap.h
#include <string.h>
#include <pcap.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <unistd.h>
#include <stdlib.h>
#include <net/ethernet.h>
#include <time.h>
/* Ethernet addresses are 6 bytes */
//#define ETHER_ADDR_LEN 6
/* Ethernet header */
struct sniff_ethernet {
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 sniff_ip {
u_char ip_vhl; /* version << 4 | header length >> 2 */
u_char ip_tos; /* type of service */
u_short ip_len; /* total length */
u_short ip_id; /* identification */
u_short ip_off; /* fragment offset field */
#define IP_RF 0x8000 /* reserved fragment flag */
#define IP_DF 0x4000 /* dont fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
u_char ip_ttl; /* time to live */
u_char ip_p; /* protocol */
u_short ip_sum; /* checksum */
struct in_addr ip_src,ip_dst; /* source and dest address */
};
#define IP_HL(ip) (((ip)->ip_vhl) & 0x0f)
#define IP_V(ip) (((ip)->ip_vhl) >> 4)
/* TCP header */
typedef u_int tcp_seq;
struct sniff_tcp {
u_short th_sport; /* source port */
u_short th_dport; /* destination port */
tcp_seq th_seq; /* sequence number */
tcp_seq th_ack; /* acknowledgement number */
u_char th_offx2; /* data offset, rsvd */
#define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4)
u_char th_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 th_win; /* window */
u_short th_sum; /* checksum */
u_short th_urp; /* urgent pointer */
};
執行效果
[root@localhost cc]# ./cap
Monitoring: enp0s8/192.168.1.0/255.255.255.0
192.168.1.66:22>> 192.168.1.1:52672
src MAC: [ 8:0:27:80:ff:b0]
dest MAC: [ a:0:27:0:0:0]
time: 15:25:4
package length: 154
TTL: 64
192.168.1.1:52672>> 192.168.1.66:22
src MAC: [ a:0:27:0:0:0]
dest MAC: [ 8:0:27:80:ff:b0]
time: 15:25:4
package length: 60
TTL: 64
192.168.1.66:22>> 192.168.1.1:52672
src MAC: [ 8:0:27:80:ff:b0]
dest MAC: [ a:0:27:0:0:0]
time: 15:25:4
package length: 378
TTL: 64
192.168.1.66:22>> 192.168.1.1:52672
src MAC: [ 8:0:27:80:ff:b0]
dest MAC: [ a:0:27:0:0:0]
time: 15:25:4
package length: 234
TTL: 64
...
...
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26239116/viewspace-2058238/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 掃雷--C語言實現C語言
- C語言實現DES加密C語言加密
- c語言實現階乘C語言
- C語言實現TCP通訊C語言TCP
- 高精度加法(C語言實現)C語言
- c語言實現this指標效果C語言指標
- C語言實現順序表C語言
- C語言實現連結串列C語言
- 漢諾塔(hanoi) C語言實現C語言
- RTree原始碼——C語言實現原始碼C語言
- C語言實現出入棧操作C語言
- C語言實現檔案加密C語言加密
- 利用棧實現佇列(C語言實現)佇列C語言
- 用fiddler實現手機抓包
- C語言實現推箱子游戲C語言
- C語言__LINE__實現原理C語言
- 排序演算法-C語言實現排序演算法C語言
- C語言實現桌面貪吃蛇C語言
- 高精度減法(C語言實現)C語言
- C語言實現繼承多型C語言繼承多型
- C語言實現萬年曆程式C語言
- C語言實現A*演算法薦C語言演算法
- C語言異常與斷言介面的實現C語言
- Linux-C語言LinuxC語言
- Charles抓包實踐
- C 語言實現泛型 swap 函式泛型函式
- C語言如何實現繼承及容器C語言繼承
- Object-C語言Block的實現方式ObjectC語言BloC
- C語言實現矩陣螺旋輸出C語言矩陣
- 探秘掃雷遊戲的C語言實現遊戲C語言
- 快速排序演算法(C語言實現)排序演算法C語言
- Linux下跨語言呼叫C++實踐LinuxC++
- 轉:linux tcpdump抓包方法LinuxTCP
- C語言呼叫 Java(Linux)C語言JavaLinux
- fiddler 實現 微信(PC端)小程式抓包
- c 語言實現 tcp/udp 伺服器功能TCPUDP伺服器
- C語言實現多級濾波—乾貨C語言
- C語言如何實現泛型程式設計?C語言泛型程式設計