版权声明:本文为博主原创文章,未经博主同意不得转载。 https://blog.csdn.net/guankle/article/details/27538031



測试执行平台:CentOS 6.5发行版,内核版本号3.11

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程序。

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

#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/if_ether.h>
#include <linux/in.h> #define BUFFER_MAX 2048 int main(int argc, char *argv[]){
int SOCKET_SRC;
char buf[BUFFER_MAX];
int n_rd; if( (SOCKET_SRC = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))) < 0 ){
fprintf(stderr, "create socket error.\n");
exit(0);
}
while(1){
n_rd = recvfrom(SOCKET_SRC, buf, BUFFER_MAX, 0, NULL, NULL);
if (n_rd<46) {
perror("recvfrom():");
printf("Incomplete packet (errno is %d)\n", errno);
close(SOCKET_SRC);
exit(0);
}
/* An Ethernet frame was written to buf, frame analysis can be processed here */
/* Termination control */
}
close(SOCKET_SRC);
return 0;
}

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

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

以太网帧(RFC894)格式

程序通过执行一次

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

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

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

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

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

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

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

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

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

typedef struct ip_hdr{ 	//header of IPV4
#ifdef __LITTLE_ENDIAN_BIFIELD
u_char ip_len:4, ip_ver:4;
#else
u_char ip_ver:4, ip_len:4;
#endif u_char ip_tos;
u_short ip_total_len;
u_short ip_id;
u_short ip_flags;
u_char ip_ttl;
u_char ip_protocol;
u_short ip_chksum;
u_int32 ip_src;
u_int32 ip_dest;
}__attribute__((packed)) IP_HDR;

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

typedef int int32;
typedef unsigned int u_int32;
typedef unsigned char u_char;
typedef unsigned short u_short;

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

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

以下是对以太网帧进行解析的详细代码。

MAC_FRM_HDR *mac_hdr; //define a Ethernet frame header
IP_HDR *ip_hdr; //define a IP header
char *tmp1, *tmp2;
int AND_LOGIC = 0xFF; mac_hdr = buf; //buf is what we got from the socket program
ip_hdr = buf + sizeof(MAC_FRM_HDR);
//udp_hdr = buf + sizeof(MAC_FRM_HDR) + sizeof(IP_HDR); //if we want to analyses the UDP/TCP tmp1 = mac_hdr->src_addr;
tmp2 = mac_hdr->dest_addr;
/* print the MAC addresses of source and receiving host */
printf("MAC: %.2X:%.2X:%.2X:%.2X:%.2X:%.2X==>" "%.2X:%.2X:%.2X:%.2X:%.2X:%.2X",
tmp1[0]&AND_LOGIC, tmp1[1]&AND_LOGIC, tmp1[2]&AND_LOGIC,tmp1[3]&AND_LOGIC,
tmp1[4]&AND_LOGIC, tmp1[5]&AND_LOGIC,
tmp2[0]&AND_LOGIC, tmp2[1]&AND_LOGIC, tmp2[2]&AND_LOGIC,tmp2[3]&AND_LOGIC,
tmp2[4]&AND_LOGIC, tmp2[5]&AND_LOGIC); tmp1 = (char*)&ip_hdr->ip_src;
tmp2 = (char*)&ip_hdr->ip_dest;
/* print the IP addresses of source and receiving host */
printf("IP: %d.%d.%d.%d => %d.%d.%d.%d",
tmp1[0]&AND_LOGIC, tmp1[1]&AND_LOGIC, tmp1[2]&AND_LOGIC,tmp1[3]&AND_LOGIC,
tmp2[0]&AND_LOGIC, tmp2[1]&AND_LOGIC, tmp2[2]&AND_LOGIC,tmp2[3]&AND_LOGIC);
/* print the IP protocol which was used by the socket communication */
switch(ip_hdr->ip_protocol) {
case IPPROTO_ICMP: LOGI("ICMP"); break;
case IPPROTO_IGMP: LOGI("IGMP"); break;
case IPPROTO_IPIP: LOGI("IPIP"); break;
case IPPROTO_TCP:
case IPPROTO_UDP:
LOGI("Protocol: %s", ip_hdr->ip_protocol == IPPROTO_TCP ? "TCP" : "UDP");
LOGI("Source port: %u, destination port: %u", udp_hdr->s_port, udp_hdr->d_port);
break;
case IPPROTO_RAW: LOGI("RAW"); break;
default: printf("Unknown, please query in inclued/linux/in.h\n"); break;
}

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

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



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

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

    转载自 https://blog.csdn.net/kleguan/article/details/27538031 1. Linux抓包源程序 在OSI七层模型中,网卡工作在物理层和数据链路层的MA ...

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

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

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

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

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

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

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

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

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

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

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

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

  8. linux下的usb抓包方法

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

  9. tcpdump 抓包让wireshark来分析

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

随机推荐

  1. HDU——2955 Robberies (0-1背包)

    题意:有N个银行,每抢一个银行,可以获得\(v_i\)的前,但是会有\(p_i\)的概率被抓.现在要把被抓概率控制在\(P\)之下,求最多能抢到多少钱. 分析:0-1背包的变形,把重量变成了概率,因为 ...

  2. SQL case when 多条件查询

    基于列的逻辑表达式,其实就是CASE表达式.可以用在SELECT,UPDATE,DELETE,SET以及IN,WHERE,ORDER BY和HAVING子句之后.下面给个简单示例:

  3. 分布式session的管理

    在分布式架构或微服务架构下,必须保证一个应用服务器上保存Session后,其它应用服务器可以同步或共享这个Session,可能会出现在A1系统登录后创建并保存Session,再次发起请求,请求被转发到 ...

  4. Linux学习笔记之Linux计划任务Crontab

    0x00 cron 简介 cron 是 UNIX, SOLARIS,LINUX 下的一个十分有用的工具.通过 cron 脚本能使计划任务定期地在系统后台自动运行. 0x01 cron 命令 cront ...

  5. 从知乎了解到,为什么Mysql禁用存储过程、外键和级联?

    打开帖子直接一张醒目的图,是阿里巴巴的Java开发手册对Mysql相关的要求. 看看下面的回复 灵剑 存储过程没有版本控制,版本迭代的时候要更新很麻烦.存储过程如果和外部程序结合起来用,更新的时候很难 ...

  6. 20145122 《Java程序设计》第5周学习总结

    教材学习内容总结 1.在Java中,异常分为受检查的异常,与运行时异常. 两者都在异常类层次结构中. 2.受检查的异常(checked exceptions),其必须被 try{}catch语句块所捕 ...

  7. ArrayList扩容

    jdk1.5 public ArrayList(int initialCapacity) { super(); if (initialCapacity < 0) throw new Illega ...

  8. Java多线程 线程状态及转换 wait sleep yield join

    线程的状态转化关系(1). 新建状态(New):新创建了一个线程对象.(2). 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中, ...

  9. Spring Cloud OAuth2(一) 搭建授权服务

    概要 本文内容主要为spring cloud 授权服务的搭建,采用jwt认证. GitHub 地址:https://github.com/fp2952/spring-cloud-base/tree/m ...

  10. [问题解决]win10误删启动项(BCD)(HP电脑亲测,无需启动盘,并非重装系统)

    昨天使用easyBCD软件,开始不太懂,手残把win10的引导删除了,后来发现电脑关机总是变成重启,无奈强制关机.今天重启了一下电脑,发现电脑已经无法打开了,这才明白昨天是误删了win10的BCD. ...