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



测试运行平台: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分析 (一),布布扣,bubuko.com

时间: 2024-10-06 02:21:42

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

c语言基于Linux下用libpcap实现抓包程序

c语言基于libpcap实现一个抓包程序过程 基于pcap的嗅探器程序的总体架构,其流程如下:(1)首先要决定用哪一个接口进行嗅探开始.在Linux中,这可能是eth0,而在BSD系统中则可能是xl1等等.我们也可以用一个字符串来定义这个设备,或者采用pcap提供的接口名来工作.(... 基于pcap的嗅探器程序的总体架构,其流程如下: (1)首先要决定用哪一个接口进行嗅探开始.在Linux中,这可能是eth0,而在BSD系统中则可能是xl1等等.我们也可以用一个字符串来定义这个设备,或者采用p

基于libpcap实现抓包程序

前言 原创文章欢迎转载,请保留出处. 若有任何疑问建议,欢迎回复. 邮箱:[email protected] 紧接着上一篇,成功通过tcpdump和wireshark抓包后,试试自己写一个抓包器.这里我们使用libpcap库开发. 创建配置工程 这里我们使用Eclipse for C/C++开发,安装的话只需要到eclipse官方下载解压就可以运行了,这里要注意的是,Eclipse要以root权限启动不然无法抓包. 首先创建一个空白的C语言项目,工具链选择Linux GCC 添加源文件,起名ma

Windows下利用原始套接字实现的一个抓包程序Demo

早就学过了套接字编程,但是原始套接字还没用过.最近听了网络安全老师的课,心血来潮,写了个抓包程序Demo,把代码分享给大家,感兴趣的可以看看.引用一句网络安全老师的话:"你们要本着技术的心态去实践,哎,一部分人,写着写着就成黑客了". #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <WinSock2.h> #include <WS2tcpip.h> #include <s

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

[创建时间:2015-08-27 22:15:17] NetAnalyzer下载地址 经过前两篇的瞎扯,你是不是已经厌倦了呢,那么这篇让我们来点有意思的吧,什么,用C#.不,这篇我们先来C++的 Winpcap开发环境配置 完成了对Winpcap的介绍,什么,你没看到Winpcap的介绍,左转,百度(其实,真的是不想复制).我们就需要做一点有用的事情,比如写一个简单的数据采集工具.当然在此之前,我们需要配置Winpcap的开发环境. (1) 运行环境设置 Win32 平台下Winpcap应用程序

tcpdump 抓包让wireshark来分析

在linux下面用tcpdump 抓包非常方便, 但是抓的包要提取出来进行分析, 还是得用wireshark来过滤分析比较方便. 下面先介绍一下 TCPDUMP 的使用 例:tcpdump host 172.16.29.40 and port 4600 -X -s 500 tcpdump采用命令行方式,它的命令格式为: tcpdump [ -adeflnNOpqStvx ] [ -c 数量 ] [ -F 文件名 ] [ -i 网络接口 ] [ -r 文件名] [ -s snaplen ] [ -

主流抓包工具使用对比分析

主流抓包工具使用对比分析 1.目前有哪几种比较流行的抓包工具 使用平台 优点 缺点 wireshark linux.windows linux平台支持好,功能强大 不能解析https的内容 fidder windows功能 功能强大,支持修改报文 仅支持windows Charles window.Mac 可以按照域名进行分层级查看 需要收费 Burp Suite 支持java的平台 黑客喜欢用的功能强大工具 界面不是很好看 2.普及下他们的基本用法 Charles使用详解 Fiddler抓包

基于HTTP访问特定URL的抓包程序该怎么写

抓包是一个简单易行的事,它可以帮你分析网络的行为.我记得早在2004年的时候,老师就讲过抓包有多么重要.        作为程序员而言,除了抓包几乎没有任何手段探测网络行为,程序员没有机会触摸网络设备,也就没有能力洞悉网络细节,程序员唯一能触摸到的就是自己的终端,在这个终端上唯一能做的就是抓包.我是半个程序员,所以不管站在谁的立场上,我都认为抓包是一个重要的事,虽然在我内心看来,抓包和分析数据包解决不了大多数的问题....        在一台承载大量业务的机器上怎么抓包成了一个问题.因为虚拟主

基于Linux C的socketEthereal程序和Package分析 (一个)

 执行测试平台:CentOS 6.5发行版,内核版本号3.11 1. Linux抓包源程序 在OSI七层模型中.网卡工作在物理层和数据链路层的MAC子层. 进行网络通信时.源主机通过socket(或其他)应用程序产生IP报文,经过各个OSI层层封装,数据包以Ethernet帧的形式进入物理层.Ethernet帧包括源主机地址.IP报文.目标地址(IP地址.port号或映射的6字节MAC地址)和须要传送到目标主机的其他信息. 目标的MAC地址是哪里来的呢?这牵扯到一个ARP协议(介乎于网络层和

linux+jmeter+python基础+抓包拦截

LINUX 一 配置jdk 环境 *需要获取root权限,或者切换为root用户 1.windows下载好,去 http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 官方网站下载jdk(linux相应版本) 2.在usr目录下创建java路径文件夹 [root bin]cd /usr mkdir java 3.将jdk-8u60-linux-x64.tar.gz放到刚才创建的文件夹下