하아찡

C# 패킷 캡쳐 -3 본문

네트워크

C# 패킷 캡쳐 -3

하아찡 2022. 9. 13. 18:20

해당 Pcap Github 주소

Sharppcap Github

 

GitHub - dotpcap/sharppcap: Official repository - Fully managed, cross platform (Windows, Mac, Linux) .NET library for capturing

Official repository - Fully managed, cross platform (Windows, Mac, Linux) .NET library for capturing packets - GitHub - dotpcap/sharppcap: Official repository - Fully managed, cross platform (Windo...

github.com

 

 

해당 장치 목록 불러오는 코드

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));
    }

    return list;
}

CaptureDeviceList.Instance를 사용하여 장치목록을 읽어온 후 해당 목록을 반환하는 방식으로 사용함.

 

 

 

패킷을 읽어올 장치 선택 코드

ICaptureDevice PcapDevice = null;
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;
		}
	}
}

ICaptureDevice에다가 목록에서 선택한 장치값이랑 같은 값을 추가하여 넣어줌.

PcapDevice 변수로 패킷을 받을 예정.

 

 

장치 활성화

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

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

            
}

.Open() 장치를 열고

OnPacketArrival에 이벤트를 하나 등록시켜줌. 해당부분이 패킷데이터를 받아오는부분. 자세한내용은 해당 Github에서 확인.

.StartCapture() 캡처를 시작함.

 

 

OnPacketArrival 이벤트

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);
    }
            
}

해당 이벤트 파라미터 "PacketCapture e" 에 e.Data를 사용하면 현재 패킷 데이터를 알수있음.

해당 데이터를 이용해서 데이터를 구분하면됩니다. 예를들어 필터링 기능을 추가한 작업이 있습니다.

"//필터시작" 부터 필터링 작업

이부분에서 이더넷헤더 IP헤더, TCP, UDP 패킷구조를 공부하여 적용하면 됩니다.

 

 

이더넷 헤더

위에 이벤트함수에서 받아온 데이터를 사용하여 이더넷 데이터를 분리합니다.

각 헤더구조는 다른 블로그분들이 잘 올려주셔서 해당 링크만 남겨드릴게요.

이더넷 패킷 구조

 

패킷의 이해, Ethernet 프레임, IP 헤더

▶ TCP/IP 네트워크 구조 Link Layer 구조 ▶ 네트워크 인터페이스 계층 이더넷(Ethernet) 프레임...

blog.naver.com

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);
            }
            else if(IPType == 0x0806)
            {
                //ARP
            }
            else if(IPType == 0x8100)
            {
                
            }
            
        }
        else if(iphead.Protocol == 17)
        {
            //UDP
        }

    }
        
}

이더넷  패킷구조대로 데이터를 넣은후 이후 IP헤더 작업을 진행합니다.

해당 코드는 단순하게 데이터를 넣는부분이니 설명은 생략하겠습니다.

 

내용이 길어져서 IP헤더와 현재 작업이 진행된 TCP코드는 다음에 진행하겠습니다.

감사합니다.

반응형

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

C# 패킷 캡쳐 - 4  (0) 2023.08.03
C# 패킷 캡쳐-2  (0) 2022.09.12
C# 패킷 캡쳐-1  (0) 2022.09.08