はじめに
今回は、ブラウザの通信を随時キャプチャして、何か処理するアプリを作ってみようと思います。
ブラウザの通信内容を、自作アプリの方で捕捉できれば、例えば情報をリアルタイムで解析したり、アイディア次第で面白いものが作れると思います。
また、暗号化された通信も復号化した上で、アプリ側で捕捉してみます。
プログラミング言語は何でも良かったのですが、Windowsアプリケーションを手軽に作れるのでC#を採用しました。
ちなみに、C#で通信のコアなところまで実装すると、他の言語への汎用性がないので、通信部分はWireshark (Tshark) を使いました。
他にも私のブログで、C#について解説している記事がありますのでご覧ください。
Wiresharkのインストール
Wiresharkとは、PC上の通信(パケット)を見るためのツールです。
WiresharkにはTsharkが同梱されますので、以下の記事をもとに構築と設定を済ませて下さい。
Visual Studio 2022のインストール
今回は、C#でアプリケーションを作成します。
以下の手順を参考に、構築を済ませて下さい。
自作アプリでPCの通信インターフェースの一覧を表示
いよいよ自作アプリを作成します。🙂
パケットキャプチャについてはWireshark(Tshark)を使用します。
フォームの名称を変更しました。
ここからは最低限の実装のみ解説しますが、見た目やリファクタリングなどはお好みで実装してみてください。
まずは、PCのネットワークインターフェースの一覧を表示する処理を実装します。
ボタンとテキストボックスを配置します。
Processを使用して、Tsharkコマンドを実行しますので、クラス内変数を定義します。
public partial class Form1 : Form
{
// プロセス tshak インターフェース一覧を表示
Process processTsInterface = null;
ボタンクリックイベントは以下です。
/// <summary>
/// インターフェース一覧 ボタンクリック
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
// コマンド
string executeCommand = "C:\\Program Files\\Wireshark\\tshark.exe";
// コマンド引数
string args = "-D";
// プロセスを起動
processTsInterface = new Process();
ProcessStartInfo processStartInfo = new ProcessStartInfo(executeCommand, args);
processStartInfo.CreateNoWindow = true;
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardError = true;
// 文字化けするので、文字コードを指定する
processStartInfo.StandardErrorEncoding = Encoding.UTF8;
processStartInfo.StandardOutputEncoding = Encoding.UTF8;
// 起動
processTsInterface = Process.Start(processStartInfo);
// 受信イベント
processTsInterface.OutputDataReceived += dataReceivedTsInterface;
processTsInterface.ErrorDataReceived += errReceivedTsInterface;
processTsInterface.BeginErrorReadLine();
processTsInterface.BeginOutputReadLine();
}
Tsharkの標準出力の結果を取得する関数を定義します。
一応エラー出力も捕捉します。
/// <summary>
/// tshrak インターフェース一覧を表示 出力
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void dataReceivedTsInterface(object sender, DataReceivedEventArgs e)
{
string packetText = e.Data;
if (packetText != null && packetText.Length > 0)
{
PrintTextBoxByThread(packetText, textBox1);
}
}
/// <summary>
/// tshrak インターフェース一覧を表示 エラー
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void errReceivedTsInterface(object sender, DataReceivedEventArgs e)
{
string packetText = e.Data;
if (packetText != null && packetText.Length > 0)
{
PrintTextBoxByThread("ERR:" + packetText, textBox1);
}
}
上記の受信イベントは別スレッドで動作するため、テキストボックスに表示を行うためには細工が必要です。
以下のような関数を定義します。
/// <summary>
/// テキストボックスに出力
/// </summary>
/// <param name="msg"></param>
/// <param name="tb"></param>
private void PrintTextBox(string msg, TextBox tb)
{
if (string.IsNullOrEmpty(tb.Text))
{
tb.Text = msg;
}
else
{
tb.Text = tb.Text + "\r\n" + msg;
}
// キャレット位置を末尾に移動
tb.SelectionStart = tb.Text.Length;
tb.Focus();
tb.ScrollToCaret();
}
/// <summary>
/// 別スレッドからのテキストボックス更新
/// </summary>
/// <param name="msg"></param>
/// <param name="tb"></param>
private void PrintTextBoxByThread(string msg, TextBox tb)
{
// 別スレッドからテキストの更新
this.Invoke((MethodInvoker)(() => PrintTextBox(msg, tb)));
}
最後に、フォームのクローズイベントで以下の処理を行います。
/// <summary>
/// フォームクローズ
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
if (processTsInterface != null && !processTsInterface.HasExited)
{
// プロセス終了
processTsInterface.Kill();
}
}
それでは、アプリケーションを実行してみましょう。
「インターフェース一覧ボタン」をクリックすると、インターフェースの一覧が出力されました。
仕組みは簡単で、Tsharkをコマンド実行し、標準出力を受け取っているだけです。
自作アプリでTsharkの通信をC#で取得
続いて、Tsharkを使いブラウザの通信をアプリ側で取得したいと思います。
インターフェースを指定するテキストボックスと、キャプチャ開始ボタン、通信内容を表示するテキストボックスを用意します。
実装の要領は先程と同じです。
Processを使用して、Tsharkコマンドを実行しますので、クラス内変数を定義します。
// プロセス Tshark パケットキャプチャを表示
Process processTsCap = null;
「パケットキャプチャ開始ボタン」のクリックイベントを実装します。
前回のWiresharkの記事同様、自サイトの通信をキャプチャするサンプルです。
/// <summary>
/// パケットキャプチャ開始 ボタンクリック
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
// 必須チェック
if (string.IsNullOrEmpty(textBox2.Text))
{
MessageBox.Show("インターフェース番号を指定してください");
return;
}
// コマンド
string executeCommand = "C:\\Program Files\\Wireshark\\tshark.exe";
// コマンド引数
// TODO: 適当に条件変えて下さい
string args = String.Format("-i {0} -l -e text -Tfields " +
"-f \"net 183.90.240.49\"",
textBox2.Text
);
// プロセスを起動
processTsCap = new Process();
ProcessStartInfo processStartInfo = new ProcessStartInfo(executeCommand, args);
processStartInfo.CreateNoWindow = true;
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardError = true;
// 文字化けするので、文字コードを指定する
processStartInfo.StandardErrorEncoding = Encoding.UTF8;
processStartInfo.StandardOutputEncoding = Encoding.UTF8;
// 起動
processTsCap = Process.Start(processStartInfo);
// 受信イベント
processTsCap.OutputDataReceived += dataReceivedTsCap;
processTsCap.ErrorDataReceived += errReceivedTsCap;
processTsCap.BeginErrorReadLine();
processTsCap.BeginOutputReadLine();
}
Tsharkの標準出力の結果を取得する関数を定義します。
一応エラー出力も捕捉します。
/// <summary>
/// tshrak パケットキャプチャを表示 出力
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void dataReceivedTsCap(object sender, DataReceivedEventArgs e)
{
string packetText = e.Data;
if (packetText != null && packetText.Length > 0)
{
PrintTextBoxByThread(packetText, textBox3);
}
}
/// <summary>
/// tshrak パケットキャプチャを表示 エラー
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void errReceivedTsCap(object sender, DataReceivedEventArgs e)
{
string packetText = e.Data;
if (packetText != null && packetText.Length > 0)
{
PrintTextBoxByThread("ERR:" + packetText, textBox3);
}
}
最後に、フォームのクローズイベントに処理を追加します。
/// <summary>
/// フォームクローズ
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
if (processTsInterface != null && !processTsInterface.HasExited)
{
// プロセス終了
processTsInterface.Kill();
}
if (processTsCap != null && !processTsCap.HasExited)
{
// プロセス終了
processTsCap.Kill();
}
}
それでは、アプリを起動してみます。
インターフェース番号を指定して、「パケットキャプチャ開始ボタン」をクリックします。
ちなみに、インターフェース名の表示については、エラー出力で取れるようです。
Chromeは一旦すべて閉じて、自作アプリを実行したまま、Chromeのショートカットを起動します。
この時、自作アプリを必ず先に実行します。後から実行では通信をうまく取得できません。
自サイトへアクセスして、通信を確認してみましょう。
暗号化された通信内容が、リアルタイムで復号化されて取得できました。
改めて、ソースの全容を載せておきます。(Form1.cs)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
// プロセス tshak インターフェース一覧を表示
Process processTsInterface = null;
// プロセス Tshark パケットキャプチャを表示
Process processTsCap = null;
public Form1()
{
InitializeComponent();
}
/// <summary>
/// インターフェース一覧 ボタンクリック
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
// コマンド
string executeCommand = "C:\\Program Files\\Wireshark\\tshark.exe";
// コマンド引数
string args = "-D";
// プロセスを起動
processTsInterface = new Process();
ProcessStartInfo processStartInfo = new ProcessStartInfo(executeCommand, args);
processStartInfo.CreateNoWindow = true;
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardError = true;
// 文字化けするので、文字コードを指定する
processStartInfo.StandardErrorEncoding = Encoding.UTF8;
processStartInfo.StandardOutputEncoding = Encoding.UTF8;
// 起動
processTsInterface = Process.Start(processStartInfo);
// 受信イベント
processTsInterface.OutputDataReceived += dataReceivedTsInterface;
processTsInterface.ErrorDataReceived += errReceivedTsInterface;
processTsInterface.BeginErrorReadLine();
processTsInterface.BeginOutputReadLine();
}
/// <summary>
/// tshrak インターフェース一覧を表示 出力
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void dataReceivedTsInterface(object sender, DataReceivedEventArgs e)
{
string packetText = e.Data;
if (packetText != null && packetText.Length > 0)
{
PrintTextBoxByThread(packetText, textBox1);
}
}
/// <summary>
/// tshrak インターフェース一覧を表示 エラー
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void errReceivedTsInterface(object sender, DataReceivedEventArgs e)
{
string packetText = e.Data;
if (packetText != null && packetText.Length > 0)
{
PrintTextBoxByThread("ERR:" + packetText, textBox1);
}
}
/// <summary>
/// テキストボックスに出力
/// </summary>
/// <param name="msg"></param>
/// <param name="tb"></param>
private void PrintTextBox(string msg, TextBox tb)
{
if (string.IsNullOrEmpty(tb.Text))
{
tb.Text = msg;
}
else
{
tb.Text = tb.Text + "\r\n" + msg;
}
// キャレット位置を末尾に移動
tb.SelectionStart = tb.Text.Length;
tb.Focus();
tb.ScrollToCaret();
}
/// <summary>
/// 別スレッドからのテキストボックス更新
/// </summary>
/// <param name="msg"></param>
/// <param name="tb"></param>
private void PrintTextBoxByThread(string msg, TextBox tb)
{
// 別スレッドからテキストの更新
this.Invoke((MethodInvoker)(() => PrintTextBox(msg, tb)));
}
/// <summary>
/// パケットキャプチャ開始 ボタンクリック
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
// 必須チェック
if (string.IsNullOrEmpty(textBox2.Text))
{
MessageBox.Show("インターフェース番号を指定してください");
return;
}
// コマンド
string executeCommand = "C:\\Program Files\\Wireshark\\tshark.exe";
// コマンド引数
// TODO: 適当に条件変えて下さい
string args = String.Format("-i {0} -l -e text -Tfields " +
"-f \"net 183.90.240.49\"",
textBox2.Text
);
// プロセスを起動
processTsCap = new Process();
ProcessStartInfo processStartInfo = new ProcessStartInfo(executeCommand, args);
processStartInfo.CreateNoWindow = true;
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardError = true;
// 文字化けするので、文字コードを指定する
processStartInfo.StandardErrorEncoding = Encoding.UTF8;
processStartInfo.StandardOutputEncoding = Encoding.UTF8;
// 起動
processTsCap = Process.Start(processStartInfo);
// 受信イベント
processTsCap.OutputDataReceived += dataReceivedTsCap;
processTsCap.ErrorDataReceived += errReceivedTsCap;
processTsCap.BeginErrorReadLine();
processTsCap.BeginOutputReadLine();
}
/// <summary>
/// tshrak パケットキャプチャを表示 出力
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void dataReceivedTsCap(object sender, DataReceivedEventArgs e)
{
string packetText = e.Data;
if (packetText != null && packetText.Length > 0)
{
PrintTextBoxByThread(packetText, textBox3);
}
}
/// <summary>
/// tshrak パケットキャプチャを表示 エラー
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void errReceivedTsCap(object sender, DataReceivedEventArgs e)
{
string packetText = e.Data;
if (packetText != null && packetText.Length > 0)
{
PrintTextBoxByThread("ERR:" + packetText, textBox3);
}
}
/// <summary>
/// フォームクローズ
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
if (processTsInterface != null && !processTsInterface.HasExited)
{
// プロセス終了
processTsInterface.Kill();
}
if (processTsCap != null && !processTsCap.HasExited)
{
// プロセス終了
processTsCap.Kill();
}
}
}
}
最後に
実装手順は以上になります。お疲れ様でした。
アイディア次第では面白いアプリが作れるかもしれませんね。
それでは。🙂
他にも私のブログで、C#について解説している記事がありますのでご覧ください。
コメント