WinPcapExtension ー ActionScriptからWinPcapを使えるようにする
■■ WinPcapExtension
ActionScriptというかAdobe AIRからWinPcapが使いたかったので、WinPcapのActionScript Native Extensionを書きました。
WinPcapExtension: https://github.com/ryujimiya/WinPcapExtension/
■■ 使用方法
1. ネットワークインターフェースの取得
WinPcapExtensionでキャプチャーするネットワークアダプタを選択します。
1.1. 初期化
WinPcapExtensionのインスタンスを new WinPcapExtension()で生成しています。
import flash.utils.ByteArray;
import livefan.packet.PacketUtil;
import livefan.winpcap.PcapDefine;
import livefan.winpcap.PcapHandle;
import livefan.winpcap.PcapIf;
import livefan.winpcap.PcapPktHdr;
import livefan.winpcap.ResultAllDevs;
import livefan.winpcap.ResultGetArrival;
import livefan.winpcap.ResultPcap;
import livefan.winpcap.Timeval;
import livefan.winpcap.WinPcapExtension;
import livefan.winpcap.WinPcapExtensionEvent;
public class NetworkSettingsWin extends NetworkSettingsWinView
{
/**
* WinPcapネイティブ拡張インスタンス
*/
private var _extension:WinPcapExtension = null;
/**
* ネットワークインターフェースインスタンス
*/
private var _pcapIf:PcapIf = null;
/**
* コンストラクタ
*/
public function NetworkSettingsWin()
{
super();
addEventListener(AIREvent.WINDOW_COMPLETE, initWin);
addEventListener(Event.CLOSING, closing);
}
/**
* ウィンドウが初期化された
* @param e
*/
private function initWin(e:AIREvent):void
{
// WinPcap拡張インスタンスの生成
_extension = new WinPcapExtension();
listNetworkIf.addEventListener(IndexChangeEvent.CHANGE, listNetworkIfIndexChange);
setupNetworkIfList();
}
}
1.2. ネットワークインターフェースの列挙
ネットワークインターフェースを取得するにはextension.pcapFindAllDevsEx(source, null);を使います。ここでsourceはPcapDefine.PCAP_SRC_IF_STRINGを指定します。
PCAP_SRC_IF_STRINGは "rpcap://"と定義されています。
pcapFindAllDevsExの戻り値はResultAllDevsで、そのpcapIfsというプロパティでネットワークインターフェースのリスト(の先頭)を取り出せます。あとはpcapIf = pcapIfsとし、pcapIf->nextで列挙していけば一覧を取得できます。
/**
* ネットワークインターフェース一覧をセットアップ
*/
private function setupNetworkIfList():void
{
/*
if (_pcapHandle != null)
{
return;
}
*/
var source:String = PcapDefine.PCAP_SRC_IF_STRING;
// ネットワークインターフェースの一覧を取得
var resAllDevs:ResultAllDevs = _extension.pcapFindAllDevsEx(source, null);
if (resAllDevs.retVal != 0)
{
Alert.show(resAllDevs.errBuf, "", Alert.OK, this);
return;
}
// ネットワークインターフェースをリストに登録
var dataProvider:ArrayCollection = new ArrayCollection();
var pcapIfs:PcapIf = resAllDevs.pcapIfs;
for (var pcapIf:PcapIf = pcapIfs; pcapIf != null; pcapIf = pcapIf.next)
{
var name:String = pcapIf.name;
var description:String = pcapIf.description;
var addresses:PcapAddr = pcapIf.addresses;
var labelStr:String = description;
dataProvider.addItem( { label: labelStr , data:pcapIf } );
}
listNetworkIf.dataProvider = dataProvider;
// インターフェース一覧を解放
resAllDevs.dispose();
}
2. パケットキャプチャー
では実際にパケットをキャプチャーする手順です。
キャプチャーの方法はWinPcapでいうpcap_loopを使う方法とpcap_next_exを使う方法の2種類あり、WinPcapExtensionはどちらにも対応しています。ここではpcap_loopを使う方法について説明します。
2.1. 初期化
WinPcapExtensionインスタンスを生成するところはネットワークインターフェース一覧取得の時と同じです。同じクラスでやるならネットワーク一覧取得のWinPcapExtensionインスタンスをそのまま使えばいいと思います。
次にイベントハンドラを設定します。
WinPcapExtensionEvent.CAPTURETHREAD_PACKETARRIVAL
WinPcapExtensionEvent.CAPTURETHREAD_THREADFUNCFINISHED
これらは、pcap_loop使用時に必要なハンドラで、pcap_next_exを利用した場合は必要ありません。
また、networkNameは前項で取得したpcapIf.nameを指定します。
public class CaptureThreadDemoWin extends CaptureThreadDemoWinView
{
private var _extension:WinPcapExtension = null;
private var _pcapHandle:PcapHandle = null;
private var _pcapIf:PcapIf = null;
private var _networkName:String = "";
public function CaptureThreadDemoWin(networkName:String)
{
super();
addEventListener(AIREvent.WINDOW_COMPLETE, initWin);
addEventListener(Event.CLOSING, closing);
_networkName = networkName;
}
private function initWin(e:AIREvent):void
{
// WinPcap拡張インスタンスの生成
_extension = new WinPcapExtension();
_extension.addEventListener(WinPcapExtensionEvent.CAPTURETHREAD_PACKETARRIVAL, onCaptureThreadPacketArrival);
_extension.addEventListener(WinPcapExtensionEvent.CAPTURETHREAD_THREADFUNCFINISHED, onCaptureThreadFinished);
textAreaDump.editable = false;
_pcapIf = getNetworkAdapter();
startCapture(_pcapIf.name);
}
private function onCaptureThreadPacketArrival(e:WinPcapExtensionEvent):void
{
// 実装する
}
private function onCaptureThreadFinished(e:WinPcapExtensionEvent):void
{
// 実装する
}
}
2.2. キャプチャーの開始、停止
キャプチャーを開始するにはまず_extension.pcapOpenでpcapHandleを取得する必要があります。
つぎに_pcapHandle.startCaptureThread()でキャプチャースレッドを起動します。そうすると、先ほど設定したイベントハンドラonCaptureThreadPacketArrivalにパケット到達時に通知されます。
キャプチャーを終了するには_pcapHandle.stopCaptureThread()を使用します。また、再開の必要がなければ、pcalHandleもクローズします。
private function startCapture(adapterName:String):Boolean
{
var resPcap:ResultPcap = _extension.pcapOpen(adapterName, 65535, PcapDefine.PCAP_OPENFLAG_PROMISCUOUS, 20, null);
if (resPcap.retVal != 0)
{
Alert.show(resPcap.errBuf, "", Alert.OK, this);
return false;
}
this._pcapHandle = resPcap.pcapHandle;
this._pcapHandle.startCaptureThread(); // CaptureThreadを起動する
return true;
}
private function stopCapture():void
{
if (_pcapHandle == null)
{
return;
}
_pcapHandle.stopCaptureThread(); // CaptureThreadを終了する
_pcapHandle.pcapClose();
_pcapHandle.dispose();
_pcapHandle = null;
}
}
2.3. フィルタリング
キャプチャーするパケットにフィルタリングをかけることもできます。
次のようにpcapSetFilterを使います。この設定はstartCaptureThreadを呼び出す前に行います。
this._pcapHandle.pcapSetFilter("src port >= 80 and src port <= 99", 1, 0xffffff);
3. パケットの取得
onCaptureThreadPacketArrivalに実装します。
理想はこのイベントが発生したとき到達パケット数は1であってほしかったのですが、パケット到達数が多いとそうもいかないようなので、まずパケット数を_pcapHandle.getArrivalPacketCount()で取得します。
あとはこのパケット数分を _pcapHandle.getArrivalPacket()を使ってパケットを取得すればいいです。
private function onCaptureThreadPacketArrival(e:WinPcapExtensionEvent):void
{
if (_pcapHandle == null)
{
return;
}
var packetCnt:int = _pcapHandle.getArrivalPacketCount();
//trace("packet = " + packetCnt);
for (var i:int = 0; i < packetCnt; i++)
{
// 到達パケットを取得する
var resGetArrival:ResultGetArrival = _pcapHandle.getArrivalPacket();
var ret:int = resGetArrival.retVal;
var header:PcapPktHdr = resGetArrival.header as PcapPktHdr;
var data:ByteArray = resGetArrival.data as ByteArray;
resGetArrival.dispose(); // 結果を破棄する (取得したheaderはnativeで使用不可となる)
if (ret != 1)
{
continue;
}
//trace(PacketUtil.hexDump(data));
// 表示
var newlineStr:String = "\r\n";
var text:String = "";
if (header != null)
{
var ts:Timeval = header.ts;
var date:Date = new Date(ts.tvSec * 1000);
text += date.toLocaleString();
text += " capLen:" + header.capLen.toString() + newlineStr;
}
text += PacketUtil.hexDump(data);
textAreaDump.text = text;
}
}
4. パケットの解析
パケットの解析にはPacketLibを使います。PacketLibはActionScriptのみで書いたパケット解析ライブラリです。
PacketLib: https://github.com/ryujimiya/PacketLib
次の関数parsePacketの引数dataには、data:ByteArray = resGetArrival.data as ByteArrayで取得したdataがセットされると思ってください。
Packet.parsePacketでパケットの生データをパケットオブジェクトに変換します。
以下の例ではlastPacket.payloadByteAry.lengthとペイロード長を取得するためにしか使っていませんが、実際パケット解析するときは、lastPacket.payloadByteAry自体が重要で、パケットのペイロードをByteArrayで取得できます。
private function parsePacket(data:ByteArray):String
{
var retStr:String = "";
//var newlineStr:String = "\r\n";
// パケットデータをパースする
var dlt:int = _pcapHandle.pcapDataLink();
var packet:Packet = Packet.parsePacket(dlt, data);
if (packet == null)
{
return retStr;
}
// 一番内側のパケットを取得する
var lastPacket:Packet = packet.getLastPacket();
// TcpPacketを指定して取得する
//var lastPacket:Packet = packet.extractPacket(TcpPacket);
// パケット情報を取得する
retStr += getQualifiedClassName(lastPacket) + " ";
if (lastPacket is TcpPacket || lastPacket is UdpPacket)
{
// TcpPacketかUdpPacketの場合
var srcAddrAsString:String = "";
var dstAddrAsString:String = "";
var srcPort:uint = 0;
var dstPort:uint = 0;
var ipPacket:IpPacket = lastPacket.parentPacket as IpPacket;
if (ipPacket != null)
{
if (ipPacket is Ipv4Packet)
{
var ipv4Packet:Ipv4Packet = ipPacket as Ipv4Packet;
srcAddrAsString = _extension.IpAddrByteArrayToString(SockAddr.AF_INET, ipv4Packet.srcAddress);
dstAddrAsString = _extension.IpAddrByteArrayToString(SockAddr.AF_INET, ipv4Packet.dstAddress);
}
else if (ipPacket is Ipv6Packet)
{
var ipv6Packet:Ipv6Packet = ipPacket as Ipv6Packet;
srcAddrAsString = "[" + _extension.IpAddrByteArrayToString(SockAddr.AF_INET6, ipv6Packet.srcAddress) + "]";
dstAddrAsString = "[" + _extension.IpAddrByteArrayToString(SockAddr.AF_INET6, ipv6Packet.dstAddress) + "]";
}
else
{
// invalid
}
}
if (lastPacket is TcpPacket)
{
var tcpPacket:TcpPacket = lastPacket as TcpPacket;
srcPort = tcpPacket.srcPort;
dstPort = tcpPacket.dstPort;
}
else if (lastPacket is UdpPacket)
{
var udpPacket:UdpPacket = lastPacket as UdpPacket;
srcPort = udpPacket.srcPort;
dstPort = udpPacket.dstPort;
}
retStr +=
srcAddrAsString
+ ":"
+ srcPort.toString()
+ "->"
+ dstAddrAsString
+ ":"
+ dstPort.toString()
+ " payload length = " + lastPacket.payloadByteAry.length.toString();
}
else
{
// その他
}
return retStr;
}
■■サンプルソースコード
Author And Source
この問題について(WinPcapExtension ー ActionScriptからWinPcapを使えるようにする), 我々は、より多くの情報をここで見つけました https://qiita.com/ryujimiya2361/items/e26cfd338b3002bae3f5著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .