하아찡
C# 패킷 캡쳐 -3 본문
해당 Pcap 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 |