原文連結:http://zodiacg.net/2016/07/in...
本系列文章譯自thePacketGeek的系列文章。原創翻譯,轉載請註明出處。
目前在這一系列文章中我們已經瞭解瞭如何捕獲資料包和使用 capture 物件,我們終於到了有趣的部分,開始對資料包進行操作了!
當我們捕獲了資料包後,它們以 packet 物件列表的形式儲存在 capture 物件中。這些 packet 物件的方法和屬性使我們能夠訪問資料包頭以及包的負載資訊。在之前的文章中提到過,我們可以使用 only_summaries 引數來控制每個資料包儲存的資訊量。
資料包摘要屬性
在捕獲時將 only_summaries 設定為 True 會使得不管捕獲的資料包的內容是何種協議, packet 物件都具有固定的屬性集。其中最有用的屬性值有:
>>> cap = pyshark.FileCapture('test.pcap', only_summaries=True)
>>>
>>> dir(cap[0])
['delta', 'destination', 'info', 'ip id', 'length', 'no', 'protocol', 'source', 'stream', 'summary_line', 'time', 'window']
delta : 當前資料包和上一個資料包捕獲時間的差值。
destination : IP層的目標地址。
info :應用層資料的簡短摘要(比如"HTTP GET /resource_folder/page.html")。
ip id : IP識別符號欄位。
length : 以位元組表示的資料包長度。
no : 資料包在列表中的索引值。
protocol : 資料包中識別出的最高層級的協議。(譯註:HTTP資料包如果是JSON的資料,此處可能是JSON而非HTTP)
source : IP層的源地址。
stream : 索引值,標識出該資料包屬於哪一個TCP流(僅用於TCP資料包)。
summary_line : 將所有的摘要屬性輸出在一個tab分隔的字串中。
time : 當前資料包到達時間與第一個資料包的差值。
window : TCP的視窗大小(僅用於TCP資料包)。
利用這些內容可以做很多事情,列印出資料包摘要只是一個開始!利用這些資料可以做出很棒的視覺化圖表來展示IP會話、頻寬使用、協議以及應用的效能指標(比如TCP資料流中的RTT值)。這都是很有用的分析,還有別的嗎?
完整的資料包屬性
如果你不僅想從捕獲的資料包中獲取摘要資訊,那麼好好看看這部分吧。使用Wireshark和tshark內建的解析器,PyShark可以將資料包的所有細節按層次分解。
比如我們先來深入研究一下DNS資料包,看一下資料包所具有的屬性。
>>> cap = pyshark.LiveCapture(interface='en0', bpf_filter='udp port 53')
>>> cap.sniff(packet_count=50)
>>> dns_1 = cap[0]
>>> dns_2 = cap[1]
>>> dns_1. #(tab auto-complete)
dns_1.captured_length dns_1.highest_layer dns_1.length dns_1.transport_layer
dns_1.dns dns_1.interface_captured dns_1.pretty_print dns_1.udp
dns_1.eth dns_1.ip dns_1.sniff_time
dns_1.frame_info dns_1.layers dns_1.sniff_timestamp
這其中有一些普通的資料包資訊屬性,比如length
,frame_info
,以及time
,還有pretty_print()
方法用於以可讀性較強的方式顯示資料包(類似於Wireshark的詳細資訊檢視)。
如果你仔細看的話能夠發現直接制定層次名的屬性(eth
和ip
),還有會根據資料包內的協議而變動的屬性(transport_layer
和highest_layer
)。
如果你要尋找特定型別的資料流量,這些屬性可以使得尋找感興趣的資訊變得很簡單。
比如下面的指令碼會列印出所有的DNS查詢和響應:
import pyshark
cap = pyshark.LiveCapture(interface='en0', bpf_filter='udp port 53')
cap.sniff(packet_count=10)
def print_dns_info(pkt):
if pkt.dns.qry_name:
print 'DNS Request from %s: %s' % (pkt.ip.src, pkt.dns.qry_name)
elif pkt.dns.resp_name:
print 'DNS Response from %s: %s' % (pkt.ip.src, pkt.dns.resp_name)
cap.apply_on_packets(print_dns_info, timeout=100)
會給出如下的結果:
DNS Request from 10.10.10.40: apple.com
DNS Request from 10.10.10.1: apple.com
DNS Request from 10.10.10.40: ipv6.icanhazip.com
DNS Request from 10.10.10.1: ipv6.icanhazip.com
DNS Request from 10.10.10.40: ipv4.icanhazip.com
DNS Request from 10.10.10.1: ipv4.icanhazip.com
動態的層引用
使用上面提到的動態變化的層屬性(比如transport_layer
和highest_layer
)讓我們在分析資料包時更靈活。
如果你對每個資料包都試圖訪問pkt.dns.qry_resp
屬性,那麼如果這個資料包不是DNS資料包就會返回AttributeError
異常。傳輸層也有類似的問題,因為有TCP和UDP兩種可能。我們可以使用動態引用的層屬性來獲取源地址和目的地址,然後使用try/except來處理既不是TCP也不是UDP資料包的情況。
import pyshark
cap = pyshark.FileCapture('test.pcap')
def print_conversation_header(pkt):
try:
protocol = pkt.transport_layer
src_addr = pkt.ip.src
src_port = pkt[pkt.transport_layer].srcport
dst_addr = pkt.ip.dst
dst_port = pkt[pkt.transport_layer].dstport
print '%s %s:%s --> %s:%s' % (protocol, src_addr, src_port, dst_addr, dst_port)
except AttributeError as e:
#ignore packets that aren't TCP/UDP or IPv4
pass
cap.apply_on_packets(print_conversation_header, timeout=100)
該指令碼會輸出:
UDP 10.10.10.12:51554 --> 239.255.255.250:1900
UDP 10.10.10.12:51554 --> 239.255.255.250:1900
UDP 10.10.10.15:58803 --> 8.8.8.8:53
UDP 8.8.8.8:53 --> 10.10.10.15:58803
TCP 10.10.10.15:58632 --> 192.168.20.197:80
TCP 192.168.20.197:80 --> 10.10.10.15:58632
TCP 10.10.10.15:58632 --> 192.168.20.197:80
無限的可能
從這幾個簡單的例子當中我們可以看出,PyShark使我們能夠輕鬆的訪問所有的資料包細節。
在分類和處理多種不同協議的時候,可以使用條件語句來創造動態的邏輯,你也可以尋找具有特定屬性的資料包來篩選特定型別的資料流量(當然要注意處理AttributeError)。
希望你喜歡這一系列文章。如果你對PyShark的應用例項感興趣,你可以看看我的Cloud-Pcap專案