原始 socket 编程

原始套接字简介

普通 socket 的权力和原始 socket 权力对比。

1.原始 socket 能够和内核同样直接对全部层进行操做(除了物理层)。能够更改 mac 更改 ip 更改端口。so dos 攻击就能够经过原始 socket 编程来伪造 ip 进行。
2.也能够访问通过网卡的全部数据.普通的 socket 只能访问发送给本身端口的数据。
这里写图片描述
linux

api 介绍

int socket(int protofamily, int type, int protocol);//返回sockfd算法

protofamily:

这个参数指定一个协议簇,也每每被称为协议域。系统存在许多能够的协议簇,常见有AF_INET──指定为IPv4协议,AF_INET6──指定为IPv6,AF_LOCAL──指定为UNIX 协议域等等。它值都是系统预先定义的宏,系统支持哪些协议咱们才可使用,不然会调用失败。协议簇是网络层的协议。一种是处理IP层即其上的数据,经过指定socket第一个参数为AF_INET来建立这种套接字。有两种原始套接字。另外一种是处理数据链路层即其上的数据,经过指定socket第一个参数为AF_PACKET来建立这种套接字。 PF_PACKET支持SOCK_DGRAM和SOCK_RAW两种socket类型。编程

type:

这个参数指定一个套接口的类型,套接口可能的类型有:SOCK_STREAM、SOCK_DGRAM、SOCK_SEQPACKET、SOCK_RAW等等,它们分别代表字节流、数据报、有序分组、原始套接口。这其实是指定内核为咱们提供的服务抽象,好比咱们要一个字节流。须要注意的,并非每一种协议簇都支持这里的全部的类型,因此类型与协议簇要匹配。api

protocol:

指定相应的传输协议,也就是诸如TCP或UDP协议等等,系统针对每个协议簇与类型提供了一个默认的协议,咱们经过把protocol设置为0来使用这个默认的值。注意这里的协议与上面的协议簇是两个不一样的概念,前者是指网络层的协议,因为它对于到传输层会出现许多协议,好比IPv4能够用来实现TCP或UDP等等传输层协议,因此称为协议簇。相应的传输层的协议就简单地称为协议。常见的协议有TCP、UDP、SCTP,要指定它们分别使用宏 IPPROTO_TCP、IPPROTO_UPD、IPPROTO_SCTP来指定。 到linux/in.h看可使用哪些传输层的协议安全

例子:

socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)发送接收ip数据包
能:该套接字能够接收协议类型为(tcp udp icmp等)发往本机的ip数据包
不能:收到非发往本地ip的数据包(ip软过滤会丢弃这些不是发往本机ip的数据包)
不能:收到从本机发送出去的数据包发送的话须要本身组织tcp udp icmp等头部.能够setsockopt来本身包装ip头部这种套接字用来写个ping程序比较适合网络

socket(PF_PACKET, SOCK_RAW|SOCK_DGRAM, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))
发送接收以太网数据帧这种套接字比较强大,能够监听网卡上的全部数据帧
能: 接收发往本地mac的数据帧
能: 接收从本机发送出去的数据帧(第3个参数须要设置为ETH_P_ALL)
能: 接收非发往本地mac的数据帧(网卡须要设置为promisc混杂模式)
协议类型一共有四个
ETH_P_IP 0x800 只接收发往本机mac的ip类型的数据帧
ETH_P_ARP 0x806 只接受发往本机mac的arp类型的数据帧
ETH_P_RARP 0x8035 只接受发往本机mac的rarp类型的数据帧
ETH_P_ALL 0x3 接收发往本机mac的全部类型ip arp rarp的数据帧, 接收从本机发出的全部类型的数据帧.(混杂模式打开的状况下,会接收到非发往本地mac的数据帧)并发

每层经常使用协议

链路层

报文格式

链路层有不少种协议,这个只是拿出一个典型的报文格式。socket

这里写图片描述tcp

数据链路层是OSI参考模型中的第二层,介乎于物理层和网络层之间。数据链路层在物理层提供的服务的基础上向网络层提供服务,其最基本的服务是将源自网络层来的数据可靠地传输到相邻节点的目标机网络层。ide

数据链路层的主要协议有:

1.Point-to-Point Protocal; PPP点到点
2.Ethernet; 以太网
3.High-Level Data Link Control Protocal HDLC高级链路控制协议
4.Frame Relay; 帧中继
5.Asynchronous Transfer Mode;ATM

网络层

报文格式:

这里写图片描述

1.Version - 4位字段,指出当前使用的 IP 版本。

2.IP Header Length (IHL) ─ 指数据报协议头长度,表示协议头具备32位字长的数量。指向数据起点。正确协议头最小值为5。(实际长度为 5*4。对于 ip->ihl = sizeof( struct iphdr ) >> 2 是由于这里是以4字节为单位的。右移2位至关于除4)

3.Type-of-Service ─ 指出上层协议对处理当前数据报所指望的服务质量,并对数据报按照重要性级别进行分配。这些8位字段用于分配优先级、延迟、吞吐量以及可靠性。(即TOS)

4.Total Length ─ 指定整个 IP 数据包的字节长度,包括数据和协议头(传输层和ip层数据总和)。其最大值为65,535字节。典型的主机能够接收576字节的数据报。

5.Identification ─ 每个IP封包都有一个16位的惟一识别码。当程序产生的数据要经过网络传送时都会被拆散成封包形式发送,当封包要进行重组的时候这个ID就是依据了。占16位。标识字段惟一地标识主机发送的每一份数据报。一般每发送一份消息它的值就会加1。RFC791认为标识字段应该由让IP发送数据报的上层来选择。假
设有两个连续的IP数据报,其中一个是由TCP生成的,而另外一个是由UDP生成的,那么它们可能具备相同的标识字段。尽管这也能够照常工做(由重组算法来
处理),可是在大多数从伯克利派生出来的系统中,每发送一个IP数据报,IP层都要把一个内核变量的值加1,无论交给IP的数据来自哪一层。内核变量的初
始值根据系统引导时的时间来设置。

6.Flags ─ 由3位字段构成,其中最低位(MF)控制分片,存在下一个分片置为1,不然置0表明结束分片。中间位(DF)指出数据包是否可进行分片。第三位即最高位保留不使用,可是必须为0。

7.Fragment Offset IP协议头格式规定当封包被分段以后,因为网路状况或其它因素影响其抵达顺序不会和当初切割顺序一至,因此当封包进行分段的时候会为各片断作好定位记录,以便在重组的时候就可以对号入座。值为多少个字节,若是封包并无被分段,则FO值为“0”。 占13位。

8.Time-to-Live生存时间字段设置了数据报能够通过的最多路由器数,表示数据包在网络上生存多久。TTL的初始值由源主机设置(一般为32或64),一旦通过一个处理它的
路由器,它的值就减去1。当该字段的值为0时,数据报就被丢弃,并发送ICMP消息通知源主机。这样当封包在传递过程当中由於某些缘由而未能抵达目的地的时
候就能够避免其一直充斥在网路上面。占8位。

9.Protocol ─ 指出在 IP 处理过程完成以后,有哪一种上层协议接收导入数据包。
这里写图片描述

10.Header Checksum ─ 帮助确保 IP 协议头的完整性。因为某些协议头字段的改变,如生存期(Time to Live),这就须要对每一个点从新计算和检验。Internet 协议头须要进行处理。
11.Source Address ─ 源主机IP地址。
12.Destination Address ─ 目标主机IP地址。
13.Options ─ 容许 IP 支持各类选项,如安全性。这是一个可变长的字段。由于IP包头长度(Header Length)部分的单位为32bit,因此IP包头的长度必须为32bit的整数倍。所以,在可选项后面,IP协议会填充若干个0,以达到32bit的整数倍

传输层

传输层咱们以 udp 为例子。tcp 还要维持连接,太烦了。dos 攻击可不须要握手创建整个连接的。

UDP 简介

UDP 报文UDP协议在IP协议上增长了复用、分用和差错检测功能。UDP的特色:
1.是无链接的,不须要连接和释放连接
2.是面向报文的,也就是说UDP协议将应用层传输下来的数据封装在一个UDP包中,不进行拆分或合并。
3.没有重传机制,是尽最大努力交付的。也就是说UDP协议没法保证数据可以准确的交付到目的主机。也不须要对接收到的UDP报文进行确认。
4.没有拥塞控制。所以UDP协议的发送速率不送网络的拥塞度影响。
5.UDP支持一对1、一对多、多对一和多对多的交互通讯。

UDP报文格式

这里写图片描述

UDP协议分为首部字段和数据字段,其中首部字段只占用8个字节,分别是个占用两个字节的源端口、目的端口、长度和检验和。
1.源端口:2字节 = 16bit =0 ~ 65535
2.目的端口:2字节
3.长度:2字节 用户数据包的长度+ 8 字节固定 udp 报头。
4.检验和:2字节在进行检验和计算时,会添加一个伪首部一块儿进行运算。


伪首部(占用12个字节)为 4个字节的源IP地址、4个字节的目的IP地址、1个字节的0、一个字节的数字17(协议类型 udp就是数字 17)、以及占用2个字节UDP长度。这个伪首部不是报文的真正首部,只是引入为了计算校验和。相对于IP协议的只计算首部,UDP检验和会把首部和数据一块儿进行校验。接收端进行的校验和与UDP报文中的校验和相与,若是无差错应该全为1。若是有误,则将报文丢弃或者发给应用层、并附上差错警告。

校验算法:
1.把校验和字段置为0
2.对IP头部中的每16bit进行二进制求和
3.若是和的高16bit不为0,则将和的高16bit和低16bit反复相加,直到和的高16bit为0,从而得到一个16bit的值
4.将该16bit的值取反,存入校验和字段。
s当接收IP包时,须要对报头进行确认,检查IP头是否有误,算法同上二、3步,而后判断取反的结果是否为0,是则正确,不然有错。

TCP 简介

1.是有链接的,面向流的。
2.是面向流的。TCP把数据流分区成适当长度的报文段(一般受该计算机链接的网络的数据链路层的最大传输单元([1] MTU)的限制)
3.有重传,去重机制。TCP协议中采用自适应的超时及重传策略。TCP的接收端必须丢弃重复的数据。
4.TCP还能提供流量控制。

TCP 报文格式

这里写图片描述

1.源端口号(16位),标识主机上发起传送的应用程序

2.目的端口(16位)标识主机上传送要到达的应用程序

3.源端和目的端的端口号,用于寻找发端和收 端应用进程。这两个值加上IP首部中的源端IP地址和目的端IP地址惟一肯定一个TCP链接。

4.顺序号字段:占32比特。用来标识从TCP源端向TCP目标端发送的数据字节流,它表示在这个报文段中的第一个数据字节。

5.确认号字段:占32比特。只有ACK标志为1时,确认号字段才有效。它包含目标端所指望收到源端的下一个数据字节。

6.头部长度字段:占4比特。给出头部占32比特的数目。没有任何选项字段的TCP头部长度为20字节;最多能够有60字节的TCP头部

7.预留:由跟在数据偏移字段后的6位构成,预留位一般为0.

8.标志位字段(U、A、P、R、S、F):占6比特。各比特的含义以下:  
  ◆URG:紧急指针(urgent pointer)有效。  
  ◆ACK:确认序号有效。  
  ◆PSH:接收方应该尽快将这个报文段交给应用层。  
  ◆RST:重建链接。  
  ◆SYN:发起一个链接。  
  ◆FIN:释放一个链接。
  
9.窗口大小字段:占16比特。此字段用来进行流量控制。单位为字节数,这个值是本机指望一次接收的字节数。

10.TCP校验和字段:占16比特。对整个TCP报文段,即TCP头部和TCP数据进行校验和计算,并由目标端进行验证

11.紧急指针字段:占16比特。它是一个偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号

12.选项字段:占32比特。可能包括”窗口扩大因子”、”时间戳”等选项。

应用例子:抓取全部IP包

typedef struct _iphdr //定义IP首部 
{ 
    unsigned char h_verlen; //4位首部长度+4位IP版本号 
    unsigned char tos; //8位服务类型TOS 
    unsigned short total_len; //16位总长度(字节) 
    unsigned short ident; //16位标识 
    unsigned short frag_and_flags; //3位标志位 
    unsigned char ttl; //8位生存时间 TTL 
    unsigned char proto; //8位协议 (TCP, UDP 或其余) 
    unsigned short checksum; //16位IP首部校验和 
    unsigned int sourceIP; //32位源IP地址 
    unsigned int destIP; //32位目的IP地址 
}IP_HEADER; 

typedef struct _udphdr //定义UDP首部
{
    unsigned short uh_sport;    //16位源端口
    unsigned short uh_dport;    //16位目的端口
    unsigned int uh_len;//16位UDP包长度
    unsigned int uh_sum;//16位校验和
}UDP_HEADER;

typedef struct _tcphdr //定义TCP首部 
{ 
    unsigned short th_sport; //16位源端口 
    unsigned short th_dport; //16位目的端口 
    unsigned int th_seq; //32位序列号 
    unsigned int th_ack; //32位确认号 
    unsigned char th_lenres;//4位首部长度/6位保留字 
    unsigned char th_flag; //6位标志位
    unsigned short th_win; //16位窗口大小
    unsigned short th_sum; //16位校验和
    unsigned short th_urp; //16位紧急数据偏移量
}TCP_HEADER; 

typedef struct _icmphdr {  
    unsigned char  icmp_type;  
    unsigned char icmp_code; /* type sub code */  
    unsigned short icmp_cksum;  
    unsigned short icmp_id;  
    unsigned short icmp_seq;  
    /* This is not the std header, but we reserve space for time */  
    unsigned short icmp_timestamp;  
}ICMP_HEADER;

void analyseIP(IP_HEADER *ip);
void analyseTCP(TCP_HEADER *tcp);
void analyseUDP(UDP_HEADER *udp);
void analyseICMP(ICMP_HEADER *icmp);


int main(void)
{
    int sockfd;
     IP_HEADER *ip;
    char buf[10240];
    ssize_t n;
    /* capture ip datagram without ethernet header */
    if ((sockfd = socket(PF_PACKET,  SOCK_DGRAM, htons(ETH_P_IP)))== -1)
    {    
        printf("socket error!\n");
        return 1;
    }
    while (1)
    {
        n = recv(sockfd, buf, sizeof(buf), 0);
        if (n == -1)
        {
            printf("recv error!\n");
            break;
        }
        else if (n==0)
            continue;
        //接收数据不包括数据链路帧头
        ip = ( IP_HEADER *)(buf);
        analyseIP(ip);
        size_t iplen =  (ip->h_verlen&0x0f)*4;
        TCP_HEADER *tcp = (TCP_HEADER *)(buf +iplen);
        if (ip->proto == IPPROTO_TCP)
        {
            TCP_HEADER *tcp = (TCP_HEADER *)(buf +iplen);
            analyseTCP(tcp);
        }
        else if (ip->proto == IPPROTO_UDP)
        {
            UDP_HEADER *udp = (UDP_HEADER *)(buf + iplen);
            analyseUDP(udp);
        }
        else if (ip->proto == IPPROTO_ICMP)
        {
            ICMP_HEADER *icmp = (ICMP_HEADER *)(buf + iplen);
            analyseICMP(icmp);
        }
        else if (ip->proto == IPPROTO_IGMP)
        {
            printf("IGMP----\n");
        }
        else
        {
            printf("other protocol!\n");
        }        
        printf("\n\n");
    }
    close(sockfd);
    return 0;
}

void analyseIP(IP_HEADER *ip)
{
    unsigned char* p = (unsigned char*)&ip->sourceIP;
    printf("Source IP   : %u.%u.%u.%u\n",p[0],p[1],p[2],p[3]);
    p = (unsigned char*)&ip->destIP;
    printf("Destination IP  : %u.%u.%u.%u\n",p[0],p[1],p[2],p[3]);

}

void analyseTCP(TCP_HEADER *tcp)
{
    printf("TCP -----\n");
    printf("Source port: %u\n", ntohs(tcp->th_sport));
    printf("Dest port: %u\n", ntohs(tcp->th_dport));
}

void analyseUDP(UDP_HEADER *udp)
{
    printf("UDP -----\n");
    printf("Source port: %u\n", ntohs(udp->uh_sport));
    printf("Dest port: %u\n", ntohs(udp->uh_dport));
}

void analyseICMP(ICMP_HEADER *icmp)
{
    printf("ICMP -----\n");
    printf("type: %u\n", icmp->icmp_type);
    printf("sub code: %u\n", icmp->icmp_code);
}

--------------------- 本文来自 helloworldyu 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/yuhaiyang457288/article/details/78090673?utm_source=copy

相关文章
相关标签/搜索