网络开发工具

发布时间 2023-10-16 13:56:31作者: 常羲和

网络开发工具

1. libpcap库

1.1 libpcap库

  • 是一个网络数据捕获开发包
  • 平台独立具有强大功能
  • 是一套高层的编程接口的集合;其隐藏了操作系统的细节,可以捕获网上的所有,包括到达其他主机的数据包
  • 使用非常广泛,几乎只要涉及到网络数据包的捕获的功能,都可以用它开发
  • 开发语言为C语言

1.2 libpcap主要的作用

  • 捕获各种数据包
  • 过滤网络数据包
  • 分析网络数据包
  • 存储网络数据包

1.3 libpcap的安装

Ubantu:

sudo apt-get install libpcap-dev

注意:

1、gcc 编译时需要加上 -lpcap

2、运行时需要使用超级权限

1.4 libpcap开发流程

1、打开网络设备

2、设置过滤规则

1)先编译过滤规则 2)设置过滤规则

3、捕获数据 【重要】

4、关闭网络设备

1.5 libpcap函数

头文件:#include <pcap/pcap.h>

常用函数:

1. pcap_lookupdev()
2. pcap_open_live()
3. pcap_lookupnet()
4. pcap_compile()、pcap_setfilter()
5. pcap_next()、pcap_loop()
6. pcap_close()

1. pcap_lookupdev

获取可用的网络设备名指针

char *pcap_lookupdev(char *errbuf);

参数:errbuf:存放相关的错误信息

返回值:成功返回设备名指针,失败返回NULL

2. pcap_lookupnet

获得指定网络设备的网络号和掩码

int pcap_lookupnet(char *device,bpf_u_int32 *netp,bpf_u_int32 *maskp,char *errbuf);

参数:

  • device:网络设备名
  • netp:存放网络号的指针
  • maskp:存放掩码的指针
  • errbuf:存放出错信息

返回值:成功0,失败-1

3. pcap_open_live

打开一个用于捕获数据的网络接口

pcap_t *pcap_open_live(const char*device,int snaplen,int promise,int to_ms,char *ebuf);

参数:

  • device:网络接口的名字

  • snaplen:捕获数据包的长度

  • promise:1代表混杂模式,其他非混杂模式

  • to_ms:等待时间,单位毫秒

    • 0,表示不等待
    • 正数,表示等待的毫秒时间
    • -1,表示无限等待
  • ebuf:存储错误信息

返回值:返回libpcap句柄

4. pcap_next

捕获一个网络数据

const u_char *pcap_next(pcap_t *p,struct pcap_pkthdr *h);

参数:

  • p:libpcap句柄
  • h:数据包头

struct pcap_pkthdr结构体:

struct pcap_pkthdr
{
    struct timeval ts; // 抓到包的时间
    bpf_u_int32 caplen; // 表示抓到的数据长度
    bpf_u_int32 len; // 表示数据包的实际长度
}

返回值:捕获的数据包的地址

5. pcap_loop

循环捕获网络数据包,每收到一个数据,就会触发callback函数

int pcap_loop(pcap_t *p,int cnt,pcap_handler callback,u_char *user);

参数:

  • p:libpcap句柄

  • cnt:指定捕获数据包的个数,如果是1,就会永无休止的捕获

  • callback:回调函数

    void callback(u_char *userarg,const struct pcap_pkthdr *pkthdr,const u_char *packet);
    
    • userarg:用户传递过来的数据
    • pkthdr:帧数据的头信息(帧数据的长度)
    • packet:帧数据
  • user:向回调函数中传递的参数,通常采用NULL

6. pcap_close

关闭libpcap的句柄

void pcap_close(pcap_t *p);

7. pcap_compile

编译BPF(Berkeley Packet Filter)过滤规则的函数,BPF规则主要用于如tcpdump和wireshark等网络抓包工具中

int pcap_compile(pcap_t *p,struct bpf_program *program,char *buf,int optimize,bpf_u_int32 mask);

参数:

  • p:libpcap句柄
  • program:存放编译号的bpf过滤规则
  • buf:过滤规则字符串
  • optmize:优化 默认为0表示不优化
  • mask:网络掩码

返回值:成功0,失败-1

过滤字符串的格式(接收数据的格式):

1.mac地址过滤
ether host 11:22:33:44:55:66 表示捕获源/目的mac地址为11:22:33:44:55:66的数据包

ether src 11:22:33:44:55:66 表示捕获源mac地址为11:22:33:44:55:66的数据包
ether dst 11:22:33:44:55:66 表示捕获目的mac地址为11:22:33:44:55:66的数据包

2.IP地址过滤
host 192.168.0.2 表示捕获源/目的地址为192.168.0.2的数据报文
src host 192.168.0.2 表示捕获源地址为192.168.0.2的数据报文
dst host 192.168.0.2 表示捕获目的地址为192.168.0.2的数据报文

ip src host 192.168.0.2表示捕获源地址为192.168.0.2的IP协议数据报。
ip dst host 192.168.0.2表示捕获目的地址为192.168.0.2的IP协议数据报。

3.端口过滤
port 8000 表示捕获源/目的端口为8000的数据报文
src port 8000 表示捕获源端口为8000的数据报文
dst port 8000 表示捕获目的端口为8000的数据报文

tcp src port 8000 表示捕获源端口为8000的TCP数据报文
tcp dst port 8000 表示捕获目的端口为8000的TCP数据报文

udp src port 8000 表示捕获源端口为8000的udp数据报文
udp dst port 8000 表示捕获目的端口为8000的udp数据报文

4. 协议过滤
tcp udp tftp http icmp...
5. 多条语句关系
and表示同时成立,or表示任意一个条件为真则成立

8. pcap_setfilter

设置BPF过滤规则对象

int pcap_setfilter(pcap *p,struct bpt_program *fp);

参数:

  • p:libpcap句柄
  • fp:编译成功的BPF过滤规则指针对象

返回值:成功0,失败-1

如:

#include <pcap.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <net/ethernet.h>
#include <netinet/ether.h>
#include <netinet/ip.h>

void  recv_data_handle(u_char *userarg,const struct pcap_pkthdr *cap_hdr,const u_char *data)
{
    if(cap_hdr->caplen > 0)
    {
        printf("捕获的数据长度:%d,实际长度:%d\n",cap_hdr->caplen,cap_hdr->len);
        //分析UDP报文,输出src ip,数据
        printf("data:%s\n",data+14+20+8);
    }
    sleep(1);
}

int main(int argc,char const *argc[])
{
    //1.获取可用的网络设备名称
    char *dev = pcap_lookupdev(NULL);
    if(dev!=NULL)
    {
        printf("网络设备名称:%s\n",dev);
    }
    
    //2.打开网络设备,开始捕获数据
    pcap_t *cap = pcap_open_live(dev,128,0,0,NULL);
    if(cap == NULL)
    {
        printf("open pcap fail!")
            return -1;
    }
    
    //编译过滤规则,成功则设置过滤规则
    char filter[]="udp dst port 8001";
    struct bpf_program program;
    if(pcap_compile(cap,&program,filter,0,0xffffff00)==0)
    {
        pcap_setfilter(cap,&program);
    }
    
    //循环捕获数据
    pcap_loop(cap,-1,recv_data_handle,NULL);
    
    //关闭网络设备
    pcap_close(cap);
    return 0;
}

2. libnet库

2.1 libnet的概述

专业的构造和发送网络数据包的开发工具包

隐藏了很多底层细节,省去了很多麻烦;如缓冲区管理、字节留顺序、校验和计算等问题,使开发者把重心放到程序的开发中

2.2 libnet安装

sudo apt-get install libnet-dev

开发时引用头文件: #include <libnet.h>

编译时加: -lnet

2.3 libnet开发流程

1、数据包内存初始化

2、构造数据包, 由UDP或TCP层到MAC层的顺序构造

3、发送数据

4、释放资源

2.4 libnet的函数

2.4.1 libnet_init

数据包内存初始化及环境建立

libnet_t *libnet_init(int injection_type,char *device,char *err_buf);

参数:

  • injection_type:构造的类型
    • LIBNET_LINK 链路层注入方式,可以直接操作链路层帧
    • LIBNET_RAW4 原始IPV4注入方式,可以构造和发送原始的IPV4数据包,但不能直接操作链路层帧
    • LIBNET_RAW6 原始IPV6注入方式
    • LIBNET_RAW 原始注入方式,这是个通用的选项,可以根据需要自动选择IPV4或IPV6注入方式
    • LIBNET_LINK_ADV 高级链路层注入方式,提供了更高级的链路层控制,可以进行更灵活的链路层操作
  • device:网络接口,如"etc0",或IP地址,亦可为NULL(自动查询搜索)
  • err_buf:存放出错的信息

返回值:成功返回一个libnet句柄,失败返回NULL

2.4.2 libnet_destroy

释放资源

void libnet_destroy(libnet_t *l);

2.4.3 libnet_build_udp

构造udp数据包

【注】libnet提供的函数,不需要将小端字节转化为大端字节

libnet_ptag_t libnet_build_udp(u_int16_t sp,
                              u_int16_t dp,
                              u_int16_t len,
                              u_int16_t sum,
                              u_int8_t *payload,
                              u_int32_t payload_s,
                              libnet_t *l,
                              libnet_ptag_t ptag);

参数:

  • sp:源端口号,不需要通过htons()函数进行转换
  • dp:目的端口号
  • len:udp包总长度
  • sum:校验和,设为0,libnet自动填充
  • payload:负载(即数据),可设置为NULL
  • payload_s:负载长度,或为0
  • l:libnet句柄
  • ptag:协议部分标记,一般给定0即可

返回值:成功返回协议标记,失败返回-1

2.4.4 libnet_build_ipv4

构造一个IPV4数据包

libnet_ptag_t libnet_build_ipv4(u_int16_t,ip_len,u_int8_t tos,
                               u_int16_t id,u_int16_t frag,
                               u_int8_t ttl,u_int8_t prot,
                               u_int16 sum,u_int32_t src,
                               u_int32_t dst,u_int8_t *payload,
                               u_int32_t payload_s,
                               libnet_t *l,libnet_ptag_t ptag);

参数:

  • ip_len:ip包总长
  • tos:服务类型
  • id:ip数据包的标识,由发送方设置
  • frag:分段偏移
  • ttl:生存时间
  • prot:上层协议
  • sum:校验和,设为0,libnet自动填充
  • src:源ip地址
  • dst:目的ip地址
  • payload:负载,可设置为NULL
  • payload_s:负载长度,或为0
  • l:libnet句柄
  • ptag:协议的部分标记,一般为0,表示新建报文

返回值:成功返回协议标记;失败返回-1

2.4.5 libnet_build_ethernet

构造一个以太网数据包,即MAC报文

libnet_ptag_t libnet_build_ethernet(u_int8_t *dst,
                                   u_int8_t *src,
                                   u_int16_t type,
                                   u_int8_t *payload,
                                   u_int32_t payload_s,
                                   libnet_t *l,
                                   libnet_ptag_t ptag); 

参数:

  • dst:目的mac
  • src:源mac
  • type:上层协议类型,可以为0x0800
  • payload:负载,即附带的数据
  • payload_s:负载长度
  • l:libnet句柄
  • ptag:协议标记

返回值:成功返回协议标记,失败返回-1

2.4.6 libnet_write

发送报文数据

int libnet_write(libnet_t * l);

返回值:失败返回-1,成功返回其他

2.4.7 案例

#include <stdio.h>
#include <libnet.h>

int main(int argc,char const *argv[])
{
    if(argc<2)
    {
        printf("format:%s 网卡名\n",argc[0]);
        return -1;
    }
    //初始化libnet
    libnet_t *net = libnet_init(LIBNET_LINK_ADV,argv[1],NULL);
    if(net == NULL)
    {
        printf("libnet_init fail");
        return -1;
    }
    printf("libnet初始化成功\n");
    
    //构建数据
    //1.udp
    u_char data_buf[64]="";
    fgets(data_buf,64,stdin);
    data_buf[strlen(data_buf)-1]=0;
    int data_len=strlen(data_buf)+strlen(data_buf)%2;
    libnet_ptag_t udp_tag=libnet_build_udp(8001,8000,
                                           8+data_len,
                                          0,data_buf,
                                          data_len,net,0);
    if(udp_tag != -1)
    {
        printf("udp tag:%d\n",udp_tag);
    }
    //2.ip
    libnet_ptag_t ip_tag=libnet_build_ipv4(20+8+data_len,
                                          0,
                                           0,
                                           0,
                                           32,
                                           17,
                                           0,
                                    inet_addr("10.12.156.xxx"),                                     inet_addr("10.12.156.xxx"),
                                           NULL,
                                           0,
                                           net,
                                           0);
    if(ip_tag!=-1)
    {
        printf("ip tag:%d\n",ip_tag);
    }
    //3.mac报文
    u_char src_mac[6]={0xxx,0xxx,0xxx,0xxx,0xxx,0xxx};
    u_char dst_mac[6]={0xxx,0xxx,0xxx,0xxx,0xxx,0xxx};
    libnet_ptag_t mac_tag=libnet_build_ethernet(dst_mac,
                                               src_mac,
                                               0x800,
                                               NULL,
                                               0,
                                               net,
                                               0);
    if(mac_tag!=-1)
    {
        printf("mac tag %d\n",mac_tag);
    }
    
    //发送数据
    int ret=libnet_write(net);
    if(ret!=-1)
    {
        printf("数据发送成功\n");
    }
    //释放空间
    libnet_destroy(net);
    return 0;
}