2017年6月22日 星期四

以 SharpPcap 實作可收聽封包的 C# 程式(winform)


上一篇文章是介紹如何撰寫簡單的console程式,這篇將分享我實作 SharpPcap  winform 程式的心得和遇到的問題。

winform表單設計
https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcSUwUZLdYMGw23ZSVF7l0lYvW1FjuHsLHvBO3xfQlQf7tI824sPSrojs6Zvbt-x_7nlW876n2j0smXRhEjd0xCUTu3GODWloXZZQGhYVSexde7e3avcnfnp1m7QvJvRm6-1k3M8vqaYxY/s400/sharpcapwinform1.png
一個TextBox,作用是輸入過濾封包的filter到TextBox。
一個ListBox,作用是選擇ListBox中的網卡設備。
兩個Button。作用是開始時按下開始監聽按鈕,結束時按下停止監聽。

form.cs程式碼
設定完介面之後,我在 form.cs 引用 SharpPcap 的元件,並在Form class中先宣告網卡設備清單。然後在 Form 的初始函式中獲取網卡清單並在入到 Listbox 中。

using SharpPcap;
using SharpPcap.LibPcap;
using PacketDotNet;

namespace webcam_alarm_for_skype
{
    public partial class Form1 : Form
    {
        LibPcapLiveDeviceList devicelist = LibPcapLiveDeviceList.Instance;
        public Form1()
        {
            InitializeComponent();
            for (int i = 0; i < devicelist.Count; i++)
            {
                ListBox.Items.Add(devicelist[i].Description);
            }
        }
    }
}




觸發事件的程式碼
我有三個事件要寫,一個是開始監聽某網卡封包的按鈕事件、一個是停止監聽的按鈕事件、一個是封包被抓取時的觸發事件。第一個按鈕事件有兩個條件要先檢查,是否選擇了監聽的網卡,是否輸入封包過條件。條件都滿足之後再啟動網卡設備。


/*開始監聽的按鈕事件*/
private void button_Click(object sender, EventArgs e)
{
        if (ListBox.SelectedIndex < 1)
        {
                MessageBox.Show("沒選網卡!!", "提示", MessageBoxButtons.OK);             
        }
        else if (TextBox.Text == "")
        {
                MessageBox.Show("沒有封包過濾的條件!!", "提示", MessageBoxButtons.OK);
        }
        else
        {
                LibPcapLiveDevice lpldevice = devicelist[ListBox.SelectedIndex - 1];             
                try
                {                 
                        /* 選擇其中一個網卡,不一定要開啟混亂模式 */               
                        lpldevice.Open(DeviceMode.Promiscuous, 1000);
                        /* 使用符合 tcpdump 格式的語法即可過濾封包,基本上 http 的封包可用 tcp and ip 過濾*/
                        lpldevice.Filter = TextBox.Text;
                        /* 當條件的封包被被截取時,執行 device_OnPacketArrival */
                        lpldevice.OnPacketArrival += new PacketArrivalEventHandler(device_OnPacketArrival);
                        /* 開始 */
                        lpldevice.StartCapture();           
                }
                catch (Exception ex)
                {
                        MessageBox.Show(ex.Message, "提示", MessageBoxButtons.OK);
                }
        }
}

/*停止監聽的按鈕事件*/
private void button2_Click(object sender, EventArgs e)
{
        /* 選擇其中一個網卡 */
        LibPcapLiveDevice lpldevice = devicelist[lst_intefaces.SelectedIndex - 1];
        lpldevice.StopCapture();
}


第二個封包觸發事件,我希望當觸發時能在UI上提示一些訊息。但因為UI的執行緒和觸發事件發生時的不同,我並不能直接在事件中修改UI,除錯時系統會跳出跨執行緒的警告,並終止程式。因此我要先確認該控制項的 InvokeRequired 屬性,若為真表示跨執行緒調用,必須透過控制項的 BeginInvoke 方法才能調用。若不為真我才能直接調用。

/*封包觸發事件*/
private void device_OnPacketArrival(object sender, CaptureEventArgs e)
{
        Packet dotnetpacket = Packet.ParsePacket(e.Packet.LinkLayerType, e.Packet.Data);
        TcpPacket tcpPacket = TcpPacket.GetEncapsulated(dotnetpacket);
        IpPacket ippacket = (IpPacket)tcpPacket.ParentPacket;
        string payload = Encoding.ASCII.GetString(tcpPacket.PayloadData);
        if (payload.Contains("封包格式"))
        {
                if (Button.InvokeRequired)
                {
                        Button.BeginInvoke(new MethodInvoker(
                                () => Button.value = "過濾到了!!"));
                }
                else
                {
                        Button.value = "過濾到了!!";
                }
        }
}


沒有留言:

張貼留言