简单用下SharpPcap

什么是SharpPcap

最近嘛,要写各种课设,计网也要
课设就算了,里面还有一个网络编程
要求捕获些IP数据报,并输出些IP数据报里面的一些数据
然后一看要求,霍,好家伙,能用C#,那就简单了

说回正题,要了解什么是SharpPcap,就要先了解WinPcap
WinPcap是在windows下抓包的工具集合,相当于一个抓包的api
有了这个,我们就不需要直接与网卡打交道了,要抓包,直接调WinPcap提供的api

SharpPcap就是把这些api打包成C#的形式,让我们可用在C#中方便的抓包

安装

这个就很简单了,直接到Nuget包管理器里面安装就是了
Nuget包管理器

(@ 24-01-11)
其实应该用NPcap的,这个比较新,WinPcap有点老了
WinPcap调用不了无线网卡 这两个SharpPcap都可以调用
NPcap下载

在写代码前,还要检查下又没有安装过WinPcap,没有的话会找不到捕获的设备
WinPcap下载
这个也是装上就完事了

写代码

需要注意的是,SharpPcap貌似重构过好多次,我看到的使用范例有好几版
但都没用,最后还是靠我自己反编译看的
(GitHub那里的范例也是不能直接用的)
总之,我这里用的是6.2.5的版本,别的版本我不管

丢一段代码,里面带着详细的注释(这段代码也是我计网课设网络编程的代码)
(@ 24-01-11)
稍微修改下,原来的代码在处理标识符的逻辑点问题
(转成BitArray会出现高低位顺序的问题)

using System;
using SharpPcap;
using PacketDotNet;
using System.Collections;

namespace Test
{
    public class Program
    {
        static void Main()
        {
            CaptureDeviceList devices = CaptureDeviceList.Instance;//获取所有可用的设备
            if (devices.Count < 1)
            {
                Console.WriteLine("No devices were found on this machine");
                return;
            }//没有就直接退出了
            Console.WriteLine("\nThe following devices are available on this machine:");
            Console.WriteLine("----------------------------------------------------\n");
            for (int i = 0; i < devices.Count; i++)
            {
                Console.WriteLine($"index:{i}|{devices[i].Description}\n");
            }//输出可用的设备及其编号

            string num = Console.ReadLine();//获取用户输入的编号
            ICaptureDevice device = devices[int.Parse(num)];//获取对应的设备对象
            device.Open(DeviceModes.Promiscuous, 10000);//准备启动设备

            string filter = "ip";
            device.Filter = filter;//设置过滤器为ip数据报

            device.OnPacketArrival += Device_OnPacketArrival;//设置收到包后的回调方法
            device.StartCapture();//开始捕捉
            Console.ReadLine();
            device.StopCapture();//如果按下任意键,终止捕捉
            device.Close();//释放设备
        }
        private static void Device_OnPacketArrival(object sender, PacketCapture e)
        {
            var ip = e.GetPacket().GetPacket().Extract<IPPacket>();//把捕捉到的包转换成ip数据报
            //因为库里没有处理标识符和片偏移的逻辑,这里要自己写处理逻辑
            var s = new BitArray(new byte[] { ip.HeaderData[5], ip.HeaderData[4] });//获取第5第6字节的数据
            var symbol = new BitArray(new byte[] { ip.HeaderData[7], ip.HeaderData[6] });//获取第7第8字节的数据
            Console.WriteLine();
            Console.WriteLine($"Version:\t{ip.Version}");
            Console.WriteLine($"Length:\t\t{ip.TotalLength}");
            Console.WriteLine($"Serial:\t\t{BitArrayToInt(s, 0, 15)}");
            Console.WriteLine($"DF:\t\t{symbol[14]}");
            Console.WriteLine($"MF:\t\t{symbol[13]}");
            Console.WriteLine($"Offest:\t\t{BitArrayToInt(symbol, 0, 12)}");
            Console.WriteLine($"From:\t\t{ip.SourceAddress}");
            Console.WriteLine($"To:\t\t{ip.DestinationAddress}");
            Console.WriteLine($"Protocol:\t{ip.Protocol}");
            for (int i = 0; i < ip.HeaderData.Length; i++)
            {
                if (ip.HeaderData[i] < 16)
                {
                    Console.Write("0{0:X} ", ip.HeaderData[i]);
                }
                else
                {
                    Console.Write("{0:X} ", ip.HeaderData[i]);
                }
                if (i % 4 == 3)
                {
                    Console.WriteLine();
                }
            }
            Console.WriteLine();
        }
        static int BitArrayToInt(BitArray bitArray, int head, int end)//只是个把二进制数组转换成数字的方法
        {
            int a = 0;
            for (int i = head; i <= end; i++)
            {
                if (bitArray[i])
                {
                    a += 1;
                }
                a *= 2;
            }
            return a;
        }
    }
}

实际上确实就这么一段代码就够了
里面的注释也已经点明了具体的功能

需要注意的是,这里通过事件机制来处理收到包的情况
也就是这一句

device.OnPacketArrival += Device_OnPacketArrival;//设置收到包后的回调方法

device收到数据报后,会触发OnPacketArrival事件
然后就可用用事件那一套机制来处理收到的包了,也就是这一段代码

private static void Device_OnPacketArrival(object sender, PacketCapture e)
{
    var ip = e.GetPacket().GetPacket().Extract<IPPacket>();//把捕捉到的包转换成ip数据报
    //因为库里没有处理标识符和片偏移的逻辑,这里要自己写处理逻辑
    var s = new BitArray(new byte[] { ip.HeaderData[5], ip.HeaderData[4] });//获取第5第6字节的数据
    var symbol = new BitArray(new byte[] { ip.HeaderData[7], ip.HeaderData[6] });//获取第7第8字节的数据
    Console.WriteLine();
    Console.WriteLine($"Version:\t{ip.Version}");
    Console.WriteLine($"Length:\t\t{ip.TotalLength}");
    Console.WriteLine($"Serial:\t\t{BitArrayToInt(s, 0, 15)}");
    Console.WriteLine($"DF:\t\t{symbol[14]}");
    Console.WriteLine($"MF:\t\t{symbol[13]}");
    Console.WriteLine($"Offest:\t\t{BitArrayToInt(symbol, 0, 12)}");
    Console.WriteLine($"From:\t\t{ip.SourceAddress}");
    Console.WriteLine($"To:\t\t{ip.DestinationAddress}");
    Console.WriteLine($"Protocol:\t{ip.Protocol}");
    for (int i = 0; i < ip.HeaderData.Length; i++)
    {
        if (ip.HeaderData[i] < 16)
        {
            Console.Write("0{0:X} ", ip.HeaderData[i]);
        }
        else
        {
            Console.Write("{0:X} ", ip.HeaderData[i]);
        }
        if (i % 4 == 3)
        {
            Console.WriteLine();
        }
    }
    Console.WriteLine();
}

这里的e就是捕获到的包,然后就可用进行一系列的处理了

Licensed under CC BY-NC-SA 4.0