欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

1、Linux下C语言 编写简单的网络嗅探器: 基本的数据包抓取分析

程序员文章站 2022-07-04 10:27:28
...

首先贴上源代码

#include <stdio.h>
#include <errno.h>  
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>  
#include <arpa/inet.h>
#include <cstdlib>
//协议相关结构体
#include <netinet/if_ether.h>
int main(){
    unsigned char *buffer;//数据缓冲区
    buffer = (unsigned char *)malloc(sizeof(unsigned char *) * 65536);// 给缓冲区分配足够的空间i    
    struct sockaddr saddr; //地址结构体
    int saddr_size=sizeof(saddr);// 记录结构体大小
    ether_header *eth;
    int sd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); //创建一个原始套接字,ETH_P_ALL 表示侦听负载为 IP 数据报的以太网帧
    //进入循环监听模式
    while(1){
      int data_size = recvfrom(sd, buffer, 65536, 0, &saddr,(socklen_t*)&saddr_size); /* 读取以太网数据帧的内容 */                
      eth = (struct ether_header *)buffer;  //转换成以太网帧头结构体
      //打印相关信息
      printf("Source MAC address: "
           "%02x:%02x:%02x:%02x:%02x:%02x\n",
           eth->ether_shost[0],eth->ether_shost[1],eth->ether_shost[2],
           eth->ether_shost[3],eth->ether_shost[4],eth->ether_shost[5]); 
      printf("Destination MAC address: "
           "%02x:%02x:%02x:%02x:%02x:%02x\n",
           eth->ether_dhost[0],eth->ether_dhost[1],eth->ether_dhost[2],
           eth->ether_dhost[3],eth->ether_dhost[4],eth->ether_dhost[5]); 
      printf("the eth protol is %02x\n",ntohs(eth->ether_type));

    }
 }


代码分析

头文件

#include <stdio.h>   -->标准IO流 printf等函数就在这里
#include <unistd.h>
#include <sys/socket.h> --> socket 函数
#include <sys/types.h>  
#include <arpa/inet.h>
#include <cstdlib>
//协议相关结构体
#include <netinet/if_ether.h>  ---> 以太帧头ether_header 结构体存储位置

头文件的作用可以自行百度, 不过多介绍了

变量定义

unsigned char *buffer; 

buffer = (unsigned char *)malloc(sizeof(unsigned char *) * 65536);

首先创建一个指针, 分配足量的空间, 让指针指向这个空间。 从socket中获取到数据包后,需要对其分析,这块内存空间就是用来临时存放数据的。

注: malloc 分配空间需要free释放, 但测试代码没有终止条件,只能ctrl+c强制退出 … 后期改进吧


struct sockaddr saddr; //地址结构体
int saddr_size=sizeof(saddr);// 记录结构体大小

用于recvfrom函数


ether_header *eth;

以太帧头部, 这个结构体定义在net/ethernet.h 下, 对应linux目录为 usr/include/net/ethernet.h

(注: netinet/if_ether.h 虽然没有这个结构体, 但是 源代码中有 #include<net/ethernet.h>的声明)


1、Linux下C语言 编写简单的网络嗅探器: 基本的数据包抓取分析

这个结构体十分简单, ETH_ALEN 可以在源代码中看出值为6,unsigned char 为8bit长度 ,6*8 对应MAC地址48个二进制位;h_dest和h_source分别存储源、目的MAC地址。 h_proto 是包类型, 具体值如下:


1、Linux下C语言 编写简单的网络嗅探器: 基本的数据包抓取分析
其中比较常用的是0x0800 表示上层协议为IP, 0x0806,表示其为ARP帧


int sd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 

int socket(int af, int type, int protocol);

af: addressfamily 地址族, PF_PACKET 是一个 可以处理所有协议的协议系列

type: SOCK_RAW 原始套接字,可以接收本机网卡上的数据帧或者数据包

protocol: ETH_P_ALL 抓取全部的数据包 (可以指定其他类型, 具体定义在 /usr/include/linux/if_ether.h中)

创建一个原始套接字, 用于抓取所有数据包

主循环

    while(1){
      int data_size = recvfrom(sd, buffer, 65536, 0, &saddr,(socklen_t*)&saddr_size); /* 读取以太网数据帧的内容 */                
      eth = (struct ether_header *)buffer;  //转换成以太网帧头结构体
      //打印相关信息
      printf("Source MAC address: "
           "%02x:%02x:%02x:%02x:%02x:%02x\n",
           eth->ether_shost[0],eth->ether_shost[1],eth->ether_shost[2],
           eth->ether_shost[3],eth->ether_shost[4],eth->ether_shost[5]); 
      printf("Destination MAC address: "
           "%02x:%02x:%02x:%02x:%02x:%02x\n",
           eth->ether_dhost[0],eth->ether_dhost[1],eth->ether_dhost[2],
           eth->ether_dhost[3],eth->ether_dhost[4],eth->ether_dhost[5]); 
      printf("the eth protol is %02x",ntohs(eth->ether_type));

    }

/*
   过程中遇到的问题总结;
     1.printf("the eth protol is %02x",ntohs(eth->ether_type));
          eth->ether_type 因为 操作系统的实现不同, 需要首先进行一次转换, 才能正确输出 ( 大端序 小端序  网络字节序 )       

     2.  int recvfrom(int s, void *buf, int len, unsigned int flags, struct sockaddr *from,int *fromlen);
          recv()用来接收远程主机经指定的socket 传来的数据, 并把数据存到由参数buf 指向的内存空间, 
          参数len 为可接收数据的最大长度. 
          参数flags 一般设0, 其他数值定义请参考recv(). 
          参数from 用来指定欲传送的网络地址, 结构sockaddr 请参考bind().    可以简单理解为存储发送源IP地址
          参数fromlen 为sockaddr 的结构长度.

          返回值:成功则返回接收到的字符数, 失败则返回-1, 错误原因存于errno 中. 

          https://blog.csdn.net/danelumax2/article/details/8567173

*/
  1. 如注释所写,当socket捕获到一个数据包时,recvfrom 会将其存入缓冲区中, 并将这个数据包的大小作为返回值返回(后期分析高层 协议的时候很有用处)
  2. eth = (struct ether_header *)buffer 让eth结构体指向数据包的头部, 数据包是按网络层顺序嵌套而成的, 最后封装的也就是MAC层, 使用ether_header结构体就能获得相应的MAC地址等信息
  3. printf( … ) 前面说过, MAC地址是存储在一个uchar数组中, print分别打印即可
  4. printf(“the eth protol is %02x”,ntohs(eth->ether_type)); 使用了ntohs()函数 ,作用是将一个16位数由网络字节顺序转换为主机字节顺序

关于字节序和网络序转换:

http://blog.chinaunix.net/uid-26727991-id-3756256.html

代码运行:

使用 gcc -o test 文件名

./test执行 即可进入监听状态

浏览器访问网页或使用ping命令都可以捕获到数据包

运行结果如下

1、Linux下C语言 编写简单的网络嗅探器: 基本的数据包抓取分析