为您推荐:
精华内容
最热下载
问答
  • 5星
    389KB qq_44625365 2021-08-23 17:20:14
  • 5星
    4KB weixin_42696271 2021-09-11 16:43:22
  • 5星
    387KB weixin_44510615 2021-06-23 18:04:15
  • 5星
    2.44MB Johnho130 2021-07-13 20:48:23
  • 5星
    12.76MB qq_31988139 2021-06-30 09:46:52
  • 5星
    9.4MB qq_31988139 2021-06-30 09:47:27
  • 5星
    201.98MB weixin_47723732 2021-01-01 19:17:28
  • 5星
    67.09MB weixin_44733961 2021-02-06 19:05:06
  • 5星
    111.52MB qq_28239511 2021-03-17 13:04:53
  • 5星
    147B liu4791959 2021-02-22 10:33:03
  • 原始套接字抓包 常用的套接字有流式套接字(SOCK_STREAM,对应于TCP)和数据报套接字(SOCK_DGRAM,对应于UDP),这两种套接字接收到的仅仅是应用层的数据,用户一般见不到MAC层、IP层和传输层(TCP/UDP)的帧头。...

    网络作业2 IP分组流量分析程序

    参考:

    http://www.cnblogs.com/rollenholt/articles/2585432.html

    http://blog.csdn.net/ctthuangcheng/article/details/9746501

    http://www.cnblogs.com/rollenholt/articles/2585432.html

    http://baike.baidu.com/link?url=PipBWz6NFljSSDX_q-AeKVO1shLJijhn4xU4iaOPAN9aaDkaQZvL3n10XCnur0rURR3-4Q6TA6HDw-N8DYR-Xq

    http://wenku.baidu.com/link?url=gomqzjumyFcoIPceA9jZXdkazLIIbGR_xxF5cu11EZVqvTRVnqiH3sl30kUgoW5nZjZEB1TdTXFZm6UZRPfIce5W7fk1m6TD1N2pTii8Ycq

    开发一个IP分组流量分析程序,实现以下功能:捕获并分析通过本地网卡的IP分组,输入捕获IP分组的时间限制,输出每个IP分组头部的主要字段(包括版本、总长度、协议、源地址与目的地址等),协议字段需要区分出具体类型(例如TCP、UDP、ICMP、IGMP、IPv6等)。

    实现方案:

    1.原始套接字抓包

    2.linux信号机制计时。

    1.原始套接字抓包

    常用的套接字有流式套接字(SOCK_STREAM,对应于TCP)和数据报套接字(SOCK_DGRAM,对应于UDP),这两种套接字接收到的仅仅是应用层的数据,用户一般见不到MAC层、IP层和传输层(TCP/UDP)的帧头。而这里需要用到的原始套接字,是可以看到原始的网络报文的。如下图所示MAC帧地址,获得的包为从目的MAC地址到CRC之前(不包括CRC)

    0b178b7b4113c807b01d6ef771287536.png

    以如下方式使用原始套接字

    (sock =  socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)) < 0 )//使用原始套接字,ETH_P_ALL可以获取所有经过本机的数据包,当然前提是网卡为混杂模式。协议参数可选择的很多,这里列几个常用的。

    005f058858e906ab21c83cbc44ac7a5f.png

    上面还提到,网卡要设置成混杂模式。根据百度百科:混杂模式(Promiscuous Mode)是指一台机器能够接收所有经过它的数据流,而不论其目的地址是否是他。是相对于通常模式(又称“非混杂模式”)而言的。

    通过以下设置:

    strncpy(ethreq.ifr_name, "wlan0", IFNAMSIZ);//指定需要经过的网卡。

    if( ioctl(sock, SIOCGIFFLAGS, ðreq) == -1 )//获得该网卡的接口信息。

    {

    perror("ioctl:");

    close(sock);

    exit(1);

    }

    ethreq.ifr_flags |= IFF_PROMISC;//设置成混杂模式。

    if( ioctl(sock, SIOCSIFFLAGS, ðreq) == -1)//将网卡的接口信息写回。

    {

    perror("ioctl:");

    close(sock);

    exit(1);

    }

    先指定要配置的网卡名称,在通过ifreq.ifr_flags 设置混杂模式。这里涉及到如下一个数据结构,

    struct ifreq

    {

    # define IFHWADDRLEN 6

    # define IFNAMSIZ IF_NAMESIZE

    union

    {

    char ifrn_name[IFNAMSIZ]; /* Interface name, e.g. "en0". */

    } ifr_ifrn;

    union

    {

    struct sockaddr ifru_addr;

    struct sockaddr ifru_dstaddr;

    struct sockaddr ifru_broadaddr;

    struct sockaddr ifru_netmask;

    struct sockaddr ifru_hwaddr;

    short int ifru_flags;

    int ifru_ivalue;

    int ifru_mtu;

    struct ifmap ifru_map;

    char ifru_slave[IFNAMSIZ]; /* Just fits the size */

    char ifru_newname[IFNAMSIZ];

    __caddr_t ifru_data;

    } ifr_ifru;

    };

    # define ifr_name ifr_ifrn.ifrn_name /* interface name*/ 在这里就是网卡eth0或eth1

    # define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ mac地址

    # define ifr_addr ifr_ifru.ifru_addr /* address */ source地址

    # define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */ 目的ip地址

    # define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ 广播地址

    # define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */ 子网掩码

    # define ifr_flags ifr_ifru.ifru_flags /* flags */ 模式标志 设置混杂模式

    # define ifr_metric ifr_ifru.ifru_ivalue /* metric */

    # define ifr_mtu ifr_ifru.ifru_mtu /* mtu */

    # define ifr_map ifr_ifru.ifru_map /* device map */

    # define ifr_slave ifr_ifru.ifru_slave /* slave device */

    # define ifr_data ifr_ifru.ifru_data /* for use by interface */

    # define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */

    # define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */

    # define ifr_qlen ifr_ifru.ifru_ivalue /* queue length */

    # define ifr_newname ifr_ifru.ifru_newname /* New name */

    # define _IOT_ifreq _IOT(_IOTS(char),IFNAMSIZ,_IOTS(char),16,0,0)

    # define _IOT_ifreq_short _IOT(_IOTS(char),IFNAMSIZ,_IOTS(short),1,0,0)

    # define _IOT_ifreq_int _IOT(_IOTS(char),IFNAMSIZ,_IOTS(int),1,0,0)

    最后使用recvfrom接收信息。

    recvfrom(sock, buffer, MAXLINE, 0, NULL, NULL);//接收通过网卡的信息。

    2.linux信号机制定时。

    linux信号机制就不多说了,这里利用信号机制和系统调用alarm弄了一个定时器。

    完整代码:

    1294025870956109824.htm

    #include "mysock.h"

    #include "analysis.h"

    unsigned int sec = 3;

    void alarm_h()

    {

    sec = 0;

    //printf("*************alarm*****************\n");

    }

    int main()

    {

    int sock;

    unsigned char buffer[MAXLINE];//接收缓冲区,以太网环境中ip数据包长度规定为46-1500,加上mac层的14(mac帧最后的4字节crc不包括在内),为64-1518,不过貌似再长了可以分片自己重组。

    int n = 0;

    struct ifreq ethreq;

    if( (sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0 )//使用原始套接字,ETH_P_ALL可以获取所有经过本机的数据包,当然前提是网卡为混杂模式。

    {

    perror("socket:");

    exit(1);

    }

    strncpy(ethreq.ifr_name, "wlan0", IFNAMSIZ);//指定需要经过的网卡。

    if( ioctl(sock, SIOCGIFFLAGS, ðreq) == -1 )//获得该网卡的接口信息。

    {

    perror("ioctl:");

    close(sock);

    exit(1);

    }

    ethreq.ifr_flags |= IFF_PROMISC;//设置成混杂模式。

    if( ioctl(sock, SIOCSIFFLAGS, ðreq) == -1)//将网卡的接口信息写回。

    {

    perror("ioctl:");

    close(sock);

    exit(1);

    }

    struct sigaction newact, oldact;//使用SIGALRM信号计时。

    newact.sa_handler = alarm_h;//信号处理函数

    sigemptyset(&newact.sa_mask);//清空屏蔽字

    newact.sa_flags = 0;

    while(1)

    {

    sigaction(SIGALRM, &newact, &oldact);//改变该信号的默认行为,旧的存在oldact中,使用之后别忘了还回来。

    printf("input seconds:\t");

    scanf("%d", &sec);//请用户输入信息。

    alarm(sec);//开始计时。sec若为0关闭计时。

    while(sec > 0)//计时时间到的时候,SIFALRM信号的处理函数会将sec置零。

    {

    printf("-----\n");

    n = recvfrom(sock, buffer, MAXLINE, 0, NULL, NULL);//接收通过网卡的信息。

    if(n < 0)

    {

    perror("recvform:");

    continue;

    }

    printf("recv %d bytes!\n", n);

    int i;

    for(i = 0 ; i < n ; i++)

    {

    printf("%02x\t", buffer[i]);//打印信息

    if( (i+1)%12 == 0)

    printf("\n");

    }

    printf("\n");

    analysis_mac(buffer);//解析数据包。这里只能解析IP数据报。

    }

    alarm(0);//停止计时器。

    sigaction(SIGALRM, &oldact, NULL);//改成默认行为。

    }

    return 0;

    }

    展开全文
    weixin_29346059 2021-05-13 14:48:09
  • 第13章节原始套接字第13章 原始套接字 通常情况下程序设计人员接触的网络知识限于如下两类: 流式套接字(SOCK_STREAM),它是一面向连接的套接字,对应于TCP应用程序。 数据报套接字(SOCK_DGRAM),它是一无连接的...

    第13章节原始套接字

    第13章 原始套接字 通常情况下程序设计人员接触的网络知识限于如下两类: 流式套接字(SOCK_STREAM),它是一种面向连接的套接字,对应于TCP应用程序。 数据报套接字(SOCK_DGRAM),它是一种无连接的套接字,对应于的UDP应用程序。 除了以上两种基本的套接字外还有一类原始套接字,它是一种对原始网络报文进行处理的套接字。 13.1 概述 前面几章介绍了基础的套接字知识,流式套接字(SOCK_STREAM)和数据报套接字(SOCK_DGRAM)涵盖了一般应用层次的TCP/IP应用。 13.2 原始套接字的创建 原始套接字的创建使用与通用的套接字创建的方法是一致的,只是在套接字类型的选项上使用的是另一个SOCK_RAW。在使用socket函数进行函数创建完毕的时候,还要进行套接字数据中格式类型的指定,设置从套接字中可以接收到的网络数据格式。 13.2.1 SOCK_RAW选项 创建原始套接字使用函数socket,第二个参数设置为SOCK_RAW,函数socket()可以创建一个原始套接字。下面的代码,创建一个AF_INET协议族中的原始套接字,协议类型为protocol。 int rawsock = socket(AF_INET, SOCK_RAW, protocol); 13.2.2 IP_HDRINCL套接字选项 使用套接字选项IP_HDRINCL设置套接字,在之后进行的接收和发送的时候,接收到的数据包含IP数据的,包含IP的头部。用户之后需要对IP层相关的数据段进行处理,例如IP头部数据的设置和分析,校验和的计算等。设置方法如下: int set = 1; if(setsockopt(rawsock, IPPROTO_IP, IP_HDRINCL, &set, sizeof(set))<0){ } 13.2.3 不需要bind()函数 原始套接字不需要使用bind()函数,因为进行发送和接收数据的时候可以指定要发送和接收的目的地址的IP。例如使用函数sendto()和函数recvfrom()来发送和接收数据,sendto()和recvfrom()函数分别需要指定IP地址。 sendto (rawsock, data, datasize, 0, (struct sockaddr *) &to, sizeof (to)); recvfrom(rawsock, data,size , 0,(struct sockaddr)&from, &len) ; 当系统对socket进行了绑定的时候,发送和接收的函数可以使用send()和recv()及read()和write()等不需要指定目的地址的函数。 13.3 原始套接字发送报文 原始套接字发送报文有如下的原则: 通常情况下可以使用sendto()函数并指定发送目的地址发送数据,当已经bind()了目标地址的时候可以使用write()或者send()发送数据。 如果使用setsockopt()设置了选项IP_RINCL,则发送的数据缓冲区指向IP头部第一个字节的头部,用户发送的数据包含IP头部之后的所有数据,需要用户自己填写IP头部和计算校验和及所包含数据的处理和计算。 如果没有设置IP_RINCL,则发送缓冲区指向IP头部后面数据区域的第一个字节,不需要用户填写IP头部,IP头部的填写工作有内核进行,内核还进行校验和的计算。 13.4 原始套接字接收报文 接收报文还有自己的一些特点,主要有如下几个: 对于ICMP的协议,绝大部分数据可以通过原始套接字获得,例如回显请求、响应,时间戳请求等。 接收的UDP和TCP协议的数据不会传给任何原始套接字接口,这些协议的数据需要通过数据链路层获得。 如果IP以分片形式到达,则所有分片都已经接收到并重组后才传给原始套接字。 内核不能识别的协议、格式等传给原始套接字,因此,可以使用原始套接字定义用户自己的协议格式。 13.5 原始套接字报文处理时的结构 本节介绍进行报文处理时常用的数据结构,包含IP头部、ICMP头部、UDP头部、TCP头部。使用这些数据格式对原始套接字进行处理,可以从底层获取高层的网络数据。 13.5.1 IP头部的结构 13.5.2 ICMP头部结构 ICMP的头部结构比较复杂,主要包含消息类型icmp_type,消息代码icmp_code、校验和icmp_cksum等,不同的ICMP类型其他部分有不同的实现。 1.ICMP的头部结构 2.不同类型的ICMP请求 13.5.3 UDP头部结构 13.5.4 TCP头部结构 TCP的头部结构主要包含发送端的源端口、接收端的目的端口、数据的序列号、上一个数据的确认号、滑动窗口大小、数据的校验和、紧急数据的偏移指针以及一些控制位等信息。 13.5

    展开全文
    weixin_40004960 2021-08-12 06:23:40
  • 6、多路复用——select() 功能:用来检测一个或多个套接字状态。 格式:int PASCAL FAR select(int nfds,fd_set FAR * readfds,fd_set FAR * writefds, fd_set FAR * exceptfds,const struct timeval FAR * timeout)...

    6、多路复用——select() 功能:用来检测一个或多个套接字状态。 格式:int PASCAL FAR select(int nfds,fd_set FAR * readfds,fd_set FAR * writefds, fd_set FAR * exceptfds,const struct timeval FAR * timeout); 参数:readfds:指向要做读检测的指针 writefds:指向要做写检测的指针 exceptfds:指向要检测是否出错的指针 timeout:最大等待时间

    7、关闭套接字——closesocket() 功能:关闭套接字s 格式:BOOL PASCAL FAR closesocket(SOCKET s);

    Windows Socket套接字原理

    一、客户机/服务器模式

    在TCP/IP网络中两个进程间的相互作用的主机模式是客户机/服务器模式(Client/Server model)。该模式的建立基于以下两点:1、非对等作用;2、通信完全是异步的。客户机/服务器模式在操作过程中采取的是主动请示方式:

    首先服务器方要先启动,并根据请示提供相应服务:(过程如下)

    1、打开一通信通道并告知本地主机,它愿意在某一个公认地址上接收客户请求。

    2、等待客户请求到达该端口。

    3、接收到重复服务请求,处理该请求并发送应答信号。

    4、返回第二步,等待另一客户请求

    5、关闭服务器。

    客户方:

    1、打开一通信通道,并连接到服务器所在主机的特定端口。

    2、向服务器发送服务请求报文,等待并接收应答;继续提出请求……

    3、请求结束后关闭通信通道并终止。

    二、典型过程图

    2.1 面向连接的套接字的系统调用时序图

    5014be9470533532620bb3c5f9156802.png

    2.2 无连接协议的套接字调用时序图

    4461a1f56a33cfcd7c1cb5168d996e92.png

    2.3 面向连接的应用程序流程图

    e66d34445f55bd0cbdfd80b5f6d6faaf.png

    三、  Socket有五种不同的类型:

    1、流式套接字(stream socket)

    定义:#define SOCK_STREAM 1

    流式套接字提供了双向、有序的、无重复的以及无记录边界的数据流服务,适合处理大量数据。它是面向联结的,必须建立数据传输链路,同时还必须对传输的数据进行验证,确保数据的准确性。因此,系统开销较大。

    2、 数据报套接字(datagram socket)

    定义:#define SOCK_DGRAM 2

    数据报套接字也支持双向的数据流,但不保证传输数据的准确性,但保留了记录边界。由于数据报套接字是无联接的,例如广播时的联接,所以并不保证接收端是否正在侦听。数据报套接字传输效率比较高。

    3、原始套接字(raw-protocol interface)

    定义:#define SOCK_RAW 3

    原始套接字保存了数据包中的完整IP头,前面两种套接字只能收到用户数据。因此可以通过原始套接字对数据进行分析。

    ◆Socket开发所必须需要的文件(以WinSock V2.0为例):

    头文件:Winsock2.h

    库文件:WS2_32.LIB

    动态库:W32_32.DLL

    四、基本套接字

    1、创建套接字——socket()

    功能:使用前创建一个新的套接字

    格式:SOCKET PASCAL FAR socket(int af,int type,int procotol);

    参数:af: 通信发生的区域

    type: 要建立的套接字类型

    procotol: 使用的特定协议

    2、指定本地地址——bind()

    功能:将套接字地址与所创建的套接字号联系起来。

    格式:int PASCAL FAR bind(SOCKET s,const struct sockaddr FAR * name,int namelen);

    参数:s: 是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。

    其它:没有错误,bind()返回0,否则SOCKET_ERROR

    地址结构说明:

    struct sockaddr_in

    {

    short sin_family;//AF_INET

    u_short sin_port;//16位端口号,网络字节顺序

    struct in_addr sin_addr;//32位IP地址,网络字节顺序

    char sin_zero[8];//保留

    }

    3、建立套接字连接——connect()和accept()

    功能:共同完成连接工作

    格式:int PASCAL FAR connect(SOCKET s,const struct sockaddr FAR * name,int namelen);

    SOCKET PASCAL FAR accept(SOCKET s,struct sockaddr FAR * name,int FAR * addrlen);

    参数:同上

    4、监听连接——listen()

    功能:用于面向连接服务器,表明它愿意接收连接。

    格式:int PASCAL FAR listen(SOCKET s, int backlog);

    5、数据传输——send()与recv()

    功能:数据的发送与接收

    格式:int PASCAL FAR send(SOCKET s,const char FAR * buf,int len,int flags);

    int PASCAL FAR recv(SOCKET s,const char FAR * buf,int len,int flags);

    参数:buf:指向存有传输数据的缓冲区的指针。

    6、多路复用——select()

    功能:用来检测一个或多个套接字状态。

    格式:int PASCAL FAR select(int nfds,fd_set FAR * readfds,fd_set FAR * writefds,

    fd_set FAR * exceptfds,const struct timeval FAR * timeout);

    参数:readfds:指向要做读检测的指针

    writefds:指向要做写检测的指针

    exceptfds:指向要检测是否出错的指针

    timeout:最大等待时间

    7、关闭套接字——closesocket()

    功能:关闭套接字s

    格式:BOOL PASCAL FAR closesocket(SOCKET s);

    JAVA Socket 套接字原理

    用Java开发网络软件非常方便和强大,Java的这种力量来源于他独有的一套强大的用于网络的 API,这些API是一系列的类和接口,均位于包java.net和javax.net中。在这篇文章中我们将介绍套接字(Socket)慨念,同时以实例说明如何使用Network API操纵套接字,在完成本文后,你就可以编写网络低端通讯软件。

    什么是套接字(Socket)?

    Network API是典型的用于基于TCP/IP网络Java程序与其他程序通讯,Network API依靠Socket进行通讯。Socket可以看成在两个程序进行通讯连接中的一个端点,一个程序将一段信息写入Socket中,该Socket将这段信息发送给另外一个Socket中,使这段信息能传送到其他程序中。如图1

    a017c1afa6bdddb71c9d18b33ad5d90a.png

    我们来分析一下图1,Host A上的程序A将一段信息写入Socket中,Socket的内容被Host A的网络管理软件访问,并将这段信息通过Host A的网络接口卡发送到Host B,Host B的网络接口卡接收到这段信息后,传送给Host B的网络管理软件,网络管理软件将这段信息保存在Host B的Socket中,然后程序B才能在Socket中阅读这段信息。

    假设在图1的网络中添加第三个主机Host C,那么Host A怎么知道信息被正确传送到Host B而不是被传送到Host C中了呢?基于TCP/IP网络中的每一个主机均被赋予了一个唯一的IP地址,IP地址是一个32位的无符号整数,由于没有转变成二进制,因此通常以小数点分隔,如:198.163.227.6,正如所见IP地址均由四个部分组成,每个部分的范围都是0-255,以表示8位地址。

    值得注意的是IP地址都是32位地址,这是IP协议版本4(简称Ipv4)规定的,目前由于IPv4地址已近耗尽,所以IPv6地址正逐渐代替Ipv4地址,Ipv6地址则是128位无符号整数。

    假设第二个程序被加入图1的网络的Host B中,那么由Host A传来的信息如何能被正确的传给程序B而不是传给新加入的程序呢?这是因为每一个基于TCP/IP网络通讯的程序都被赋予了唯一的端口和端口号,端口是一个信息缓冲区,用于保留Socket中的输入/输出信息,端口号是一个16位无符号整数,范围是0-65535,以区别主机上的每一个程序(端口号就像房屋中的房间号),低于256的短口号保留给标准应用程序,比如pop3的端口号就是110,每一个套接字都组合进了IP地址、端口、端口号,这样形成的整体就可以区别每一个套接字t,下面我们就来谈谈两种套接字:流套接字和自寻址数据套接字。

    流套接字(Stream Socket)

    无论何时,在两个网络应用程序之间发送和接收信息时都需要建立一个可靠的连接,流套接字依靠TCP协议来保证信息正确到达目的地,实际上,IP包有可能在网络中丢失或者在传送过程中发生错误,任何一种情况发生,作为接受方的 TCP将联系发送方TCP重新发送这个IP包。这就是所谓的在两个流套接字之间建立可靠的连接。

    流套接字在C/S程序中扮演一个必需的角色,客户机程序(需要访问某些服务的网络应用程序)创建一个扮演服务器程序的主机的IP地址和服务器程序(为客户端应用程序提供服务的网络应用程序)的端口号的流套接字对象。

    客户端流套接字的初始化代码将IP地址和端口号传递给客户端主机的网络管理软件,管理软件将IP地址和端口号通过NIC传递给服务器端主机;服务器端主机读到经过NIC传递来的数据,然后查看服务器程序是否处于监听状态,这种监听依然是通过套接字和端口来进行的;如果服务器程序处于监听状态,那么服务器端网络管理软件就向客户机网络管理软件发出一个积极的响应信号,接收到响应信号后,客户端流套接字初始化代码就给客户程序建立一个端口号,并将这个端口号传递给服务器程序的套接字(服务器程序将使用这个端口号识别传来的信息是否是属于客户程序)同时完成流套接字的初始化。

    如果服务器程序没有处于监听状态,那么服务器端网络管理软件将给客户端传递一个消极信号,收到这个消极信号后,客户程序的流套接字初始化代码将抛出一个异常对象并且不建立通讯连接,也不创建流套接字对象。这种情形就像打电话一样,当有人的时候通讯建立,否则电话将被挂起。

    这部分的工作包括了相关联的三个类:InetAddress, Socket, 和 ServerSocket。 InetAddress对象描绘了32位或128位IP地址,Socket对象代表了客户程序流套接字,ServerSocket代表了服务程序流套接字,所有这三个类均位于包java.net中。

    InetAddress类

    InetAddress类在网络API套接字编程中扮演了一个重要角色。参数传递给流套接字类和自寻址套接字类构造器或非构造器方法。InetAddress描述了32位或64位IP地址,要完成这个功能,InetAddress类主要依靠两个支持类Inet4Address 和 Inet6Address,这三个类是继承关系,InetAddrress是父类,Inet4Address 和 Inet6Address是子类。

    由于InetAddress类只有一个构造函数,而且不能传递参数,所以不能直接创建InetAddress对象,比如下面的做法就是错误的:

    InetAddress ia = new InetAddress ();

    但我们可以通过下面的5个工厂方法创建来创建一个InetAddress对象或InetAddress数组:

    . getAllByName(String host)方法返回一个InetAddress对象的引用,每个对象包含一个表示相应主机名的单独的IP地址,这个IP地址是通过host参数传递的,对于指定的主机如果没有IP地址存在那么这个方法将抛出一个UnknownHostException 异常对象。

    . getByAddress(byte [] addr)方法返回一个InetAddress对象的引用,这个对象包含了一个Ipv4地址或Ipv6地址,Ipv4地址是一个4字节数组,Ipv6地址是一个16字节地址数组,如果返回的数组既不是4字节的也不是16字节的,那么方法将会抛出一个UnknownHostException异常对象。

    . getByAddress(String host, byte [] addr)方法返回一个InetAddress对象的引用,这个InetAddress对象包含了一个由host和4字节的addr数组指定的IP地址,或者是host和16字节的addr数组指定的IP地址,如果这个数组既不是4字节的也不是16位字节的,那么该方法将抛出一个UnknownHostException异常对象。

    . getByName(String host)方法返回一个InetAddress对象,该对象包含了一个与host参数指定的主机相对应的IP地址,对于指定的主机如果没有IP地址存在,那么方法将抛出一个UnknownHostException异常对象。

    . getLocalHost()方法返回一个InetAddress对象,这个对象包含了本地机的IP地址,考虑到本地主机既是客户程序主机又是服务器程序主机,为避免混乱,我们将客户程序主机称为客户主机,将服务器程序主机称为服务器主机。

    上面讲到的方法均提到返回一个或多个InetAddress对象的引用,实际上每一个方法都要返回一个或多个Inet4Address/Inet6Address对象的引用,调用者不需要知道引用的子类型,相反调用者可以使用返回的引用调用InetAddress对象的非静态方法,包括子类型的多态以确保重载方法被调用。

    InetAddress和它的子类型对象处理主机名到主机IPv4或IPv6地址的转换,要完成这个转换需要使用域名系统,下面的代码示范了如何通过调用getByName(String host)方法获得InetAddress子类对象的方法,这个对象包含了与host参数相对应的IP地址:

    InetAddress ia = InetAddress.getByName ("www.javajeff.com"));

    一但获得了InetAddress子类对象的引用就可以调用InetAddress的各种方法来获得InetAddress子类对象中的IP地址信息,比如,可以通过调用getCanonicalHostName()从域名服务中获得标准的主机名;getHostAddress()获得IP地址,getHostName()获得主机名,isLoopbackAddress()判断IP地址是否是一个loopback地址。

    List1 是一段示范代码:InetAddressDemo

    // InetAddressDemo.java

    import java.net.*;

    class InetAddressDemo {  public static void main (String [] args) throws UnknownHostException  {   String host = "localhost";

    if (args.length == 1)    host = args [0];

    InetAddress ia = InetAddress.getByName (host);

    System.out.println ("Canonical Host Name = " +         ia.getCanonicalHostName ());   System.out.println ("Host Address = " +         ia.getHostAddress ());   System.out.println ("Host Name = " +         ia.getHostName ());   System.out.println ("Is Loopback Address = " +         ia.isLoopbackAddress ());  } }

    当无命令行参数时,代码输出类似下面的结果:

    Canonical Host Name = localhost Host Address = 127.0.0.1 Host Name = localhost Is Loopback Address = true

    InetAddressDemo给了你一个指定主机名作为命令行参数的选择,如果没有主机名被指定,那么将使用localhost(客户机的),InetAddressDemo通过调用getByName(String host)方法获得一个InetAddress子类对象的引用,通过这个引用获得了标准主机名,主机地址,主机名以及IP地址是否是loopback地址的输出。

    Socket类

    当客户程序需要与服务器程序通讯的时候,客户程序在客户机创建一个socket对象,Socket类有几个构造函数。两个常用的构造函数是 Socket(InetAddress addr, int port) 和 Socket(String host, int port),两个构造函数都创建了一个基于Socket的连接服务器端流套接字的流套接字。对于第一个InetAddress子类对象通过addr参数获得服务器主机的IP地址,对于第二个函数host参数包被分配到InetAddress对象中,如果没有IP地址与host参数相一致,那么将抛出UnknownHostException异常对象。两个函数都通过参数port获得服务器的端口号。假设已经建立连接了,网络API将在客户端基于Socket的流套接字中捆绑客户程序的IP地址和任意一个端口号,否则两个函数都会抛出一个IOException对象。

    如果创建了一个Socket对象,那么它可能通过调用Socket的 getInputStream()方法从服务程序获得输入流读传送来的信息,也可能通过调用Socket的 getOutputStream()方法获得输出流来发送消息。在读写活动完成之后,客户程序调用close()方法关闭流和流套接字,下面的代码创建了一个服务程序主机地址为198.163.227.6,端口号为13的Socket对象,然后从这个新创建的Socket对象中读取输入流,然后再关闭流和Socket对象。

    Socket s = new Socket ("198.163.227.6", 13); InputStream is = s.getInputStream (); // Read from the stream. is.close (); s.close ();

    接下面我们将示范一个流套接字的客户程序,这个程序将创建一个Socket对象,Socket将访问运行在指定主机端口10000上的服务程序,如果访问成功客户程序将给服务程序发送一系列命令并打印服务程序的响应。List2使我们创建的程序SSClient的源代码:

    Listing 2: SSClient.java

    // SSClient.java

    import java.io.*; import java.net.*;

    class SSClient {  public static void main (String [] args)  {   String host = "localhost";

    // If user specifies a command-line argument, that argument   // represents the host name.

    if (args.length == 1)    host = args [0];

    BufferedReader br = null;   PrintWriter pw = null;   Socket s = null;

    try   {    // Create a socket that attempts to connect to the server    // program on the host at port 10000.

    s = new Socket (host, 10000);

    // Create an input stream reader that chains to the socket's    // byte-oriented input stream. The input stream reader    // converts bytes read from the socket to characters. The    // conversion is based on the platform's default character    // set.

    InputStreamReader isr;    isr = new InputStreamReader (s.getInputStream ());

    // Create a buffered reader that chains to the input stream    // reader. The buffered reader supplies a convenient method    // for reading entire lines of text.

    br = new BufferedReader (isr);

    // Create a print writer that chains to the socket's byte-    // oriented output stream. The print writer creates an    // intermediate output stream writer that converts    // characters sent to the socket to bytes. The conversion    // is based on the platform's default character set.

    pw = new PrintWriter (s.getOutputStream (), true);

    // Send the DATE command to the server.

    pw.println ("DATE");

    // Obtain and print the current date/time.

    System.out.println (br.readLine ());    // Send the PAUSE command to the server. This allows several    // clients to start and verifies that the server is spawning    // multiple threads.

    pw.println ("PAUSE");    // Send the DOW command to the server.

    pw.println ("DOW");

    // Obtain and print the current day of week.

    System.out.println (br.readLine ());

    // Send the DOM command to the server.      pw.println ("DOM");

    // Obtain and print the current day of month.

    System.out.println (br.readLine ());

    // Send the DOY command to the server.

    pw.println ("DOY");

    // Obtain and print the current day of year.

    System.out.println (br.readLine ());   }   catch (IOException e)   {    System.out.println (e.toString ());   }   finally   {    try    {     if (br != null)      br.close ();

    if (pw != null)      pw.close ();

    if (s != null)      s.close ();    }    catch (IOException e)    {     }   }  } }

    运行这段程序将会得到下面的结果:

    Tue Jan 29 18:11:51 CST 2002 TUESDAY 29 29

    SSClient创建了一个Socket对象与运行在主机端口10000的服务程序联系,主机的IP地址由host变量确定。SSClient将获得Socket的输入输出流,围绕BufferedReader的输入流和PrintWriter的输出流对字符串进行读写操作就变得非常容易,SSClient个服务程序发出各种date/time命令并得到响应,每个响应均被打印,一旦最后一个响应被打印,将执行Try/Catch/Finally结构的Finally子串,Finally子串将在关闭Socket之前关闭BufferedReader 和 PrintWriter。

    在SSClient源代码编译完成后,可以输入java SSClient 来执行这段程序,如果有合适的程序运行在不同的主机上,采用主机名/IP地址为参数的输入方式,比如www.sina.com.cn是运行服务器程序的主机,那么输入方式就是java SSClient www.sina.com.cn。

    技巧

    Socket类包含了许多有用的方法。比如getLocalAddress()将返回一个包含客户程序IP地址的InetAddress子类对象的引用;getLocalPort()将返回客户程序的端口号;getInetAddress()将返回一个包含服务器IP地址的InetAddress子类对象的引用;getPort()将返回服务程序的端口号。

    ServerSocket类

    由于SSClient使用了流套接字,所以服务程序也要使用流套接字。这就要创建一个ServerSocket对象,ServerSocket有几个构造函数,最简单的是ServerSocket(int port),当使用ServerSocket(int port)创建一个ServerSocket对象,port参数传递端口号,这个端口就是服务器监听连接请求的端口,如果在这时出现错误将抛出IOException异常对象,否则将创建ServerSocket对象并开始准备接收连接请求。

    接下来服务程序进入无限循环之中,无限循环从调用ServerSocket的accept()方法开始,在调用开始后accept()方法将导致调用线程阻塞直到连接建立。在建立连接后accept()返回一个最近创建的Socket对象,该Socket对象绑定了客户程序的IP地址或端口号。

    由于存在单个服务程序与多个客户程序通讯的可能,所以服务程序响应客户程序不应该花很多时间,否则客户程序在得到服务前有可能花很多时间来等待通讯的建立,然而服务程序和客户程序的会话有可能是很长的(这与电话类似),因此为加快对客户程序连接请求的响应,典型的方法是服务器主机运行一个后台线程,这个后台线程处理服务程序和客户程序的通讯。

    为了示范我们在上面谈到的慨念并完成SSClient程序,下面我们创建一个SSServer程序,程序将创建一个ServerSocket对象来监听端口10000的连接请求,如果成功服务程序将等待连接输入,开始一个线程处理连接,并响应来自客户程序的命令。下面就是这段程序的代码:

    Listing 3: SSServer.java

    // SSServer.java

    import java.io.*; import java.net.*; import java.util.*;

    class SSServer {  public static void main (String [] args) throws IOException  {    System.out.println ("Server starting...\n");

    // Create a server socket that listens for incoming connection   // requests on port 10000.

    ServerSocket server = new ServerSocket (10000);

    while (true)   {    // Listen for incoming connection requests from client    // programs, establish a connection, and return a Socket    // object that represents this connection.

    Socket s = server.accept ();

    System.out.println ("Accepting Connection...\n");

    // Start a thread to handle the connection.

    new ServerThread (s).start ();   }  } }

    class ServerThread extends Thread {  private Socket s;

    ServerThread (Socket s)  {   this.s = s;  }

    public void run ()  {   BufferedReader br = null;   PrintWriter pw = null;

    try   {    // Create an input stream reader that chains to the socket's    // byte-oriented input stream. The input stream reader    // converts bytes read from the socket to characters. The    // conversion is based on the platform's default character    // set.

    InputStreamReader isr;    isr = new InputStreamReader (s.getInputStream ());

    // Create a buffered reader that chains to the input stream    // reader. The buffered reader supplies a convenient method    // for reading entire lines of text.

    br = new BufferedReader (isr);

    // Create a print writer that chains to the socket's byte-    // oriented output stream. The print writer creates an    // intermediate output stream writer that converts    // characters sent to the socket to bytes. The conversion    // is based on the platform's default character set.

    pw = new PrintWriter (s.getOutputStream (), true);

    // Create a calendar that makes it possible to obtain date    // and time information.

    Calendar c = Calendar.getInstance ();

    // Because the client program may send multiple commands, a    // loop is required. Keep looping until the client either    // explicitly requests termination by sending a command    // beginning with letters BYE or implicitly requests    // termination by closing its output stream.

    do    {     // Obtain the client program's next command.

    String cmd = br.readLine ();

    // Exit if client program has closed its output stream.

    if (cmd == null)      break;        // Convert command to uppercase, for ease of comparison.

    cmd = cmd.toUpperCase ();

    // If client program sends BYE command, terminate.

    if (cmd.startsWith ("BYE"))      break;

    // If client program sends DATE or TIME command, return     // current date/time to the client program.

    if (cmd.startsWith ("DATE") || cmd.startsWith ("TIME"))      pw.println (c.getTime ().toString ());

    // If client program sends DOM (Day Of Month) command,     // return current day of month to the client program.

    if (cmd.startsWith ("DOM"))      pw.println ("" + c.get (Calendar.DAY_OF_MONTH));

    // If client program sends DOW (Day Of Week) command,     // return current weekday (as a string) to the client     // program.

    if (cmd.startsWith ("DOW"))      switch (c.get (Calendar.DAY_OF_WEEK))     {      case Calendar.SUNDAY : pw.println ("SUNDAY");       break;

    case Calendar.MONDAY : pw.println ("MONDAY");       break;

    case Calendar.TUESDAY : pw.println ("TUESDAY");       break;

    case Calendar.WEDNESDAY: pw.println ("WEDNESDAY");       break;

    case Calendar.THURSDAY : pw.println ("THURSDAY");       break;

    case Calendar.FRIDAY : pw.println ("FRIDAY");       break;

    case Calendar.SATURDAY : pw.println ("SATURDAY");     }

    // If client program sends DOY (Day of Year) command,     // return current day of year to the client program.

    if (cmd.startsWith ("DOY"))      pw.println ("" + c.get (Calendar.DAY_OF_YEAR));

    // If client program sends PAUSE command, sleep for three      // seconds.       if (cmd.startsWith ("PAUSE"))     try     {      Thread.sleep (3000);     }     catch (InterruptedException e)     {     }    }    while (true);    {    catch (IOException e)    {     System.out.println (e.toString ());    }    finally    {     System.out.println ("Closing Connection...\n");

    try     {      if (br != null)       br.close ();

    if (pw != null)        pw.close ();

    if (s != null)        s.close ();     }     catch (IOException e)     {     }    }   } }

    运行这段程序将得到下面的输出:

    Server starting... Accepting Connection... Closing Connection...

    SSServer的源代码声明了一对类:SSServer 和ServerThread;SSServer的main()方法创建了一个ServerSocket对象来监听端口10000上的连接请求,如果成功, SSServer进入一个无限循环中,交替调用ServerSocket的 accept() 方法来等待连接请求,同时启动后台线程处理连接(accept()返回的请求)。线程由ServerThread继承的start()方法开始,并执行ServerThread的run()方法中的代码。

    一旦run()方法运行,线程将创建BufferedReader, PrintWriter和 Calendar对象并进入一个循环,这个循环由读(通过BufferedReader的 readLine())来自客户程序的一行文本开始,文本(命令)存储在cmd引用的string对象中,如果客户程序过早的关闭输出流,会发生什么呢?答案是:cmd将得不到赋值。

    注意必须考虑到这种情况:在服务程序正在读输入流时,客户程序关闭了输出流,如果没有对这种情况进行处理,那么程序将产生异常。

    一旦编译了SSServer的源代码,通过输入Java SSServer来运行程序,在开始运行SSServer后,就可以运行一个或多个SSClient程序。

    DatagramSocket类

    DatagramSocket类在客户端创建自寻址套接字与服务器端进行通信连接,并发送和接受自寻址套接字。虽然有多个构造函数可供选择,但我发现创建客户端自寻址套接字最便利的选择是DatagramSocket()函数,而服务器端则是DatagramSocket(int port)函数,如果未能创建自寻址套接字或绑定自寻址套接字到本地端口,那么这两个函数都将抛出一个SocketException对象,一旦程序创建了DatagramSocket对象,那么程序分别调用send(DatagramPacket dgp)和 receive(DatagramPacket dgp)来发送和接收自寻址数据包,

    List4显示的DGSClient源代码示范了如何创建自寻址套接字以及如何通过套接字处理发送和接收信息

    Listing 4: DGSClient.java // DGSClient.java

    import java.io.*; import java.net.*;

    class DGSClient {  public static void main (String [] args)  {   String host = "localhost";

    // If user specifies a command-line argument, that argument   // represents the host name.     if (args.length == 1)    host = args [0];

    DatagramSocket s = null;

    try   {    // Create a datagram socket bound to an arbitrary port.

    s = new DatagramSocket ();

    // Create a byte array that will hold the data portion of a    // datagram packet''s message. That message originates as a    // String object, which gets converted to a sequence of    // bytes when String''s getBytes() method is called. The    // conversion uses the platform''s default character set.

    byte [] buffer;    buffer = new String ("Send me a datagram").getBytes ();

    // Convert the name of the host to an InetAddress object.    // That object contains the IP address of the host and is    // used by DatagramPacket.

    InetAddress ia = InetAddress.getByName (host);

    // Create a DatagramPacket object that encapsulates a

    // reference to the byte array and destination address    // information. The destination address consists of the    // host''s IP address (as stored in the InetAddress object)    // and port number 10000 -- the port on which the server    // program listens.

    DatagramPacket dgp = new DatagramPacket (buffer,         buffer.length,         ia,         10000);

    // Send the datagram packet over the socket.

    s.send (dgp);

    // Create a byte array to hold the response from the server.    // program.

    byte [] buffer2 = new byte [100];

    // Create a DatagramPacket object that specifies a buffer    // to hold the server program''s response, the IP address of    // the server program''s computer, and port number 10000.

    dgp = new DatagramPacket (buffer2,       buffer.length,       ia,       10000);

    // Receive a datagram packet over the socket.

    s.receive (dgp);

    // Print the data returned from the server program and stored    // in the datagram packet.

    System.out.println (new String (dgp.getData ()));

    }   catch (IOException e)   {    System.out.println (e.toString ());   }   finally   {    if (s != null)     s.close ();    }  } }

    DGSClient由创建一个绑定任意本地(客户端)端口好的DatagramSocket对象开始,然后装入带有文本信息的数组buffer和描述服务器主机IP地址的InetAddress子类对象的引用,接下来,DGSClient创建了一个DatagramPacket对象,该对象加入了带文本信息的缓冲器的引用,InetAddress子类对象的引用,以及服务端口号10000, DatagramPacket的自寻址数据包通过方法sent()发送给服务器程序,于是一个包含服务程序响应的新的DatagramPacket对象被创建,receive()得到响应的自寻址数据包,然后自寻址数据包的getData()方法返回该自寻址数据包的一个引用,最后关闭DatagramSocket。

    DGSServer服务程序补充了DGSClient的不足,List5是DGSServer的源代码:

    Listing 5: DGSServer.java // DGSServer.java

    import java.io.*; import java.net.*;

    class DGSServer {  public static void main (String [] args) throws IOException  {   System.out.println ("Server starting ...\n");

    // Create a datagram socket bound to port 10000. Datagram   // packets sent from client programs arrive at this port.

    DatagramSocket s = new DatagramSocket (10000);

    // Create a byte array to hold data contents of datagram   // packet.

    byte [] data = new byte [100];

    // Create a DatagramPacket object that encapsulates a reference   // to the byte array and destination address information. The   // DatagramPacket object is not initialized to an address   // because it obtains that address from the client program.

    DatagramPacket dgp = new DatagramPacket (data, data.length);

    // Enter an infinite loop. Press Ctrl+C to terminate program.

    while (true)   {    // Receive a datagram packet from the client program.

    s.receive (dgp);

    // Display contents of datagram packet.

    System.out.println (new String (data));

    // Echo datagram packet back to client program.

    s.send (dgp);  } } }

    DGSServer创建了一个绑定端口10000的自寻址套接字,然后创建一个字节数组容纳自寻址信息,并创建自寻址包,下一步,DGSServer进入一个无限循环中以接收自寻址数据包、显示内容并将响应返回客户端,自寻址套接没有关闭,因为循环是无限的。

    在编译DGSServer 和DGSClient的源代码后,由输入java DGSServer开始运行DGSServer,然后在同一主机上输入Java DGSClient开始运行DGSClient,如果DGSServer与DGSClient运行于不同主机,在输入时注意要在命令行加上服务程序的主机名或IP地址,如:java DGSClient www.yesky.com

    展开全文
    weixin_29250403 2021-02-26 18:14:54
  • 原始套接字创建方法: socket(AF_INET, SOCK_RAW, protocol); 重点在protocol字段,这里就不能简单的将其值为0了。在头文件netinet/in.h中定义了系统中该字段目前能取的值,注意:有些系统中不一定实现了netinet/...

    比较新的内核才支持

    socket(fd, SOCK_RAW, IPPROTO_UDP); // 指定 TCP/UDP/ICMPint val = 1;ret = setsockopt(test->state.sockfd, IPPROTO_IPV6, IPV6_HDRINCL, &val, sizeof(val)); // 比较新的内核才支持remote.sin6_port = 0; // 必须设置成0sendto (socketFd, buffer, len, 0, (struct sockaddr *) &remote, sizeof(remote));

    一、修改iphdr+tcphdr

    对于TCP或UDP的程序开发,焦点在Data字段,我们没法直接对TCP或UDP头部字段进行赤裸裸的修改,当然还有IP头。换句话说,我们对它们头部操作的空间非常受限,只能使用它们已经开放给我们的诸如源、目的IP,源、目的端口等等。

    原始套接字的创建方法:

    socket(AF_INET, SOCK_RAW, protocol);

    重点在protocol字段,这里就不能简单的将其值为0了。在头文件netinet/in.h中定义了系统中该字段目前能取的值,注意:有些系统中不一定实现了netinet/in.h中的所有协议。源代码的linux/in.h中和netinet/in.h中的内容一样。我们常见的有IPPROTO_TCP,IPPROTO_UDP和IPPROTO_ICMP。

    用这种方式我就可以得到原始的IP包了,然后就可以自定义IP所承载的具体协议类型,如TCP,UDP或ICMP,并手动对每种承载在IP协议之上的报文进行填充。

    先简单复习一下TCP报文的格式

    6c5084752c87addfb4ea74494d8908a3.png

    ee2ee27bc7a9e6f27d865d6b2730db67.png

    原始套接字还提供了一个非常有用的参数IP_HDRINCL:

    1、当开启该参数时:我们可以从IP报文首部第一个字节开始依次构造整个IP报文的所有选项,但是IP报文头部中的标识字段(设置为0时)和IP首部校验和字段总是由内核自己维护的,不需要我们关心。

    2、如果不开启该参数:我们所构造的报文是从IP首部之后的第一个字节开始,IP首部由内核自己维护,首部中的协议字段被设置成调用socket()函数时我们所传递给它的第三个参数。

    开启IP_HDRINCL特性的模板代码一般为:

    const int on =1;if (setsockopt (sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) {printf("setsockopt error!\n");}

    所以,我们还得复习一下IP报文的首部格式:

    0b83036e3a873c5f888685581c67225d.png

    同样,我们重点关注IP首部中的着色部分区段的填充情况。

    #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include unsigned csum_tcpudp_nofold(unsigned saddr, unsigned daddr,unsigned len, unsigned proto, unsigned sum){unsigned long long s = (unsigned)sum;s += (unsigned)saddr;s += (unsigned)daddr;s += (proto + len) << 8;s += (s >> 32);return (unsigned)s;}unsigned short check_sum(unsigned short *addr, int len, unsigned sum){int nleft = len;unsigned short *w = addr;unsigned short ret = 0;while (nleft > 1) {sum += *w++;nleft -= 2;}if (nleft == 1) {*(unsigned char *)(&ret) = *(unsigned char *)w;sum += ret;}sum = (sum>>16) + (sum&0xffff);sum += (sum>>16);ret = ~sum;return ret;}//在该函数中构造整个IP报文,最后调用sendto函数将报文发送出去void attack(int skfd, struct sockaddr_in *target, unsigned short srcport){char buf[256] = {0};struct ip *ip;struct tcphdr *tcp;int ip_len;int op_len = 12;//在我们TCP的报文中Data没有字段,所以整个IP报文的长度ip_len = sizeof(struct ip) + sizeof(struct tcphdr) + op_len;//开始填充IP首部ip=(struct ip*)buf;ip->ip_v = IPVERSION;ip->ip_hl = sizeof(struct ip)>>2;ip->ip_tos = 0;ip->ip_len = htons(ip_len);ip->ip_id = 0;ip->ip_off = 0;ip->ip_ttl = MAXTTL;ip->ip_p = IPPROTO_TCP;ip->ip_sum = 0;ip->ip_dst = target->sin_addr;//开始填充TCP首部tcp = (struct tcphdr*)(buf+sizeof(struct ip));tcp->source = htons(srcport);tcp->dest = target->sin_port;srand(time(NULL));tcp->doff = (sizeof(struct tcphdr) + op_len) >> 2; // tcphdr + optiontcp->syn = 1;tcp->check = 0;tcp->window = ntohs(14600);int i = ip_len - op_len;// mss = 1460buf[i++] = 0x02;buf[i++] = 0x04;buf[i++] = 0x05;buf[i++] = 0xb4;// sackbuf[i++] = 0x01;buf[i++] = 0x01;buf[i++] = 0x04;buf[i++] = 0x02;// wsscale = 7buf[i++] = 0x01;buf[i++] = 0x03;buf[i++] = 0x03;buf[i++] = 0x07;int T = 1;while(1) {if (T == 0) break;T--;tcp->seq = random();//源地址伪造,我们随便任意生成个地址,让服务器一直等待下去//ip->ip_src.s_addr = random();//自定义源地址192.168.204.136 = 0xc0a8cc88; 反转赋值ip->ip_src.s_addr = 0x88cca8c0;unsigned sum = csum_tcpudp_nofold(ip->ip_src.s_addr, ip->ip_dst.s_addr, sizeof(struct tcphdr)+op_len, IPPROTO_TCP, 0);tcp->check = check_sum((unsigned short*)tcp, sizeof(struct tcphdr)+op_len, sum);// ip->ip_sum = check_sum((unsigned short*)ip, sizeof(struct ip), 0);sendto(skfd, buf, ip_len, 0, (struct sockaddr*)target, sizeof(struct sockaddr_in));}}int main(int argc, char** argv){int skfd;struct sockaddr_in target;struct hostent *host;const int on = 1;unsigned short srcport;if (argc != 4) {printf("Usage:%s dstip dstport srcport\n", argv[0]);exit(1);}bzero(&target, sizeof(struct sockaddr_in));target.sin_family = AF_INET;target.sin_port = htons(atoi(argv[2]));if (inet_aton(argv[1], &target.sin_addr) == 0) {host = gethostbyname(argv[1]);if(host == NULL) {printf("TargetName Error:%s\n", hstrerror(h_errno));exit(1);}target.sin_addr = *(struct in_addr *)(host->h_addr_list[0]);}//将协议字段置为IPPROTO_TCP,来创建一个TCP的原始套接字if (0 > (skfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP))) {perror("Create Error");exit(1);}//用模板代码来开启IP_HDRINCL特性,我们完全自己手动构造IP报文if (0 > setsockopt(skfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on))) {perror("IP_HDRINCL failed");exit(1);}//因为只有root用户才可以play with raw socket :)setuid(getpid());srcport = atoi(argv[3]);attack(skfd, &target, srcport);}

    原始套接字上也可以调用connet、bind之类的函数

    修改mac+iphdr+tcphdr

    blog.chinaunix.net/uid-23069658-id-3283534.html

    在Linux系统中要从链路层(MAC)直接收发数帧,比较普遍的做法就是用libpcap和libnet两个动态库来实现。但今天我们就要用原始套接字来实现这个功能。

    463b98a6adc2f95273cafe7cd0a80882.png

    这里的2字节帧类型用来指示该数据帧所承载的上层协议是IP、ARP或其他。

    为了实现直接从链路层收发数据帧,我们要用到原始套接字的如下形式:

    socket(PF_PACKET, type, protocol)

    1、其中type字段可取SOCK_RAW或SOCK_DGRAM。它们两个都使用一种与设备无关的标准物理层地址结构struct sockaddr_ll{},但具体操作的报文格式不同:

    SOCK_RAW:直接向网络硬件驱动程序发送(或从网络硬件驱动程序接收)没有任何处理的完整数据报文(包括物理帧的帧头),这就要求我们必须了解对应设备的物理帧帧头结构,才能正确地装载和分析报文。也就是说我们用这种套接字从网卡驱动上收上来的报文包含了MAC头部,如果我们要用这种形式的套接字直接向网卡发送数据帧,那么我们必须自己组装我们MAC头部。这正符合我们的需求。

    SOCK_DGRAM:这种类型的套接字对于收到的数据报文的物理帧帧头会被系统自动去掉,然后再将其往协议栈上层传递;同样地,在发送时数据时,系统将会根据sockaddr_ll结构中的目的地址信息为数据报文添加一个合适的MAC帧头。

    2、protocol字段,常见的,一般情况下该字段取ETH_P_IP,ETH_P_ARP,ETH_P_RARP或ETH_P_ALL,当然链路层协议很多,肯定不止我们说的这几个,但我们一般只关心这几个就够我们用了。这里简单提一下网络数据收发的一点基础。协议栈在组织数据收发流程时需要处理好两个方面的问题:“从上倒下”,即数据发送的任务;“从下到上”,即数据接收的任务。数据发送相对接收来说要容易些,因为对于数据接收而言,网卡驱动还要明确什么样的数据该接收、什么样的不该接收等问题。protocol字段可选的四个值及其意义如下:

    protocol 值 作用

    ETH_P_IP 0X0800 只接收发往目的MAC是本机的IP类型的数据帧

    ETH_P_ARP 0X0806 只接收发往目的MAC是本机的ARP类型的数据帧

    ETH_P_RARP 0X8035 只接受发往目的MAC是本机的RARP类型的数据帧

    ETH_P_ALL 0X0003 接收发往目的MAC是本机的所有类型(ip,arp,rarp)的数据帧,同时还可以接收从本机发出去的所有数据帧。在混杂模式打开的情况下,还会接收到发往目的MAC为非本地硬件地址的数据帧。

    protocol字段可取的所有协议参见/usr/include/linux/if_ether.h头文件里的定义。

    最后,格外需要留心一点的就是,发送数据的时候需要自己组织整个以太网数据帧。和地址相关的结构体就不能再用前面的struct sockaddr_in{}了,而是struct sockaddr_ll{},如下:

    struct sockaddr_ll{unsigned short sll_family; /* 总是 AF_PACKET */unsigned short sll_protocol; /* 物理层的协议 */int sll_ifindex; /* 接口号 */unsigned short sll_hatype; /* 报头类型 */unsigned char sll_pkttype; /* 分组类型 */unsigned char sll_halen; /* 地址长度 */unsigned char sll_addr[8]; /* 物理层地址 */};

    sll_protocoll:取值在linux/if_ether.h中,可以指定我们所感兴趣的二层协议;

    sll_ifindex:置为0表示处理所有接口,对于单网卡的机器就不存在“所有”的概念了。如果你有多网卡,该字段的值一般通过ioctl来搞定,模板代码如下,如果我们要获取eth0接口的序号,可以使用如下代码来获取:

    struct sockaddr_ll sll;struct ifreq ifr;strcpy(ifr.ifr_name, "eth0");ioctl(sockfd, SIOCGIFINDEX, &ifr);sll.sll_ifindex = ifr.ifr_ifindex;

    sll_hatype:ARP硬件地址类型,定义在 linux/if_arp.h 中。 取ARPHRD_ETHER时表示为以太网。

    sll_pkttype:包含分组类型。目前,有效的分组类型有:目标地址是本地主机的分组用的 PACKET_HOST,物理层广播分组用的 PACKET_BROADCAST ,发送到一个物理层多路广播地址的分组用的 PACKET_MULTICAST,在混杂(promiscuous)模式下的设备驱动器发向其他主机的分组用的 PACKET_OTHERHOST,源于本地主机的分组被环回到分组套接口用的 PACKET_OUTGOING。这些类型只对接收到的分组有意义。

    sll_addr和sll_halen指示物理层(如以太网,802.3,802.4或802.5等)地址及其长度,严格依赖于具体的硬件设备。类似于获取接口索引sll_ifindex,要获取接口的物理地址,可以采用如下代码:

    struct ifreq ifr;strcpy(ifr.ifr_name, "eth0");ioctl(sockfd, SIOCGIFHWADDR, &ifr);

    缺省情况下,从任何接口收到的符合指定协议的所有数据报文都会被传送到原始PACKET套接字口,而使用bind系统调用并以一个sochddr_ll结构体对象将PACKET套接字与某个网络接口相绑定,就可使我们的PACKET原始套接字只接收指定接口的数据报文。

    接下来我们简单介绍一下网卡是怎么收报的,如果你对这部分已经很了解可以跳过这部分内容。网卡从线路上收到信号流,网卡的驱动程序会去检查数据帧开始的前6个字节,即目的主机的MAC地址,如果和自己的网卡地址一致它才会接收这个帧,不符合的一般都是直接无视。然后该数据帧会被网络驱动程序分解,IP报文将通过网络协议栈,最后传送到应用程序那里。往上层传递的过程就是一个校验和“剥头”的过程,由协议栈各层去实现。

    接下来我们来写个简单的抓包程序,将那些发给本机的IPv4报文全打印出来:

    #include #include #include #include #include #include #include #include #include int main(int argc, char **argv){int sock, n;char buffer[2048];struct ethhdr *eth;struct iphdr *iph;if (0 > (sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP)))) {perror("socket");exit(1);}while (1) {printf("=====================================\n");//注意:在这之前我没有调用bind函数,原因是什么呢?n = recvfrom(sock, buffer, 2048, 0, NULL, NULL);printf("%d bytes read\n", n);//接收到的数据帧头6字节是目的MAC地址,紧接着6字节是源MAC地址。eth = (struct ethhdr*)buffer;printf("Dest MAC addr:%02x:%02x:%02x:%02x:%02x:%02x\n",eth->h_dest[0],eth->h_dest[1],eth->h_dest[2],eth->h_dest[3],eth->h_dest[4],eth->h_dest[5]);printf("Source MAC addr:%02x:%02x:%02x:%02x:%02x:%02x\n",eth->h_source[0],eth->h_source[1],eth->h_source[2],eth->h_source[3],eth->h_source[4],eth->h_source[5]);iph = (struct iphdr*)(buffer + sizeof(struct ethhdr));//我们只对IPV4且没有选项字段的IPv4报文感兴趣if(iph->version == 4 && iph->ihl == 5){unsigned char *sd, *dd;sd = (unsigned char*)&iph->saddr;dd = (unsigned char*)&iph->daddr;printf("Source Host: %d.%d.%d.%d Dest host: %d.%d.%d.%d\n", sd[0], sd[1], sd[2], sd[3], dd[0], dd[1], dd[2], dd[3]);// printf("Source host:%s\n", inet_ntoa(iph->saddr));// printf("Dest host:%s\n", inet_ntoa(iph->daddr));}}return 0;}

    构造mac源地址包,注意目标mac地址要正确,可以本机先抓包看看是什么

    #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include unsigned csum_tcpudp_nofold(unsigned saddr, unsigned daddr,unsigned len, unsigned proto, unsigned sum){unsigned long long s = (unsigned)sum;s += (unsigned)saddr;s += (unsigned)daddr;s += (proto + len) << 8;s += (s >> 32);return (unsigned)s;}unsigned short check_sum(unsigned short *addr, int len, unsigned sum){int nleft = len;unsigned short *w = addr;unsigned short ret = 0;while (nleft > 1) {sum += *w++;nleft -= 2;}if (nleft == 1) {*(unsigned char *)(&ret) = *(unsigned char *)w;sum += ret;}sum = (sum>>16) + (sum&0xffff);sum += (sum>>16);ret = ~sum;return ret;}int change(char c){if (c >= 'a') return c-'a'+10;if (c >= 'A') return c-'A'+10;return c-'0';}//在该函数中构造整个IP报文,最后调用sendto函数将报文发送出去void attack(int skfd, struct sockaddr_ll *target, char **argv){char buf[512]={0};struct ethhdr *eth;struct ip *ip;struct tcphdr *tcp;int pks_len;int i;int op_len = 12;unsigned short dstport;dstport = atoi(argv[3]);//在我们TCP的报文中Data没有字段,所以整个IP报文的长度pks_len = sizeof(struct ethhdr) + sizeof(struct ip) + sizeof(struct tcphdr) + op_len;eth = (struct ethhdr *) buf;/*eth->h_dest[0] = 0x00;eth->h_dest[1] = 0x50;eth->h_dest[2] = 0x56;eth->h_dest[3] = 0xee;eth->h_dest[4] = 0x14;eth->h_dest[5] = 0xa6;*/for (i=0;i<6;i++)eth->h_dest[i] = change(argv[1][i*3])*16 + change(argv[1][i*3+1]);/*eth->h_source[0] = 0x00;eth->h_source[1] = 0x0b;eth->h_source[2] = 0x28;eth->h_source[3] = 0xd7;eth->h_source[4] = 0x26;eth->h_source[5] = 0xa6;*/eth->h_proto = ntohs(ETH_P_IP);//开始填充IP首部ip=(struct ip*)(buf + sizeof(struct ethhdr));ip->ip_v = IPVERSION;ip->ip_hl = sizeof(struct ip) >> 2;ip->ip_tos = 0;ip->ip_len = htons(pks_len - sizeof(struct ethhdr));ip->ip_id = 0;ip->ip_off = 0;ip->ip_ttl = MAXTTL;ip->ip_p = IPPROTO_TCP;ip->ip_sum = 0;ip->ip_dst.s_addr = inet_addr(argv[2]);//开始填充TCP首部srand(time(NULL));tcp = (struct tcphdr*)(buf + sizeof(struct ethhdr) + sizeof(struct ip));tcp->source = random()%50000+10000;tcp->dest = ntohs(dstport);tcp->seq = random();tcp->doff = (sizeof(struct tcphdr) + op_len) >> 2;tcp->syn = 1;tcp->check = 0;tcp->window = ntohs(14600);i = pks_len - op_len;// mss = 1460buf[i++] = 0x02;buf[i++] = 0x04;buf[i++] = 0x05;buf[i++] = 0xb4;// sackbuf[i++] = 0x01;buf[i++] = 0x01;buf[i++] = 0x04;buf[i++] = 0x02;// wsscale = 7buf[i++] = 0x01;buf[i++] = 0x03;buf[i++] = 0x03;buf[i++] = 0x07;int T = 1;while(1) {if (T == 0) break;T--;//源地址伪造,我们随便任意生成个地址,让服务器一直等待下去ip->ip_src.s_addr = random();//自定义源地址192.168.204.136 => 0xc0a8cc88//ip->ip_src.s_addr = 0x8fcca8c0;unsigned sum = csum_tcpudp_nofold(ip->ip_src.s_addr, ip->ip_dst.s_addr, sizeof(struct tcphdr)+op_len, IPPROTO_TCP, 0);tcp->check = check_sum((unsigned short*)tcp, sizeof(struct tcphdr)+op_len, sum);ip->ip_sum = check_sum((unsigned short*)ip, sizeof(struct ip), 0);sendto(skfd, buf, pks_len, 0, (struct sockaddr*)target, sizeof(struct sockaddr_ll));}}int main(int argc, char** argv){int skfd;struct sockaddr_ll target;struct hostent *host;const int on=1;if (argc != 4) {printf("Usage:%s dstmac dstip dstport\n", argv[0]);exit(1);}if (strlen(argv[1]) != 17) {printf("Usage: dstmac must be xx:xx:xx:xx:xx:xx\n");exit(1);}//将协议字段置为IPPROTO_TCP,来创建一个TCP的原始套接字if (0 > (skfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP)))) {perror("Create Error");exit(1);}// macbzero(&target, sizeof(struct sockaddr_ll));struct ifreq ifr;strncpy(ifr.ifr_name, "eth0", IFNAMSIZ);ioctl(skfd, SIOCGIFINDEX, &ifr);target.sll_ifindex = ifr.ifr_ifindex;/*target.sll_family = AF_PACKET;target.sll_protocol = ntohs(80);target.sll_hatype = ARPHRD_ETHER;target.sll_pkttype = PACKET_OTHERHOST;target.sll_halen = ETH_ALEN;memset(target.sll_addr,0,8);target.sll_addr[0] = 0x00;target.sll_addr[1] = 0x0C;target.sll_addr[2] = 0x29;target.sll_addr[3] = 0x61;target.sll_addr[4] = 0xB6;target.sll_addr[5] = 0x43;*//*//http://blog.chinaunix.net/uid-305141-id-2133781.htmlstruct sockaddr_ll sll;struct ifreq ifstruct;memset (&sll, 0, sizeof (sll));sll.sll_family = PF_PACKET;sll.sll_protocol = htons (ETH_P_IP);strcpy (ifstruct.ifr_name, "eth0");ioctl (skfd, SIOCGIFINDEX, &ifstruct);sll.sll_ifindex = ifstruct.ifr_ifindex;strcpy (ifstruct.ifr_name, "eth0");ioctl (skfd, SIOCGIFHWADDR, &ifstruct);memcpy (sll.sll_addr, ifstruct.ifr_ifru.ifru_hwaddr.sa_data, ETH_ALEN);sll.sll_halen = ETH_ALEN;if (bind (skfd, (struct sockaddr *) &sll, sizeof (sll)) == -1) {printf ("bind: ERROR\n");return -1;}memset(&ifstruct, 0, sizeof(ifstruct));strcpy (ifstruct.ifr_name, "eth0");if (ioctl (skfd, SIOCGIFFLAGS, &ifstruct) == -1) {perror ("iotcl()\n");printf ("Fun:%s Line:%d\n", __func__, __LINE__);return -1;}ifstruct.ifr_flags |= IFF_PROMISC;if(ioctl(skfd, SIOCSIFFLAGS, &ifstruct) == -1) {perror("iotcl()\n");printf ("Fun:%s Line:%d\n", __func__, __LINE__);return -1;}*///因为只有root用户才可以play with raw socket :)setuid(getpid());// attack(skfd, &sll, srcport);attack(skfd, &target, argv);}

    展开全文
    weixin_39576127 2021-05-13 14:48:18
  • qq_17045267 2021-06-17 15:29:41
  • baidu_19348579 2021-11-04 15:13:11
  • HeroicLee 2021-10-21 18:10:53
  • baidu_39133469 2021-03-28 09:49:45
  • baidu_19348579 2021-11-09 14:20:59
  • c15855680841 2021-04-13 21:30:35
  • Windgs_YF 2021-11-03 17:29:49
  • ArtAndLife 2020-12-18 00:15:30
  • drippingstone 2021-04-19 19:49:07
  • qq_53111905 2021-08-09 17:46:10
  • code_peak 2021-07-11 15:17:14
  • weixin_39640195 2021-05-15 08:15:05
  • weixin_36431035 2021-05-10 19:28:00
  • qq_34999565 2021-08-09 10:50:22
  • zero_zero_seven 2021-11-24 17:42:10
  • dlz0836 2021-01-29 14:42:09
  • pyf09 2021-02-07 12:48:45
  • touchuyht 2021-07-13 11:29:01
  • weixin_35156503 2021-05-18 10:22:47
  • zhengjian1996 2021-01-23 09:52:51
  • weixin_34187444 2021-02-28 07:00:42
  • weixin_53587094 2021-01-14 16:44:59
  • jin787730090 2021-06-10 14:34:15
  • qq_43425914 2021-01-13 15:48:53
  • qq_33166886 2021-01-27 14:03:01
  • code_peak 2021-07-11 18:03:33

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 71,601
精华内容 28,640
关键字:

原始套接字可创建几种套接字