하아찡

C# 패킷 캡쳐 - 4 본문

네트워크

C# 패킷 캡쳐 - 4

하아찡 2023. 8. 3. 19:37

TCP 코드부분입니다.

public class TCP
    {
        public byte[] SP { get; set; }//송신지 포트
        public byte[] DP { get; set; }//수신지 포트
        public byte[] SequnceNum { get; set; }
        public byte[] ACKNum { get; set; }
        public byte DataOffset { get; set; }//4bit
        public byte Reserverd { get; set; }//3bit - 0 0 0
        public bool NS { get; set; }
        public bool CWR { get; set; }
        public bool ECE { get; set; }
        public bool URG { get; set; }
        public bool ACK { get; set; }
        public bool PSH { get; set; }
        public bool RST { get; set; }
        public bool SYN { get; set; }
        public bool FIN { get; set; }
        public byte[] WinSize { get; set; }
        public byte[] Checksum { get; set; }
        public byte[] URGPointer { get; set; }
        public byte[] Option { get; set; }
        public TCP(List<byte> _data, ReadOnlySpan<byte> aaaa)
        {
            int index = 0;//데이터 넣을때 사용하는 인덱스
            int keepindex = 0;
            int Total = 0;//해당 데이터들 다 넣고 Option 사이즈 정할때 사용
            byte bit;
            byte[] bits = new byte[8];//데이터 플레그들 넣어서 저장할라고

            SP = new byte[2];
            DP = new byte[2];
            SequnceNum = new byte[4];
            ACKNum = new byte[4];
            WinSize = new byte[2];
            Checksum = new byte[2];
            URGPointer = new byte[2];
            Total = SP.Length + DP.Length + SequnceNum.Length + ACKNum.Length + WinSize.Length + Checksum.Length + URGPointer.Length + 2 + 4;
            //Option = new byte[_data.Count - Total - 4];//전달받은 데이터 - 현재 
            
            for(int i = keepindex; i < keepindex + SP.Length;i++,index++)
                SP[i - keepindex] = _data[i];
            keepindex = index;

            for (int i = keepindex; i < keepindex + DP.Length; i++, index++)
                DP[i - keepindex] = _data[i];
            keepindex = index;

            for (int i = keepindex; i < keepindex + SequnceNum.Length; i++, index++)
                SequnceNum[i - keepindex] = _data[i];
            keepindex = index;

            for (int i = keepindex; i < keepindex + ACKNum.Length; i++, index++)
                ACKNum[i - keepindex] = _data[i];
            keepindex = index;

            bit = 0b_1111_0000;
            DataOffset = (byte)(((_data[index] & bit) >> 4) * 4);

            bit = 0b_0000_1110;
            Reserverd = (byte)((_data[index] & bit) >> 1);

            bit = 0b_0000_0001;
            bits[0] = (byte)(_data[index++] & bit);//다음인덱스로 넘어감
            NS = BitConverter.ToBoolean(bits, 0);

            //플레그데이터 넣기위해 사용
            bit = 0b_1000_0000;
            for (int i = 0; i < 8; i++)
            {
                bits[i] = (byte)(_data[index] & (bit << i));
            }
            
            CWR = BitConverter.ToBoolean(bits, 0);
            ECE = BitConverter.ToBoolean(bits, 1);
            URG = BitConverter.ToBoolean(bits, 2);//긴급포인터
            ACK = BitConverter.ToBoolean(bits, 3);//필드에 값이 채워져있음을 알리는 플래그
            PSH = BitConverter.ToBoolean(bits, 4);//1일경우 이후 세그먼트가  없음
            RST = BitConverter.ToBoolean(bits, 5);//ESTABLISHED 상태인 상대에게 연결리셋요청
            SYN = BitConverter.ToBoolean(bits, 6);//동기화세그먼트
            FIN = BitConverter.ToBoolean(bits, 7);//연결종료세그먼트
            index++;
            keepindex = index;


            for (int i = keepindex; i < keepindex + WinSize.Length; i++, index++)
                WinSize[i - keepindex] = _data[i];
            keepindex = index;

            for (int i = keepindex; i < keepindex + Checksum.Length; i++, index++)
                Checksum[i - keepindex] = _data[i];
            keepindex = index;

            for (int i = keepindex; i < keepindex + URGPointer.Length; i++, index++)
                URGPointer[i - keepindex] = _data[i];
            keepindex = index;

            if(DataOffset != 20)
            {
                Option = new byte[DataOffset - 20];
                for (int i = keepindex; i < keepindex + Option.Length; i++, index++)
                    Option[i - keepindex] = _data[i];
                keepindex = index;
            }
        }
    }

 

깔끔하게 작성된 코드가 아니여서 참고용으로만 봐주시면 감사하겠습니다.

최근까지 블로그를 관리를 하지못해 누락된부분을 이제확인했네요. 머쓱

TCP프로토콜 구조를 그대로 가져와서 데이터를 처리해주는 작업을 했습니다. 해당 작업을 통해 소스포트와 목적지포트를 구해서 포트별로 필터링을 통해 자신이 원하는 포트를 확인하실 수 있습니다.

 

 

필터링 처리부분입니다.

구조는 아래와같이 구성했습니다.

struct Filter
{
	public string Condition;
	public string val;
}

 

필터링 등록방식은 TextBox를통해 Split으로 구분해줬습니다.

public void SetFilter(bool _b,string str)
{
	bFilter = _b;
	TxtFilter = str;
	Filters = new List<Filter>();
	if (_b)
	{
		foreach (var filter in TxtFilter.Split(','))
		{
			Filter f = new Filter();
			f.Condition = filter.Split(':')[0];
			f.val = filter.Split(':')[1];
			Filters.Add(f);
		}
	}            
}

여러개 필터링을 할경우 쉼표로 구분하고 / 데이터들은 콜론으로 구분해줍니다.

예를들어 sp(소스포트):80은 출발지포트 80인 애들만 보여줘라

다른 예는 sp(소스포트):80, dp(목적지포트):52632는 소스포트는 80번 혹은 목적지포트가 52632인 애들만 보여줘라. 입니다.

 

전체적인 Capture코드 입니다.

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using SharpPcap;

namespace Packet
{
    public class Capture
    {
        //해당 디바이스 패킷데이터를 읽어옴
        ICaptureDevice PcapDevice = null;

        //디바이스가 검색했는지
        bool bDeviceCheck = false;

        //디바이스를 선택했는지
        bool bSetDevice = false;

        //필터작동
        bool bFilter = false;
        string TxtFilter;
        List<Filter> Filters;
        ListBox lb = null;
        public Capture()
        {
            if(0 < CaptureDeviceList.Instance.Count)
            {
                //해당값이 false 경우 모든 함수 작동x
                //버전 확인 예외
                bDeviceCheck = true;
            }
        }

        public struct Version
        {
            public string pcap { get; set; }
            public string sharpcap { get; set; }
        };
        public Version Versions()
        {
            Version val = new Version();
            val.pcap = "";//Pcap.Version.Substring(0, Pcap.Version.IndexOf('('));
            //val.sharpcap = SharpPcap.Version.VersionString;

            return val;
        }
        
        public void Setlb(ListBox _lb)
        {
            lb = _lb;
        }
        public List<string> DeviceList()
        {
            List<string> list = new List<string>();
            if (!bDeviceCheck)
                return list;

            foreach(var nic in CaptureDeviceList.Instance)
            {
                var friendName = nic.ToString().Split('\n')[1];
                //데이터 구조 -> FriendName: "해당데이터"
                list.Add(friendName.Substring(friendName.IndexOf(' ') + 1));
                //Console.WriteLine(nic);
            }

            return list;
        }


        
        public void SetDevice(string FriendName)
        {
            //디바이스를 선택해줌
            if (!bDeviceCheck)
                return;

            foreach (var nic in CaptureDeviceList.Instance)
            {
                var friendName = nic.ToString().Split('\n')[1];
                string str = friendName.Substring(friendName.IndexOf(' ') + 1);
                //선택한 디바이스와 같을경우 선택한다
                if (str == FriendName)
                {
                    bSetDevice = true;
                    PcapDevice = nic;
                    break;
                }
            }
        }


        public void ReadPacket()
        {
            if (!bSetDevice)
            {
                MessageBox.Show("장치를 선택해주세요.");
                return;
            }

            PcapDevice.Open();
            PcapDevice.OnPacketArrival += Device_OnPacketArrival; 
            PcapDevice.StartCapture();

        }
        private string IPHeadProtocal(int i)
        {
            string ProtocolType = "";
            if (i == 1)
                ProtocolType = "ICMP";
            else if (i == 6)
                ProtocolType = "TCP";
            else if (i == 17)
                ProtocolType = "UDP";
            else
                ProtocolType = "NONE";
            return ProtocolType;
        }

        private void Device_OnPacketArrival(object s, PacketCapture e)
        {
            //패킷읽는 이벤트 부분

            //패킷데이터를 출력창으로 확인해보고싶을때 주석 풀기
            //ReadOnlySpan<byte> test = e.Data;
            //Console.WriteLine(test.ToString());

            EthernetFrame Frame = new EthernetFrame(e.Data);
            string str = "";
            string sp, dp;//포트값 저장
            //str = $"{IPHeadProtocal(Frame.iphead.Protocol)}    {Frame.iphead.PrintSourceAdd()} -> {Frame.iphead.PrintDestAdd()}";

            if (Frame.iphead.Protocol == 6)
            {
                if (BitConverter.IsLittleEndian)
                {
                    Array.Reverse(Frame.tcp.DP);
                    Array.Reverse(Frame.tcp.SP);
                }
                sp = BitConverter.ToUInt16(Frame.tcp.SP, 0).ToString();
                dp = BitConverter.ToUInt16(Frame.tcp.DP, 0).ToString();
            }
            else if (Frame.iphead.Protocol == 17)
            {
                //현재 UDP작업을 안해둬서 빈칸으로 둠
                //UDP
                sp = "";
                dp = "";
            }
            else
            {
                sp = "";
                dp = "";
            }

            
            if(!bFilter)
                //필터를 적용하지 않았을때.
                ListviewAddItem(IPHeadProtocal(Frame.iphead.Protocol), Frame.iphead.PrintSourceAdd(), Frame.iphead.PrintDestAdd(),sp,dp);
            else
            {
                //필터적용
                string sa = Frame.iphead.PrintSourceAdd();  //송신지 주소
                string da = Frame.iphead.PrintDestAdd();    //수신지 주소
                int index = 0;
                bool[] Check = new bool[Filters.Count];     //해당 필터에 적합한지 체크하기위해 각 필터 true값 저장
                bool _Check = true;                         //최종 필터 확인

                //필터 시작
                foreach (var filter in Filters)
                {
                    if ("sa" == filter.Condition)
                    {
                        //송신지 IP
                        if (filter.val == sa)
                            Check[index++] = true;

                    }
                    else if ("da" == filter.Condition)
                    {
                        //수신지 IP
                        if (filter.val == da)
                            Check[index++] = true;
                    }
                    else if ("sp" == filter.Condition)
                    {
                        //송신지 Port
                        if (filter.val == sp)
                            Check[index++] = true;
                    }
                    else if ("dp" == filter.Condition)
                    {
                        //수신지 Port
                        if (filter.val == dp)
                            Check[index++] = true;
                    }
                    else
                    {
                        //해당필터에 적합하지않음.
                        Check[index++] = false;
                    }
                }

                foreach(var check in Check)
                {
                    if (!check)
                    {
                        //조건 틀린게있음
                        _Check = false;
                        break;
                    }
                }


                if(_Check)
                    ListviewAddItem(IPHeadProtocal(Frame.iphead.Protocol), sa, da, sp, dp);
            }
            
        }

        private delegate void Dgt_ListboxAddItem(string Type, string Source, string Dest,string SP,string DP);
        private void ListviewAddItem(string Type,string Source, string Dest,string SP,string DP)
        {
            if (lb.InvokeRequired)
            {
                Dgt_ListboxAddItem t = ListviewAddItem;
                object[] objs = new object[] { Type,Source,Dest,SP,DP };
                lb.Invoke(t, objs);
            }
            else
            {

                lb.Items.Add($"Type : {String.Format("{0,4}", Type)}   Address : {Source} -> {Dest}   Port : {String.Format("{0,5}", SP)} -> {String.Format("{0,5}", DP)}");
                //lv.TopItem = lv.Items[lv.Items.Count - 1];
            }
        }

        public void SetFilter(bool _b,string str)
        {
            bFilter = _b;
            TxtFilter = str;
            Filters = new List<Filter>();
            if (_b)
            {
                foreach (var filter in TxtFilter.Split(','))
                {
                    Filter f = new Filter();
                    f.Condition = filter.Split(':')[0];
                    f.val = filter.Split(':')[1];
                    Filters.Add(f);
                }
            }
            
        }
    }
    struct Filter
    {
        public string Condition;
        public string val;
    }
    
    public class IPHead
    {
        public byte Version { get; set; }//4bit
        public byte HL { get; set; }//4bit --header len
        public byte TOS { get; set; }//1byte
        public byte[] TotalLen { get; set; }//2byte
        public byte[] identifi { get; set; }//2byte

        public byte IPFlags { get; set; }//3bit
        public byte[] FragmentOffset { get; set; }//1byte + 5bit

        public byte TTL { get; set; }//1byte
        public byte Protocol { get; set; }//1byte

        public byte[] HeaderChecksum { get; set; }//2byte
        public byte[] SourceAdd { get; set; }
        public byte[] DestAdd { get; set; }

        public byte[] IPOption { get; set; }

        public IPHead(List<byte> _data)
        {
            int index = 0;
            byte BitCheck = 0b_1111_0000; 
            int keepIndex = 0;

            TotalLen = new byte[2];
            identifi = new byte[2];
            FragmentOffset = new byte[2];
            HeaderChecksum = new byte[2];
            SourceAdd = new byte[4];
            DestAdd = new byte[4];

            

            Version = (byte)((byte)(_data[index] &  BitCheck) >> 4);

            BitCheck = 0b_0000_1111;
            HL = (byte)(((byte)(_data[index] & BitCheck)) * 4) ;
            index++;
            TOS = _data[index++];
            keepIndex = index;

            for (int i = index; i < keepIndex + TotalLen.Length; i++, index++)
                TotalLen[i - keepIndex] = _data[i];
            keepIndex = index;

            for (int i = index; i < keepIndex + identifi.Length; i++, index++)
                identifi[i - keepIndex] = _data[i];
            keepIndex = index;

            for (int i = index; i < keepIndex + FragmentOffset.Length; i++, index++)
                FragmentOffset[i - keepIndex] = _data[i];
            keepIndex = index;

            BitCheck = 0b_1110_0000;
            IPFlags = (byte)((byte)(FragmentOffset[0] & BitCheck) >> 5);

            BitCheck = 0b_0001_0000;
            FragmentOffset[0] = (byte)(FragmentOffset[0] & BitCheck);

            TTL = _data[index++];
            Protocol = _data[index++];
            keepIndex = index;

            for (int i = index; i < keepIndex + HeaderChecksum.Length; i++, index++)
                HeaderChecksum[i - keepIndex] = _data[i];
            keepIndex = index;

            
            for (int i = index; i < keepIndex + SourceAdd.Length; i++, index++)
                SourceAdd[i - keepIndex] = _data[i];
            keepIndex = index;

            for (int i = index; i < keepIndex + DestAdd.Length; i++, index++)
                DestAdd[i - keepIndex] = _data[i];
            keepIndex = index;

            if(HL == 20 || HL == 0)
            {
                IPOption = new byte[1];
                
            }
            else
            {
                //데이터가있을경우
                IPOption = new byte[HL - 20];
                for (int i = index; i < keepIndex + (HL - 20); i++, index++)
                    IPOption[i - keepIndex] = _data[i];
            }
                


        }

        public string PrintSourceAdd()
        {
            string str = $"{String.Format("{0,3}", SourceAdd[0])}.{String.Format("{0,3}", SourceAdd[1])}.{String.Format("{0,3}", SourceAdd[2])}.{String.Format("{0,3}", SourceAdd[3])}";

            return str;
        }

        public string PrintDestAdd()
        {
            string str = $"{String.Format("{0,3}", DestAdd[0])}.{String.Format("{0,3}", DestAdd[1])}.{String.Format("{0,3}", DestAdd[2])}.{String.Format("{0,3}", DestAdd[3])}";

            return str;
        }

        
    }


    public class EthernetFrame
    {
        public byte[] Preamble { get; set; }
        public byte SFD { get; set; }
        public byte[] DestAdd { get; set; }
        public byte[] SourceAdd { get; set; }
        public byte[] Type { get; set; }
        public List<byte> Data { get; set; }
        public byte[] FCS { get; set; }

        public IPHead iphead { get; set; }
        public TCP tcp { get; set; }
        public EthernetFrame(ReadOnlySpan<byte> _data)
        {
            int DataLen = _data.Length;
            int index = 0;
            int keepIndex = 0;
            

            Preamble = new byte[7];
            DestAdd = new byte[6];
            SourceAdd = new byte[6];
            Type = new byte[2];
            FCS = new byte[4];
            Data = new List<byte>();
            SFD = 0;

            /*
            for (int i = index; i < keepIndex + 7; i++, index++)
            {
                Preamble[i] = _data[i];
            }
            SFD = _data[index++];
            */
            keepIndex = index;
            //Dest Address
            for (int i = index; i < keepIndex + DestAdd.Length; i++, index++)
                DestAdd[i - keepIndex] = _data[i];
            keepIndex = index;

            //Source Address
            for (int i = index; i < keepIndex + SourceAdd.Length; i++, index++)
                SourceAdd[i - keepIndex] = _data[i];
            keepIndex = index;

            //Type
            for (int i = index; i < keepIndex + Type.Length; i++, index++)
                Type[i - keepIndex] = _data[i];
            keepIndex = index;

            //Data
            for (int i = index; i < DataLen; i++, index++)
                Data.Add(_data[i]);
            keepIndex = index;


            /*
            //FCS
            for (int i = index; i < keepIndex + FCS.Length; i++, index++)
                FCS[i - keepIndex] = _data[i];
            */
            iphead = new IPHead(Data);

            //리틀엔디안 시스템일경우 해당 배열값을 반대로 바꿔서 값을 출력한다.
            //이작업을 안하면 해당 정수값을이 이상하게 나옴
            if (BitConverter.IsLittleEndian)
                Array.Reverse(Type);

            short IPType = BitConverter.ToInt16(Type, 0);
            if (iphead.Protocol == 6)
            {
                //TCP
                if(IPType == 0x0800)
                {
                    //IP4
                    Data.RemoveRange(0, iphead.HL);
                    tcp = new TCP(Data,_data);
                    Console.WriteLine(tcp);
                }
                else if(IPType == 0x0806)
                {
                    //ARP
                }
                else if(IPType == 0x8100)
                {
                    //VLAN
                }
                
            }
            else if(iphead.Protocol == 17)
            {
                //UDP
            }

        }
        
    }
    public class TCP
    {
        public byte[] SP { get; set; }//송신지 포트
        public byte[] DP { get; set; }//수신지 포트
        public byte[] SequnceNum { get; set; }
        public byte[] ACKNum { get; set; }
        public byte DataOffset { get; set; }//4bit
        public byte Reserverd { get; set; }//3bit - 0 0 0
        public bool NS { get; set; }
        public bool CWR { get; set; }
        public bool ECE { get; set; }
        public bool URG { get; set; }
        public bool ACK { get; set; }
        public bool PSH { get; set; }
        public bool RST { get; set; }
        public bool SYN { get; set; }
        public bool FIN { get; set; }
        public byte[] WinSize { get; set; }
        public byte[] Checksum { get; set; }
        public byte[] URGPointer { get; set; }
        public byte[] Option { get; set; }
        public TCP(List<byte> _data, ReadOnlySpan<byte> aaaa)
        {
            int index = 0;//데이터 넣을때 사용하는 인덱스
            int keepindex = 0;
            int Total = 0;//해당 데이터들 다 넣고 Option 사이즈 정할때 사용
            byte bit;
            byte[] bits = new byte[8];//데이터 플레그들 넣어서 저장할라고

            SP = new byte[2];
            DP = new byte[2];
            SequnceNum = new byte[4];
            ACKNum = new byte[4];
            WinSize = new byte[2];
            Checksum = new byte[2];
            URGPointer = new byte[2];
            Total = SP.Length + DP.Length + SequnceNum.Length + ACKNum.Length + WinSize.Length + Checksum.Length + URGPointer.Length + 2 + 4;
            //Option = new byte[_data.Count - Total - 4];//전달받은 데이터 - 현재 
            
            for(int i = keepindex; i < keepindex + SP.Length;i++,index++)
                SP[i - keepindex] = _data[i];
            keepindex = index;

            for (int i = keepindex; i < keepindex + DP.Length; i++, index++)
                DP[i - keepindex] = _data[i];
            keepindex = index;

            for (int i = keepindex; i < keepindex + SequnceNum.Length; i++, index++)
                SequnceNum[i - keepindex] = _data[i];
            keepindex = index;

            for (int i = keepindex; i < keepindex + ACKNum.Length; i++, index++)
                ACKNum[i - keepindex] = _data[i];
            keepindex = index;

            bit = 0b_1111_0000;
            DataOffset = (byte)(((_data[index] & bit) >> 4) * 4);

            bit = 0b_0000_1110;
            Reserverd = (byte)((_data[index] & bit) >> 1);

            bit = 0b_0000_0001;
            bits[0] = (byte)(_data[index++] & bit);//다음인덱스로 넘어감
            NS = BitConverter.ToBoolean(bits, 0);

            //플레그데이터 넣기위해 사용
            bit = 0b_1000_0000;
            for (int i = 0; i < 8; i++)
            {
                bits[i] = (byte)(_data[index] & (bit << i));
            }
            
            CWR = BitConverter.ToBoolean(bits, 0);
            ECE = BitConverter.ToBoolean(bits, 1);
            URG = BitConverter.ToBoolean(bits, 2);//긴급포인터
            ACK = BitConverter.ToBoolean(bits, 3);//필드에 값이 채워져있음을 알리는 플래그
            PSH = BitConverter.ToBoolean(bits, 4);//1일경우 이후 세그먼트가  없음
            RST = BitConverter.ToBoolean(bits, 5);//ESTABLISHED 상태인 상대에게 연결리셋요청
            SYN = BitConverter.ToBoolean(bits, 6);//동기화세그먼트
            FIN = BitConverter.ToBoolean(bits, 7);//연결종료세그먼트
            index++;
            keepindex = index;


            for (int i = keepindex; i < keepindex + WinSize.Length; i++, index++)
                WinSize[i - keepindex] = _data[i];
            keepindex = index;

            for (int i = keepindex; i < keepindex + Checksum.Length; i++, index++)
                Checksum[i - keepindex] = _data[i];
            keepindex = index;

            for (int i = keepindex; i < keepindex + URGPointer.Length; i++, index++)
                URGPointer[i - keepindex] = _data[i];
            keepindex = index;

            if(DataOffset != 20)
            {
                Option = new byte[DataOffset - 20];
                for (int i = keepindex; i < keepindex + Option.Length; i++, index++)
                    Option[i - keepindex] = _data[i];
                keepindex = index;
            }
        }
    }

    public class UDP
    {
        public UDP(List<byte> _data)
        {
            //작업전
        }
    }
}

 

반응형

'네트워크' 카테고리의 다른 글

C# 패킷 캡쳐 -3  (3) 2022.09.13
C# 패킷 캡쳐-2  (0) 2022.09.12
C# 패킷 캡쳐-1  (0) 2022.09.08