首页 > 其他分享 >6、原始套接字

6、原始套接字

时间:2024-08-28 14:24:12浏览次数:11  
标签:IP sock raw 原始 fd 接字 include buf

一.原始套接字概述

raw socket,即原始套接字,可以接收本机网卡上的数据帧或者数据包,对于监听网络的流量和分析是很有作用的,一共可以有4种方式创建这种socket。

在我们学习TCP和UDP通信的时候,所用的知识点其实已经可以满足我们的日常开发需求,那么我们学习原始套接字,加强对网络的理解

在使用网络的过程中,以下情况如何解决:

能否截获网络中的数据?
怎样发送一个自定义的 IP 包?---今天的内容
怎样伪装本地的 IP、 MAC?
网络攻击是怎么回事?
路由器、交换机怎样实现?

1、原始套接字的应用方向

原始套接字(SOCK_RAW)

1、 一种不同于 SOCK_STREAM、 SOCK_DGRAM 的套接字,它实现于系统核心

2、 可以接收本机网卡上所有的数据帧(数据包) ,对于监听网络流量和分析网络数据很有作用

3、 开发人员可发送自己组装的数据包到网络上

4、 广泛应用于高级网络编程

5、 网络专家、黑客通常会用此来编写奇特的网络程序

网络通信协议部分:

|

二.创建原始套接字

int socket(PF_PACKET, SOCK_RAW, protocol)
功能:
创建链路层的原始套接字
参数:
protocol:指定可以接收或发送的数据包类型(协议)
ETH_P_IP:IPV4 数据包
ETH_P_ARP:ARP 数据包
ETH_P_ALL:任何协议类型的数据包
返回值:
成功(>0):链路层套接字
失败(<0):出错

 int socket(PF_PACKET, SOCK_RAW, ETH_P_ALL)

1、原始套接字获取套接字的方式:

#include <sys/socket.h>
#include <netinet/ether.h>

sock_raw_fd = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
//已过时,不再使用
sock_raw_fd = socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL));

2、使用tcp或者UDP的时候,第三个参数的设置:

socket(AF_INET, SOCK_STREAM, 0)

三.数据包详解

使用原始套接字进行编程开发时,首先要对不同协议的数据包进行学习,需要手动对 IP、 TCP、 UDP、 ICMP 等包头进行组装或者拆解

/usr/include/net

在 TCP/IP 协议栈中的每一层为了能够正确解析出上层的数据包,从而使用一些“协议类型”来标记

组包和解包的过程示意图:

比如我们要使用TCP--协议类型6

UDP--协议类型17

1、组装/拆解 udp 数据包流程

发送数据--组包

接收数据--解包

2、MAC报文头(链路层):

目的地址和源地址:MAC地址

类型:协议类型

3、IP数据报头(网络层):

1.版本: IP协议的版本。 通信双方使用过的IP协议的版本必须一致, 目前最广泛使用的IP协议版本号为3
4( 即IPv4 )
2.首部长度: 单位是32位( 4字节)
3.服务类型: 一般不适用, 取值为0。 前3位: 优先级, 第4-7位: 延时, 吞吐量, 可靠性, 花费。 第8
位保留
4.总长度: 指首部加上数据的总长度, 单位为字节。 最大长度为65535字节。
5.标识( identification) : 用来标识主机发送的每一份数据报。 IP软件在存储器中维持一个计数器, 每
产生一个数据报, 计数器就加1, 并将此值赋给标识字段。
6.标志( flag) : 目前只有两位有意义。
 标志字段中的最低位记为MF。 MF=1即表示后面“还有分片”的数据报。 MF=0表示这已是若干数据
报片中的最后一个。
 标志字段中间的一位记为DF, 意思是“不能分片”, 只有当DF=0时才允许分片
7.片偏移: 指出较长的分组在分片后, 某片在源分组中的相对位置, 也就是说, 相对于用户数据段的
起点, 该片从何处开始。 片偏移以8字节为偏移单位。
8.生存时间: TTL, 表明是数据报在网络中的寿命, 即为“跳数限制”, 由发出数据报的源点设置这个字
段。 路由器在转发数据之前就把TTL值减一, 当TTL值减为零时, 就丢弃这个数据报。 通常设置为32、
64、 128。
9.协议: 指出此数据报携带的数据时使用何种协议, 以便使目的主机的IP层知道应将数据部分上交给
哪个处理过程, 常用的ICMP(1),IGMP(2),TCP(6),UDP(17),IPv6( 41)
10.首部校验和: 只校验数据报的首部, 不包括数据部分。
11.源地址: 发送方IP地址
12.目的地址: 接收方IP地址
13.选项: 用来定义一些任选项; 如记录路径、 时间戳等。 这些选项很少被使用, 同时并不是所有主机
和路由器都支持这些选项。 一般忽略不计。

4、UDP数据报头(传输层):

5、TCP数据报头(传输层):

TCP/IP协议组包的结构:

原始套接字可以接受MAC层传输的数据,换句话说,原始套接字可以接收到我们当前主机通信的所有IP的数据,那么我们就可以直接使用接收函数去进行接收数据,然后进行查看。

6、ARP数据报头(链路层)

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/ether.h>



int main(int argv ,char *argc[])
{
    int  sock_raw_fd =0;
    unsigned char buf[1500]="";
    sock_raw_fd = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
    printf("sock_raw_fd:%d\n",sock_raw_fd);

    while(1)
    {
        //目前处于MAC层
        unsigned char dst_mac[18]="";
        unsigned char src_mac[18]="";
        unsigned short type=0;
        int len=recvfrom(sock_raw_fd,buf,sizeof(buf),0,NULL,NULL);
        if(len<0)
        {
            perror("recvfrom:");
            exit(-1);
        }
        sprintf( dst_mac,"%02x:%02x:%02x:%02x:%02x:%02x",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5]);
        sprintf( src_mac,"%02x:%02x:%02x:%02x:%02x:%02x",buf[6],buf[7],buf[8],buf[9],buf[10],buf[11]);

        printf("src_mac:%s--->dst_mac:%s\n",src_mac,dst_mac);
        type=ntohs(*(unsigned short *)(buf+12));
        printf("type-->%x\n",type);
        sleep(1);


       
    if(type==0x0800)
    {
        unsigned int ip_len=0;
        unsigned char *ip_addr=buf+14;
        unsigned char  ip_type=0;
         //解析IP层数据
        printf("/************************IP协议*************************/");
        ip_len=(buf[14]&0x0f)*4;
        printf("\nip_len:%d\n",ip_len);

        //获取IP数据报的总长度

      //IP中包含的协议类型
      ip_type=buf[23];
      printf("ip_type-->%x\n",ip_type);




    if(ip_type==0x06)
    {
         unsigned short dst_port=0;
         unsigned short src_port=0;
        printf("/************************TCP协议*************************/\n");
        src_port=ntohs(*(unsigned short *)(buf+14+ip_len));
        dst_port=ntohs(*(unsigned short *)(buf+14+ip_len+2));
        printf("src_port:%d---->dst_port:%d\n",src_port,dst_port);


    }

    else if(ip_type==17)
    {
         printf("/************************UDP协议*************************/\n");
    }


    }
    else if(type==0x0806)
    {
        printf("/************************ARP协议*************************/\n");
    }
      else if(type==0x8035)
    {
        printf("/************************RARP协议*************************/\n");
    }

    }

    close(sock_raw_fd);
    return 0;
}

四.ARP 数据解析

如何去获取ARP表格内容???

首先在学习ARP数据解析,必须清楚MAC报文头内容:

1.发送ARP请求--以广播的形式发送的

获取当前局域网的IP和MAC

(UBUNTU中的ARP表格内容)

Dest MAC: 0xff 0xff 0xff 0xff 0xff 0xff

Src MAC: 0x00 0x0c 0x29 0x21 0x96 0xce

帧类型:0x08 0x06

硬件类型:0x00 0x01

协议类型:0x08 0x00

硬件地址长度:0x06

协议地址长度:0x04

OP: 0X00 0X01

发送端的以太网地址:0x00 0x0c 0x29 0x21 0x96 0xce

发送端的IP地址:192.168.2.93

目的以太网地址:0x00 0x00 0x00 0x00 0x00 0x00

目的IP地址:192.168.2.17

2.接收ARP应答

Dest MAC: 0x00 0x0c 0x29 0x21 0x96 0xce

Src MAC: 88-A4-C2-EA-5D-20

帧类型:0x08 0x06

硬件类型:0x00 0x01

协议类型:0x08 0x00

硬件地址长度:0x06

协议地址长度:0x04

OP: 0X00 0X02

发送端的以太网地址:88-A4-C2-EA-5D-20

发送端的IP地址:192.168.2.17

目的以太网地址:0x00 0x0c 0x29 0x21 0x96 0xce

目的IP地址:192.168.2.93

1、混杂模式

1、 指一台机器的网卡能够接收所有经过它的数据包,而不论其目的地址是否是它。

2、 一般计算机网卡都工作在非混杂模式下,如果设置网卡为混杂模式需要 root 权限

2、sendto 发送数据

sendto(sock_raw_fd, msg, msg_len, 0,(struct sockaddr*)&sll, sizeof(sll));
注意:
1、 sock_raw_fd:原始套接字
2、 msg:发送的消息(封装好的协议数据)
3、 sll:本机网络接口,指发送的数据应该从本机的哪个网卡出去,而不是以前的
目的地址

本机网络接口:

包含了网络传输过程中协议地址及类型
#include <netpacket/packet.h>
struct sockaddr_ll sll;
struct sockaddr_ll

ioctl函数的使用:

ssize_t my_sendto(int socket, const void *message, size_t length, char *if_name)
{
    // 获取接口
    struct ifreq ethreq;//获取接口--ens33
    strncpy(ethreq.ifr_name, if_name, IFNAMSIZ);
    if (-1 == ioctl(socket, SIOCGIFINDEX, &ethreq))
    {
        perror("ioctl");
        close(socket);
        _exit(-1);
    }

    // 定义接口结构
    struct sockaddr_ll sll;
    bzero(&sll, sizeof(sll));
    sll.sll_ifindex = ethreq.ifr_ifindex;

    // 发送帧数据
    int len = sendto(socket, message, length, 0, (struct sockaddr *)&sll, sizeof(sll));

    return len;
}

3、获取当前局域网内的设备信息

1.使用ARP协议进行广播---发送

ssize_t my_sendto(int socket, const void *message, size_t length, char *if_name)

2.等待局域网内设备给回应--接收

recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, NULL);
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/ether.h>
#include <pthread.h>
#include <netpacket/packet.h>
#include <sys/ioctl.h>
#include <net/if.h>

ssize_t my_sendto(int socket, const void *message, size_t length, char *if_name);
void *Rev_Data(void *arg);
int main(void)
{
    int  sock_raw_fd =0;
     pthread_t p_th1;
    // unsigned char rev_buf[64]="";
    // unsigned char buf[1500]="";
    sock_raw_fd = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
    printf("sock_raw_fd:%d\n",sock_raw_fd);
    //创建一个线程
    pthread_create(&p_th1,NULL,Rev_Data,&sock_raw_fd);

    //线程分离
    pthread_detach(p_th1);
//发送ARP请求
// for()
for(int i=1;i<=255;i++)
{
            unsigned char buf[]={

                    0xff,0xff ,0xff, 0xff ,0xff ,0xff,     
                    0x00 ,0x0c ,0x29 ,0x21 ,0x96, 0xce,
                    0x08  ,0x06,
                    0x00 ,0x01,
                    0x08, 0x00,
                    0x06,
                    0x04,
                    0X00 ,0X01,
                    0x00 ,0x0c ,0x29 ,0x21 ,0x96, 0xce,
                    192,168,2,93,
                    0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00,
                    192,168,2,i,
        };
//my_sento函数进行发送
int sendlen=my_sendto(sock_raw_fd,buf,42,"ens33");
// printf("%d---%d\n",i,sendlen);
usleep(20000);

}
sleep(2);
close(sock_raw_fd);
    return 0;
}

ssize_t my_sendto(int socket, const void *message, size_t length, char *if_name)
{
    // 获取接口
    struct ifreq ethreq;//获取接口--ens33
    strncpy(ethreq.ifr_name, if_name, IFNAMSIZ);
    if (-1 == ioctl(socket, SIOCGIFINDEX, &ethreq))
    {
        perror("ioctl");
        close(socket);
        _exit(-1);
    }

    // 定义接口结构
    struct sockaddr_ll sll;
    bzero(&sll, sizeof(sll));
    sll.sll_ifindex = ethreq.ifr_ifindex;

    // 发送帧数据
    int len = sendto(socket, message, length, 0, (struct sockaddr *)&sll, sizeof(sll));

    return len;
}
void *Rev_Data(void *arg)
{
int sock_fd=0;
unsigned short op=0;
unsigned char src_mac[18]="";

unsigned char src_ip[16]="";
sock_fd=*(unsigned int *)arg;
printf("sock_fd-->%d\n",sock_fd);
unsigned char rev_buf[64]="";
while(1)
{
     int rev_len=  recvfrom(sock_fd,rev_buf,64,0,NULL,NULL);
 if(rev_len<0)
 {
    printf("rev_error!!\n");
    return 0;
 }
    op=ntohs(*(unsigned short *)(rev_buf+20));
    // printf("op-->%d\n",op);

    if(op==0x02)  //应答信号
    {
        sprintf(src_mac,"%02x:%02x:%02x:%02x:%02x:%02x",
        rev_buf[6],rev_buf[7],rev_buf[8],rev_buf[9],rev_buf[10],rev_buf[11]);
        sprintf(src_ip,"%d.%d.%d.%d",rev_buf[28],rev_buf[29],rev_buf[30],rev_buf[31]);
        printf("%s-->%s\n",src_ip,src_mac);
    }
}
    return NULL;
}










标签:IP,sock,raw,原始,fd,接字,include,buf
From: https://blog.csdn.net/weixin_48471271/article/details/141639405

相关文章

  • 网络通讯协议与套接字
    网络通讯协议与套接字一、基本概念网络网络就是一种辅助双方或者多方能够连接在一起的工具,为了让在不同的电脑上运行的软件,之间能够互相传递数据,就需要借助网络的功能IP地址IP地址的概念和作用地址就是用来标记地点的,用来在网络中标记一台电脑,比如192.168.1.1......
  • 非阻塞套接字与IO多路复用
    非阻塞套接字与IO多路复用非阻塞套接字#【本机环境运行】#01-TCP非堵塞通信.py#使用TCP调试助手作为客户端importsockettcp_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)tcp_socket.bind(("",9000))tcp_socket.listen(128)#设置监听套接字为非......
  • 学懂C++(三十八):深入详解C++网络编程:套接字(Socket)开发技术
    目录一、概述与基础概念1.1套接字(Socket)概念1.2底层原理与网络协议1.2.1网络协议1.2.2套接字工作原理二、C++套接字编程核心技术2.1套接字编程的基本步骤2.2套接字编程详细实现2.2.1创建套接字2.2.2绑定地址2.2.3监听和接受连接(服务端)2.2.4客户端连接2.......
  • Linux中套接字可读|可写--SO_RCVLOWAT和SO_SNDLOWAT
    目录SO_RCVLOWAT(接收低水位标记)SO_SNDLOWAT(发送低水位标记)注意事项在GNULinux中,SO_RCVLOWAT和SO_SNDLOWAT是用于设置套接字选项的两个标志,分别用于设置接收和发送低水位标记。这些选项可以帮助应用程序控制网络通信的流量控制。SO_RCVLOWAT(接收低水位标记)......
  • 裁剪图像--原始大小变换
    用QPainter画出图像,画出要取出的矩形位置头文件源码如下:CusImageCrop.h点击查看代码`#pragmaonce#include<QObject>#include<QPaintEvent>#include<QLabel>#include<QMutex>#include<QTimer>#pragmaexecution_character_set("utf-8")class......
  • UDP网络套接字
    一、Socket编程预备1.1理解通行的本质    在网络通信中将数据传输到主机是目的吗?其实并不是,在现实生活中,我们通过网络聊天是人与人在聊天,下载软件是人在下载,浏览网页是人在浏览,而我们聊天用的微信、下载用的应用商店、浏览用的浏览器在主机中是不同的进程,所以人在主......
  • 1998-2023年上市公司生命周期测算数据(含原始数据+计算代码+结果)
    1998-2023年上市公司生命周期测算数据(含原始数据+计算代码+结果)1、时间:1998-2023年2、来源:上市公司年报、csmar3、指标:证券代码、证券简称、资产总计、净利润、购建固定资产无形资产和其他长期资产支付的现金、营业收入增长率B、股利分配率、行业代码、上市日期、公司成立日......
  • 网络编程套接字(一)
    目录源IP地址与目的IP地址端口号MAC地址网络字节序socket常见APIsockaddr结构基于UDP协议实现通信测试源IP地址与目的IP地址在IP数据包头部中,有两个IP地址,分别叫做源IP地址,和目的IP地址,源IP地址是数据发送者的IP地址,目的IP地址是数据接收者的地址。源IP地址......
  • 如何从 python socket.sendmsg 获取套接字 Tx 时间戳
    在阅读此处、此处和此处时,我发现在Linux系统上,您可以通过设置套接字选项来请求接收和传输的数据包的时间戳。我目前可以使用SO_TIMESTAMPNS和SO_TIMESTAMPING来通过recvmsg获取Rx时间戳。使用sendmsg我不知道......
  • 2000-2023年上市公司财务困境数据合集(ZScore、RLPM、MertonDD、OScore)(含原始数据+计算
    2000-2023年上市公司财务困境数据合集(ZScore、RLPM、MertonDD、OScore)(含原始数据+计算结果)1、时间:2000-2023年2、来源:上市公司年报3、范围:A股上市公司4、指标:MertonDD模型:证券代码、证券简称、统计截止日期、是否发生ST或*ST或PT、是否发生暂停上市、行业代码、行业名称......