Web、サーバ、ソフトウェア、バグ・脆弱性 などの情報を何人かで集まって書いていく IT/Web情報系ブログ

【C#】SharpPcap備忘録

投稿日:   投稿者:ktm@s

とあるゲームの効率化を求めて補助ツールを書いた。
普通にゲームを遊んでいたら勝手にパケットから欲しいデータを拾って整形するようなヤツだ。

その際にSharpPcapというC#でWinPcapを扱うためのライブラリを使ったが、絶対に覚えていられないので自分へのメモとして残す。

この記事を読む人は「筆者はSharpPcapはおろかパケットもC#も何もわかってない」ことを理解の上読んで欲しい。
単にこうやったら動いたというものをメモするのでコードは各自奇麗にしてね。

開発環境

Pcap.Netというものもあるようだが、最終リリースが2年新しい事とバグ対応の面からSharpPcapを選択した。

  • VS2019
  • .Net Framework 4.8
  • SharpPcap 5.1

SharpPcap備忘録

スポンサーリンク

SharpPcapのインストール

NuGetからインストールした。

Install-Package SharpPcap

SharpPcapのサンプルコード

検索したら情報が古く使えないものも多くあった。
基本的にはGithubを見てサンプルコードに修正を加えていく形になる。

Official repository - Fully managed, cross platform (Windows, Mac, Linux) .NET library for capturing packets - chmorgan/sharppcap

今回はTCPパケットだけ欲しかったのでExample6.DumpTCP.csを参考にした。
(表示は同じだけどページはちゃんと違うよ)

Official repository - Fully managed, cross platform (Windows, Mac, Linux) .NET library for capturing packets - chmorgan/sharppcap

devicesを自動で設定する

サンプルコードではConsole.ReadLine()でデバイスリストからキャプチャするデバイスを入力しているが、起動ごとに指定するのも面倒なため自動化した。
using System.Net; を忘れずに。

IPHostEntry ipentry = Dns.GetHostEntry(Dns.GetHostName());

string ip = "";
foreach (IPAddress ipaddr in ipentry.AddressList)
{
       if (ipaddr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
       {
             ip = ipaddr.ToString();
             break;
       }
}

// ネットワークアダプタ一覧
var devices = CaptureDeviceList.Instance;
if (devices.Count < 1)
{
       MessageBox.Show("ネットワークアダプタが見つかりません");
       return;
}

int i = 0;
foreach (var dev in devices)
{
       string str = dev.ToString();
       if (str.Contains(ip))
       {
             break;
       }
       i++;
}

var device = devices[i];

IPアドレスでフィルタする

いちいちイベントが発生するのでデバッグ中にIPアドレスフィルタが役立った。

string filter = "ip and tcp and dst host IPアドレス";

IPアドレスやportとのマッチ

IPアドレスフィルタはしていられないが特定のサーバーからのデータがみたい時。

// IPアドレスの場合
string ip = "IPアドレス";
var ipPacket = (PacketDotNet.IPPacket)tcpPacket.ParentPacket;

if (ip == ipPacket.SourceAddress.ToString())
{
}

// Portの場合
ushort port = 8080;
if (port == tcpPacket.SourcePort)
{
}

接続先の変更(SYNパケット)の確認

新たにサーバーに接続したという判別をしたい場合、tcpPacket.Synchronize がtrueか調べる。
サーバーに接続してからn個めのパケットを~みたいに決め撃つとき便利。

if (tcpPacket.Synchronize == true)
{
}

特定のバイト列を探す

自分の場合は欲しいデータがzlibだったのでtcpPacket.PayloadDataからヘッダを探す必要があった。
以下のページのベストアンサーが良さそうだったので使わせてもらった。

お世話になります。主題の通りなのですが、配列から配列を検索する手法、または直接的なメソッドを探しています。現在はとりあえず以下のようなコードで実現していますが、よりよい方法があればぜひお教えください。また、C#に限らず他の言語でも、「この言語なら一行で書ける」等ございましたらお聞かせください。//&

ヘッダまでにゴミがあるときとかにもこの値を活用した。

byte[] pattern = new byte[] { 0x00, 0x00, 0x00, 0x00 };

int headerAddr = searchZlibHeader.SearchBytes(tcpPacket.PayloadData, pattern);

if (headerAddr != -1)
{
}

分割されて送られるデータを追いかける

必要なデータがサイズ上の問題で分割された場合、結合していきたいが「次のパケットはどれ?」という問題に当たる。
パケットには次のパケットはこれという番号があるようで、tcpPacket.SequenceNumber から計算できる。
AcknowledgmentNumber (次のパケットの番号という理解)は、SequenceNumber に現在の PayloadData.Length を足すことで求められる。

uint acknowledgmentNumber = tcpPacket.SequenceNumber + (uint)tcpPacket.PayloadData.Length;

if (acknowledgmentNumber == tcpPacket.SequenceNumber)
{
}

フォームにリアルタイムにデータを表示したい

SharpPcapの動作テスト自体は簡単に終わったが、取得したデータをリアルタイムにUIに反映させるには少々厄介だとわかった。
イベントを受けている間フォームが固まっていたからだ。
動いたものを書くが、非同期(やれTask.Runだasync/awaitだ)についてはまるで理解してない。

非同期実行

自分の場合はForm1_ShownイベントのところにSharpPcap周りのコードを雑にコピペしていたので、奇麗にやりたい人は例によって勝手にやってください。
using System.Threading.Tasks; をお忘れなく。

// asyncをつける
private async void Form1_Shown(object sender, EventArgs e)

// キャプチャ開始部分を await Task.Run();
await Task.Run(() =>
     {
     // キャプチャ開始
     device.Capture();
});

UI更新

UIはSystem.Timers.Timer で1秒に1回更新させる形にした。
Form1_Load に適当にTimerを追加した。

例えばlabelのテキストを書き換えたいとして、別スレッドからのUI更新は普通に書いても動かないらしい。
なのでInvokeとかいうものを使った。

private void Form1_Load(object sender, EventArgs e)
{
        // Systemからなのはあいまい参照回避
        var timer = new System.Timers.Timer(1000);
        timer.Elapsed += new ElapsedEventHandler(uiTimer);
        timer.Start();
}

// TimerでのUI変更処理(1000ms)
public void uiTimer(object sender, ElapsedEventArgs e)
{
        this.Invoke((MethodInvoker)(() => label1.Text = "ほげ"));
}

あとがき

C#の非同期は意味わからんかったし、もうパケットは見たくないけど、今回色々やって得た知識は今後も便利につかえそうだぜ。

パケットやzlibについて色々教えてくれたxx2zz大先生に感謝。

- C# , , ,

Message

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

関連記事