博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
c语言实现linux抓包
阅读量:2434 次
发布时间:2019-05-10

本文共 10808 字,大约阅读时间需要 36 分钟。

验证安装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官网的文章。
/* 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/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/26239116/viewspace-2058238/

你可能感兴趣的文章
智能手机Smartphone开发从零起步(五)(转)
查看>>
SEO技巧中你可能没有注意的细节(转)
查看>>
微软开始二代Windows Live 不见Cloud OS踪影
查看>>
SQL Server 7.0 入门(四)(转)
查看>>
心得分享:防火墙程序使用的几点经验(转)
查看>>
创建ISAPI扩展(转)
查看>>
Windows Server 2003 R2 Beta 2将公测(转)
查看>>
Pocket PC应用程序中使用SQL Server CE(转)
查看>>
病毒及木马预警一周播报(06.04.17~04.23)(转)
查看>>
黑客口述:我的第一台3389肉鸡的经历(转)
查看>>
关于 cleanup stack 和 two phase consturction [1](转)
查看>>
Oracle数据导入导出imp/exp (转)
查看>>
如何构建固定网(PSTN)短消息系统(转)
查看>>
Delphi文件管理(三)(转)
查看>>
关于网线的一些问题的解答(转)
查看>>
深度分析Win 2003自动升级补丁功能(转)
查看>>
使用Carbide.vs与VS.NET2003构建Symbian开发平台-S60 平台(转)
查看>>
来访者地址统计,很好的一个程序!(转)
查看>>
UpdateWindow函数 (转)
查看>>
移动通信的主要测量指标及注意事项(转)
查看>>