scapy 其中迭代器的實現細節

2021-08-25 15:49:13 字數 4088 閱讀 8773

前面知道了在scapy中,不管是ip包,還是以太包,還有tcp包,或者它們集合在一起的包都是乙個packet,這是在資料報組織意義上講的,另外從資料報最終組合的意義上講,scapy最引人入勝的地方還在於packet本身也是乙個生成器,即generator,這是python中乙個很重要的概念。packet是乙個生成器並不是為了方便或者美觀,而是scapy本身要求的。

由於packet表示的是乙個單獨層的資料報或者多層組合後的資料報,比如tcp資料段作為ip資料報的載荷(payload),因此如果設計的好一些,我們希望能按照協議棧本身理解資料報的方式去組合資料報,也就是說,完全按照分層協議的自解釋機制,來一層一層從下往上將資料報構建好,比如在鏈路層的時候,看到上層是ip,那麼就生成乙個ip物件,然後在ip層,看到上層是tcp,那麼生成乙個tcp物件,依次類推,這樣就會很方便,如果按照這個思路去設計,我覺得任何人都能想到遞迴和列舉,剩下的就是針對不同語言的編碼問題了,scapy由python實現,將packet本身作為乙個generoter也是乙個很自然的想法,這是因為對於乙個自鏈路層開始向上構建的資料報,它只知道ip是它的payload,而不知道ip上面是什麼,對於ip來講也是一樣,可是不管怎樣,任何的payload都是乙個packet,因此如果我們能從鏈路層開始對其payload做for...in...操作的話,就能遍歷到整個的協議層,將以太層的payload設定為payloads(複數,因為這個payload還包含有更上層的payloads...),然後第一次我們取出eth本身,第二次取出ip1,第三次取出tcp1...,針對於每一層的packet,它本身又是乙個生成器,生成器生成乙個迭代器,該迭代器返回的next即是它本身,這樣再通過遞迴就可以將所有層次的資料報組合在一起了。

所有這一切的開始都在str函式,在str中:

def __str__(self):

return self.__iter__().next().build()

self.__iter__().next()得到了ether物件,呼叫它的build即可先構建以太頭,再構建payload:

def do_build(self):

p=""

for f in self.fields_desc:

p = f.addfield(self, p, self.__getattr__(f))

pkt = p+str(self.payload)

return pkt

在do_build中,str(self.payload)又會開啟一次針對payload的__str__()(間接的對payload的__iter__的呼叫)的呼叫,從而開始了構建ip物件的過程,依次類推。這樣就需要將__iter__實現的非常好:

00def __iter__(self):

01 def loop(todo, done, self=self):

02 if todo:

03 eltname = todo.pop()

04 elt = self.__getattr__(eltname)

05 if not isinstance(elt, gen):

06 if self.fieldtype[eltname].islist:

07 elt = setgen([elt])

08 else:

09 elt = setgen(elt)

10 for e in elt:

11 done[eltname] = e

12 for x in loop(todo[:], done):

13 yield x

14 else:

15 if isinstance(self.payload,nopayload):

16 payloads = [none]

17 else:

18 payloads = self.payload

19 for payl in payloads:

20 done2 = done.copy()

21 for k in done2:

22 if isinstance(done2[k], randnum):

23 done2[k] = int(done2[k])

24 pkt = self.__class__(**done2)

25 pkt.underlayer = self.underlayer

26 pkt.overload_fields = self.overload_fields.copy()

27 if payl is none:

28 yield pkt

29 else:

30 yield pkt/payl

31 return loop(map(lambda x:str(x), self.fields.keys()), {})

其實它只有一行,那就是第31行,它呼叫了乙個子函式,首先map函式將所有我們手工設定的該層packet的fields轉化成乙個list,然後呼叫loop,整個loop分為兩大塊,以17行的else做分割。在前半部分,將fields鍊錶中的字段乙個個pop出,比如對於ip物件而言就是src,dst,ttl之類,直到沒有了的時候再次在12行遞迴呼叫loop的時候會進入後半部分else分支,此時處理它的payloads(所有的上層資料),這個核心的實現盡在第19行,乙個for...in的呼叫,這裡就會對payloads呼叫其__iter__方法,過程又回到了第31行,只是這次的self變了,也就是物件變了,變成了上一層資料的packet物件了,然後一切如故...。

在l3packetsocket的send中從str的呼叫開始構建整個以太幀,整個呼叫過程如下:

00 call ethernet obj-__str__ 1

01 call ethernet obj-__iter__ 2

02 call ethernet obj-__iter__-loop 3

03 沒有為ether設定屬性,因此直接進入else

04 call ip obj-__iter__ 4

05 call ip obj-__iter__-loop 5

06 取出dst

07 call ip obj-__iter__-loop 6

08 取出src

09 call ip obj-__iter__-loop 7

10 沒有其它屬性了,進入else

11 call tcp obj-__iter__ 8

12 call tcp obj-__iter__-loop 9

13 取出sport

14 call tcp obj-__iter__-loop 10

15 取出dport

16 call tcp obj-__iter__-loop 11

17 沒有其它屬性了,進入else

18 call none obj-__iter__ 12

19 11返回tcp obj

20 10返回tcp obj

21 9返回tcp obj

22 8返回ip obj

23 7返回ip obj

24 6返回ip obj

25 4返回ether obj

26 2返回ether obj

27 call ethernet obj-__iter__-next 13

28 13返回ether obj

29 call ether obj-build 14

30 call ether obj.payload-str 15

31 call ip obj-__str__

32 從第4行到第24行

33 ...

從32行可以看出,在從下到上的構建過程中,在構建上一層的時候也即是構建此層的payload的時候,需要對payload呼叫str函式,由於在第一次構建ether obj的時候已經做過這件事了,也就是說,在構建鏈路層資料報的時候呼叫str(ether obj),由於__iter__的實現有遞迴的性質,以上各層的packet物件其實已經構造好了,實在沒有必要再來一次,就是說,如果現在有4件事,分別分1,2,3,4,從1開始做,我們希望的是1-2-3-4這個順序,或者1,2,3,4也可以,然而scapy的方式卻是:1,2,3,4-2,3,4-3,4-4這實在是乙個我認為可以改進的地方。不過既然scapy實現了如此複雜又美妙的__iter__以及資料報兼作generator的角色,我覺得已經可以抵消美中之不足矣!

scapy 其中迭代器的實現細節

前面知道了在scapy中,不管是ip包,還是以太包,還有tcp包,或者它們集合在一起的包都是乙個packet,這是在資料報組織意義上講的,另外從資料報最終組合的意義上講,scapy最引人入勝的地方還在於packet本身也是乙個生成器,即generator,這是python中乙個很重要的概念。pack...

迭代器的實現

inte ce ipeople int age string void eat void work void speak class people ipeople public people string n,int a,string s public string name set public ...

C 迭代器的實現

using system using system.collections.generic using system.linq using system.text using system.threading.tasks namespace iterator using system using s...