转载自 https://blog.csdn.net/kleguan/article/details/27538031

1. Linux抓包源程序

在OSI七层模型中,网卡工作在物理层和数据链路层的MAC子层。

进行网络通信时,源主机通过socket(或其它)应用程序产生IP报文,经过各个OSI层层封装,数据包以Ethernet帧的形式进入物理层。Ethernet帧包含源主机地址、IP报文、目标地址(IP地址、端口号或映射的6字节MAC地址)和需要传送到目标主机的其它信息。

目标的MAC地址是哪里来的呢?这牵扯到一个ARP协议(介乎于网络层和数据链路层的一个协议)。第一次传送某个目的IP地址的数据的时候,先会发出一个ARP包,其MAC的目标地址是广播地址,里面说到:"谁是xxx.xxx.xxx.xxx这个IP地址的主人?"因为是广播包,所有这个局域网的主机都收到了这个ARP请求。收到请求的主机将这个IP地址和自己的相比较,如果不相同就不予理会,如果相同就发出ARP响应包。这个IP地址的主机收到这个ARP请求包后回复的ARP响应里说到:"我是这个IP地址的主人"。这个包里面就包括了他的MAC地址。以后的给这个IP地址的帧的目标MAC地址就被确定了。

 

就这样,以太网帧开始在数据链路层传播。Ethernet帧在链路层基于广播方式传播,即网段内的所有网卡都能观察该帧,但只有一个网卡通过对比6字节MAC地址发现与自己相符,然后它就接收该帧。而其它网卡则放弃该帧。(其它网卡也可以接受该帧,即实际的网络Sniffer,可进行信息窃取等操作)

网卡得到Ethernet帧后,通过网络驱动程序和上层协议对其进行还原操作,即层层剥离报文头后将数据交由目标主机的socket(或其它)应用程序使用。

如果我们需要原始的以太网帧,以便观察与目标主机进行通信的源主机信息,则可以通过建立基于PF_PACKET的socket应用程序实现。PF_PACKET协议簇允许应用程序直接获得网络驱动程序得到的数据帧信息。PF_PACKET支持SOCK_DGRAM和SOCK_RAW两种socket类型,前者利用操作系统处理报文头,而后者则将以太网帧直接交由应用程序处理。需要注意到是,只有root权限用户才能使用PF_PACKET程序。

下面的代码即可实现由应用程序直接获得以太网帧的需求。

  1. #include <unistd.h>
  2. #include <sys/socket.h>
  3. #include <sys/types.h>
  4. #include <linux/if_ether.h>
  5. #include <linux/in.h>
  6. #define BUFFER_MAX 2048
  7. int main(int argc, char *argv[]){
  8. int  SOCKET_SRC;
  9. char buf[BUFFER_MAX];
  10. int n_rd;
  11. if( (SOCKET_SRC = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))) < 0 ){
  12. fprintf(stderr, "create socket error.\n");
  13. exit(0);
  14. }
  15. while(1){
  16. n_rd = recvfrom(SOCKET_SRC, buf, BUFFER_MAX, 0, NULL, NULL);
  17. if (n_rd<46) {
  18. perror("recvfrom():");
  19. printf("Incomplete packet (errno is %d)\n",  errno);
  20. close(SOCKET_SRC);
  21. exit(0);
  22. }
  23. /* An Ethernet frame was written to buf, frame analysis can be processed here */
  24. /* Termination control */
  25. }
  26. close(SOCKET_SRC);
  27. return 0;
  28. }

2. 数据包(以太网帧)分析

一个以太网帧(RFC894)的数据格式如下图所示。

以太网帧(RFC894)格式

程序通过执行一次

  1. n_rd = recvfrom(SOCKET_SRC, buf, BUFFER_MAX, 0, NULL, NULL);

就将上面一条以太网帧写入buf中。

(1) 为了从buf中提取以太网报文头,我们可以定义如下结构体。

  1. typedef struct mac_frm_hdr {
  2. char dest_addr[6]; //destination MAC address shall be defined first.
  3. char src_addr[6];
  4. short type;
  5. }__attribute__((packed)) MAC_FRM_HDR;

定义该结构体时,需要注意以下几点。

a.各属性需按帧格式的出现顺序定义。

b.数据类型长度必须和帧中相应区域的长度相同。

c. 使用__attribute__((packed))取消编译器自动优化对齐结构体,也是为了保证属性长度和帧中相应区域的长度相同。

(2)为了提取IP报文头,根据IP报文头的格式,我们可定义如下结构体。

  1. typedef struct ip_hdr{  //header of IPV4
  2. #ifdef __LITTLE_ENDIAN_BIFIELD
  3. u_char ip_len:4, ip_ver:4;
  4. #else
  5. u_char ip_ver:4, ip_len:4;
  6. #endif
  7. u_char  ip_tos;
  8. u_short ip_total_len;
  9. u_short ip_id;
  10. u_short ip_flags;
  11. u_char  ip_ttl;
  12. u_char  ip_protocol;
  13. u_short ip_chksum;
  14. u_int32 ip_src;
  15. u_int32 ip_dest;
  16. }__attribute__((packed)) IP_HDR;

为保证各属性长度与IP报文头中一致,我们应该在定义该结构体前作如下声明。

  1. typedef int int32;
  2. typedef unsigned int u_int32;
  3. typedef unsigned char u_char;
  4. typedef unsigned short u_short;

注意事项参考定义以太网帧结构体。

(3)为了提取UDP/TCP报文头,可根据UDP/TCP报文头格式,定义相应结构体,这里不作赘述。

下面是对以太网帧进行解析的具体代码。

  1. MAC_FRM_HDR *mac_hdr; //define a Ethernet frame header
  2. IP_HDR *ip_hdr;       //define a IP header
  3. char *tmp1, *tmp2;
  4. int AND_LOGIC = 0xFF;
  5. mac_hdr = buf;  //buf is what we got from the socket program
  6. ip_hdr = buf + sizeof(MAC_FRM_HDR);
  7. //udp_hdr = buf + sizeof(MAC_FRM_HDR) + sizeof(IP_HDR); //if we want to analyses the UDP/TCP
  8. tmp1 = mac_hdr->src_addr;
  9. tmp2 = mac_hdr->dest_addr;
  10. /* print the MAC addresses of source and receiving host */
  11. printf("MAC: %.2X:%.2X:%.2X:%.2X:%.2X:%.2X==>" "%.2X:%.2X:%.2X:%.2X:%.2X:%.2X",
  12. tmp1[0]&AND_LOGIC, tmp1[1]&AND_LOGIC, tmp1[2]&AND_LOGIC,tmp1[3]&AND_LOGIC,
  13. tmp1[4]&AND_LOGIC, tmp1[5]&AND_LOGIC,
  14. tmp2[0]&AND_LOGIC, tmp2[1]&AND_LOGIC, tmp2[2]&AND_LOGIC,tmp2[3]&AND_LOGIC,
  15. tmp2[4]&AND_LOGIC, tmp2[5]&AND_LOGIC);
  16. tmp1 = (char*)&ip_hdr->ip_src;
  17. tmp2 = (char*)&ip_hdr->ip_dest;
  18. /* print the IP addresses of source and receiving host */
  19. printf("IP: %d.%d.%d.%d => %d.%d.%d.%d",
  20. tmp1[0]&AND_LOGIC, tmp1[1]&AND_LOGIC, tmp1[2]&AND_LOGIC,tmp1[3]&AND_LOGIC,
  21. tmp2[0]&AND_LOGIC, tmp2[1]&AND_LOGIC, tmp2[2]&AND_LOGIC,tmp2[3]&AND_LOGIC);
  22. /* print the IP protocol which was used by the socket communication */
  23. switch(ip_hdr->ip_protocol) {
  24. case IPPROTO_ICMP: LOGI("ICMP"); break;
  25. case IPPROTO_IGMP: LOGI("IGMP"); break;
  26. case IPPROTO_IPIP: LOGI("IPIP"); break;
  27. case IPPROTO_TCP:
  28. case IPPROTO_UDP:
  29. LOGI("Protocol: %s", ip_hdr->ip_protocol == IPPROTO_TCP ? "TCP" : "UDP");
  30. LOGI("Source port: %u, destination port: %u", udp_hdr->s_port, udp_hdr->d_port);
  31. break;
  32. case IPPROTO_RAW: LOGI("RAW"); break;
  33. default: printf("Unknown, please query in inclued/linux/in.h\n"); break;
  34. }

/*************************************************************************
    > Author: kleguan
    >  如用不当之处,欢迎指正

>  转载请注明出处
   ************************************************************************/

(转载)基于Linux C的socket抓包程序和Package分析的更多相关文章

  1. 基于Linux C的socket抓包程序和Package分析 (一)

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/guankle/article/details/27538031  測试执行平台:CentOS 6 ...

  2. Linux使用tcpdump命令抓包并使用wireshark分析

    Linux使用tcpdump命令抓包并使用wireshark分析 介绍 有时分析客户端和服务器网络交互的问题时,为了查找问题,需要分别在客户端和服务器上抓包,我们的客户端一般是windows上的,抓包 ...

  3. 【转载】linux下的usb抓包方法

    1 linux下的usb抓包方法 1.配置内核使能usb monitor: make menuconfig                   Device Drivers -->        ...

  4. UNIX网络编程——尝试探索基于Linux C的网卡抓包过程

     抓包首先便要知道经过网卡的数据其实都是通过底层的链路层(MAC),在Linux系统中我们获取网卡的数据流量其实是直接从链路层收发数据帧.至于如何进行TCP/UDP连接本文就不再赘述(之前的一段关于w ...

  5. linux下的usb抓包方法

    1 linux下的usb抓包方法1.配置内核使能usb monitor: make menuconfig                   Device Drivers -->        ...

  6. NetAnalyzer笔记 之 三. 用C++做一个抓包程序

    [创建时间:2015-08-27 22:15:17] NetAnalyzer下载地址 经过前两篇的瞎扯,你是不是已经厌倦了呢,那么这篇让我们来点有意思的吧,什么,用C#.不,这篇我们先来C++的 Wi ...

  7. tcpdump 抓包让wireshark来分析

    在linux下面用tcpdump 抓包非常方便, 但是抓的包要提取出来进行分析, 还是得用wireshark来过滤分析比较方便. 下面先介绍一下 TCPDUMP 的使用 例:tcpdump host ...

  8. linux 下检查java jar包 程序是否正常 shell

    linux 下检查java jar包 程序是否正常 shell http://injavawetrust.iteye.com BATCH_SERVER="batch.jar" NR ...

  9. Linux下如何让jar包程序在后台一直执行

    Linux下如何让Jar包程序在后台一直执行 shell命令 nohup java -jar xxx.jar & &:让程序后台执行. nohub:让程序控制台输出转移到nohub.o ...

随机推荐

  1. 基于webpack的前端工程化开发解决方案探索(二):代码分割与图片加载

    今天我们继续来进行webpack工程化开发的探索! 首先来验证上一篇文章   基于webpack的前端工程化开发解决方案探索(一):动态生成HTML  中的遗留问题:webpack将如何处理按需加载的 ...

  2. linux查看log软件

    可以使用LNAV软件查看log,还是比较方便的 安装步骤 $ sudo apt install lnav 获取帮助信息 $ lnav -h 查看日志 $ lnav 查看指定日志(后面加上绝对路径) $ ...

  3. ajax 传递数组参数

    一.ajax 传递数组参数 需要添加: traditional: true, let typeIDArr = [,,,,,]; var that = this; var url = '@Url.Act ...

  4. Spring IOC 总结

    IOC 简介 IOC是(Inversion of Control,控制反转)的简写.Spring提供IOC容器,将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的的过度程序耦合.它由DI( ...

  5. linux内核makefile概览

    linux内核makefile概览 本博客参照内核官方英文文档 linux的内核makefile主要用于编译整个内核源码,按照用户的需求生成各种目标文件,对于用户来说,编译内核时非常简单的,只需要几个 ...

  6. Jenkins服务器的安装

    Jenkins服务器的安装 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.安装jdk 详情请参考:https://www.cnblogs.com/yinzhengjie/p/1 ...

  7. spring的@Scheduled定时任务,同一时间段的定时任务只会执行一个,其余的会被阻塞,@Scheduled注解定时任务并发执行解决办法,即多线程运行定时任务

    原文:https://blog.csdn.net/qq_35937303/article/details/88851064 现有两个定时任务 @Component("aa") pu ...

  8. 【原创】STM32低功耗模式及中断唤醒(基于BMI160及RTC)的研究

    预研目标 六轴静止时,终端进入低功耗模式:六轴震动时,终端正常工作模式,从而极大减少非工作时的电流消耗. 解决方案 机器静止时,依据六轴算法,CPU进入休眠(停止)模式:机器工作时,触发六轴中断唤醒C ...

  9. Improving Sequential Recommendation with Knowledge-Enhanced Memory Networks(知识图谱)

    本文作者:杨昆霖,2015级本科生,目前研究方向为知识图谱,推荐系统,来自中国人民大学大数据管理与分析方法研究北京市重点实验室. 引言 经常上购物网站时,注意力会被首页上的推荐吸引过去,往往本来只想买 ...

  10. 项目Beta冲刺——凡事预则立

    班级:软件工程1916|W 作业:项目Beta冲刺(团队) 团队名称:Echo 作业目标:规定代码规范,明确冲刺任务与计划 目录 团队博客汇总 讨论组长是否重选的议题和结论 下一阶段需要改进完善的功能 ...