數據包處理利器――Scapy基礎知識

北京新浪網 (2021-05-18 09:23)
分享| 分享至新浪微博 分享至facebook 分享至PLURK 分享至twitter

原標題:數據包處理利器――Scapy基礎知識

什麼 是scapy

Scapy是功能強大的互動式數據包處理程序。它能夠偽造或解碼各種協議的數據包,在線發送,捕獲,匹配請求和響應等。它可以輕鬆處理大多數經典任務,例如掃描,跟蹤路由,探測,單元測試,攻擊或網路發現,它可以代替hping,arpspoof,arp-sk,arping,p0f甚至Nmap,tcpdump和tshark的某些部分。。它在其他工具無法處理的許多其他特定任務上也表現出色,例如發送無效幀,組合技術(VLAN跳變+ ARP緩存中毒,WEP加密通道上的VOIP解碼等等)

安裝scapy

直接pip安裝即可,我使用的是python3

pip3 install scapy  scapy基本使用

輸入scapy回車進入scapy的shell 可以使用ls()來查看scapy支持的協議

使用lsc()查看scapy支持的函數

還可以使用ls()獲取協議包含的參數

發送和接收數據包

send

- 在第3層發送數據包(Scapy創建第2層標頭),不接收任何數據包。

loop 參數默認為0,如果它的值不是0,那麼數據包將一直循環發送,直到按CTRL-C為止。 count 可用於設置要發送的數據包的確切數量。 inter 可用於設置每個數據包之間的秒數。 >>> send(IP(dst='8.8.8.8')/TCP(dport=53, flags='S')) . Sent 1 packets. >>>  >>> send(IP(dst='8.8.8.8')/TCP(dport=53, flags='S'), count=10) .......... Sent 10 packets. >>> >>> send(IP(dst='8.8.8.8')/TCP(dport=53, flags='S'), loop=1) ......................... [... snipped ...] Sent 1503 packets. 

sendp

與send()相同,但在第2層發送數據包(必須提供第2層標頭),不接收任何數據包。 使用iface到設置界面上發送數據包。(如果未設置,將使用conf.iface的值) >>> sendp(Ether()/IP(dst="1.2.3.4",ttl=(1,4)), iface="eth0") .... Sent 4 packets.  >>> sendp("I』m travelling on Ethernet", iface="eth0", loop=1, inter=0.2)  >>> sendp(rdpcap("/tmp/pcapfile")) # tcpreplay ........... Sent 11 packets. 

sr

發送數據包並接收響應。 sr()返回兩個列表,第一個列表包含響應的,第二個列表包含未響應的。 >>> sr(IP(dst="60.205.177.168")/TCP(dport=[21,22,23])) Begin emission: Finished sending 3 packets. ...**...............................^C Received 36 packets, got 2 answers, remaining 1 packets (,  ) >>> ans,unans=_ >>> unans.summary() IP / TCP 172.17.51.80:ftp_data > 60.205.177.168:telnet S >>> ans[0] (>,  >) >>> ans[0][0] > 

sr1

發送所有數據包並僅記錄第一個響應。 >>> p=sr1(IP(dst="www.baidu.com")/ICMP()/"asdqwe") Begin emission: Finished sending 1 packets. .* Received 2 packets, got 1 answers, remaining 0 packets 

srloop

循環發送,接收響應並顯示響應。 該函數返回幾個數據包和響應,以及未響應的。 >>> packet = IP(dst='60.205.177.168')/ICMP() >>> srloop(packet) RECV 1: IP / ICMP 60.205.177.168 > 172.17.51.80 echo-reply 0 RECV 1: IP / ICMP 60.205.177.168 > 172.17.51.80 echo-reply 0 RECV 1: IP / ICMP 60.205.177.168 > 172.17.51.80 echo-reply 0 RECV 1: IP / ICMP 60.205.177.168 > 172.17.51.80 echo-reply 0 ^C         Sent 4 packets, received 4 packets. 100.0% hits. (,  )  使用Scapy創建數據包 Scapy數據包的創建與網路中的分層方法一致。 數據包的基本構建塊是一層,而整個數據包則是通過將各個層堆疊在一起而構建的。 scapy通過在TCP / IP的不同層上為每個協議定義數據包頭,然後按順序堆疊這些層,來構造數據包。

在一行中創建數據包

>>> packet = Ether()/IP(dst='8.8.8.8')/TCP(dport=53,flags='S') 

分別創建每個圖層並使用'/'運算符將它們堆疊

>>> l2 = Ether() >>> l3 = IP(dst='8.8.8.8/30') >>> l4 = TCP(dport=53, flags = 'S') >>> packet = l2/l3/l4 

Scapy IP表示法

Scapy接受普通的IP表示法,CIDR表示法,主機名。

>>> packet = IP(dst = '8.8.8.8') >>> packet = IP(dst = 'scanme.nmap.org') >>> packet = IP(dst = '8.8.8.8/30') >>> [a for a in packet] [, , , ] >>> packet = IP(dst = 'egadz.metasploit.com/30') 

創建一組數據包

我們可以使用Scapy創建一組數據包

>>> pkts = IP(ttl=[1,3,5,(7,10)])/TCP() >>> [pkt for pkt in pkts] [>,  >,  >,  >,  >,  >,  >]  >>> packet=IP(dst="192.168.*.1-10")/TCP(dport=(0,100))  >>> [a for a in packet] [>,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >,  >, ... 

檢查數據包

獲取數據包的詳細說明以及數據類型

>>> packet = IP()/TCP() >>> ls(packet) version    : BitField             = 4               (4) ihl        : BitField             = None            (None) tos        : XByteField           = 0               (0) len        : ShortField           = None            (None) id         : ShortField           = 1               (1) flags      : FlagsField           = 0               (0) frag       : BitField             = 0               (0) ttl        : ByteField            = 64              (64) proto      : ByteEnumField        = 6               (0) chksum     : XShortField          = None            (None) src        : Emph                 = '127.0.0.1'     (None) dst        : Emph                 = '127.0.0.1'     ('127.0.0.1') options    : PacketListField      = []              ([]) [-- snipped --] 

show

顯示詳細的包頭

>>> packet.show() ###[ IP ]###    version= 4   ihl= None   tos= 0x0   len= None   id= 1   flags=    frag= 0   ttl= 64   proto= tcp   chksum= None   src= 127.0.0.1   dst= 127.0.0.1   options ###[ TCP ]###       sport= ftp_data      dport= http      seq= 0      ack= 0      dataofs= None      reserved= 0      flags= S      window= 8192      chksum= None      urgptr= 0      options= [] 

show2

與show()類似,但可以組裝數據包並計算校驗和和IHL(報頭長度,最小值是5)。

>>> packet.show2() ###[ IP ]###    version= 4   ihl= 5   tos= 0x0   len= 40   id= 1   flags=    frag= 0   ttl= 64   proto= tcp   chksum= 0x7ccd   src= 127.0.0.1   dst= 127.0.0.1   options ###[ TCP ]###       sport= ftp_data      dport= http      seq= 0      ack= 0      dataofs= 5      reserved= 0      flags= S      window= 8192      chksum= 0x917c      urgptr= 0      options= [] 

summary

顯示數據包的簡短的摘要

>>> packet.summary() 'IP / TCP 127.0.0.1:ftp_data > 127.0.0.1:http S' 

與數據包內部的欄位進行交互

>>> Ether(dst="d8:55:a3:fe:80:78")/IP(dst="8.8.8.8") > >>> packet=_ >>> packet.dst 'd8:55:a3:fe:80:78' >>> packet[IP].dst '8.8.8.8' 

檢查數據包中是否存在層

haslayer方法

>>> if packet.haslayer(IP): ...:     print (packet[IP].dst) ...:  8.8.8.8 

使用in構造

>>> pkt = IP()/TCP()/DNS() >>> DNS in pkt True  Scapy的sprintf sprintf()方法是Scapy的強大功能之一,在編寫自定義工具時非常方便。 sprintf 用數據包中的值填充格式字元串,就像C語言庫中的sprintf一樣,不同的是這裏用數據包中的欄位值填充格式字元串。 >>> packet.sprintf("Ethernet source is %Ether.src% and IP proto is %IP.proto%") 'Ethernet source is 00:16:3e:0c:d1:ad and IP proto is tcp' >>> a.sprintf("%dst% %IP.dst% vlan=%Dot1Q.vlan%") '00:00:d4:ae:3f:71 192.168.0.1 vlan=42' >>> >>>a.sprintf(" %TCP.flags% | %5s,TCP.flags% | %#05xr,TCP.flags%") ' RA | RA    | 0x014' 

數據包處理程序

我們可以使用lambda函數編寫處理TCP數據包的數據包處理程序,但該功能僅適用於TCP數據包。

>>>  f=lambda x:x.sprintf("%IP.dst%:%TCP.dport%") >>> f(IP(dst="8.8.8.8")/TCP()) '8.8.8.8:http' >>> f(IP(dst="8.8.8.8")/UDP()) '8.8.8.8:??' 

還可以使用sprintf()中的條件子字元串來實現處理其它層的目的。條件子字元串僅在數據包中存在某個層時才觸發,否則將被忽略。還可以!用於檢查是否缺少圖層。條件子字元串格式: {[!]層:子字元串}

>>> f=lambda x: x.sprintf("=> {IP:ip=%IP.dst% {UDP:dport=%UDP.dport%} ...: ... {TCP:%TCP.dport%/%TCP.flags%}{ICMP:type=%r,ICMP.type%}} ...: ... {!IP:not an IP packet}") >>> f(IP()/TCP()) '=> ip=127.0.0.1 http/S' >>> f(IP()/UDP()) '=> ip=127.0.0.1 dport=domain' >>> f(IP()/ICMP()) '=> ip=127.0.0.1 type=8' >>> f(Ether()/ARP()) '=> not an IP packet'  導入與導出數據

PCAP格式

從PCAP文件導入數據包。

pkts = rdpcap("temp.cap") pkts = sniff(offline="temp.cap") 

將數據包導出到pcap文件。

wrpcap("temp.cap",pkts) 

十六進位轉儲格式

Scapy允許以各種十六進位格式導出數據包。 使用hexdump()函數使用hexdump格式顯示一個或多個數據包: >>> hexdump(s) 0000  D8 55 A3 FE 80 78 00 16 3E 0C D1 AD 08 00 45 00  .U...x..>.....E. 0010  00 28 00 01 00 00 40 06 8B 5E AC 11 33 50 08 08  .(....@..^..3P.. 0020  08 08 00 14 00 50 00 00 00 00 00 00 00 00 50 02  .....P........P. 0030  20 00 A0 0D 00 00        

十六進位字元串

還可以使用str()函數將整個數據包轉換為十六進位字元串

>>> s >> >>> str(s) WARNING: Calling str(pkt) on Python 3 makes no sense! "b'\xd8U\xa3\xfe\x80x\x00\x16>\x0c\xd1\xad\x08\x00E\x00\x00(\x00\x01\x00\x00@\x06\x8b^\xac\x113P\x08\x08\x08\x08\x00\x14 \x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xa0\r\x00\x00'" 

Base64

Scapy可以使用export_object()函數導出數據包的base64編碼數據。 >>> export_object(s) b'eNprYEouTk4sqNTLSaxMLSrWyzHici3JSC3iKmTQDCpk1EiOT85PSU0u5krNAzG4Cpki7BkYGA7PCD20+PC+Qw0VDGJ2PIcnHlrLweDKwKDBwMjA4MB2qDvu0BpB4wAOIGAQYQhggIIAJgWGQwt4GRgKmSPYgPycxJLMPMNClrZC1qBCNnfHGxoeDcsdkv2AoKSQPUkPALURLMU=' >>> new_pkt = import_object  嗅探

Sniff()

sniff()函數可幫助我們捕獲所有流量: 包括count,filter,iface,lfilter,prn,timeout選項。 >>> sniff(count=4, iface='eth0')  

可以添加過濾以捕獲需要的數據包,使用標準的tcpdump / libpcap語法:

>>> pkts = sniff(count=1,filter="tcp and host 60.205.177.168 and port 80") >>> pkts.summary() Ether / IP / TCP 172.17.51.80:54578 > 60.205.177.168:http S  可以做類似tcpdump的簡單流量分析器 >>>  pkts = sniff(count=5,filter="host 60.205.177.168",prn=lambda x:x.summary()) Ether / IP / TCP 172.17.51.80:54624 > 60.205.177.168:http S Ether / IP / TCP 60.205.177.168:54624 > 172.17.51.80:http S Ether / IP / TCP 172.17.51.80:http > 60.205.177.168:54624 SA Ether / IP / TCP 60.205.177.168:http > 172.17.51.80:54624 SA Ether / IP / TCP 172.17.51.80:54624 > 60.205.177.168:http A  也可以從pcap文件中嗅探數據包。 pkts = sniff(offline='test.pcap') >>> pkts.nsummary() 0000 Ether / IP / TCP 172.16.16.128:1606 > 74.125.95.104:http S 0001 Ether / IP / TCP 74.125.95.104:http > 172.16.16.128:1606 SA 0002 Ether / IP / TCP 172.16.16.128:1606 > 74.125.95.104:http A 0003 Ether / IP / TCP 172.16.16.128:1606 > 74.125.95.104:http PA / Raw 0004 Ether / IP / TCP 74.125.95.104:http > 172.16.16.128:1606 A / Padding >>> sniff(offline='test.pcap', lfilter = lambda s: s[TCP].flags == 18, prn = lambda x: x[IP].dst) 192.168.1.1