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

首先贴上源代码

#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>的声明)


image-20201020193534496

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


image-20201020193632143
其中比较常用的是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命令都可以捕获到数据包

运行结果如下

image-20201020200828693

  • 8
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
关于嗅探器的源代码#include <winsock2.h> #include <windows.h> #include <ws2tcpip.h> #include <stdio.h> #include <stdlib.h> #pragma comment(lib,"ws2_32.lib") #define MAX_HOSTNAME_LAN 255 #define SIO_RCVALL _WSAIOW(IOC_VENDOR,1) #define MAX_ADDR_LEN 16 struct ipheader { unsigned char ip_hl:4; unsigned char ip_v:4; unsigned char ip_tos; unsigned short int ip_len; unsigned short int ip_id; unsigned short int ip_off; unsigned char ip_ttl; unsigned char ip_p; unsigned short int ip_sum; unsigned int ip_src; unsigned int ip_dst; }; typedef struct tcpheader { unsigned short int sport; unsigned short int dport; unsigned int th_seq; unsigned int th_ack; unsigned char th_x:4; unsigned char th_off:4; unsigned char Flags; unsigned short int th_win; unsigned short int th_sum; unsigned short int th_urp; }TCP_HDR; typedef struct udphdr { unsigned short sport; unsigned short dport; unsigned short len; unsigned short cksum; }UDP_HDR; void main(){ SOCKET sock; WSADATA wsd; DWORD dwBytesRet; unsigned int optval = 1; unsigned char *dataudp,*datatcp; int i,pCount=0,lentcp, lenudp; SOCKADDR_IN sa,saSource, saDest; struct hostent FAR * pHostent; char FAR name[MAX_HOSTNAME_LAN]; char szSourceIP[MAX_ADDR_LEN], szDestIP[MAX_ADDR_LEN],RecvBuf[65535] = {0}; struct udphdr *pUdpheader; struct ipheader *pIpheader; struct tcpheader *pTcpheader; WSAStartup(MAKEWORD(2,1),&wsd); if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_IP))==SOCKET_ERROR) exit(1); gethostname(name, MAX_HOSTNAME_LAN); pHostent = gethostbyname(name); sa.sin_family = AF_INET; sa.sin_port = htons(6000); memcpy(&sa.sin_addr.S_un.S_addr, pHostent->h_addr_list[0], pHostent->h_length); bind(sock, (SOCKADDR *)&sa, sizeof(sa)); if ((WSAGetLastError())==10013) exit(1); WSAIoctl(sock, SIO_RCVALL, &optval, sizeof(optval), NULL, 0, &dwBytesRet, NULL, NULL); pIpheader = (struct ipheader *)RecvBuf; pTcpheader = (struct tcpheader *)(RecvBuf+ sizeof(struct ipheader )); pUdpheader = (struct udphdr *) (RecvBuf+ sizeof(struct ipheader )); while (1){ memset(RecvBuf, 0, sizeof(RecvBuf)); recv(sock, RecvBuf, sizeof(RecvBuf), 0); saSource.sin_addr.s_addr = pIpheader->ip_src; strncpy(szSourceIP, inet_ntoa(saSource.sin_addr), MAX_ADDR_LEN); saDest.sin_addr.s_addr = pIpheader->ip_dst; strncpy(szDestIP, inet_ntoa(saDest.sin_addr), MAX_ADDR_LEN); lentcp =(ntohs(pIpheader->ip_len)-(sizeof(struct ipheader)+sizeof(struct tcpheader))); lenudp =(ntohs(pIpheader->ip_len)-(sizeof(struct ipheader)+sizeof(struct udphdr))); if((pIpheader->ip_p)==IPPROTO_TCP&&lentcp!=0){ printf("*******************************************\n"); pCount++; datatcp=(unsigned char *) RecvBuf+sizeof(struct ipheader)+sizeof(struct tcpheader); printf("-TCP-\n"); printf("\n%s\n",szDestIP); printf("\n%i\n",ntohs(pTcpheader->dport)); printf("datatcp address->%x\n",datatcp); printf("size of ipheader->%i\n",sizeof(struct ipheader)); printf("size of tcpheader->%i\n",sizeof(struct tcpheader)); printf("size of the hole packet->%i\n",ntohs(pIpheader->ip_len)); printf("\nchar Packet%i [%i]=\"",pCount,lentcp-1); for (i=0;i<lentcp;i++){ printf("\\x%.2x",*(datatcp+i)); if (i==0) printf("\"\n\""); } printf("\";\n\n\n"); for (i=0;i<lentcp;i++){ if( *(datatcp+i)<=127&&*(datatcp+i)>=20) printf("%c",*(datatcp+i)); else printf("."); } printf("\n\n*******************************************\n"); } if((pIpheader->ip_p)==IPPROTO_UDP&&lentcp!=0){ pCount++; dataudp=(unsigned char *) RecvBuf+sizeof(struct ipheader)+sizeof(struct udphdr); printf("-UDP-\n"); printf("\n%s\n",szDestIP); printf("\n%d\n",ntohs(pTcpheader->dport)); printf("UDP%x\n",dataudp); printf("IP%i\n",sizeof(struct ipheader)); printf("UDP%i\n",sizeof(struct udphdr)); printf("%i\n",ntohs(pIpheader->ip_len)); printf("\nchar Packet%i [%i]=\"",pCount,lenudp-1); for (i=0;i<lenudp;i++){ printf("\\x%.2x",*(dataudp+i)); if (i==0) printf("\"\n\""); } printf("\";\n\n\n"); for (i=0;i<lenudp;i++){ if( *(dataudp+i)<=127&&*(dataudp+i)>=20) printf("%c",*(dataudp+i)); else printf("."); } printf("\n\n*******************************************\n"); } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值