高级用法

ASN.1和SNMP¶

什么是ASN.1?

注意

这只是我对ASN.1的观点,请尽可能简单地解释一下。对于更多的理论或学术观点,我相信您会在Internet上找到更好的选择。

ASN.1是一种符号,其目的是指定数据交换的格式。它与数据编码方式无关。数据编码在编码规则中指定。

最常用的编码规则是BER(基本编码规则)和DER(特殊编码规则)。两者看起来相同,但指定后者以保证编码的唯一性。当谈论密码,哈希和签名时,此属性非常有趣。

ASN.1提供了基本对象:整数,多种字符串,浮点数,布尔值,容器等。它们被分组在所谓的通用类中。给定的协议可以提供其他对象,这些对象将在Context类中分组。例如,SNMP定义PDU_GET或PDU_SET对象。还有Application和Private类。

这些对象中的每一个都有一个标记,编码规则将使用该标记。来自1的标签用于通用类。1是布尔值,2是整数,3是位串,6是OID,48是序列。Context该类中的标签始于0xa0。遇到标有0xa0的对象时,我们需要知道上下文才能对其进行解码。例如,在SNMP上下文中,0xa0是PDU_GET对象,而在X509上下文中,它是证书版本的容器。

通过组装所有这些基本砖块对象来创建其他对象。使用先前定义或现有对象的序列和数组(集合)完成合成。最终对象(X509证书,SNMP数据包)是一棵树,其非叶子节点是序列和集合对象(或派生的上下文对象),并且叶子节点是整数,字符串,OID等。

Scapy的和ASN.1

Scapy提供了一种轻松编码或解码ASN.1并对这些编码器/解码器进行编程的方法。它比ASN.1解析器应有的宽松得多,并且它会忽略约束。它不会取代ASN.1解析器或ASN.1编译器。实际上,它已经被编写为能够对损坏的ASN.1进行编码和解码。它可以处理损坏的编码字符串,也可以创建它们。

ASN.1引擎

注意:此处提供的许多类定义都使用元类。如果您不完全看待源代码,而仅依靠我的捕获,您可能会认为它们有时表现出某种魔术行为。``Scapy ASN.1引擎提供了用于链接对象及其标签的类。他们从继承ASN1_Class第一个是ASN1_Class_UNIVERSAL,它为大多数通用对象提供标签。每个新的上下文(SNMPX509)将从其继承并添加自己的对象。

class ASN1_Class_UNIVERSAL(ASN1_Class):
    name = "UNIVERSAL"
# [...]
    BOOLEAN = 1
    INTEGER = 2
    BIT_STRING = 3
# [...]

class ASN1_Class_SNMP(ASN1_Class_UNIVERSAL):
    name="SNMP"
    PDU_GET = 0xa0
    PDU_NEXT = 0xa1
    PDU_RESPONSE = 0xa2

class ASN1_Class_X509(ASN1_Class_UNIVERSAL):
    name="X509"
    CONT0 = 0xa0
    CONT1 = 0xa1
# [...]

所有ASN.1对象都由简单的Python实例表示,这些实例充当原始值的外壳。简单逻辑ASN1_Object由其继承者处理因此,它们非常简单:

class ASN1_INTEGER(ASN1_Object):
    tag = ASN1_Class_UNIVERSAL.INTEGER

class ASN1_STRING(ASN1_Object):
    tag = ASN1_Class_UNIVERSAL.STRING

class ASN1_BIT_STRING(ASN1_STRING):
    tag = ASN1_Class_UNIVERSAL.BIT_STRING

可以组合这些实例以创建ASN.1树:

>>> x=ASN1_SEQUENCE([ASN1_INTEGER(7),ASN1_STRING("egg"),ASN1_SEQUENCE([ASN1_BOOLEAN(False)])])
>>> x
<ASN1_SEQUENCE[[<ASN1_INTEGER[7]>, <ASN1_STRING['egg']>, <ASN1_SEQUENCE[[<ASN1_BOOLEAN[False]>]]>]]>
>>> x.show()
# ASN1_SEQUENCE:
  <ASN1_INTEGER[7]>
  <ASN1_STRING['egg']>
  # ASN1_SEQUENCE:
    <ASN1_BOOLEAN[False]>

编码引擎

与标准一样,ASN.1和编码是独立的。我们刚刚看到了如何创建复合的ASN.1对象。要对其进行编码或解码,我们需要选择一个编码规则。Scapy目前仅提供BER(实际上,它可能是DER。DER看起来像BER,除了仅授权了最小限度的编码,这很可能就是我所做的)。我将此称为ASN.1编解码器。

使用编解码器提供的类方法完成编码和解码。例如,BERcodec_INTEGER该类提供了.enc().dec()类方法,可以在编码的字符串和其类型的值之间进行转换。它们都继承自BERcodec_Object,它能够解码任何类型的对象:

>>> BERcodec_INTEGER.enc(7)
'\x02\x01\x07'
>>> BERcodec_BIT_STRING.enc("egg")
'\x03\x03egg'
>>> BERcodec_STRING.enc("egg")
'\x04\x03egg'
>>> BERcodec_STRING.dec('\x04\x03egg')
(<ASN1_STRING['egg']>, '')
>>> BERcodec_STRING.dec('\x03\x03egg')
Traceback (most recent call last):
  File "<console>", line 1, in ?
  File "/usr/bin/scapy", line 2099, in dec
    return cls.do_dec(s, context, safe)
  File "/usr/bin/scapy", line 2178, in do_dec
    l,s,t = cls.check_type_check_len(s)
  File "/usr/bin/scapy", line 2076, in check_type_check_len
    l,s3 = cls.check_type_get_len(s)
  File "/usr/bin/scapy", line 2069, in check_type_get_len
    s2 = cls.check_type(s)
  File "/usr/bin/scapy", line 2065, in check_type
    (cls.__name__, ord(s[0]), ord(s[0]),cls.tag), remaining=s)
BER_BadTag_Decoding_Error: BERcodec_STRING: Got tag [3/0x3] while expecting <ASN1Tag STRING[4]>
### Already decoded ###
None
### Remaining ###
'\x03\x03egg'
>>> BERcodec_Object.dec('\x03\x03egg')
(<ASN1_BIT_STRING['egg']>, '')

ASN.1对象使用其.enc()方法进行编码必须使用我们要使用的编解码器调用此方法。在ASN1_Codecs对象中引用了所有编解码器。raw()也可以使用。在这种情况下,将使用默认编解码器(conf.ASN1_default_codec)。

>>> x.enc(ASN1_Codecs.BER)
'0\r\x02\x01\x07\x04\x03egg0\x03\x01\x01\x00'
>>> raw(x)
'0\r\x02\x01\x07\x04\x03egg0\x03\x01\x01\x00'
>>> xx,remain = BERcodec_Object.dec(_)
>>> xx.show()
# ASN1_SEQUENCE:
  <ASN1_INTEGER[7L]>
  <ASN1_STRING['egg']>
  # ASN1_SEQUENCE:
    <ASN1_BOOLEAN[0L]>

>>> remain
''

默认情况下,使用Universal该类完成解码,这意味着Context将不会解码该类中定义的对象这样做有充分的理由:解码取决于上下文!

>>> cert="""
... MIIF5jCCA86gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMC
... VVMxHTAbBgNVBAoTFEFPTCBUaW1lIFdhcm5lciBJbmMuMRwwGgYDVQQLExNB
... bWVyaWNhIE9ubGluZSBJbmMuMTcwNQYDVQQDEy5BT0wgVGltZSBXYXJuZXIg
... Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyOTA2MDAw
... MFoXDTM3MDkyODIzNDMwMFowgYMxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRB
... T0wgVGltZSBXYXJuZXIgSW5jLjEcMBoGA1UECxMTQW1lcmljYSBPbmxpbmUg
... SW5jLjE3MDUGA1UEAxMuQU9MIFRpbWUgV2FybmVyIFJvb3QgQ2VydGlmaWNh
... dGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
... ggIBALQ3WggWmRToVbEbJGv8x4vmh6mJ7ouZzU9AhqS2TcnZsdw8TQ2FTBVs
... RotSeJ/4I/1n9SQ6aF3Q92RhQVSji6UI0ilbm2BPJoPRYxJWSXakFsKlnUWs
... i4SVqBax7J/qJBrvuVdcmiQhLE0OcR+mrF1FdAOYxFSMFkpBd4aVdQxHAWZg
... /BXxD+r1FHjHDtdugRxev17nOirYlxcwfACtCJ0zr7iZYYCLqJV+FNwSbKTQ
... 2O9ASQI2+W6p1h2WVgSysy0WVoaP2SBXgM1nEG2wTPDaRrbqJS5Gr42whTg0
... ixQmgiusrpkLjhTXUr2eacOGAgvqdnUxCc4zGSGFQ+aJLZ8lN2fxI2rSAG2X
... +Z/nKcrdH9cG6rjJuQkhn8g/BsXS6RJGAE57COtCPStIbp1n3UsC5ETzkxml
... J85per5n0/xQpCyrw2u544BMzwVhSyvcG7mm0tCq9Stz+86QNZ8MUhy/XCFh
... EVsVS6kkUfykXPcXnbDS+gfpj1bkGoxoigTTfFrjnqKhynFbotSg5ymFXQNo
... Kk/SBtc9+cMDLz9l+WceR0DTYw/j1Y75hauXTLPXJuuWCpTehTacyH+BCQJJ
... Kg71ZDIMgtG6aoIbs0t0EfOMd9afv9w3pKdVBC/UMejTRrkDfNoSTllkt1Ex
... MVCgyhwn2RAurda9EGYrw7AiShJbAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMB
... Af8wHQYDVR0OBBYEFE9pbQN+nZ8HGEO8txBO1b+pxCAoMB8GA1UdIwQYMBaA
... FE9pbQN+nZ8HGEO8txBO1b+pxCAoMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG
... 9w0BAQUFAAOCAgEAO/Ouyuguh4X7ZVnnrREUpVe8WJ8kEle7+z802u6teio0
... cnAxa8cZmIDJgt43d15Ui47y6mdPyXSEkVYJ1eV6moG2gcKtNuTxVBFT8zRF
... ASbI5Rq8NEQh3q0l/HYWdyGQgJhXnU7q7C+qPBR7V8F+GBRn7iTGvboVsNIY
... vbdVgaxTwOjdaRITQrcCtQVBynlQboIOcXKTRuidDV29rs4prWPVVRaAMCf/
... drr3uNZK49m1+VLQTkCpx+XCMseqdiThawVQ68W/ClTluUI8JPu3B5wwn3la
... 5uBAUhX0/Kr0VvlEl4ftDmVyXr4m+02kLQgH3thcoNyBM5kYJRF3p+v9WAks
... mWsbivNSPxpNSGDxoPYzAlOL7SUJuA0t7Zdz7NeWH45gDtoQmy8YJPamTQr5
... O8t1wswvziRpyQoijlmn94IM19drNZxDAGrElWe6nEXLuA4399xOAU++CrYD
... 062KRffaJ00psUjf5BHklka9bAI+1lHIlRcBFanyqqryvy9lG2/QuRqT9Y41
... xICHPpQvZuTpqP9BnHAqTyo5GJUefvthATxRCC4oGKQWDzH9OmwjkyB24f0H
... hdFbP9IcczLd+rn4jM8Ch3qaluTtT4mNU0OrDhPAARW0eTjb/G49nlG2uBOL
... Z8/5fNkiHfZdxRwBL5joeiQYvITX+txyW/fBOmg=
... """.decode("base64")
>>> (dcert,remain) = BERcodec_Object.dec(cert)
Traceback (most recent call last):
  File "<console>", line 1, in ?
  File "/usr/bin/scapy", line 2099, in dec
    return cls.do_dec(s, context, safe)
  File "/usr/bin/scapy", line 2094, in do_dec
    return codec.dec(s,context,safe)
  File "/usr/bin/scapy", line 2099, in dec
    return cls.do_dec(s, context, safe)
  File "/usr/bin/scapy", line 2218, in do_dec
    o,s = BERcodec_Object.dec(s, context, safe)
  File "/usr/bin/scapy", line 2099, in dec
    return cls.do_dec(s, context, safe)
  File "/usr/bin/scapy", line 2094, in do_dec
    return codec.dec(s,context,safe)
  File "/usr/bin/scapy", line 2099, in dec
    return cls.do_dec(s, context, safe)
  File "/usr/bin/scapy", line 2218, in do_dec
    o,s = BERcodec_Object.dec(s, context, safe)
  File "/usr/bin/scapy", line 2099, in dec
    return cls.do_dec(s, context, safe)
  File "/usr/bin/scapy", line 2092, in do_dec
    raise BER_Decoding_Error("Unknown prefix [%02x] for [%r]" % (p,t), remaining=s)
BER_Decoding_Error: Unknown prefix [a0] for ['\xa0\x03\x02\x01\x02\x02\x01\x010\r\x06\t*\x86H...']
### Already decoded ###
[[]]
### Remaining ###
'\xa0\x03\x02\x01\x02\x02\x01\x010\r\x06\t*\x86H\x86\xf7\r\x01\x01\x05\x05\x000\x81\x831\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x1d0\x1b\x06\x03U\x04\n\x13\x14AOL Time Warner Inc.1\x1c0\x1a\x06\x03U\x04\x0b\x13\x13America Online Inc.1705\x06\x03U\x04\x03\x13.AOL Time Warner Root Certification Authority 20\x1e\x17\r020529060000Z\x17\r370928234300Z0\x81\x831\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x1d0\x1b\x06\x03U\x04\n\x13\x14AOL Time Warner Inc.1\x1c0\x1a\x06\x03U\x04\x0b\x13\x13America Online Inc.1705\x06\x03U\x04\x03\x13.AOL Time Warner Root Certification Authority 20\x82\x02"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x02\x0f\x000\x82\x02\n\x02\x82\x02\x01\x00\xb47Z\x08\x16\x99\x14\xe8U\xb1\x1b$k\xfc\xc7\x8b\xe6\x87\xa9\x89\xee\x8b\x99\xcdO@\x86\xa4\xb6M\xc9\xd9\xb1\xdc<M\r\x85L\x15lF\x8bRx\x9f\xf8#\xfdg\xf5$:h]\xd0\xf7daAT\xa3\x8b\xa5\x08\xd2)[\x9b`O&\x83\xd1c\x12VIv\xa4\x16\xc2\xa5\x9dE\xac\x8b\x84\x95\xa8\x16\xb1\xec\x9f\xea$\x1a\xef\xb9W\\\x9a$!,M\x0eq\x1f\xa6\xac]Et\x03\x98\xc4T\x8c\x16JAw\x86\x95u\x0cG\x01f`\xfc\x15\xf1\x0f\xea\xf5\x14x\xc7\x0e\xd7n\x81\x1c^\xbf^\xe7:*\xd8\x97\x170|\x00\xad\x08\x9d3\xaf\xb8\x99a\x80\x8b\xa8\x95~\x14\xdc\x12l\xa4\xd0\xd8\xef@I\x026\xf9n\xa9\xd6\x1d\x96V\x04\xb2\xb3-\x16V\x86\x8f\xd9 W\x80\xcdg\x10m\xb0L\xf0\xdaF\xb6\xea%.F\xaf\x8d\xb0\x8584\x8b\x14&\x82+\xac\xae\x99\x0b\x8e\x14\xd7R\xbd\x9ei\xc3\x86\x02\x0b\xeavu1\t\xce3\x19!\x85C\xe6\x89-\x9f%7g\xf1#j\xd2\x00m\x97\xf9\x9f\xe7)\xca\xdd\x1f\xd7\x06\xea\xb8\xc9\xb9\t!\x9f\xc8?\x06\xc5\xd2\xe9\x12F\x00N{\x08\xebB=+Hn\x9dg\xddK\x02\xe4D\xf3\x93\x19\xa5\'\xceiz\xbeg\xd3\xfcP\xa4,\xab\xc3k\xb9\xe3\x80L\xcf\x05aK+\xdc\x1b\xb9\xa6\xd2\xd0\xaa\xf5+s\xfb\xce\x905\x9f\x0cR\x1c\xbf\\!a\x11[\x15K\xa9$Q\xfc\xa4\\\xf7\x17\x9d\xb0\xd2\xfa\x07\xe9\x8fV\xe4\x1a\x8ch\x8a\x04\xd3|Z\xe3\x9e\xa2\xa1\xcaq[\xa2\xd4\xa0\xe7)\x85]\x03h*O\xd2\x06\xd7=\xf9\xc3\x03/?e\xf9g\x1eG@\xd3c\x0f\xe3\xd5\x8e\xf9\x85\xab\x97L\xb3\xd7&\xeb\x96\n\x94\xde\x856\x9c\xc8\x7f\x81\t\x02I*\x0e\xf5d2\x0c\x82\xd1\xbaj\x82\x1b\xb3Kt\x11\xf3\x8cw\xd6\x9f\xbf\xdc7\xa4\xa7U\x04/\xd41\xe8\xd3F\xb9\x03|\xda\x12NYd\xb7Q11P\xa0\xca\x1c\'\xd9\x10.\xad\xd6\xbd\x10f+\xc3\xb0"J\x12[\x02\x03\x01\x00\x01\xa3c0a0\x0f\x06\x03U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff0\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14Oim\x03~\x9d\x9f\x07\x18C\xbc\xb7\x10N\xd5\xbf\xa9\xc4 (0\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14Oim\x03~\x9d\x9f\x07\x18C\xbc\xb7\x10N\xd5\xbf\xa9\xc4 (0\x0e\x06\x03U\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x01\x860\r\x06\t*\x86H\x86\xf7\r\x01\x01\x05\x05\x00\x03\x82\x02\x01\x00;\xf3\xae\xca\xe8.\x87\x85\xfbeY\xe7\xad\x11\x14\xa5W\xbcX\x9f$\x12W\xbb\xfb?4\xda\xee\xadz*4rp1k\xc7\x19\x98\x80\xc9\x82\xde7w^T\x8b\x8e\xf2\xeagO\xc9t\x84\x91V\t\xd5\xe5z\x9a\x81\xb6\x81\xc2\xad6\xe4\xf1T\x11S\xf34E\x01&\xc8\xe5\x1a\xbc4D!\xde\xad%\xfcv\x16w!\x90\x80\x98W\x9dN\xea\xec/\xaa<\x14{W\xc1~\x18\x14g\xee$\xc6\xbd\xba\x15\xb0\xd2\x18\xbd\xb7U\x81\xacS\xc0\xe8\xddi\x12\x13B\xb7\x02\xb5\x05A\xcayPn\x82\x0eqr\x93F\xe8\x9d\r]\xbd\xae\xce)\xadc\xd5U\x16\x800\'\xffv\xba\xf7\xb8\xd6J\xe3\xd9\xb5\xf9R\xd0N@\xa9\xc7\xe5\xc22\xc7\xaav$\xe1k\x05P\xeb\xc5\xbf\nT\xe5\xb9B<$\xfb\xb7\x07\x9c0\x9fyZ\xe6\xe0@R\x15\xf4\xfc\xaa\xf4V\xf9D\x97\x87\xed\x0eer^\xbe&\xfbM\xa4-\x08\x07\xde\xd8\\\xa0\xdc\x813\x99\x18%\x11w\xa7\xeb\xfdX\t,\x99k\x1b\x8a\xf3R?\x1aMH`\xf1\xa0\xf63\x02S\x8b\xed%\t\xb8\r-\xed\x97s\xec\xd7\x96\x1f\x8e`\x0e\xda\x10\x9b/\x18$\xf6\xa6M\n\xf9;\xcbu\xc2\xcc/\xce$i\xc9\n"\x8eY\xa7\xf7\x82\x0c\xd7\xd7k5\x9cC\x00j\xc4\x95g\xba\x9cE\xcb\xb8\x0e7\xf7\xdcN\x01O\xbe\n\xb6\x03\xd3\xad\x8aE\xf7\xda\'M)\xb1H\xdf\xe4\x11\xe4\x96F\xbdl\x02>\xd6Q\xc8\x95\x17\x01\x15\xa9\xf2\xaa\xaa\xf2\xbf/e\x1bo\xd0\xb9\x1a\x93\xf5\x8e5\xc4\x80\x87>\x94/f\xe4\xe9\xa8\xffA\x9cp*O*9\x18\x95\x1e~\xfba\x01<Q\x08.(\x18\xa4\x16\x0f1\xfd:l#\x93 v\xe1\xfd\x07\x85\xd1[?\xd2\x1cs2\xdd\xfa\xb9\xf8\x8c\xcf\x02\x87z\x9a\x96\xe4\xedO\x89\x8dSC\xab\x0e\x13\xc0\x01\x15\xb4y8\xdb\xfcn=\x9eQ\xb6\xb8\x13\x8bg\xcf\xf9|\xd9"\x1d\xf6]\xc5\x1c\x01/\x98\xe8z$\x18\xbc\x84\xd7\xfa\xdcr[\xf7\xc1:h'

Context班必须指定:

>>> (dcert,remain) = BERcodec_Object.dec(cert, context=ASN1_Class_X509)
>>> dcert.show()
# ASN1_SEQUENCE:
  # ASN1_SEQUENCE:
    # ASN1_X509_CONT0:
      <ASN1_INTEGER[2L]>
    <ASN1_INTEGER[1L]>
    # ASN1_SEQUENCE:
      <ASN1_OID['.1.2.840.113549.1.1.5']>
      <ASN1_NULL[0L]>
    # ASN1_SEQUENCE:
      # ASN1_SET:
        # ASN1_SEQUENCE:
          <ASN1_OID['.2.5.4.6']>
          <ASN1_PRINTABLE_STRING['US']>
      # ASN1_SET:
        # ASN1_SEQUENCE:
          <ASN1_OID['.2.5.4.10']>
          <ASN1_PRINTABLE_STRING['AOL Time Warner Inc.']>
      # ASN1_SET:
        # ASN1_SEQUENCE:
          <ASN1_OID['.2.5.4.11']>
          <ASN1_PRINTABLE_STRING['America Online Inc.']>
      # ASN1_SET:
        # ASN1_SEQUENCE:
          <ASN1_OID['.2.5.4.3']>
          <ASN1_PRINTABLE_STRING['AOL Time Warner Root Certification Authority 2']>
    # ASN1_SEQUENCE:
      <ASN1_UTC_TIME['020529060000Z']>
      <ASN1_UTC_TIME['370928234300Z']>
    # ASN1_SEQUENCE:
      # ASN1_SET:
        # ASN1_SEQUENCE:
          <ASN1_OID['.2.5.4.6']>
          <ASN1_PRINTABLE_STRING['US']>
      # ASN1_SET:
        # ASN1_SEQUENCE:
          <ASN1_OID['.2.5.4.10']>
          <ASN1_PRINTABLE_STRING['AOL Time Warner Inc.']>
      # ASN1_SET:
        # ASN1_SEQUENCE:
          <ASN1_OID['.2.5.4.11']>
          <ASN1_PRINTABLE_STRING['America Online Inc.']>
      # ASN1_SET:
        # ASN1_SEQUENCE:
          <ASN1_OID['.2.5.4.3']>
          <ASN1_PRINTABLE_STRING['AOL Time Warner Root Certification Authority 2']>
    # ASN1_SEQUENCE:
      # ASN1_SEQUENCE:
        <ASN1_OID['.1.2.840.113549.1.1.1']>
        <ASN1_NULL[0L]>
      <ASN1_BIT_STRING['\x000\x82\x02\n\x02\x82\x02\x01\x00\xb47Z\x08\x16\x99\x14\xe8U\xb1\x1b$k\xfc\xc7\x8b\xe6\x87\xa9\x89\xee\x8b\x99\xcdO@\x86\xa4\xb6M\xc9\xd9\xb1\xdc<M\r\x85L\x15lF\x8bRx\x9f\xf8#\xfdg\xf5$:h]\xd0\xf7daAT\xa3\x8b\xa5\x08\xd2)[\x9b`O&\x83\xd1c\x12VIv\xa4\x16\xc2\xa5\x9dE\xac\x8b\x84\x95\xa8\x16\xb1\xec\x9f\xea$\x1a\xef\xb9W\\\x9a$!,M\x0eq\x1f\xa6\xac]Et\x03\x98\xc4T\x8c\x16JAw\x86\x95u\x0cG\x01f`\xfc\x15\xf1\x0f\xea\xf5\x14x\xc7\x0e\xd7n\x81\x1c^\xbf^\xe7:*\xd8\x97\x170|\x00\xad\x08\x9d3\xaf\xb8\x99a\x80\x8b\xa8\x95~\x14\xdc\x12l\xa4\xd0\xd8\xef@I\x026\xf9n\xa9\xd6\x1d\x96V\x04\xb2\xb3-\x16V\x86\x8f\xd9 W\x80\xcdg\x10m\xb0L\xf0\xdaF\xb6\xea%.F\xaf\x8d\xb0\x8584\x8b\x14&\x82+\xac\xae\x99\x0b\x8e\x14\xd7R\xbd\x9ei\xc3\x86\x02\x0b\xeavu1\t\xce3\x19!\x85C\xe6\x89-\x9f%7g\xf1#j\xd2\x00m\x97\xf9\x9f\xe7)\xca\xdd\x1f\xd7\x06\xea\xb8\xc9\xb9\t!\x9f\xc8?\x06\xc5\xd2\xe9\x12F\x00N{\x08\xebB=+Hn\x9dg\xddK\x02\xe4D\xf3\x93\x19\xa5\'\xceiz\xbeg\xd3\xfcP\xa4,\xab\xc3k\xb9\xe3\x80L\xcf\x05aK+\xdc\x1b\xb9\xa6\xd2\xd0\xaa\xf5+s\xfb\xce\x905\x9f\x0cR\x1c\xbf\\!a\x11[\x15K\xa9$Q\xfc\xa4\\\xf7\x17\x9d\xb0\xd2\xfa\x07\xe9\x8fV\xe4\x1a\x8ch\x8a\x04\xd3|Z\xe3\x9e\xa2\xa1\xcaq[\xa2\xd4\xa0\xe7)\x85]\x03h*O\xd2\x06\xd7=\xf9\xc3\x03/?e\xf9g\x1eG@\xd3c\x0f\xe3\xd5\x8e\xf9\x85\xab\x97L\xb3\xd7&\xeb\x96\n\x94\xde\x856\x9c\xc8\x7f\x81\t\x02I*\x0e\xf5d2\x0c\x82\xd1\xbaj\x82\x1b\xb3Kt\x11\xf3\x8cw\xd6\x9f\xbf\xdc7\xa4\xa7U\x04/\xd41\xe8\xd3F\xb9\x03|\xda\x12NYd\xb7Q11P\xa0\xca\x1c\'\xd9\x10.\xad\xd6\xbd\x10f+\xc3\xb0"J\x12[\x02\x03\x01\x00\x01']>
    # ASN1_X509_CONT3:
      # ASN1_SEQUENCE:
        # ASN1_SEQUENCE:
          <ASN1_OID['.2.5.29.19']>
          <ASN1_BOOLEAN[-1L]>
          <ASN1_STRING['0\x03\x01\x01\xff']>
        # ASN1_SEQUENCE:
          <ASN1_OID['.2.5.29.14']>
          <ASN1_STRING['\x04\x14Oim\x03~\x9d\x9f\x07\x18C\xbc\xb7\x10N\xd5\xbf\xa9\xc4 (']>
        # ASN1_SEQUENCE:
          <ASN1_OID['.2.5.29.35']>
          <ASN1_STRING['0\x16\x80\x14Oim\x03~\x9d\x9f\x07\x18C\xbc\xb7\x10N\xd5\xbf\xa9\xc4 (']>
        # ASN1_SEQUENCE:
          <ASN1_OID['.2.5.29.15']>
          <ASN1_BOOLEAN[-1L]>
          <ASN1_STRING['\x03\x02\x01\x86']>
  # ASN1_SEQUENCE:
    <ASN1_OID['.1.2.840.113549.1.1.5']>
    <ASN1_NULL[0L]>
  <ASN1_BIT_STRING['\x00;\xf3\xae\xca\xe8.\x87\x85\xfbeY\xe7\xad\x11\x14\xa5W\xbcX\x9f$\x12W\xbb\xfb?4\xda\xee\xadz*4rp1k\xc7\x19\x98\x80\xc9\x82\xde7w^T\x8b\x8e\xf2\xeagO\xc9t\x84\x91V\t\xd5\xe5z\x9a\x81\xb6\x81\xc2\xad6\xe4\xf1T\x11S\xf34E\x01&\xc8\xe5\x1a\xbc4D!\xde\xad%\xfcv\x16w!\x90\x80\x98W\x9dN\xea\xec/\xaa<\x14{W\xc1~\x18\x14g\xee$\xc6\xbd\xba\x15\xb0\xd2\x18\xbd\xb7U\x81\xacS\xc0\xe8\xddi\x12\x13B\xb7\x02\xb5\x05A\xcayPn\x82\x0eqr\x93F\xe8\x9d\r]\xbd\xae\xce)\xadc\xd5U\x16\x800\'\xffv\xba\xf7\xb8\xd6J\xe3\xd9\xb5\xf9R\xd0N@\xa9\xc7\xe5\xc22\xc7\xaav$\xe1k\x05P\xeb\xc5\xbf\nT\xe5\xb9B<$\xfb\xb7\x07\x9c0\x9fyZ\xe6\xe0@R\x15\xf4\xfc\xaa\xf4V\xf9D\x97\x87\xed\x0eer^\xbe&\xfbM\xa4-\x08\x07\xde\xd8\\\xa0\xdc\x813\x99\x18%\x11w\xa7\xeb\xfdX\t,\x99k\x1b\x8a\xf3R?\x1aMH`\xf1\xa0\xf63\x02S\x8b\xed%\t\xb8\r-\xed\x97s\xec\xd7\x96\x1f\x8e`\x0e\xda\x10\x9b/\x18$\xf6\xa6M\n\xf9;\xcbu\xc2\xcc/\xce$i\xc9\n"\x8eY\xa7\xf7\x82\x0c\xd7\xd7k5\x9cC\x00j\xc4\x95g\xba\x9cE\xcb\xb8\x0e7\xf7\xdcN\x01O\xbe\n\xb6\x03\xd3\xad\x8aE\xf7\xda\'M)\xb1H\xdf\xe4\x11\xe4\x96F\xbdl\x02>\xd6Q\xc8\x95\x17\x01\x15\xa9\xf2\xaa\xaa\xf2\xbf/e\x1bo\xd0\xb9\x1a\x93\xf5\x8e5\xc4\x80\x87>\x94/f\xe4\xe9\xa8\xffA\x9cp*O*9\x18\x95\x1e~\xfba\x01<Q\x08.(\x18\xa4\x16\x0f1\xfd:l#\x93 v\xe1\xfd\x07\x85\xd1[?\xd2\x1cs2\xdd\xfa\xb9\xf8\x8c\xcf\x02\x87z\x9a\x96\xe4\xedO\x89\x8dSC\xab\x0e\x13\xc0\x01\x15\xb4y8\xdb\xfcn=\x9eQ\xb6\xb8\x13\x8bg\xcf\xf9|\xd9"\x1d\xf6]\xc5\x1c\x01/\x98\xe8z$\x18\xbc\x84\xd7\xfa\xdcr[\xf7\xc1:h']>

ASN.1层

尽管这可能不错,但这只是一个ASN.1编码器/解码器。与Scapy无关。

ASN.1字段

Scapy提供ASN.1字段。它们将包装ASN.1对象,并提供必要的逻辑以将字段名称绑定到该值。ASN.1数据包将被描述为ASN.1字段的树。然后,每个字段名将以普通形式作为普通Packet对象提供(例如:访问SNMP数据包的version字段,您不需要知道有多少个容器包装它)。

每个ASN.1字段都通过其标签链接到ASN.1对象。

ASN.1数据包

ASN.1数据包继承自Packet类。fields_desc它们定义字段ASN1_codecASN1_root属性而不是字段列表第一个是编解码器(例如:)ASN1_Codecs.BER,第二个是与ASN.1字段混合的树。

一个完整的例子:SNMP¶

SNMP定义了新的ASN.1对象。我们需要定义它们:

class ASN1_Class_SNMP(ASN1_Class_UNIVERSAL):
    name="SNMP"
    PDU_GET = 0xa0
    PDU_NEXT = 0xa1
    PDU_RESPONSE = 0xa2
    PDU_SET = 0xa3
    PDU_TRAPv1 = 0xa4
    PDU_BULK = 0xa5
    PDU_INFORM = 0xa6
    PDU_TRAPv2 = 0xa7

这些对象是PDU,实际上是序列容器的新名称(上下文对象通常是这种情况:它们是具有新名称的旧容器)。这意味着创建相应的ASN.1对象和BER编解码器非常简单:

class ASN1_SNMP_PDU_GET(ASN1_SEQUENCE):
    tag = ASN1_Class_SNMP.PDU_GET

class ASN1_SNMP_PDU_NEXT(ASN1_SEQUENCE):
    tag = ASN1_Class_SNMP.PDU_NEXT

# [...]

class BERcodec_SNMP_PDU_GET(BERcodec_SEQUENCE):
    tag = ASN1_Class_SNMP.PDU_GET

class BERcodec_SNMP_PDU_NEXT(BERcodec_SEQUENCE):
    tag = ASN1_Class_SNMP.PDU_NEXT

# [...]

元类为所有事物都自动注册并且ASN.1对象和BER编解码器可以相互找到这一事实提供了魔力。

ASN.1字段也很简单:

class ASN1F_SNMP_PDU_GET(ASN1F_SEQUENCE):
    ASN1_tag = ASN1_Class_SNMP.PDU_GET

class ASN1F_SNMP_PDU_NEXT(ASN1F_SEQUENCE):
    ASN1_tag = ASN1_Class_SNMP.PDU_NEXT

# [...]

现在,最困难的部分是ASN.1数据包:

SNMP_error = { 0: "no_error",
               1: "too_big",
# [...]
             }

SNMP_trap_types = { 0: "cold_start",
                    1: "warm_start",
# [...]
                  }

class SNMPvarbind(ASN1_Packet):
    ASN1_codec = ASN1_Codecs.BER
    ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("oid","1.3"),
                                ASN1F_field("value",ASN1_NULL(0))
                                )

class SNMPget(ASN1_Packet):
    ASN1_codec = ASN1_Codecs.BER
    ASN1_root = ASN1F_SNMP_PDU_GET( ASN1F_INTEGER("id",0),
                                    ASN1F_enum_INTEGER("error",0, SNMP_error),
                                    ASN1F_INTEGER("error_index",0),
                                    ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind)
                                    )

class SNMPnext(ASN1_Packet):
    ASN1_codec = ASN1_Codecs.BER
    ASN1_root = ASN1F_SNMP_PDU_NEXT( ASN1F_INTEGER("id",0),
                                     ASN1F_enum_INTEGER("error",0, SNMP_error),
                                     ASN1F_INTEGER("error_index",0),
                                     ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind)
                                     )
# [...]

class SNMP(ASN1_Packet):
    ASN1_codec = ASN1_Codecs.BER
    ASN1_root = ASN1F_SEQUENCE(
        ASN1F_enum_INTEGER("version", 1, {0:"v1", 1:"v2c", 2:"v2", 3:"v3"}),
        ASN1F_STRING("community","public"),
        ASN1F_CHOICE("PDU", SNMPget(),
                     SNMPget, SNMPnext, SNMPresponse, SNMPset,
                     SNMPtrapv1, SNMPbulk, SNMPinform, SNMPtrapv2)
        )
    def answers(self, other):
        return ( isinstance(self.PDU, SNMPresponse)    and
                 ( isinstance(other.PDU, SNMPget) or
                   isinstance(other.PDU, SNMPnext) or
                   isinstance(other.PDU, SNMPset)    ) and
                 self.PDU.id == other.PDU.id )
# [...]
bind_layers( UDP, SNMP, sport=161)
bind_layers( UDP, SNMP, dport=161)

那不是那么困难。如果您认为实现SNMP编码/解码的时间不能太短,并且我可能已经削减了太多,那么请看完整的源代码。

现在,如何使用它?照常:

>>> a=SNMP(version=3, PDU=SNMPget(varbindlist=[SNMPvarbind(oid="1.2.3",value=5),
...                                            SNMPvarbind(oid="3.2.1",value="hello")]))
>>> a.show()
###[ SNMP ]###
  version= v3
  community= 'public'
  \PDU\
   |###[ SNMPget ]###
   |  id= 0
   |  error= no_error
   |  error_index= 0
   |  \varbindlist\
   |   |###[ SNMPvarbind ]###
   |   |  oid= '1.2.3'
   |   |  value= 5
   |   |###[ SNMPvarbind ]###
   |   |  oid= '3.2.1'
   |   |  value= 'hello'
>>> hexdump(a)
0000   30 2E 02 01 03 04 06 70  75 62 6C 69 63 A0 21 02   0......public.!.
0010   01 00 02 01 00 02 01 00  30 16 30 07 06 02 2A 03   ........0.0...*.
0020   02 01 05 30 0B 06 02 7A  01 04 05 68 65 6C 6C 6F   ...0...z...hello
>>> send(IP(dst="1.2.3.4")/UDP()/SNMP())
.
Sent 1 packets.
>>> SNMP(raw(a)).show()
###[ SNMP ]###
  version= <ASN1_INTEGER[3L]>
  community= <ASN1_STRING['public']>
  \PDU\
   |###[ SNMPget ]###
   |  id= <ASN1_INTEGER[0L]>
   |  error= <ASN1_INTEGER[0L]>
   |  error_index= <ASN1_INTEGER[0L]>
   |  \varbindlist\
   |   |###[ SNMPvarbind ]###
   |   |  oid= <ASN1_OID['.1.2.3']>
   |   |  value= <ASN1_INTEGER[5L]>
   |   |###[ SNMPvarbind ]###
   |   |  oid= <ASN1_OID['.3.2.1']>
   |   |  value= <ASN1_STRING['hello']>

从MIB解析OID¶

关于OID对象

OID对象使用一个ASN1_OID创建

>>> o1=ASN1_OID("2.5.29.10")
>>> o2=ASN1_OID("1.2.840.113549.1.1.1")
>>> o1,o2
(<ASN1_OID['.2.5.29.10']>, <ASN1_OID['.1.2.840.113549.1.1.1']>)

加载一个MIB¶

Scapy可以解析MIB文件,并知道OID及其名称之间的映射:

>>> load_mib("mib/*")
>>> o1,o2
(<ASN1_OID['basicConstraints']>, <ASN1_OID['rsaEncryption']>)

我已使用的MIB文件附在此页面上。

Scapy的MIB数据库

所有MIB信息都存储在conf.mib对象中。该对象可用于查找名称的OID

>>> conf.mib.sha1_with_rsa_signature
'1.2.840.113549.1.1.5'

或解决OID:

>>> conf.mib._oidname("1.2.3.6.1.4.1.5")
'enterprises.5'

甚至可以用图形表示:

>>> conf.mib._make_graph()

自动机

Scapy可以轻松创建网络自动机。Scapy不会遵循特定模型,例如Moore或Mealy自动机。它为您提供了一种灵活的选择方式。

Scapy中的自动机是确定性的。它具有不同的状态。一个开始状态以及一些结束和错误状态。从一种状态过渡到另一种状态。转换可以是特定条件下的转换,特定数据包接收时的转换或超时的转换。进行过渡时,可以运行一个或多个动作。一个动作可以绑定许多转换。参数可以从状态传递到转换,也可以从状态传递到状态和动作。

从程序员的角度来看,状态,转换和动作是Automaton子类的方法。装饰它们以提供自动机工作所需的元信息。

第一个例子

让我们从一个简单的例子开始。我习惯用大写字母写状态,但是任何使用Python语法有效的方法也可以使用。

class HelloWorld(Automaton):
    @ATMT.state(initial=1)
    def BEGIN(self):
        print "State=BEGIN"

    @ATMT.condition(BEGIN)
    def wait_for_nothing(self):
        print "Wait for nothing..."
        raise self.END()

    @ATMT.action(wait_for_nothing)
    def on_nothing(self):
        print "Action on 'nothing' condition"

    @ATMT.state(final=1)
    def END(self):
        print "State=END"

在此示例中,我们可以看到3个装饰器:

  • ATMT.state 用于表示方法是状态,并且对于特殊状态,可以将初始,最终和错误可选参数设置为非零。

  • ATMT.condition指示当自动机状态达到指示状态时要运行的方法。参数是表示该状态的方法的名称

  • ATMT.action 将方法绑定到过渡并在进行过渡时运行。

运行此示例将得到以下结果:

>>> a=HelloWorld()
>>> a.run()
State=BEGIN
Wait for nothing...
Action on 'nothing' condition
State=END

下图描述了这个简单的自动机:

_images / ATMT_HelloWorld.png

可以使用以下代码从代码中自动绘制图形:

>>> HelloWorld.graph()

改变状态

所述ATMT.state装饰变换的方法成返回异常的功能。如果引发该异常,则自动机状态将更改。如果更改发生在过渡中,则将调用绑定到该过渡的动作。保留给替换方法的函数的参数,并最终将其传递给该方法。异常具有方法action_parameters,可以在引发异常之前调用该方法,以便它将存储要传递给绑定到当前转换的所有操作的参数。

例如,让我们考虑以下状态:

@ATMT.state()
def MY_STATE(self, param1, param2):
    print "state=MY_STATE. param1=%r param2=%r" % (param1, param2)

可以通过以下代码达到此状态:

@ATMT.receive_condition(ANOTHER_STATE)
def received_ICMP(self, pkt):
    if ICMP in pkt:
        raise self.MY_STATE("got icmp", pkt[ICMP].type)

假设我们想将一个动作绑定到此过渡,这还需要一些参数:

@ATMT.action(received_ICMP)
def on_ICMP(self, icmp_type, icmp_code):
    self.retaliate(icmp_type, icmp_code)

条件应变为:

@ATMT.receive_condition(ANOTHER_STATE)
def received_ICMP(self, pkt):
    if ICMP in pkt:
        raise self.MY_STATE("got icmp", pkt[ICMP].type).action_parameters(pkt[ICMP].type, pkt[ICMP].code)

真实的例子

这是来自Scapy的真实示例。它实现了可以发出读取请求的TFTP客户端。

_images / ATMT_TFTP_read.png
class TFTP_read(Automaton):
    def parse_args(self, filename, server, sport = None, port=69, **kargs):
        Automaton.parse_args(self, **kargs)
        self.filename = filename
        self.server = server
        self.port = port
        self.sport = sport

    def master_filter(self, pkt):
        return ( IP in pkt and pkt[IP].src == self.server and UDP in pkt
                 and pkt[UDP].dport == self.my_tid
                 and (self.server_tid is None or pkt[UDP].sport == self.server_tid) )

    # BEGIN
    @ATMT.state(initial=1)
    def BEGIN(self):
        self.blocksize=512
        self.my_tid = self.sport or RandShort()._fix()
        bind_bottom_up(UDP, TFTP, dport=self.my_tid)
        self.server_tid = None
        self.res = b""

        self.l3 = IP(dst=self.server)/UDP(sport=self.my_tid, dport=self.port)/TFTP()
        self.last_packet = self.l3/TFTP_RRQ(filename=self.filename, mode="octet")
        self.send(self.last_packet)
        self.awaiting=1

        raise self.WAITING()

    # WAITING
    @ATMT.state()
    def WAITING(self):
        pass

    @ATMT.receive_condition(WAITING)
    def receive_data(self, pkt):
        if TFTP_DATA in pkt and pkt[TFTP_DATA].block == self.awaiting:
            if self.server_tid is None:
                self.server_tid = pkt[UDP].sport
                self.l3[UDP].dport = self.server_tid
            raise self.RECEIVING(pkt)
    @ATMT.action(receive_data)
    def send_ack(self):
        self.last_packet = self.l3 / TFTP_ACK(block = self.awaiting)
        self.send(self.last_packet)

    @ATMT.receive_condition(WAITING, prio=1)
    def receive_error(self, pkt):
        if TFTP_ERROR in pkt:
            raise self.ERROR(pkt)

    @ATMT.timeout(WAITING, 3)
    def timeout_waiting(self):
        raise self.WAITING()
    @ATMT.action(timeout_waiting)
    def retransmit_last_packet(self):
        self.send(self.last_packet)

    # RECEIVED
    @ATMT.state()
    def RECEIVING(self, pkt):
        recvd = pkt[Raw].load
        self.res += recvd
        self.awaiting += 1
        if len(recvd) == self.blocksize:
            raise self.WAITING()
        raise self.END()

    # ERROR
    @ATMT.state(error=1)
    def ERROR(self,pkt):
        split_bottom_up(UDP, TFTP, dport=self.my_tid)
        return pkt[TFTP_ERROR].summary()

    #END
    @ATMT.state(final=1)
    def END(self):
        split_bottom_up(UDP, TFTP, dport=self.my_tid)
        return self.res

它可以像这样运行,例如:

>>> TFTP_read("my_file", "192.168.1.128").run()

详细文档

装饰器

状态装饰器

状态是由ATMT.state函数的结果修饰的方法它可以采取3个可选参数,initialfinalerror,即,当设置为True,表示状态是声母,韵母或错误状态。

class Example(Automaton):
    @ATMT.state(initial=1)
    def BEGIN(self):
        pass

    @ATMT.state()
    def SOME_STATE(self):
        pass

    @ATMT.state(final=1)
    def END(self):
        return "Result of the automaton: 42"

    @ATMT.state(error=1)
    def ERROR(self):
        return "Partial result, or explanation"
# [...]
过渡的装饰器

转变是由某种原因引起的装饰方法ATMT.conditionATMT.receive_conditionATMT.timeout它们都以与它们相关的状态方法作为参数。ATMT.timeout还具有一个必填timeout参数,以秒为单位提供超时值。ATMT.conditionATMT.receive_condition具有一个可选prio参数,以便可以强制评估条件的顺序。默认优先级为0。具有相同优先级的转换按不确定的顺序调用。

当自动机切换到给定状态时,将执行该状态的方法。然后,在特定时刻调用transitions方法,直到触发一个新状态(类似)。首先,在状态方法返回后,修饰的方法通过增加prio来运行。然后,每次接收到一个数据包并由主过滤器接受时,所有装饰的样板都将通过生长prio进行调用。自从我们进入当前空间以来达到超时时,将调用相应的修饰方法。raise self.MY_NEW_STATE()ATMT.conditionATMT.receive_conditionATMT.timeout

class Example(Automaton):
    @ATMT.state()
    def WAITING(self):
        pass

    @ATMT.condition(WAITING)
    def it_is_raining(self):
        if not self.have_umbrella:
            raise self.ERROR_WET()

    @ATMT.receive_condition(WAITING, prio=1)
    def it_is_ICMP(self, pkt):
        if ICMP in pkt:
            raise self.RECEIVED_ICMP(pkt)

    @ATMT.receive_condition(WAITING, prio=2)
    def it_is_IP(self, pkt):
        if IP in pkt:
            raise self.RECEIVED_IP(pkt)

    @ATMT.timeout(WAITING, 10.0)
    def waiting_timeout(self):
        raise self.ERROR_TIMEOUT()
装饰器

动作是由ATMT.action函数的返回修饰的方法此函数将绑定到的过渡方法作为第一个参数,并将可选的优先级prio作为第二个参数。默认优先级为0。可以将操作方法​​进行多次修饰以绑定到许多转换。

class Example(Automaton):
    @ATMT.state(initial=1)
    def BEGIN(self):
        pass

    @ATMT.state(final=1)
    def END(self):
        pass

    @ATMT.condition(BEGIN, prio=1)
    def maybe_go_to_end(self):
        if random() > 0.5:
            raise self.END()
    @ATMT.condition(BEGIN, prio=2)
    def certainly_go_to_end(self):
        raise self.END()

    @ATMT.action(maybe_go_to_end)
    def maybe_action(self):
        print "We are lucky..."
    @ATMT.action(certainly_go_to_end)
    def certainly_action(self):
        print "We are not lucky..."
    @ATMT.action(maybe_go_to_end, prio=1)
    @ATMT.action(certainly_go_to_end, prio=1)
    def always_action(self):
        print "This wasn't luck!..."

两个可能的输出是:

>>> a=Example()
>>> a.run()
We are not lucky...
This wasn't luck!...
>>> a.run()
We are lucky...
This wasn't luck!...

重载方法

钩子有两种方法可以重载:

  • parse_args()使用在__init__()指定的参数调用方法run()使用它来参数化自动机的行为。

  • master_filter()每次嗅探数据包时都会调用方法,并确定自动机是否感兴趣。在使用特定协议时,您将在这里确保数据包属于您所属于的连接,因此您无需在每个转换中都进行所有的健全性检查。

PipeTools

Pipetool是一个智能管道系统,可以执行复杂的流数据管理。PipeTools和自动机之间有多种区别:

  • PipeTools没有状态:数据总是按照相同的模式发送

  • PipeTools不是基于套接字的,而是可以处理多种数据源(和输出),例如用户输入,pcap输入(还可以进行嗅探)

  • PipeTools不是基于类的,而是通过手动链接其所有零件来实现的。这样做有缺点,但允许动态添加源,运行时消耗并为同一源设置多个消耗

注意

Pipetool默认对象位于内部 scapy.pipetool

类的类型

数据管理使用三种不同的对象类别:

  • Sources

  • Drains

  • Sinks

它们由PipeEngine对象执行和处理

运行时,pipetool引擎会等待源中的所有可用数据,然后将其发送到链接到它的排水管中。然后,数据从“排水”到“排水”,直到到达接收器(该数据的最终状态)为止。

这是PipeTool系统可以执行的基本演示

_images / pipetool_engine.png

例如,此引擎是使用以下代码生成的:

>>> s = CLIFeeder()
>>> s2 = CLIHighFeeder()
>>> d1 = Drain()
>>> d2 = TransformDrain(lambda x: x[::-1])
>>> si1 = ConsoleSink()
>>> si2 = QueueSink()
>>>
>>> s > d1
>>> d1 > si1
>>> d1 > si2
>>>
>>> s2 >> d1
>>> d1 >> d2
>>> d2 >> si1
>>>
>>> p = PipeEngine()
>>> p.add(s)
>>> p.add(s2)
>>> p.graph(target="> the_above_image.png")

让我们启动PipeEngine:

>>> p.start()

现在,让我们玩一下:

>>> s.send("foo")
>'foo'
>>> s2.send("bar")
>>'rab'
>>> s.send("i like potato")
>'i like potato'
>>> print(si2.recv(), ":", si2.recv())
foo : i like potato

让我们研究一下这里发生的情况:

  • PipeEngine中有两条运河,一条较低,一条较高。有些资料写在较低的一个上,有些资料写在较高的一个上,有些都写在两个上。

  • 大多数水源都可以与上下渠中的任何排水系统相连。使用>表示在下层运河上的链接,而在>>上表示上层的链接

  • 如上所示,当我们在下运河中的s中发送一些数据时,它经过排水管,然后被发送到QueueSinkConsoleSink

  • 当我们在s2中发送一些数据时,它先经过Drain,然后经过TransformDrain,在此之前,数据被反转(请参阅lambda),然后才发送到ConsoleSink这就解释了为什么我们只有QueueSink内部的较低来源的数据:较高的来源尚未链接。

大部分水槽都从上下运河接收。这可以使用help(ConsoleSink)进行验证

>>> help(ConsoleSink)
Help on class ConsoleSink in module scapy.pipetool:
class ConsoleSink(Sink)
 |  Print messages on low and high entries
 |     +-------+
 |  >>-|--.    |->>
 |     | print |
 |   >-|--'    |->
 |     +-------+
 |
 [...]

来源

Source是生成一些数据的类。

与Scapy集成有多种源类型,可以按原样使用,但您也可以创建自己的源。

默认源类

对于任何此类,请查看help([theclass])以获取更多信息或所需的参数。

  • CLIFeeder:专门用于交互式软件的源。send(data)在下运河产生事件数据

  • CLIHighFeeder:与CLIFeeder相同,但写在较高的运河上

  • PeriodicSource:定期在低运河上生成消息。

  • 自动来源:默认来源,必须扩展以创建自定义来源。

创建自定义源

要创建自定义源,必须扩展AutoSource该类。

Source除非您真的确定自己在做什么,否则不要使用默认类:它仅在内部使用,并且缺少某些实现。AutoSource是由使用。

要通过它发送数据,对象必须调用其self._gen_data(msg)self._gen_high_data(msg)函数,这些函数将数据发送到PipeEngine中。

Source也应(如果可能)设置self.is_exhaustedTrue空,以允许干净地停止PipeEngine如果源是无限的,则将需要强制停止(请参阅下面的PipeEngine)

例如,以下是CLIHighFeeder的实现方式:

class CLIFeeder(CLIFeeder):
    def send(self, msg):
        self._gen_high_data(msg)
    def close(self):
        self.is_exhausted = True

排水管

默认Drain类

漏极需要链接到您正在使用的条目上。它可以位于较低的一个(使用>)或较高的一个(使用>>)上。请参阅上面的基本示例。

  • 排水:最基本的排水方式。如果链接正确,将同时通过高低输入。

  • TransformDrain:将功能应用于消息上下限较高的消息

  • UpDrain:从低入口到高出口重复消息

  • DownDrain:重复从高入口到低出口的消息

创建一个自定义的Drain¶

要创建自定义流失,必须扩展Drain类。

Drain对象将从其下部管接收数据push的方法,以及从其较高运河high_push方法。

要将数据发送回下一个链接的“漏极/接收器”,它必须调用self._send(msg)self._high_send(msg)方法。

例如,以下是TransformDrain的实现方式:

class TransformDrain(Drain):
    def __init__(self, f, name=None):
        Drain.__init__(self, name=name)
        self.f = f
    def push(self, msg):
        self._send(self.f(msg))
    def high_push(self, msg):
        self._high_send(self.f(msg))

水槽

接收器是消息的目的地。

A Sink接收数据,如Drain,但之后不发送任何消息。

低条目的push()消息来自,高条目的消息来自high_push()

默认接收器类
Sink

什么也没做; 接口以扩展自定义接收器。

所有接收器均具有以下构造函数参数:

参量

名称str)–元素的易读名称

所有接收器都应至少实现以下方法之一:

push

PipeEngine低端条目有新消息时调用

参量

msg –消息数据

退货

没有

返回类型

没有

high_push

PipeEngine当出现新的高位消息时调用

参量

msg –消息数据

退货

没有

返回类型

没有

ConsoleSink

将消息的低和高条目打印到stdout

RawConsoleSink

使用,在低和高条目上打印消息os.write()

参量

换行符bool)–打印每个数据包后,请包含换行符默认为True。

TermSink

在单独的终端(xterm或cmd)上的低和高条目上打印消息。

参量
  • keeptermbool)– stop() 调用后使终端窗口保持打开状态默认为True。

  • 换行符bool)–打印每个数据包后,请包含换行符默认为True。

  • openearlybool)–在调用构造函数时自动启动终端,而不是等待start()默认为True。

QueueSink

将消息中的低位和高位收集到Queue

邮件以出队recv()

高和低条目共享相同的内容Queue

recv

从队列中读取下一条消息。

如果队列中没有可用消息,则返回无。

参量
  • blockbool)–阻止执行,直到队列中有可用数据包为止。默认为True。

  • 超时整数浮点数)–控制的等待时间,如果block=True如果为None(默认),则此方法将永远等待。如果为非负数,则为放弃(并返回无)之前要等待的秒数。

WiresharkSink

Packet从低进入Wireshark的。

数据包被写入pcap流(如WrpcapSink),并在其上流式传输到新的Wireshark进程stdin

Wireshark使用自变量运行,这导致它被 视为捕获设备。之后的参数将被追加。-ki -stdinargs

Extends WrpcapSink.

Parameters
args

Additional arguments for the Wireshark process.

This must be either None (the default), or a list of str.

This attribute has no effect after calling PipeEngine.start().

See wireshark(1) for more details.

class WrpcapSink

Writes Packet on the low entry to a pcap file.

Ignores all messages on the high entry.

Note

Due to limitations of the pcap format, all packets must be of the same link type. This class will not mutate packets to conform with the expected link type.

Parameters
  • fname (str) – Filename to write packets to.

  • linktype (None or int) – See linktype.

linktype

Set an explicit link-type (DLT_) for packets. This must be an int or None.

This is the same as the wrpcap() linktype parameter.

If None (the default), the linktype will be auto-detected on the first packet. This field will not be updated with the result of this auto-detection.

This attribute has no effect after calling PipeEngine.start().

Create a custom Sink

To create a custom sink, one must extend Sink and implement push() and/or high_push().

This is a simplified version of ConsoleSink:

class ConsoleSink(Sink):
    def push(self, msg):
        print(">%r" % msg)
    def high_push(self, msg):
        print(">>%r" % msg)

The PipeEngine class

The PipeEngine class is the core class of the Pipetool system. It must be initialized and passed the list of all Sources.

There are two ways of passing sources:

  • during initialization: p = PipeEngine(source1, source2, ...)

  • using the add(source) method

A PipeEngine class must be started with .start() function. It may be force-stopped with the .stop(), or cleanly stopped with .wait_and_stop()

A clean stop only works if the Sources is exhausted (has no data to send left).

It can be printed into a graph using .graph() methods. see help(do_graph) for the list of available keyword arguments.

Scapy advanced PipeTool objects

Note

Unlike the previous objects, those are not located in scapy.pipetool but in scapy.scapypipes

Now that you know the default PipeTool objects, here are some more advanced ones, based on packet functionalities.

  • SniffSource : Read packets from an interface and send them to low exit.

  • RdpcapSource : Read packets from a PCAP file send them to low exit.

  • InjectSink : Packets received on low input are injected (sent) to an interface

  • WrpcapSink : Packets received on low input are written to PCAP file

  • UDPDrain : UDP payloads received on high entry are sent over UDP (complicated, have a look at help(UDPDrain))

  • FDSourceSink : Use a file descriptor as source and sink

  • TCPConnectPipe : TCP connect to addr:port and use it as source and sink

  • TCPListenPipe : TCP listen on [addr:]port and use the first connection as source and sink (complicated, have a look at help(TCPListenPipe))

Triggering

Some special sort of Drains exists: the Trigger Drains.

Trigger Drains are special drains, that on receiving data not only pass it by but also send a “Trigger” input, that is received and handled by the next triggered drain (if it exists).

For example, here is a basic TriggerDrain usage:

>>> a = CLIFeeder()
>>> d = TriggerDrain(lambda msg: True) # Pass messages and trigger when a condition is met
>>> d2 = TriggeredValve()
>>> s = ConsoleSink()
>>> a > d > d2 > s
>>> d ^ d2 # Link the triggers
>>> p = PipeEngine(s)
>>> p.start()
INFO: Pipe engine thread started.
>>>
>>> a.send("this will be printed")
>'this will be printed'
>>> a.send("this won't, because the valve was switched")
>>> a.send("this will, because the valve was switched again")
>'this will, because the valve was switched again'
>>> p.stop()

Several triggering Drains exist, they are pretty explicit. It is highly recommended to check the doc using help([the class])

  • TriggeredMessage : Send a preloaded message when triggered and trigger in chain

  • TriggerDrain : Pass messages and trigger when a condition is met

  • TriggeredValve : Let messages alternatively pass or not, changing on trigger

  • TriggeredQueueingValve : Let messages alternatively pass or queued, changing on trigger

  • TriggeredSwitch : Let messages alternatively high or low, changing on trigger

       </div>

      </div>
      <footer>

<div class="rst-footer-buttons" role="navigation" aria-label="页脚导航">

    <a href="extending.html" class="btn btn-neutral float-right" title="建立自己的工具" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right"></span></a>

    <a href="usage.html" class="btn btn-neutral float-left" title="用法" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>

</div>


©版权所有2008-2019 Philippe Biondi和Scapy社区 修订版9bf51c28

使用Sphinx使用Read the Docs提供主题构建