PacketBellのCap.csを利用して*.pcapをロードする

WireShark自身がパケットキャプチャに利用しているWinPcapにはNICやキャプチャファイルをオープンするpcap_open()という関数があります。

PacketBellというフリーソフトウェアCap.csソースコードにpcap_open()等を利用する例があり、このコードを再利用して*.pcapをロードできるようにメソッド"public int Load(string path)"を追加してみました。

using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Diagnostics;

public class Cap
{
    [StructLayout(LayoutKind.Sequential)]
    struct pcap_if
    {
        public IntPtr next;       //    struct pcap_if *next;
        public IntPtr name;       //	char *name;		/* name to hand to "pcap_open_live()" */
        public IntPtr description;//	char *description;	/* textual description of interface, or NULL */
        public IntPtr addresses;  //	struct pcap_addr *addresses;
        public uint flags;        //	bpf_u_int32 flags;	/* PCAP_IF_ interface flags */
    };
    [StructLayout(LayoutKind.Sequential)]
    public struct pcap_pkthdr
    {
        public int tv_sec;         /* seconds */
        public int tv_usec;        /* and microseconds */
        public uint caplen;    	   /* length of portion present */
        public uint len;     	   /* length this packet (off wire) */
    };
    [StructLayout(LayoutKind.Sequential)]
    public struct ethernet_hdstr
    {
        public byte dst0; public byte dst1; public byte dst2;
        public byte dst3; public byte dst4; public byte dst5;
        public byte src0; public byte src1; public byte src2;
        public byte src3; public byte src4; public byte src5;
        public byte type_u; public byte type_l;
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct ip_hdstr
    {
        public byte version_length;
        public byte differentiated_services_field;
        public byte total_length_u; public byte total_length_l;
        public byte identification_u; public byte identification_l;
        public byte flags; byte fragment_offset;
        public byte time_to_live;
        public byte protocol;
        public byte checksum_u; public byte checksum_l;
        public byte src0; public byte src1; public byte src2; public byte src3;
        public byte dst0; public byte dst1; public byte dst2; public byte dst3;
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct tcp_hdstr
    {
        public byte src_u; public byte src_l;
        public byte dst_u; public byte dst_l;
        public byte seq0; public byte seq1; public byte seq2; public byte seq3;
        public byte ack0; public byte ack1; public byte ack2; public byte ack3;
        public byte length;
        public byte flags;
        public byte window_u; public byte window_l;
        public byte checksum_u; public byte checksum__l;
    }
    [StructLayout(LayoutKind.Sequential)]
    struct udp_hdstr
    {
        public byte src_u; public byte src_l;
        public byte dst_u; public byte dst_l;
        public byte length_u; public byte length_l;
        public byte checksum_u; public byte checksum__l;
    }

    [DllImport("wpcap.dll")]
    private static extern int pcap_findalldevs_ex(string source, ref IntPtr auth, ref IntPtr alldevs, StringBuilder errbuf);
    [DllImport("wpcap.dll")]
    private static extern int pcap_findalldevs(ref IntPtr alldevs, StringBuilder errbuf);
    [DllImport("wpcap.dll")]
    private static extern int pcap_freealldevs(ref IntPtr alldevs);
    [DllImport("wpcap.dll")]
    private static extern IntPtr pcap_open(string source, int snaplen, int flags, int read_timeout, ref IntPtr auth, StringBuilder errbuf);
    [DllImport("wpcap.dll")]
    private static extern int pcap_next_ex(IntPtr adhandle, ref IntPtr header, ref IntPtr pkt_data);
    private const string PCAP_SRC_IF_STRING = "rpcap://";
    private const int PCAP_ERRBUF_SIZE = 256;
    private const int PCAP_OPENFLAG_PROMISCUOUS = 1;
    public const int ETHERNET_HDSTR_LENGTH = 14;
    public const int NAME = 0;
    public const int DESC = 1;
    public const int SEC = 0;
    public const int USEC = 1;
    public const int NOPROTO = 1;
    public const int PORT = 4;
    private int dev_num = 0;
    IntPtr alldevsPtr = new IntPtr();
    IntPtr adhandlePtr;
    /* Find All Devices
       return: number of devices found
     */
    public int FindAllDevs()
    {
        IntPtr auth = new IntPtr();
        StringBuilder errbuf = new StringBuilder(PCAP_ERRBUF_SIZE);
        if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, ref auth, ref alldevsPtr, errbuf) == -1)
        {
            Debug.Write("Error in pcap_findalldevs:" + errbuf);
            return -1;
        }
        IntPtr devPtr = alldevsPtr;
        pcap_if dev;
        dev_num = 0;
        while (!devPtr.Equals(IntPtr.Zero))
        {
            dev = (pcap_if)Marshal.PtrToStructure(devPtr, typeof(pcap_if));
            devPtr = dev.next;
            dev_num++;
        }
        return dev_num;
    }
    public string[,] DevNames()
    {
        string[,] r = new string[dev_num, 2];
        pcap_if dev;
        int i = 0;
        IntPtr devPtr = alldevsPtr;
        while (!devPtr.Equals(IntPtr.Zero))
        {
            dev = (pcap_if)Marshal.PtrToStructure(devPtr, typeof(pcap_if));
            r[i, NAME] = Marshal.PtrToStringAnsi(dev.name);
            if (!dev.description.Equals(IntPtr.Zero))
                r[i, DESC] = Marshal.PtrToStringAnsi(dev.description);
            devPtr = dev.next;
            i++;
        }
        return r;
    }
    public int Open(int inum)
    {
        IntPtr auth = new IntPtr();
        StringBuilder errbuf = new StringBuilder(PCAP_ERRBUF_SIZE);
        if (inum < 0 || dev_num <= inum)
        {
            Debug.Write("Out of bound:" + inum + "\n");
            return -2;
        }
        IntPtr devPtr = alldevsPtr;
        pcap_if dev = (pcap_if)Marshal.PtrToStructure(devPtr, typeof(pcap_if));
        for (int i = 0; i < inum; i++)
        {
            devPtr = dev.next;
            dev = (pcap_if)Marshal.PtrToStructure(devPtr, typeof(pcap_if));
        }
        string devname = Marshal.PtrToStringAnsi(dev.name);
        // Debug.Write("\nOpening "+devname+" ...\n");
        adhandlePtr = pcap_open(devname,
                                       65536,            // portion of the packet to capture. 
                                                         // 65536 guarantees that the whole packet will be captured on all the link layers
                                       PCAP_OPENFLAG_PROMISCUOUS,    // promiscuous mode
                                       1000,             // read timeout
                                       ref auth,         // authentication on the remote machine
                                       errbuf            // error buffer
                                       );
        if (adhandlePtr.Equals(IntPtr.Zero))
        {
            Debug.Write("\nUnable to open the adapter. " + Marshal.PtrToStringAnsi(dev.name) + " is not supported by WinPcap\n");
            return -1;
        }
        return 0;
    }
    public int Load(string path)
    {
        string devname = "file://" + path;
        IntPtr auth = new IntPtr();
        StringBuilder errbuf = new StringBuilder(PCAP_ERRBUF_SIZE);
        adhandlePtr = pcap_open(devname,
                                65536,                  // portion of the packet to capture. 
                                                        // 65536 guarantees that the whole packet will be captured on all the link layers
                                0,                      // promiscuous mode
                                1000,                   // read timeout
                                ref auth,               // authentication on the remote machine
                                errbuf                  // error buffer
                                );
        if (adhandlePtr.Equals(IntPtr.Zero))
        {
            throw new ApplicationException("\nUnable to load dumped file: " + path);
        }
        return 0;
    }
    IntPtr headerPtr = new IntPtr();
    IntPtr pkt_dataPtr = new IntPtr();
    private int next(ref long[] time, ref int proto, ref int[] src, ref int[] dst, ref string payload, bool pay_flag)
    {
        int res;
        res = pcap_next_ex(adhandlePtr, ref headerPtr, ref pkt_dataPtr);
        if (res < 1) return res;
        pcap_header = (pcap_pkthdr)Marshal.PtrToStructure(headerPtr, typeof(pcap_pkthdr));
        ethernet_header = (ethernet_hdstr)Marshal.PtrToStructure(pkt_dataPtr, typeof(ethernet_hdstr));
        time[SEC] = pcap_header.tv_sec;
        time[USEC] = pcap_header.tv_usec;
        proto = NOPROTO; int offset = 0;
        if (ethernet_header.type_u == 0x8 && ethernet_header.type_l == 0)
        {
            ip_header = (ip_hdstr)Marshal.PtrToStructure((IntPtr)((int)pkt_dataPtr + 14), typeof(ip_hdstr));
            src[0] = ip_header.src0; src[1] = ip_header.src1; src[2] = ip_header.src2; src[3] = ip_header.src3;
            dst[0] = ip_header.dst0; dst[1] = ip_header.dst1; dst[2] = ip_header.dst2; dst[3] = ip_header.dst3;
            proto = ip_header.protocol;
            if ((ip_header.version_length & 0xf0) == 0x40)
            {
                offset = ETHERNET_HDSTR_LENGTH + 4 * (ip_header.version_length & 0xf);
                if (ip_header.protocol == 0x11)
                {       // UDP
                    udp_header = (udp_hdstr)Marshal.PtrToStructure((IntPtr)((int)pkt_dataPtr + offset), typeof(udp_hdstr));
                    src[PORT] = udp_header.src_u * 256 + udp_header.src_l; dst[PORT] = udp_header.dst_u * 256 + udp_header.dst_l;
                    offset += 8;
                }
                else if (ip_header.protocol == 0x6)
                { //tcp
                    tcp_header = (tcp_hdstr)Marshal.PtrToStructure((IntPtr)((int)pkt_dataPtr + offset), typeof(tcp_hdstr));
                    src[PORT] = tcp_header.src_u * 256 + tcp_header.src_l; dst[PORT] = tcp_header.dst_u * 256 + tcp_header.dst_l;
                    offset += (tcp_header.length & 0xf0) / 4;
                }
            }
        }
        if (pay_flag)
        {
            payload = Marshal.PtrToStringAnsi((IntPtr)((int)pkt_dataPtr + offset), (int)pcap_header.len - offset);
        }
        return res;
    }
    public int next(ref long[] time, ref int proto, ref int[] src, ref int[] dst, ref string p)
    {
        return next(ref time, ref proto, ref src, ref dst, ref p, true);
    }
    string dummy_s = "";
    public int next(ref long[] time, ref int proto, ref int[] src, ref int[] dst)
    {
        return next(ref time, ref proto, ref src, ref dst, ref dummy_s, false);
    }
    public pcap_pkthdr pcap_header;
    public ethernet_hdstr ethernet_header;
    public ip_hdstr ip_header;
    udp_hdstr udp_header;
    tcp_hdstr tcp_header;
}

この変更したCap.csを利用して下記のようにキャプチャファイルのパケットを解析することが可能です。

public void Load(string path)
{
    Cap cap = new Cap();
    cap.Load(path);
    long[] times = new long[2];
    int proto = -1;
    int[] src = new int[5];
    int[] dst = new int[5];
    string data = "";
    int result;
    while ((result = cap.next(ref times, ref proto, ref src, ref dst, ref data)) != -2)
    {
        if (result != 1) continue;
        if (src[Cap.PORT] != 80) continue; // HTTPだけ処理する場合
        /* 目的のパケットを解析する処理 */
    }
}

このCap.csのおかげでリアルタイムパケットキャプチャ処理やキャプチャファイルのロード処理がかなり楽できます。cwbpさんに感謝。