`

rawsocket发送tcp包

 
阅读更多
testTcp.h
#ifndef TESTTCP_H
#define TESTTCP_H
#include <endian.h>

#pragma pack(1)

//ip协议头
struct IPHeader
{
    unsigned char headerLen:4;
    unsigned char version:4;
    unsigned char tos; //服务类型
    unsigned short totalLen; //总长度
    unsigned short id; //标识
    unsigned short flagOffset; //3位标志+13位片偏移
    unsigned char ttl; //TTL
    unsigned char protocol; //协议
    unsigned short checksum; //首部检验和
    unsigned int srcIP; //源IP地址
    unsigned int dstIP; //目的IP地址
};

 //TCP首部
struct TCPHeader
{
    unsigned short srcPort; //源端口
    unsigned short dstPort; //目的端口
    //封包序号 如果 TCP 数据太大时(大于 IP 封包的容许程度), 就得要进行分段.这个 Sequence Number 就是记录每个封包的序号,
    //可以让收受端重新将 TCP 的数据组合起来。序号字段的值则指的是本报文段所发送的数据的第一个字节的序号
    unsigned int seqNum;
    //确认号 为了确认主机端确实有收到我们 client 端所送出的封包数据,我们 client 端当然希望能够收到主机方面的响应,那就是这个 Acknowledge Number 的用途了
    //当 client 端收到这个确认码时,就能够确定之前传递的封包已经被正确的收下了.这个号是期望收到对方的下一个报文段的数据的第一个字节的序号
    unsigned int ackNum;
#if LITTLE_ENDIAN
    unsigned char reserved1:4; //保留6位中的4位首部长度
    unsigned char headerLen:4;        //tcp头部长度
    //flags
    unsigned char fin:1; // 用来释放一个连接。当FIN=1时表示要求释放连接。
    unsigned char syn:1; //同步当SYN=1时,表示这是一个连接请求或连接接受报文。
    unsigned char rst:1; //复位,当TCP连接中出现了严重差错,必须释放连接。
    unsigned char psh:1; //当两个应用程序进行通信时,当PSH=1时,表示尽快地用“推送”给应用程序,而不用等到缓冲区满了再向上交付。
    unsigned char ack:1; //当ACK=1时,确认字段有效,在连接建立后的所有报文段都必须把ACK置为1。
    unsigned char urg:1; //紧急指针,当URG=1时,表示紧急指针有效,应该尽快传送。用来处理避免TCP数据流中断
    unsigned char reseverd2:2; //保留6位中的2位
#else
    unsigned char headerLen:4;        //tcp头部长度
    unsigned char reserved1:4; //保留6位中的4位首部长度
    unsigned char reseverd2:2; //保留6位中的2位
    unsigned char urg:1; //当URG=1时,表示紧急指针有效,应该尽快传送。
    unsigned char ack:1; //当ACK=1时,确认字段有效,在连接建立后的所有报文段都必须把ACK置为1。
    unsigned char psh:1; //当两个应用程序进行通信时,当PSH=1时,表示尽快地用“推送”给应用程序,而不用等到缓冲区满了再向上交付。
    unsigned char rst:1; //复位,当TCP连接中出现了严重差错,必须释放连接。
    unsigned char syn:1; //同步当SYN=1时,表示这是一个连接请求或连接接受报文。
    unsigned char fin:1; // 用来释放一个连接。当FIN=1时表示要求释放连接。
#endif
    unsigned short windownSize; //16位窗口大小
    unsigned short checksum; //16位TCP检验和
    unsigned short urgPtr; //16位紧急指针 当URG=1时.表示紧急指针有效.应该尽快传送,不要按本来的列队次序传送
};

//TCP伪首部
struct PseudoHeader
{
    unsigned int srcIP; //源地址
    unsigned int dstIP; //目的地址
    unsigned char mustBeZero;//置空,用于填充对齐
    unsigned char protocol; //协议类型
    unsigned short len; //TCP长度
};

//Max Segment Size最大segment长度
struct MSSOption
{
    unsigned char kind;
    unsigned char length;
    unsigned short maxValue;
};

//Selective Acknowledgment
struct SACKOption
{
    unsigned char kind;
    unsigned char length;
};

//rtt即等于现在的时间tcp_time_stamp减去Timestamp Echo Reply
struct TimestampsOption
{
    unsigned char kind;
    unsigned char length;
    unsigned int timestamp;
    unsigned int timestampReply;
};

struct NoOperation
{
    unsigned char type;
};

struct WindowScaleOption
{
    unsigned char kind;
    unsigned char length;
    unsigned char shiftCount;
};

#pragma pack()

#endif // TESTTCP_H




testTcp.cpp
#include "testTcp.h"
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <errno.h>
using namespace std;

int g_tcpSeqNum = 111;
int g_tcpAckNum = 0;

//ip数字转字符串
void ipLLToStr(long long ip_num,char* ip_str)
{
    unsigned int iptok1 = (ip_num & 0xFF000000) >> 24;
    unsigned int iptok2 = (ip_num & 0x00FF0000) >> 16;
    unsigned int iptok3 = (ip_num & 0x0000FF00) >> 8;
    unsigned int iptok4 = ip_num & 0x000000FF;
    char ip[32];
    bzero(ip,sizeof(ip));
    snprintf(ip,sizeof(ip),"%d.%d.%d.%d",iptok1,iptok2,iptok3,iptok4);
    strcpy(ip_str,ip);
}

//打印ip头
void printIPHeader(IPHeader* ipHeader)
{
    char srcIPStr[64] = "",dstIPStr[64]="";
    ipLLToStr(ntohl(ipHeader->srcIP),srcIPStr);
    ipLLToStr(ntohl(ipHeader->dstIP),dstIPStr);
    int totalLen = ntohs(ipHeader->totalLen);
    char ipHeaderStr[256] = "";
    snprintf(ipHeaderStr,sizeof(ipHeaderStr),"ip header: version:%d,tos:%d,protocol:%d,ttl:%d,srcIP:%s,dstIP:%s,totalLen:%d"
             ,ipHeader->version,ipHeader->tos,ipHeader->protocol,ipHeader->ttl,srcIPStr,dstIPStr,totalLen);
    cout << ipHeaderStr << endl;
}

//打印tcp头
void printTCPHeader(TCPHeader* tcpHeader)
{
    char tcpHeaderStr[256] = "";
    snprintf(tcpHeaderStr,sizeof(tcpHeaderStr),"tcp header:srcPort:%d,dstPort:%d,seqNum:%u,ackNum:%u,headerLen:%d,fin:%d,syn:%d,rst:%d"
             ",psh:%d,ack:%d,urg:%d,windowsSize:%d,checksum:%d,urgPtr:%d",ntohs(tcpHeader->srcPort),ntohs(tcpHeader->dstPort)
             ,ntohl(tcpHeader->seqNum),ntohl(tcpHeader->ackNum),tcpHeader->headerLen<<2,tcpHeader->fin,tcpHeader->syn,tcpHeader->rst
             ,tcpHeader->psh,tcpHeader->ack,tcpHeader->urg,ntohs(tcpHeader->windownSize),ntohs(tcpHeader->checksum),tcpHeader->urgPtr);
    cout << tcpHeaderStr << endl;
}

//构造IP头
void buildIPHeader(IPHeader* ipHeader,int totalLen,int srcIPNum,int dstIPNum)
{
    ipHeader->headerLen = sizeof(IPHeader)>>2;
    ipHeader->version = IPVERSION;
    //服务类型
    ipHeader->tos = 0;
    ipHeader->totalLen = htons(totalLen);
    ipHeader->id=htons(0);
    //设置flag标记为0
    ipHeader->flagOffset=htons(0x02 << 13); ;
    //运用的协议为TCP协议
    ipHeader->protocol=IPPROTO_TCP;
    //一个封包在网络上可以存活的时间
    ipHeader->ttl=64;
    ipHeader->srcIP = srcIPNum;
    ipHeader->dstIP = dstIPNum;
}

//tcp和udp的校验和算法是相同的
unsigned short calChecksum(unsigned short* buffer, int size)
{
    unsigned long cksum = 0;
    while(size>1)
    {
        cksum += *buffer++;
        size -= sizeof(unsigned short);
    }
    if(size){
        cksum += *(unsigned char*)buffer;
    }
    cksum = (cksum>>16) + (cksum&0xffff); //将高16bit与低16bit相加
    cksum += (cksum>>16); //将进位到高位的16bit与低16bit 再相加
    return (unsigned short)(~cksum);
}

//构造tcp协议头
void buildTCPHeader(TCPHeader* tcpHeader,sockaddr_in* srcAddr,sockaddr_in* dstAddr,IPHeader* ipHeader,int headerLen,int seqNum,int ackNum,char syn,char ack)
{
    tcpHeader->srcPort = srcAddr->sin_port;
    tcpHeader->dstPort = dstAddr->sin_port;
    tcpHeader->seqNum = htonl(seqNum);
    tcpHeader->ackNum = htonl(ackNum);
    tcpHeader->urg = 0;
    tcpHeader->ack = ack;
    tcpHeader->psh = 0;
    tcpHeader->rst = 0;
    tcpHeader->syn = syn;
    tcpHeader->fin = 0;
    tcpHeader->windownSize = htons(14600);
    tcpHeader->checksum = 0;
    tcpHeader->urgPtr = 0;
    tcpHeader->headerLen = headerLen>>2;

    //根据伪首部的buf计算ip头的校验和
    char psdHeaderBuf[256] = "";
    PseudoHeader* psdHeader = (PseudoHeader*)psdHeaderBuf;
    psdHeader->srcIP = ipHeader->srcIP;
    psdHeader->dstIP = ipHeader->dstIP;
    psdHeader->mustBeZero = 0;
    psdHeader->protocol = ipHeader->protocol;
    psdHeader->len = htons(sizeof(TCPHeader));
    memcpy(psdHeaderBuf+sizeof(PseudoHeader),tcpHeader,sizeof(TCPHeader));
    tcpHeader->checksum = calChecksum((unsigned short*)psdHeaderBuf, sizeof(PseudoHeader)+sizeof(TCPHeader));
}

//发送syn
int sendTcp(int sockfd,sockaddr_in* srcAddr,sockaddr_in* dstAddr,char syn,char ack,bool addMssOption,bool addSACKOption,bool addTsOption,bool addNoOption
            ,bool addWindowScale)
{
    char buf[1024] = "";
    int totalLen = sizeof(IPHeader) + sizeof(TCPHeader);
    if(addMssOption){
        totalLen += sizeof(MSSOption);
    }
    if(addSACKOption){
        totalLen += sizeof(SACKOption);
    }
    if(addTsOption){
        totalLen += sizeof(TimestampsOption);
    }
    if(addNoOption){
        totalLen += sizeof(NoOperation);
    }
    if(addWindowScale){
        totalLen += sizeof(WindowScaleOption);
    }
    int pos = 0;
    IPHeader* ipHeader = (IPHeader*)buf;
    buildIPHeader(ipHeader,totalLen,srcAddr->sin_addr.s_addr,dstAddr->sin_addr.s_addr);
    printIPHeader(ipHeader);
    pos += sizeof(IPHeader);
    TCPHeader* tcpHeader = (TCPHeader*)(buf+pos);
    buildTCPHeader(tcpHeader,srcAddr,dstAddr,ipHeader,totalLen-sizeof(IPHeader),g_tcpSeqNum++,g_tcpAckNum,syn,ack);
    printTCPHeader(tcpHeader);
    pos += sizeof(TCPHeader);
    if(addMssOption)
    {
        MSSOption* mssOption = (MSSOption*)(buf+pos);
        mssOption->kind = 2;//htons(2);
        mssOption->length = sizeof(MSSOption);//htons(4);
        mssOption->maxValue = htons(1460);
        pos += sizeof(MSSOption);
    }
    if(addSACKOption)
    {
        SACKOption* sackOption = (SACKOption*)(buf+pos);
        sackOption->kind = 4;
        sackOption->length = sizeof(SACKOption);
        pos += sizeof(SACKOption);
    }
    if(addTsOption)
    {
        TimestampsOption* tsOption = (TimestampsOption*)(buf+pos);
        tsOption->kind = 8;
        tsOption->length = sizeof(TimestampsOption);
        tsOption->timestamp = htonl(111);
        tsOption->timestampReply = 0;
        pos += sizeof(TimestampsOption);
    }
    if(addNoOption)
    {
        NoOperation* noOption = (NoOperation*)(buf+pos);
        noOption->type = 1;
        pos += sizeof(NoOperation);
    }
    if(addWindowScale)
    {
        WindowScaleOption* windowScaleOption = (WindowScaleOption*)(buf+pos);
        windowScaleOption->kind = 3;
        windowScaleOption->length = sizeof(WindowScaleOption);
        windowScaleOption->shiftCount = 6;
    }

    if(sendto(sockfd,buf,totalLen,0,(struct sockaddr *)dstAddr,sizeof(*dstAddr))<0){
        perror("sendto error");
    }
    return 0;
}

//解析syn+ack
void parseSynAck(int sockfd,sockaddr_in* dstAddr)
{
    char recvBuf[1024] = "";
    socklen_t cliLen = sizeof(dstAddr);
    int recvLen = recvfrom(sockfd,recvBuf,sizeof(recvBuf),0,(sockaddr*)&dstAddr,&cliLen);
    if(recvLen<=0)
    {
        cout << "parse syc ack fail,recvLen:" << recvLen << endl;
        return;
    }
    int pos = 0;
    IPHeader* ipHeader = (IPHeader*)recvBuf;
    pos += sizeof(IPHeader);
    printIPHeader(ipHeader);
    TCPHeader* tcpHeader = (TCPHeader*)(recvBuf+pos);
    printTCPHeader(tcpHeader);
    g_tcpSeqNum = ntohl(tcpHeader->ackNum);
}

int main()
{
    int sockfd = socket(AF_INET,SOCK_RAW,IPPROTO_TCP);
    if( sockfd < 0)
    {
        cout << strerror(errno) << endl;
        return -1;
    }


    char srcStr[32] = "172.16.96.52";
    char dstStr[32] = "192.168.145.51";
    int srcPort = 50852;
    int dstPort = 6699;

    sockaddr_in srcAddr;
    bzero(&srcAddr,sizeof(srcAddr));
    srcAddr.sin_family=AF_INET;
    srcAddr.sin_addr.s_addr = inet_addr(srcStr);
    srcAddr.sin_port = htons(srcPort);

    sockaddr_in dstAddr;
    bzero(&dstAddr,sizeof(dstAddr));
    dstAddr.sin_family=AF_INET;
    dstAddr.sin_addr.s_addr = inet_addr(dstStr);
    dstAddr.sin_port = htons(dstPort);

    //IPPROTO_TP说明用户自己填写IP报文
    //IP_HDRINCL表示由内核来计算IP报文的头部校验和,和填充那个IP的id
    int on = 1;
    setsockopt(sockfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on));
    cout << "syn:" << endl;
    char syn = 1,ack = 0;
    sendTcp(sockfd,&srcAddr,&dstAddr,syn,ack,true,true,true,true,true);

    cout << "syn+ack:" << endl;
    parseSynAck(sockfd,&dstAddr);

    //ack
//    syn = 0,ack = 1;
//    sendTcp(sockfd,&srcAddr,&dstAddr,syn,ack,true,true,true,true,true);
    return 0;
}

syn:
ip header: version:4,tos:0,protocol:6,ttl:64,srcIP:172.16.96.52,dstIP:192.168.145.51,totalLen:60
tcp header:srcPort:50852,dstPort:6699,seqNum:111,ackNum:0,headerLen:40,fin:0,syn:1,rst:0,psh:0,ack:0,urg:0,windowsSize:14600,checksum:59258,urgPtr:0
syn+ack:
ip header: version:4,tos:0,protocol:6,ttl:62,srcIP:192.168.145.51,dstIP:172.16.96.52,totalLen:60
tcp header:srcPort:6699,dstPort:50852,seqNum:3363298119,ackNum:112,headerLen:40,fin:0,syn:1,rst:0,psh:0,ack:1,urg:0,windowsSize:14480,checksum:34955,urgPtr:0

//理论上在这我们应该发送一个ack,但实际上在发完syn后,操作系统内核收到syc+ack,它检查内核里的socket,
//发现没有一个socket对应于这个包,于是自动回复rst,关闭连接,所以我们无法再回复ack,因为连接已断开
分享到:
评论

相关推荐

    基于raw socket的UDP数据的发送和接收

    总结来说,基于Raw Socket的UDP数据发送和接收是一项涉及网络协议底层操作的技术,适用于需要高度定制网络包的应用。它要求开发者具备深厚的网络协议知识,以确保数据的正确传输和解析。在实际应用中,务必谨慎操作...

    RawSend_RawSocket基于MAC发送_sock_raw_C++_原始Socket发送_

    在本主题中,“RawSend_RawSocket基于MAC发送_sock_raw_C++_原始Socket发送_”指的是使用C++语言通过原始套接字(Raw Socket)向指定MAC地址发送以太网数据帧的过程。以下将详细介绍这一技术及其相关知识点。 1. **...

    raw socket来实现ip报文的发送

    注意,如果需要发送TCP或UDP数据,还需要构造相应的传输层头部。 4. 发送数据:使用`sendto()`或`write()`函数将构造好的数据包发送到目标IP地址。 5. 接收响应:如果需要,可以创建另一个Raw Socket来接收响应...

    raw socket发送报文

    在计算机网络编程中,"raw socket发送报文"是一个高级话题,主要涉及到网络协议栈的底层操作。Raw sockets允许程序员直接操作数据链路层(如Ethernet或PPP)的数据包,而不是通过传输层协议(如TCP或UDP)进行通信。...

    rawsocket抓包侦听

    **rawsocket抓包侦听**是一种网络数据包捕获技术,它通过使用操作系统底层的原始套接字(raw sockets)来直接访问网络层的数据包,而不是通过应用层协议(如TCP或UDP)进行通信。这种方法使得开发者可以直接查看和...

    利用rawsocket实现本机网络报文监控解析

    例如,`RawSocketTest.cpp`和`RawSocket_Test.cpp`可能包含测试Raw Socket功能的代码,而`PacketStruct.h`可能定义了用于存储和解析报文结构的类或结构体。`NetCapture`系列的文件可能是项目工程文件,用于编译和...

    udp_raw.rar_RAW_raw socket_raw socket delphi_raw_socket _udp_raw

    RAW Socket是操作系统提供的一种底层网络编程接口,允许程序员直接操作网络协议栈,而不依赖于标准的TCP、UDP等协议。使用RAW Socket,开发者可以构建自定义的协议,实现更灵活的网络通信。然而,这也意味着需要...

    网络编程中如何使用raw socket发送magic packet网络唤醒数据包实现远程唤醒

    2. 数据链路层的直接操作:通过raw socket发送的数据不会自动添加IP或TCP/UDP头部,所以需要手动构造这些头部,包括IP地址和端口号等信息。 3. 打开raw socket:在Linux下,使用`socket(AF_PACKET, SOCK_RAW, htons...

    raw socket demo

    在压缩包文件名称列表中,"rawsocket"可能是源代码文件或相关资源文件的名称。实际操作时,可以解压该文件,查看源代码以了解如何在具体编程语言中实现raw socket的功能。 总结,raw sockets提供了一种直接与网络...

    多线程实现无连接的Raw Socket通信.rar

    在本项目中,多线程用于同时处理多个Raw Socket通信任务,比如接收和发送数据可能由不同的线程负责。在Windows上,可以使用`CreateThread()`函数创建新线程,或者使用`std::thread`库(如果项目使用C++11及以上版本...

    raw socket 大合集

    **原始套接字(Raw Socket)深度解析** 原始套接字,或称为“raw socket”,在计算机网络编程中扮演着重要角色。它允许程序员访问网络协议的底层细节,包括IP和TCP/IP协议栈的直接交互。这个大合集提供了一系列关于...

    TCP RAW_SOCKET源码

    TCP RAW_SOCKET工作在OSI模型的网络层或更低层,它允许应用程序访问IP头部和TCP头部,甚至可以创建自己的IP包和TCP包。通过这种方式,开发者可以直接操控网络数据的封装和解封装,实现更灵活的网络通信。 二、使用...

    IP包读取分析(raw socket)

    本篇将重点探讨"IP包读取分析(raw socket)"这一主题,特别关注在Windows环境下如何使用raw socket进行网络通信。 首先,我们需要理解什么是raw socket。Raw socket是一种允许程序员访问网络协议栈的底层接口,它...

    python 使用raw socket进行TCP SYN扫描实例

    【Python使用Raw Socket进行TCP SYN扫描】 在网络安全领域,端口扫描是一种常见的技术,用于检测目标主机上开放的服务。端口扫描分为多种类型,其中包括TCP SYN扫描,它是一种非侵入性的扫描方法,不会完全建立TCP...

    基于Raw Socket的数据包捕获程序

    Raw Socket的工作原理是,它允许程序发送和接收未封装在任何特定协议(如TCP或UDP头)中的IP数据包。通过这种方式,我们可以捕获网络上的原始数据包,对其进行解析和分析,从而理解网络通信的细节。 描述中提到,这...

    用RawSocket实现的Sniffer的C++Builder源程序

    标题中的“用RawSocket实现的Sniffer的C++Builder源程序”揭示了这是一个使用C++Builder编程环境,通过Raw Socket技术开发的网络嗅探器(Sniffer)项目。网络嗅探器是一种工具,用于捕获并分析网络上的数据包,这...

    利用raw socket 仿sinffer

    在IT领域,原始套接字(Raw Socket)是一种特殊的网络编程接口,允许程序员直接操作网络协议的底层细节,包括IP头部、TCP头部或UDP头部。它不像普通的套接字那样处理高层协议的数据封装和解封装,而是允许我们访问...

Global site tag (gtag.js) - Google Analytics