美文网首页
TCP数据包的简单分析

TCP数据包的简单分析

作者: 李die喋 | 来源:发表于2019-08-14 15:00 被阅读0次

TCP首部格式

TCP

TCP的首部相对于UDP的首部还是相对复杂的,下面我们依次分析下每一个字段代表的意思。

  • 源端口(Source Port)

表示发送端端口号,占16位。

  • 目标端口(Destination Port)

表示接收端端口号,占16位。

  • 序列号(Sequence Number)

占32位。指发送数据的位置,每发送一次数据,就累加一次该数据字节数的大小。

  • 确认号(Acknowledgement Number)

占32位。确认应答号的值为接收到的数据包的序列号的值+1,表示下一个发送的数据包占所有数据编号的位置。

  • 数据偏移
    表示TCP所传输的数据部分应该从TCP包的那个位置开始计算,也可以看成TCP首部的长度。该字段占4位,单位为4字节。但上图所示TCP的首部为20字节长,因此TCP的首部中有长度不确定的字段。

  • 保留
    该字段主要是为了以后扩展的时候使用,上图显示占6位。一般都设置为0,即使收到的包在该字段不为0,此包也不会被丢弃。其实这六位中的后两位分别为CWR和ECE。

CWR:CWR和后面的ECE都用于IP首部的ECN字段。ECE标志为1时,则通知对方已将拥塞窗口缩小。就是之前提到的TCP可靠传输技术的一种——拥塞控制。

ECE:为1,通知通信对方,从对方到这边的网络有拥塞。

  • URG

为1,表示包中有需要紧急处理的数据。对于需要紧急处理的数据,会在后面的紧急指针中再进行解释。

  • ACK

为1,确认应答字段有效。TCP规定除了最初建立连接的SYN包之外必须设置为1

  • PSH

为1,表示需要将收到的数据立刻传给上层应用协议。为0,不需要立即传而是先进行缓存。

  • RST

为1,表示TCP连接中出现异常必须强制断开连接。(未被使用的端口发来连接请求、切断电源、死机等情况)

  • SYN

建立连接。为1,表示希望建立连接,并在序列号的字段进行序列号初始值的设定。

  • FIN

为1,表示希望断开连接。

  • 窗口大小

用于通知从相同TCP首部的确认应答号所指位置开始能够接受的数据大小(8字节)。

  • 校验和

TCP的校验和

检验和目的

为了发现TCP首部和数据在发送端到接收端之间发生的任何改动。如果接收方检测到检验和有差错,TCP段会被直接丢弃。

TCP在计算校验和时,要加上一个12字节的伪首部。


image

检验和计算过程

TCP首部校验和 = TCP首部 + TCP数据 + TCP伪首部

  • 将TCP报文中的检验和字段置为0
  • 将伪首部 首部 数据部分分为16位依次相加,如果长度为奇数则在后面补0
  • 将计算的值取反得到校验和

下面是TCP数据包的代码解析:

public class TCPacket {
    private final static int SOURCEPORT_BIT = 0;
    private final static int DESTINATIONPORT_BIT = 2;//占2字节
    private final static int SEQUENCE_BIT = 4;//序列号,占4字节
    private final static int ACKNOWLEDGEMENT_BIT = 8;//应答号
    private final static int HEADER_LENGTH_BIT = 12;//数据偏移 占4位
    private final static int TAG_BIT = 13;
    private final static int WINDOW_SIZE_BIT = 14;//窗口大小
    private final static int CHECK_SUM_BIT = 16;//占两个字节
    private final static int URGENT_POINTER_BIT = 18;//紧急指针,当URG为1时这里会进行描述

    public byte[] m_Data;
    public int m_Offset;

    public TCPacket(byte[] data,int offset){
        this.m_Data = data;
        this.m_Offset = offset;
    }

    //获取源端口
    public int getSourcePort(){
        return Packet.readShort(m_Data,m_Offset + SOURCEPORT_BIT);
    }

    //获取目的端口
    public int getDestinationPort(){
        return Packet.readShort(m_Data,m_Offset + DESTINATIONPORT_BIT);
    }

    public int getSequenceNumber(){
        return Packet.readInt(m_Data,m_Offset + SEQUENCE_BIT);
    }

    public int getAcknowledgementNumber(){
        return Packet.readInt(m_Data,m_Offset + ACKNOWLEDGEMENT_BIT);
    }

    //获取TCP头部的长度 占4字节
    public int getHeaderLength(){
        return ((m_Data[m_Offset + HEADER_LENGTH_BIT] & 0xFF) >> 4) * 4;
    }

    //获取第13个字节的后六位
    public int getTag(){
        return m_Data[m_Offset + TAG_BIT] & 0x2F;
    }

    public boolean isURG(){
        return (getTag() >> 5) == 1;
    }

    public boolean isACK(){
        return ((getTag() >> 4) & 1) == 1;
    }

    public boolean isPSH(){
        return ((getTag() >>3) & 1) == 1;
    }

    public boolean isRST(){
        return ((getTag() >>2) & 1) == 1;
    }

    public boolean isSYN(){
        return ((getTag() >>1) & 1) == 1;
    }

    public boolean isFIN(){
        return (getTag() & 1) == 1;
    }

    public int getWindowSize(){
        return Packet.readShort(m_Data,m_Offset + WINDOW_SIZE_BIT);
    }

    public short getCheckSum(){
        return Packet.readShort(m_Data,m_Offset + CHECK_SUM_BIT);
    }

    public void setCheckSum(short value){
        Packet.writeInt(m_Data,m_Offset + CHECK_SUM_BIT,value);
    }

    //当ARG=1时,使用此方法
    public int getUrgentPointer(){
        return Packet.readShort(m_Data,m_Offset + URGENT_POINTER_BIT);
    }
}

计算校验和的几个方法:

//计算TCP的校验和 TCP伪首部+TCP首部+TCP数据
public static boolean computeTCPChecksum(IPacket iPacket,TCPacket tcPacket){
    //TCP首部+TCP数据 = IP整体长度 - IP首部
    int tcp_length = iPacket.getTotalLength() - iPacket.getHeaderLength();
    if(tcp_length < 0)
        return false;
    //计算 TCP伪首部 = 源IP地址 + 目标IP地址 + 协议号 + TCP包长度
    long sum = getPseudoHeadLength(iPacket,tcp_length);

    short oldChecksum = tcPacket.getCheckSum();
    tcPacket.setCheckSum((short) 0);//将校验和置0

    short newChecksum = checksum(sum,tcPacket.m_Data,tcPacket.m_Offset,tcp_length);

    tcPacket.setCheckSum(newChecksum);
    return oldChecksum == newChecksum
}

//计算伪首部长度
public static long getPseudoHeadLength(IPacket iPacket,int len){
    byte[] buf = iPacket.m_Data;
    int offset = iPacket.m_Offset + IPacket.SOURCE_IP_BIT;
    long sum = 0;
    //ip包中地址占8字节 计算地址
    int address = 8;
    while (address > 1){
        sum += readShort(buf,offset) & 0xFFFF;
        offset += 2;
        address -= 2;
    }
    if(address > 1){//可能会有剩余的字节
        sum += (buf[offset] & 0xFF) << 8;
    }
    //在此基础上计算协议号
    sum += iPacket.getProtocol() & 0xFF;
    //在此基础上计算TCP包长度
    sum += len;
    return sum;
}

//计算校验和
public static short checksum(long sum,byte[] buf,int offset,int len){
    while (len > 1){
        sum += readShort(buf,offset) & 0xFFFF;
        offset += 2;
        len -= 2;
    }
    if(len > 0){
        sum += (buf[offset] & 0xFF) << 8;
    }

    while((sum >> 16) > 0){
        sum = (sum & 0xFFFF) + (sum >> 16);
    }

    return (short) ~sum;
}

在写项目的过程中会继续完善,有什么问题请大家指出。

相关文章

  • TCP数据包的简单分析

    TCP首部格式 TCP的首部相对于UDP的首部还是相对复杂的,下面我们依次分析下每一个字段代表的意思。 源端口(S...

  • 《Wireshark数据包分析实战》(一)数据包分析简介与网络基

    学习使用Wireshark的读书笔记 数据包分析,通常也被称为数据包嗅探或协议分析。流行的数据包分析软件包括tcp...

  • 网络协议分层

    1- 通过wireshark 分析网络数据包 1.1 以太网数据包 1.2 IP 数据包 1.3 TCP 数据包 ...

  • 网络 Conclusion

    1. TCP和UDP TCP/UDP详细分析TCP是点对点的通信,UDP是广播TCP控制了数据包的发送序列的产生,...

  • get 和 post的区别

    GET和POST还有一个重大区别,简单的说: GET产生一个TCP数据包;POST产生两个TCP数据包。 长的说:...

  • TCP三次握手原理

    本文主要内容 TCP数据包格式 三次握手原理 TCP的传输和确认 TCP的序号和确认序号 1、TCP数据包格式 T...

  • WpsecCTF Misc部分

    数据包分析1 wireshark打开,之后追踪tcp流,然后得到password,但是发现密码中出现不可键入的值,...

  • tcp包头、三次握手、四次挥手、状态

    1. TCP协议 1.1 TCP数据包头 要了解三次握手和四次挥手,首先需要了解下TCP数据包头的结构,如下: 源...

  • Linux TCP 重传类算法性能评估方案

    来源 /腾讯课堂Coding学院(ID:ke_coding) 导语 TCP数据包重传类算法是要让TCP连接从数据包...

  • Bugku-telnet

    题目提供了一个压缩包,解压之后是一个数据包 常规思路用Wireshark打开分析数据包。 可以看到先进行了TCP三...

网友评论

      本文标题:TCP数据包的简单分析

      本文链接:https://www.haomeiwen.com/subject/vdigjctx.html