概要
scapyのTips集(順次追加)
公式ドキュメント
Scapy API reference
https://scapy.readthedocs.io/en/latest/api/scapy.html
Tips
Ethernetアダプタのインターフェース名を知りたい
get_windows_if_list()
を使います。
各APIのパラメータにあるifaceに設定可能です。
参考
Scapy can't see_use some Ethernet interfaces on Windows · Issue #1542 · secdev_scapy · GitHubの gpotter2 commented on 7 Aug 2018dで書かれている
コード
from scapy.all import *
get_windows_if_list()
結果
nameの値が各APIの引数ifaceに設定できる。
下記の例でいうと、'イーサネット 6'
, 'イーサネット 2'
など
MACやIPアドレスから所望のEthernetアダプタを見つけます。
nameをifaceに設定することができます。
In [6]: from scapy.all import *
...: get_windows_if_list()
Out[6]:
[{'name': 'イーサネット 6',
'win_index': 15,
'description': 'ASIX AX88179 USB 3.0 to Gigabit Ethernet Adapter #2',
'guid': '{93E3CFB5-11A7-43F4-9BE0-E42AD69529A3}',
'mac': '74:03:bd:7f:83:21',
'ipv4_metric': 5,
'ipv6_metric': 5,
'ips': ['fe80::b964:9105:342:b0e1', '192.168.51.123']},
...
{'name': 'イーサネット 2',
'win_index': 11,
'description': 'Intel(R) Ethernet Connection (5) I219-LM',
'guid': '{6091155D-A8EA-491C-BA3D-CD5CB22B29BE}',
'mac': '40:b0:34:1a:78:6f',
'ipv4_metric': 25,
'ipv6_metric': 0,
'ips': ['10.168.38.67']},
...
メモ
Nmap Loopback Adapter (127.0.0.1同士の通信を確認するための仮想的なアダプタ)もここに出てくるが、sniff()で取得したpacketをwrpcap()でpcap化しようとすると失敗する。調査中。
実インターフェースでも発生した。
実インターフェースで発生したのはsniff()で取得したpacketをリストに入れて格納してしまっており、
それをwrpcap()に渡していたためであった。
イメージでいうと以下のようになっていた。
[<Sniffed: TCP:0 UDP:5 ICMP:0 Other:5>, <Sniffed: TCP:0 UDP:5 ICMP:0 Other:5>, ...]
(scapy.plist.PacketListのインスタンスのリストを作成していた)
append()などがサポートされていなかったが+=
が使えたので、以下の書き方ができた。
all_packets = None
# ループ
...
pkts = scpy.sniff(iface="イーサネット 4", count=10)
if all_packets :
all_packets += pkts # 初回以降は+=で追加していく。
else:
all_packets = pkts # 初回
wrpcap(fname, all_packets )
ループバック("127.0.0.1")を見たいときにLoopback Pseudo-Interface 1を選択してしまうとダメな様子。
File "C:\Python37\lib\site-packages\scapy\arch\windows\__init__.py", line 706, in dev_from_pcapname
raise ValueError("Unknown pypcap network interface %r" % pcap_name)
ValueError: Unknown pypcap network interface 'Loopback Pseudo-Interface 1'
ifcaceに "Npcap Loopback Adapter" を設定すると"127.0.0.1"のpcapを取得することができた。
ただ、wiresharkで直接"Npcap Loopback Adapter"を測定するとパケットを適切に解析できるが、
取得したpcapを開くと、データ的には同じにも関わらず、N/Aが表示されてしまう・・・。
実際のインターフェースなら取得できるので、問題なしとするか。。。githubかstack overflowでQAしてみるか。。。
https://github.com/nmap/nmap/issues/200
によって、
https://wiki.wireshark.org/NullLoopback
になっている?
受信したパケットのサマリ(IPアドレス、ポート)をとりあえず簡単にコンソールに出したい
参考
scapy.sendrecv.sniff(*args, **kwargs)
https://scapy.readthedocs.io/en/latest/api/scapy.sendrecv.html#scapy.sendrecv.sniff
コード
以下は同期のメソッドなのでCtrl+Cで止める必要あり。
受信パケット数やタイムアウトなどで停止条件を設定することもできる。
from scapy.all import *
sniff(iface='イーサネット 4', prn=lambda x: x.summary())
結果
In [7]: from scapy.all import *
...: sniff(iface='イーサネット 4', prn=lambda x: x.summary())
Ether / IP / UDP 192.168.50.12:30490 > 237.50.0.1:30490 / Raw
Ether / IP / UDP 192.168.50.12:30490 > 237.50.0.1:30490 / Raw
Ether / IP / UDP 192.168.50.12:30490 > 237.50.0.1:30490 / Raw
Ether / IP / UDP 192.168.50.12:30490 > 237.50.0.1:30490 / Raw
Ether / IP / UDP 192.168.50.12:30490 > 237.50.0.1:30490 / Raw
Ether / IP / UDP 192.168.50.12:30490 > 237.50.0.1:30490 / Raw
Ether / IP / UDP 192.168.50.12:30490 > 237.50.0.1:30490 / Raw
Ether / ARP who has 192.168.51.2 says 192.168.51.1 / Padding
Ether / IP / UDP 192.168.50.12:30490 > 237.50.0.1:30490 / Raw
Ether / ARP who has 192.168.51.2 says 192.168.51.1 / Padding
Ether / ARP who has 192.168.51.2 says 192.168.51.1 / Padding
Ether / IP / UDP 192.168.50.12:30490 > 237.50.0.1:30490 / Raw
Ether / ARP who has 192.168.51.2 says 192.168.51.1 / Padding
Ether / ARP who has 192.168.51.2 says 192.168.51.1 / Padding
Ether / IP / UDP 192.168.50.12:30490 > 237.50.0.1:30490 / Raw
Ether / ARP who has 192.168.51.2 says 192.168.51.1 / Padding
Ether / ARP who has 192.168.51.2 says 192.168.51.1 / Padding
Out[7]: <Sniffed: TCP:0 UDP:10 ICMP:0 Other:7>
別スレッドでパケット受信して、ログに出力したい+pcapにしたい。
AsyncSnifferについては別で書く。
ここではPython Scapyを使ったネットワークプログラミング - Qiitaの記事をベースにpcapにするメソッドを追加したクラス例。
コード
import threading
import scapy.all as scpy
import datetime
from logging import getLogger, basicConfig, NullHandler,DEBUG, INFO, WARNING
mylogger = getLogger(__name__)
mylogger.addHandler(NullHandler())
class Sniffer(threading.Thread):
def __init__(self, iface, count=20, timeout=3, logger=None):
self.stop_event = threading.Event() #停止させるかのフラグ
self.thread = threading.Thread(target = self._sniffer_main_loop)
self.iface = iface
self.count = count
self.timeout = timeout
self.packets = None
if logger:
self.log = logger
else:
self.log = mylogger
def start(self):
self.log.debug(f"try to start thread.")
self.packets = None
self.stop_event.clear()
self.thread.start()
def stop(self):
self.log.debug(f"try to stop thread.")
self.stop_event.set()
self.thread.join()
def _sniffer_main_loop(self):
self.log.debug(f"start thread.")
try:
while not self.stop_event.is_set():
pkts = scpy.sniff(iface=self.iface, count=self.count, timeout=self.timeout)
if self.packets:
self.packets += pkts
else:
self.packets = pkts
except Exception as e:
self.log.error(f"exception occurred. stop sniffer main loop. {e}")
self.stop_event.set()
self.log.debug(f"end thread.")
def create_pcap(self, fname=datetime.datetime.now().strftime("doip_test_%H%M%S.cap")):
self.stop() if not self.stop_event.is_set() else None
scpy.wrpcap(fname, self.packets)
結果
使い方は別途追記。