Last-modified: 2011-12-26 (月) 01:24:42
C#/SharpPcapで単純なポートスキャンをする

概要

C#の勉強を兼ねて、単純なポートスキャンコードを書いてみました。
raw socketで実現出来ると思ったのですが、実験したところIPパケットまでは送れましたが、TCPパケットは送れませんでした。
どうやらXP SP2からセキュリティ上の理由により、raw socketに制限が設けられたそうです。

そこで、Windows版Wireshark、nmapなどでも利用されているWinPcapを使うことにしました。
SharpPcapというC#用のラッパーがあったので、それを使います。
TCP/IPヘッダに関しては以下を

SharpPcapの使い方に関しては以下を

ポートスキャンに関しては以下を主に参考にしました。

事前準備

  1. SharpPcapをダウンロードします。
    http://sourceforge.net/projects/sharppcap/
  2. SharpPcapを任意の場所へ解凍します。
  3. 今回使用するプロジェクトの参照設定にPacketDotNet.dllとSharpPcap.dllを追加します。

サンプルコード

デバイスと送信先IPを入力すると、Well known portに対してSYNスキャンを試みます。
開いているポートがあれば、表示します。
(Debug.WriteLineでコンソールへの出力もしているので、Debugビルドしてください)

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
 
 
 
 
 
 
 
 
 
-
|
-
|
-
|
-
|
|
-
!
-
|
|
!
|
-
!
|
|
|
|
-
|
!
|
|
|
|
|
-
!
|
-
-
|
!
-
|
|
!
!
|
-
|
|
!
|
-
!
|
|
-
!
|
|
|
|
-
!
|
-
!
|
-
-
!
|
|
|
-
!
|
|
-
!
|
|
|
-
!
|
|
|
|
-
|
!
|
|
|
-
|
-
-
!
|
-
!
|
-
|
!
-
|
-
-
!
!
|
!
|
-
-
|
!
|
-
|
!
|
-
-
!
|
-
-
!
|
!
!
|
-
|
!
!
!
|
-
!
!
|
-
|
!
|
|
|
!
!
!
using System;
using PacketDotNet;
using SharpPcap;
using System.Diagnostics;
using System.Net;
using System.Net.NetworkInformation;
 
 
namespace winpcap_test
{
    public class Program
    {
        public static void Main(string[] args)
        {
            try
            {
                Debug.Listeners.Add(new TextWriterTraceListener(Console.Out));
 
                // デバイスが無ければ終了
                if (LivePcapDeviceList.Instance.Count < 1)
                {
                    Debug.WriteLine("No. Devices were found on this machine");
                    return;
                }
 
                // デバイス一覧から、使用するデバイスを選択させる
                Debug.WriteLine("The following devices are available on this machine:");
                Debug.WriteLine("----------------------------------------------------");
                Debug.WriteLine("");
                int i = 0;
                foreach (LivePcapDevice dev in LivePcapDeviceList.Instance)
                {
                    Debug.WriteLine(String.Format("{0}) {1}", i++, dev.Description));
                }
                Debug.WriteLine("");
                Debug.Write("-- Please choose a device to send a packet on: ");
                i = int.Parse(Console.ReadLine());
                LivePcapDevice device = LivePcapDeviceList.Instance[i];
 
                // 送信元IPアドレス取得
                String srcIPAddress = "";
                foreach (PcapAddress addr in device.Interface.Addresses)
                {
                    // ARPを送って送信先MACアドレスを取得したいので、IPv4アドレスを探す
                    // ネットマスクの有無をみる簡易判定
                    if (addr.Netmask.ToString() != "")
                    {
                        srcIPAddress = addr.Addr.ToString();
                        break;
                    }
                }
                if (srcIPAddress == "")
                {
                    Debug.WriteLine("No. IP Address were found on this devices");
                    return;
                }
 
                // 送信先IPアドレス取得
                Console.Write("-- Please target IP address: ");
                String targetIPAddress = Console.ReadLine();
 
                // 送信先MACアドレス取得
                ARP arp = new ARP();
                PhysicalAddress targetMacAddress = arp.Resolve(IPAddress.Parse(targetIPAddress),
                                                             device.Name,
                                                             IPAddress.Parse(srcIPAddress));
                
                // デバイスオープン
                device.Open(DeviceMode.Normal, 20);
 
                // SYNスキャン
                Debug.WriteLine("-- Scanning well known tcp port. Please wait...");
                for (ushort targetPort = 0; targetPort <= 1024; targetPort++)
                {
                    // TCPヘッダ生成
                    TcpPacket tcpPacket = new TcpPacket(1024, targetPort);
                    tcpPacket.Syn = !tcpPacket.Syn;
                    tcpPacket.WindowSize = 4096;
 
                    // IPヘッダ生成
                    IPv4Packet ipPacket = new IPv4Packet(IPAddress.Parse(srcIPAddress),
                                                         IPAddress.Parse(targetIPAddress));
 
                    // Ethernetヘッダ生成
                    EthernetPacket ethernetPacket = new EthernetPacket(device.Interface.MacAddress,
                                                                       targetMacAddress,
                                                                       EthernetPacketType.None);
 
                    // TCPパケット作成
                    ipPacket.PayloadPacket = tcpPacket;
                    tcpPacket.UpdateTCPChecksum();
                    ethernetPacket.PayloadPacket = ipPacket;
                    ipPacket.UpdateIPChecksum();
 
                    // フィルタセット
                    // ターゲットIP、Portからのパケットかつ、SYN/ACKまたはRSTが立っているものをキャッチする
                    device.Filter = "src host " + targetIPAddress + " and src port " + targetPort + " and (tcp[13] & 18 != 0) or (tcp[13] & 4 != 0)";
 
                    const int maxRetryCount = 3;
                    for (int retryCount = 0; retryCount < maxRetryCount; retryCount++)
                    {
                        try
                        {
                            // TCPパケット送信
                            device.SendPacket(ethernetPacket);
 
                            // 受信パケット取得
                            RawPacket rawpacket = device.GetNextPacket();
 
                            // 取得できなかったら、数回繰り返す
                            // それでダメなら、filtered判定
                            if (rawpacket == null)
                            {
                                if ((retryCount + 1) >= maxRetryCount)
                                {
                                    //Debug.WriteLine("Port " + targetport + " is filtered");
                                    break;
                                }
                                continue;
                            }
                            else
                            {
                                // SYN/ACKフラグが立っていたら、open判定
                                // RSTフラグが立っていたらclosed判定
                                TcpPacket response = TcpPacket.GetEncapsulated(Packet.ParsePacket(rawpacket));
                                if (response.Ack == true && response.Syn == true)
                                {
                                    Debug.WriteLine("Port " + targetPort + " is open");
                                }
                                else if (response.Rst == true)
                                {
                                    //Debug.WriteLine("Port " + targetport + " is closed");
                                }
                                else
                                {
                                    //Debug.WriteLine("Port " + targetport + " is unknown");
                                }
                                break;
                            }
                        }
                        catch (Exception e)
                        {
                            Debug.WriteLine(String.Format("{0}({1})", e.Message, e.Source));
                        }
                    }
                }
 
                // 後始末
                device.Close();
            }
            catch (Exception e)
            {
                Debug.WriteLine(String.Format("Error:{0}({1})", e.Message, e.Source));
            }
 
            Debug.WriteLine("-- Please return any keys.");
            Console.ReadLine();
        }
    }
}

検証時のビルド環境

検証時の実行環境

参考