Ubuntu 20.04。
文章目錄
起步
一、下載libpcap 庫
前往http://www.tcpdump.org/release/ ,下載格式為xxxx.tar.gz的文件。
二、安裝
- 解壓文件到你的當前目錄
tar zxvf xxxx.tar.gz
- 進入剛才解開的libpcap目錄,生成Makefile文件
sudo ./configure
配置過程如果出現錯誤,
請查看你是否安裝了所有的依賴包bison, m4, flex以及libpcap-dev(安裝方法 sudo apt install ****)
嘗試運行 sudo apt install bison m4 flex libpcap-dev -y
- 將生成的庫安裝到系統默認目錄中。此目錄為 /usr/lib ,如果需要修改,可以修改文件Makefile 的 prefix。
sudo make install
三、使用
- 編譯選項:
gcc 源文件.c -lpcap -o 輸出文件名
-
Warning不用管:
-
測試程序:
#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;
}
- 測試程序運行結果:
完整程序 (們)
注意,
解析報文的時候,
結構體很可能因為字節對齊出現問題!
如下:
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;
}
}
時間戳處理結果: