首頁

目前文章總數:157 篇

  

最後更新:2024年 12月 07日

0002. 網卡流量與封包偵測工具

日期:2022年 06月 16日

標籤: Asp.NET Core Network TCP UDP

摘要:個人作品


開發所需:1. C# .netCore 6.0
     2. Visual Studio 2022以上
解決問題:偵測目前網路卡IP的流量與封包內容
範例檔案:Githu連結※用Visual Studio 2022
執行程式:RAR檔案要解壓縮
基本介紹:本篇分為5大部分。第3-5部分屬於筆記,避免未來闡述實現方式時遺忘
     前言
第一部分:程式介面與應用程式功能介紹
第二部分:捕捉網路卡流量紀錄說明
第三部分:OSI模型與TCP/IP參考模型說明
第四部分:代碼實現-專案架構
第五部分:代碼實現-Layer:2-IPv4封包解析
第六部分:代碼實現-Layer:3-TCP/UDP封包解析






前言


最早完成是在2017年,如今補完封包實作並且重構當時的代碼
該工具可以透過程式去擷取網路卡的封包與流量,在某些狀況如客製公司產品的網路傳輸封包內容時可以利用
或者透過代碼檢測是否有惡意的網路流量時
若是要完整的取得所有封包詳細內容可以下載更專業的 Wireshark軟體
※本篇實現[網路層]IPv4 與 [傳輸層]TCP與UDP封包



第一部分:程式介面與應用程式功能介紹

Step 1:執行程式

下載GitHub的檔案後,裡面有 SnifferNetworkCard.exe 可以執行,但注意以下:
1. 這是.netCore撰寫,可以跨平台
2. Windows 執行時記得要用管理員權限,因為要偵測網卡的流量


Step 2:程式介面

主程式分三個區塊
1. 網卡下拉式選單:可以看到當前電腦的網卡IP
2. 即時流量表:如果有收到封包,會記錄下來,並且會往下增加每個封包
3. 連線IP表:當前即時的IP封包,對於同個IP的封包,會用同一列


Step 3:選擇網卡

下拉選單中的網卡包含本機的虛擬網卡(VMWare),以及實際的網卡
目前實體的網卡位址是192.168.145.125


Step 4:選擇網卡

左邊有每個封包進來該網卡的Byte數量
右邊有目前發現的IP位址與協定
※ 1024Byte = 1Kb
※ 1024Kb = 1Mb




第二部分:捕捉網路卡流量紀錄說明

Step 1:檢視封包內容

當程式執行後,會在.exe的資料夾下產生2個檔案
UDP.txt :UDP封包內容
TCP.txt :TCP封包內容


Step 2:UDP封包內容-1

點擊UDP.txt 檔案後,有如下的格式
Wiki百科的UDP(使用者資料協定)說明
其中黑框是資料,有兩種紀錄模式 Base64 與 UTF8,如果不可解析在UTF8的轉換時會是亂碼
※UDP是不可靠的傳輸,有很多時候不用對正確的DATA做解析就可以拿到明文,如果是明文用UTF8可以很直接的看到(如黑框UTF8的地方)


Step 3:UDP封包內容-2

出自維基百科,以下是UDP表頭的區塊配置,在UDP.txt中都會列出實際內容


Step 4:TCP封包內容-1

點擊UDP.txt 檔案後,有如下的格式
Wiki百科的TCP(傳輸控制協定)說明
其中黑框是資料,有兩種紀錄模式 Base64 與 UTF8,如果不可解析在UTF8的轉換時會是亂碼
※TCP是可靠的傳輸,但相對的標頭Byte佔用空間比較大,通常需要對應正確的解析方式才可以拿到明文


Step 5:TCP封包內容-2

出自維基百科,以下是TCP表頭的區塊配置,在TCP.txt中都會列出實際內容




第三部分:OSI模型與TCP/IP參考模型說明

Step 1:參考模型介紹-OSI模型

開放式系統互聯模型(英語:Open System Interconnection Model,縮寫:OSI;簡稱為OSI模型
ISO發布了著名的ISO/IEC 7498標準,它定義了網路互聯的7層框架,也就是開放式系統互聯參考模型。
第7層 應用層:應用層(Application Layer)提供為應用軟體而設計的介面,以設定與另一應用軟體之間的通訊。
第6層 表達層:表達層(Presentation Layer)把數據轉換為能與接收者的系統格式相容並適合傳輸的格式。
第5層 會話層:會議層(Session Layer)負責在數據傳輸中設定和維護電腦網路中兩台電腦之間的通訊連接。
第4層 傳輸層:傳輸層(Transport Layer)把傳輸表頭(TH)加至資料以形成封包。傳輸表頭包含了所使用的協定等傳送資訊。
第3層 網路層:網路層(Network Layer)決定數據的路徑選擇和轉寄,將網路表頭(NH)加至數據包,以形成封包。網路表頭包含了網路資料。
第2層 資料連結層:資料連結層(Data Link Layer)負責網路尋址、錯誤偵測和改錯。當表頭和表尾被加至數據包時,會形成資訊框(Info Box)。
第1層 實體層:實體層(Physical Layer)在區域網路上傳送資料框(Data Frame),它負責管理電腦通訊裝置和網路媒體之間的互通。

Step 2:發送按鈕-TCP/IP參考模型

網際網路協議套組(英語:Internet Protocol Suite,縮寫IPS)
TCP/IP參考模型是一個抽象的分層模型,這個模型中,所有的TCP/IP系列網路協定都歸類到4個抽象的「層」中。每一抽象層建立在低一層提供的服務上,並且為高一層提供服務。
第4層 應用層:該層包括所有和應用程式協同工作,利用基礎網路交換應用程式專用的資料的協定。
第3層 傳輸層:傳輸層(transport layer)的協定,能夠解決諸如端到端可靠性(「資料是否已經到達目的地?」)和保證資料按照正確的順序到達這樣的問題。
第2層 網路互連層:正如最初所定義的,網路層解決在一個單一網路上傳輸封包的問題。
第2層 網路存取(連結)層實際上並不是網際網路協定組中的一部分,但是它是封包從一個裝置的網路層傳輸到另外一個裝置的網路層的方法。

Step 3:模型對應

實際上兩個模型沒有1對1的關係
在第4、5部分的代碼中,皆是以TCP/IP參考模型為主,說明程式的實現原理




第四部分:代碼實現-專案架構

Step 1:專案架構介紹

  1. SnifferNetworkCardForm.cs:From的主UI介面
  2. Common:整個程式中重複利用的共用程式
  3. Service: SocketService.cs實現Socket網路交互;WriteRecordTxtService.cs實現紀錄封包到文字檔(UDP.txt與TCP.txt)
  4. HeaderProtocol:對應IPv4與TCP、UDP封包的各區塊解析,轉成人可辨識的資料
  5. ViewModel:存成展示物件用


Step 2:程式執行流程

程式執行流程如下圖


Step 3:說明Socket工作-綁定網卡

一開始會透過UI介面選擇網卡後觸發此Method,然後設定網卡,進行蒐集的工作 GetCaptureWorking()


/// <summary>
/// 重新設定Socket繫結網卡
/// </summary>
/// <param name="newNetwrokCard">使用者選擇的網卡</param>
public void ResetNowConnectionNewtwork(string newNetwrokCard)
{
    this.NowConnectionNewtwork = newNetwrokCard;
    this.GetCaptureWorking();
}



Step 4:說明Socket工作-捕捉封包

設定Socket基本設定,以捕獲IPv4的封包為主,並且在接收到時將Byte呼叫OnReceive()接收封包方法


/// <summary>
/// 取得來源封包工作
/// </summary>
private void GetCaptureWorking()
{
    //開始捕捉封包
    if (!IsCapturing)
    {
        IsCapturing = true;
        //建立Socket-初始設定
        //AddressFamily.InterNetwork:IPv4  說明 https://docs.microsoft.com/zh-tw/dotnet/api/system.net.sockets.addressfamily?view=net-6.0
        //SocketType 說明: https://docs.microsoft.com/zh-tw/dotnet/api/system.net.sockets.sockettype?view=net-6.0
        MainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
        //將Socket與網路卡位址做繫結 - 設定
        MainSocket.Bind(new IPEndPoint(IPAddress.Parse(NowConnectionNewtwork), 0));
        //封包的標頭-設定
        //SocketOptionName.HeaderIncluded:表示應用程式將提供輸出資料包的 IP 標頭。
        MainSocket.SetSocketOption(SocketOptionLevel.IP,
                                   SocketOptionName.HeaderIncluded,
                                   true);
        byte[] byTrue = new byte[4] { 1, 0, 0, 0 };
        byte[] byOut = new byte[4] { 1, 0, 0, 0 };
        //捕捉的內容-設定
        //IOControlCode.ReceiveAll:啟用網路上所有 IPv4 封包的接收。 通訊端必須擁有通訊協定家族 InterNetwork
        MainSocket.IOControl(IOControlCode.ReceiveAll,
                             byTrue,
                             byOut);
        //開始進行接收,OnReceive是收到封包後的工作
        MainSocket.BeginReceive(_ByteData, 0, _ByteData.Length, SocketFlags.None,
            new AsyncCallback(OnReceive), null);
    }
    else//切換/重新繫節網卡時重置Socket
    {
        IsCapturing = false;
        MainSocket.Close();
    }
}



Step 5:說明Socket工作-接收封包

OnReceive()接收封包方法,會分析每次封包的長度nReceived,然後傳送給 ParseFlowData() 方法,進行封包解析


/// <summary>
/// 當接收到資料時的工作
/// </summary>
/// <param name="asyncResult">儲存非同步作業的相關資訊</param>
public void OnReceive(IAsyncResult asyncResult)
{
    try
    {
        //資料byte的結尾長度
        int nReceived = MainSocket.EndReceive(asyncResult);
        //解析封包串流資料
        ParseFlowData(_ByteData, nReceived);
        if (IsCapturing)//持續捕捉
        {
            _ByteData = new byte[Consts.SocketPackageByteLength];
            MainSocket.BeginReceive(_ByteData, 0, _ByteData.Length, SocketFlags.None, new AsyncCallback(OnReceive), null);
        }
    }
    catch (ObjectDisposedException ex)
    {
        MessageBox.Show(ex.Message);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}



Step 6:說明Socket工作-封包解析

ParseFlowData()進行封包解析,會將得到的資料
以下此行代碼會先觸發IPv4的標頭建構式工作,進行封包分析,可以參考第五部分,有IPv4的程式分析
依照得到的Protocol是TCP/UDP然後觸發封包解析並記錄到文字檔.txt,可以參考第六部分,有TCP/UDP的程式分析

var ipHeader = new IPv4Header(byteData, nReceived);



ParseFlowData()詳細完整代碼,並且會傳回Form上的歷史紀錄表與IP紀錄表上做顯示


/// <summary>
/// IP對應TCP或者UDP (Header)之流量
/// </summary>
/// <param name="byteData">封包內容</param>
/// <param name="nReceived">結尾長度</param>
private void ParseFlowData(
    byte[] byteData,
    int nReceived)
{
    
    var ipHeader = new IPv4Header(byteData, nReceived);
    //Filter:sourceAddress 為當前使用者選擇的網路卡IP 因為我們是要監測 連到這個網路卡的流量 而非該網路卡傳送出去的流量
    if (ipHeader.Header.SourceAddress.ToString() == this.NowConnectionNewtwork)
    {
        //IP單一節點資料
        var IPNode = new IPConnectionData();
        //連到我們網卡的IP
        IPNode.SourceIP = ipHeader.Header.DestinationAddress.ToString();
        //連到我們網卡的協定機制
        IPNode.prototol = ipHeader.Header.Protocol.ToString();
        //長度
        IPNode.flow = ipHeader.Header.TotalLength;
        //現在時間
        IPNode.Receive_DateTime = DateTime.Now;
        //產生ListViewItem資料並且呈現在窗體上
        var nodeData = ProductNodeMethod.Invoke(IPNode);
        AddListViewNodeMethod.Invoke(nodeData);
    }
}





第五部分:代碼實現-Layer:2-IPv4封包解析

Step 1:IPv4表頭區塊

IPv4有14個固定區塊 + 1個變動區塊(Data)
可以知道固定占了160bit為20Byte,每個IPv4封包都會至少有20Byte的空間
【版本】第1區塊: 4bit ,0~3
【首部長度】第2區塊:4bit ,4~7
【區分服務】第3區塊:6bit ,8~13
【顯式擁塞通告】第4區塊:2bit ,14~15
【全長】第5區塊:16bit ,16~31
【識別碼】第6區塊:16bit ,32~47
【標誌】第7區塊:3bit,48~50
【分片偏移】第8區塊:13bit,51~63
【存活時間】第9區塊:8bit,64~71
【協定】第10區塊:8bit,72~79
【首部核對和】第11區塊:16bit,80~95
【來源IP位址】第12區塊:16bit,96~127
【目的IP位址】第13區塊:16bit,128~159
【選項(如首部長度>5)】第14區塊:3bit,160
【資料】第15區塊:變動Bit 160~


Step 2:IPv4封包解析

代碼如下,先逐步將15個區塊的封包做bit的轉換後,再存入物件對應的Property中


/// <summary>
/// IPv4標頭
/// </summary>
public IPv4Header(byte[] byBuffer, int nReceived)
{
    try
    {
        //Create MemoryStream out of the received bytes
        MemoryStream memoryStream = new MemoryStream(byBuffer, 0, nReceived);
        //Next we create a BinaryReader out of the MemoryStream
        BinaryReader binaryReader = new BinaryReader(memoryStream);
        //第1區塊
        var byteTemp = binaryReader.ReadByte();
        _version = (byteTemp & 0b11110000) >> 4;
        //第2區塊
        _internetHeaderLength = (byteTemp & 0b00001111);
        //第3區塊
        byteTemp = binaryReader.ReadByte();
        _differentiatedServices = (byteTemp & 0b11111100) >> 2;
        //第4區塊
        _explicitCongestionNotification = (byteTemp & 0b00000011);
        //第5區塊
        _totalLength = (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());
        //第6區塊
        _identification = (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());
        //第7區塊
        var shrotTemp = binaryReader.ReadInt16();
        _flags = (shrotTemp & 0b1110000000000000) >> 13;
        //第8區塊
        _fragmentOffset = (shrotTemp & 0b0001111111111111);
        //第9區塊
        byteTemp = binaryReader.ReadByte();
        _timeToLive = byteTemp;
        //第10區塊
        byteTemp = binaryReader.ReadByte();
        _protocol = byteTemp;
        //第11區塊
        _headerChecksum = (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());
        //第12區塊
        _sourceAddress = (uint)binaryReader.ReadInt32();
        //第13區塊
        _destinationAddress = (uint)binaryReader.ReadInt32();
        //第14區塊
        if (_internetHeaderLength > 5)
        {
            //3個bit的Options
            _options += binaryReader.ReadBoolean() ? 4 : 0;
            _options += binaryReader.ReadBoolean() ? 2 : 0;
            _options += binaryReader.ReadBoolean() ? 1 : 0;
            //1個Padding
            binaryReader.ReadBoolean();
        }
        //第15區塊,取得方式: 總長度 - 現在binaryReader已讀取的Byte,剩下的都是Data
        _dataLength = _totalLength - binaryReader.BaseStream.Position;
        Array.Copy(byBuffer
               , binaryReader.BaseStream.Position
               , _data, 0,
               _dataLength);
        var selfProtocol = (ProtocolType)_protocol;
        if (selfProtocol == ProtocolType.Udp)
        {
            new UDPHeader(_data, (int)_dataLength);
        }
        else if (selfProtocol == ProtocolType.Tcp)
        {
            new TCPHeader(_data, (int)_dataLength);
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(
            ex.Message, 
            "錯誤訊息", 
            MessageBoxButtons.OK,
            MessageBoxIcon.Error
            );
    }
}



Step 3:取得Protocol後L2往L3

其中在IPv4的Data分析完畢後,會取得Protocol將剩下的Data往UDP/TCP做分析

var selfProtocol = (ProtocolType)_protocol;
if (selfProtocol == ProtocolType.Udp)
{
    new UDPHeader(_data, (int)_dataLength);
}
else if (selfProtocol == ProtocolType.Tcp)
{
    new TCPHeader(_data, (int)_dataLength);
}





第六部分:代碼實現-Layer:3-TCP/UDP封包解析

Step 1:UDP表頭區塊

UDP有4個固定區塊 + 1個變動區塊(Data)
可以知道固定占了64bit為8Byte,每個UDP封包都會至少有8Byte的空間占用
【來源連接埠】第1區塊:2Byte,0~15
【目的連接埠】第2區塊:2Byte ,16~31
【報文長度】第3區塊:2Byte,32~47
【校驗和】第4區塊:2Byte,48~63
【資料】第5區塊:變動Bit 64~


Step 2:UDP封包解析

代碼如下,先逐步將5個區塊的封包做bit的轉換後,再存入物件對應的Property中
最後在將資料存在UDP.txt中


/// <summary>
/// Layer3 取得UDP的標頭資訊 - 共有4個區塊固定Bit區塊(標頭)與最後1個變動Bit區塊(資料)
/// </summary>
public class UDPHeader
{
    private int _sourcePort;
    private int _destinationPort;
    private int _length;
    private int _checksum;
    private byte[] _data = new byte[4096];
    private long _dataLength = 8;
    public UDPHeaderViewModel Header
    {
        get
        {
            return new UDPHeaderViewModel()
            {
                SourcePort = _sourcePort.ToString(),
                DestinationPort = _destinationPort.ToString(),
                Length = _length.ToString(),
                Checksum = _checksum.ToString(),
                Data = Data(null),
            };
        }
    }
    /// <summary>
    /// 取得UDP標頭資料
    /// </summary>
    public UDPHeader(byte[] byBuffer, int nReceived)
    {
        try
        {
            MemoryStream memoryStream = new MemoryStream(byBuffer, 0, nReceived);
            BinaryReader binaryReader = new BinaryReader(memoryStream);
            //第1區塊
            _sourcePort = (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());
            //第2區塊
            _destinationPort = (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());
            //第3區塊
            _length = (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());
            //第4區塊
            _checksum = IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());
            //第5區塊,UDP標頭前4個區塊固定加總為 8 Byte
            _dataLength = nReceived - 8;
            Array.Copy(byBuffer
                   , 8
                   , _data
                   , 0
                   , _dataLength);
            if (_length > 0)
            {
                var writeString = new StringBuilder();
                var useClass =new UDPHeaderViewModel();
                writeString.AppendLine($@"{Util.GetDescription<UDPHeaderViewModel>(nameof(useClass.SourcePort))} : {Header.SourcePort}");
                writeString.AppendLine($@"{Util.GetDescription<UDPHeaderViewModel>(nameof(useClass.DestinationPort))} : {Header.DestinationPort}");
                writeString.AppendLine($@"{Util.GetDescription<UDPHeaderViewModel>(nameof(useClass.Length))}(Byte)  : {Header.Length}");
                writeString.AppendLine($@"{Util.GetDescription<UDPHeaderViewModel>(nameof(useClass.Checksum))} : {Header.Checksum}");
                writeString.AppendLine($@"實際封包總長(Byte) : {nReceived} ");
                writeString.AppendLine($@"實際Data總長(Byte) : {_dataLength} ");
                writeString.AppendLine($@"{Util.GetDescription<UDPHeaderViewModel>(nameof(useClass.Data))}(Base64) : {Data(null)}");
                writeString.AppendLine($@"{Util.GetDescription<UDPHeaderViewModel>(nameof(useClass.Data))}(UTF8) : {Data(Encoding.UTF8)}");                    
                WriteRecordTxtService.Instance.Value.WriteText(
                    "",
                    true,
                    "UDP", 
                    writeString.ToString()
                    );
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(
                  ex.Message,
                  "錯誤訊息",
                  MessageBoxButtons.OK,
                  MessageBoxIcon.Error
                  );
        }
    }
}



Step 3:TCP表頭區塊

UDP有18個固定區塊 + 1個變動區塊(Data)
可以知道固定占了160bit為20Byte,每個TCP封包都會至少有20Byte的空間占用
【來源連接埠】第1區塊:2Byte,0~15
【目的連接埠】第2區塊:2Byte ,16~31
【序列號碼】第3區塊:4Byte ,32~63
【確認號碼(當ACK設定)】第4區塊:4Byte ,64~95
【資料偏移】第5區塊:4bit ,96~99
【保留】第6區塊:3bit ,100~102
【ECN顯式擁塞通知】第7區塊:1bit, 103
【CWD減少視窗擁塞】第8區塊:1bit, 104
【ECN-Echo】第9區塊:1bit, 105
【URG緊急】第10區塊:1bit, 106
【ACK應答響應】第11區塊:1bit, 107
【PUS推送】第12區塊:1bit, 108
【RST復位】第13區塊:1bit, 109
【Syn同步】第14區塊:1bit, 110
【Fin結束】第15區塊:1bit, 111
【窗口大小】第16區塊:2byte, 112~127
【校驗和】第17區塊:2Byte, 128~143
【緊急指標】第18區塊:2Byte, 144~159
【資料】第19區塊:變動Bit 160~


Step 4:TCP封包解析

代碼如下,先逐步將19個區塊的封包做bit的轉換後,再存入物件對應的Property中
最後在將資料存在TCP.txt中


/// <summary>
/// Layer3 取得TCP的標頭資訊 - 共有18個區塊固定Bit區塊(標頭)與最後1個變動Bit區塊(資料)
/// </summary>
public class TCPHeader
{
    private int _sourcePort;
    private int _destinationPort;
    private uint _sequenceNumber;
    private uint _acknowledgementNumber;
    private int _headerLength;
    private int _reserved;
    private int _ecnEcho;
    private int _explicitCongestionNotificationFlag;
    private int _congestionWindowReduced;
    private int _urgent;
    private int _acknowledgment;
    private int _push;
    private int _reset;
    private int _sync;
    private int _finish;
    private int _checksum;
    private int _urgentPointer;
    private int _window;
    private byte[] _data = new byte[4096];
    private long _dataLength = 8;
    public TCPHeaderViewModel Header
    {
        get
        {
            return new TCPHeaderViewModel()
            {
                SourcePort = _sourcePort.ToString(),
                DestinationPort = _destinationPort.ToString(),
                SequenceNumber = _sequenceNumber.ToString(),
                AcknowledgementNumber = _acknowledgementNumber.ToString(),
                HeaderLength = _headerLength.ToString(),
                Reserved = _reserved.ToString(),
                ExplicitCongestionNotificationFlag = _explicitCongestionNotificationFlag.ToString(),
                CongestionWindowReduced = _congestionWindowReduced.ToString(),
                EcnEcho = _ecnEcho.ToString(),
                Urgent = _urgent.ToString(),
                Acknowledgment = _acknowledgment.ToString(),
                Push = _push.ToString(),
                Reset = _reset.ToString(),
                Sync = _sync.ToString(),
                Finish = _finish.ToString(),
                Window = _window.ToString(),
                Checksum = _checksum.ToString(),
                UrgentPointer = _urgentPointer.ToString(),
                Data = Data(null),
            };
        }
    }
    public TCPHeader(byte[] byBuffer, int nReceived)
    {
        try
        {
            MemoryStream memoryStream = new MemoryStream(byBuffer, 0, nReceived);
            BinaryReader binaryReader = new BinaryReader(memoryStream);
            //var byteTemp = binaryReader.ReadByte();
            //第1區塊
            _sourcePort = (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());
            //第2區塊
            _destinationPort = (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());
            //第3區塊
            _sequenceNumber = (uint)IPAddress.NetworkToHostOrder(binaryReader.ReadInt32());
            //第4區塊
            _acknowledgementNumber = (uint)IPAddress.NetworkToHostOrder(binaryReader.ReadInt32());
            //第5區塊
            var byteTemp = binaryReader.ReadByte();
            _headerLength = (byteTemp & 0b11110000) >> 4;
            //第6區塊
            _reserved = (byteTemp & 0b00001110) >> 1;
            //第7區塊
            _explicitCongestionNotificationFlag = (byteTemp & 0b000000001);
            //第8區塊
            byteTemp = binaryReader.ReadByte();
            _congestionWindowReduced = (byteTemp & 0b10000000) >> 7;
            //第9區塊
            _ecnEcho = (byteTemp & 0b01000000) >> 6;
            //第10區塊
            _urgent = (byteTemp & 0b00100000) >> 5;
            //第11區塊
            _acknowledgment = (byteTemp & 0b00010000) >> 4;
            //第12區塊
            _push = (byteTemp & 0b00001000) >> 3;
            //第13區塊
            _reset = (byteTemp & 0b00000100) >> 2;
            //第14區塊
            _sync = (byteTemp & 0b00000010) >> 1;
            //第15區塊
            _finish = (byteTemp & 0b000000001);
            //第16區塊
            _window = (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());
            //第17區塊
            _checksum = (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());
            //第18區塊
            _urgentPointer = (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());
            //第19區塊,取得方式: 總長度 - 現在binaryReader已讀取的Byte,剩下的都是Data
            _dataLength = (_headerLength * 4) - binaryReader.BaseStream.Position;
            Array.Copy(byBuffer
                   , binaryReader.BaseStream.Position
                   , _data, 0,
                   _dataLength);
            if (_dataLength > 0)
            {
                var writeString = new StringBuilder();
                var useClass = new TCPHeaderViewModel();
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.SourcePort))} : {Header.SourcePort}");
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.DestinationPort))} : {Header.DestinationPort}");
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.SequenceNumber))} : {Header.SequenceNumber}");
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.AcknowledgementNumber))} : {Header.AcknowledgementNumber}");
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.HeaderLength))}(Byte) : {Header.HeaderLength}");
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.Reserved))} : {Header.Reserved}");
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.ExplicitCongestionNotificationFlag))} : {Header.ExplicitCongestionNotificationFlag}");
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.CongestionWindowReduced))} : {Header.CongestionWindowReduced}");
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.EcnEcho))} : {Header.EcnEcho}");
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.Urgent))} : {Header.Urgent}");
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.Acknowledgment))} : {Header.Acknowledgment}");
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.Push))} : {Header.Push}");
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.Reset))} : {Header.Reset}");
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.Sync))} : {Header.Sync}");
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.Finish))} : {Header.Finish}");
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.Window))} : {Header.Window}");
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.Checksum))} : {Header.Checksum}");
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.UrgentPointer))} : {Header.UrgentPointer}");
                writeString.AppendLine($@"實際封包總長(Byte) : {nReceived} ");
                writeString.AppendLine($@"實際Data總長(Byte) : {_dataLength} ");
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.Data))}(Base64) : {Data(null)}");
                writeString.AppendLine($@"{Util.GetDescription<TCPHeaderViewModel>(nameof(useClass.Data))}(UTF8) : {Data(Encoding.UTF8)}");
                WriteRecordTxtService.Instance.Value.WriteText(
                    "",
                    true,
                    "TCP",
                    writeString.ToString()
                    );
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(
                  ex.Message,
                  "錯誤訊息",
                  MessageBoxButtons.OK,
                  MessageBoxIcon.Error
                  );
        }
    }
    /// <summary>
    ///【資料】第19區塊:變動Bit 160~
    /// </summary>
    private string Data(Encoding? useEnconding)
    {
        //取得UDP Data實際資料
        byte[] newBytes = new byte[_dataLength];
        Array.Copy(_data, newBytes, _dataLength);
        if (useEnconding == null)
        {
            return Convert.ToBase64String(newBytes);
        }
        else
        {
            return useEnconding.GetString(newBytes);
        }
    }
}