高级用法

ASN 1和SNMP

什么是ASN?1?

注解

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

ASN.1是一种表示法,其目标是指定数据交换的格式。它独立于数据的编码方式。在编码规则中指定了数据编码。

最常用的编码规则是BER(基本编码规则)和DER(可分辨编码规则)。两者看起来都一样,但后者是为了保证编码的唯一性而指定的。当谈到加密、哈希和签名时,这个属性非常有趣。

ASN.1提供了基本对象:整数、许多类型的字符串、浮点数、布尔值、容器等。它们在所谓的通用类中分组。给定的协议可以提供将在上下文类中分组的其他对象。例如,snmp定义pdu-get或pdu-set对象。还有应用程序类和私有类。

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

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

Saby和ASN 1

scapy提供了一种轻松编码或解码ASN.1的方法,还为这些编码器/解码器编程。它比ASN.1解析器应该做的要宽松得多,而且它忽略了约束。它既不能取代ASN.1解析器,也不能取代ASN.1编译器。事实上,它被编写成能够编码和解码损坏的ASN.1。它可以处理损坏的编码字符串,也可以创建这些字符串。

ASN 1发动机

注意:这里介绍的许多类定义都使用元类。如果您没有精确地查看源代码,并且只依赖于我的捕获,那么您可能会认为它们有时会表现出一种神奇的行为。 `` Scapy ASN.1 engine provides classes to link objects and their tags. They inherit from the `` ASN1'类'.第一个是 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实例表示,这些实例充当原始值的nutshell。简单的逻辑由 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看起来像BER,但只有最小的编码是经过授权的,这很可能就是我所做的)。我称之为ASN.1编解码器。

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

>>> 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编码器/解码器。还没有和诈骗有关的事。

ASN. 1字段

scapy提供ASN.1字段。它们将包装ASN.1对象,并提供将字段名绑定到值的必要逻辑。ASN.1数据包将被描述为ASN.1字段树。然后每个字段名将作为普通字段名提供 Packet 对象,具有扁平的风格(例如:要访问SNMP数据包的版本字段,不需要知道包装它的容器数)。

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

ASN.1包

ASN.1包从包类继承。而不是 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 automata这样的特定模型。它为您提供了一种灵活的方式来选择您的出行方式。

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

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

第一实例

让我们从一个简单的例子开始。我认为这个惯例是用大写字母来写状态,但是用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 decorator将方法转换为返回异常的函数。如果您引发该异常,自动化状态将更改。如果更改发生在转换中,则将调用绑定到此转换的操作。将保留给函数替换方法的参数,并最终传递给该方法。异常具有一个方法操作参数,可以在引发它之前调用该参数,以便存储要传递到绑定到当前转换的所有操作的参数。

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

@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。具有相同优先级的转换按不确定的顺序调用。

当自动机切换到给定状态时,将执行状态的方法。然后在特定时刻调用转换方法,直到触发新的状态(例如 raise self.MY_NEW_STATE() )首先,在状态的方法返回之后, ATMT.condition 装饰方法是由不断增长的普里奥。然后,每次主过滤器接收和接受一个包时 ATMT.receive_condition 装饰过的霍德被称为成长的普里奥。当我们进入当前空间后超时时,相应的 ATMT.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和automatons之间存在不同之处:

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

  • pipetools不基于套接字,但可以处理更多不同的数据源(和输出),如用户输入、PCAP输入(但也可以嗅探)

  • pipetools不是基于类的,而是通过手动链接所有部分来实现的。它有缺点,但允许动态添加源、运行时排出以及为同一源设置多个排出。

注解

管道工具默认对象位于内部 scapy.pipetool

类类型

用于数据管理的对象有3种:

  • 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")

让我们启动管道引擎:

>>> 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

让我们来研究一下这里发生了什么:

  • 管道发动机中有两条运河,一条较低的运河和一条较高的运河。一些消息来源写在下面的一个,一些写在上面的一个,一些写在上面的两个。

  • 大多数水源可以连接到任何排水沟,无论是在较低和较高的运河。使用 > 指示低矮运河上的一条连接线,以及 >> 在上面的那个。

  • 当我们发送一些数据时 s 如上图所示,它位于下运河上,穿过 Drain 然后发送到 QueueSink 以及对 ConsoleSink

  • 当我们发送一些数据时 s2 ,它先经过漏极,再经过数据反向的变压器漏极(见lambda),然后发送到 ConsoleSink 只有。这就解释了为什么我们只有队列接收器中较低源的数据:较高源没有链接。

大多数水槽都来自上下运河。使用 help(ConsoleSink)

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

来源

源是生成某些数据的类。

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

默认源类

任何一门课,都可以看看 help([theclass]) 以获取更多信息或所需参数。

  • clifeeder:一种特别用于交互式软件的源。它的 send(data) 在下运河上生成事件数据

  • clihighfeeder:与clifeeder相同,但写在较高的运河上。

  • 周期性来源:定期在低位通道上生成信息。

  • autosource:默认源,必须扩展才能创建自定义源。

创建自定义源

要创建自定义源,必须扩展 AutoSource 班级。

不使用默认值 Source 类,除非您确实确定要执行的操作:它只在内部使用,并且缺少一些实现。这个 AutoSource 是为了使用。

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

源还应(如果可能)设置 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

排水沟

默认排水类

在你使用的入口需要连接排水管。它可以在下一个(使用 > )或上一个(使用 >> )请参见上面的基本示例。

  • 排水:最基本的排水。如果链接正确,则会同时传递低输入和高输入。

  • TransformDrain:对低输入和高输入的消息应用函数

  • 上升:从低入口到高出口重复信息

  • 下行:从高入口到低出口重复信息

创建自定义排水沟

要创建自定义排水,必须扩展 Drain 班级。

A Drain 对象将在ITS中接收来自下运河的数据。 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() .

默认接收器类
class Sink

不执行任何操作;用于扩展自定义接收器的接口。

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

参数

name (str) -- 元素的人类可读名称

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

push()

被称为 PipeEngine 当有新的低输入信息时。

参数

msg -- 消息数据

返回

没有

返回类型

None

high_push()

被称为 PipeEngine 当有新的高输入信息时。

参数

msg -- 消息数据

返回

没有

返回类型

None

class ConsoleSink

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

class RawConsoleSink

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

参数

newlines (bool) -- 在打印每个数据包后包含一个新行字符。默认为true。

class TermSink

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

参数
  • keepterm (bool) -- 之后使终端窗口保持打开状态 stop() 被调用。默认为true。

  • newlines (bool) -- 在打印每个数据包后包含一个新行字符。默认为true。

  • openearly (bool) -- 调用构造函数时自动启动终端,而不是等待 start() . 默认为true。

class QueueSink

将低和高条目上的消息收集到 Queue .

消息与出列 recv() .

高和低条目共享相同 Queue .

recv()

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

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

参数
  • block (bool) -- 阻止执行,直到队列中有数据包可用。默认为true。

  • timeout (None, int or float) -- 控制如果 block=True . 如果没有(默认),这个方法将永远等待。如果是非负数,这是在放弃(并且不返回)之前等待的秒数。

class WiresharkSink

溪流 Packet 从低入口到Wireshark。

数据包被写入 pcap 流(如 WrpcapSink ,并流到其上的新wireshark进程 stdin .

Wireshark和 -ki - 使它处理 stdin 作为捕获设备。中的参数 args 将在此之后追加。

延伸 WrpcapSink .

参数
args

wireshark进程的其他参数。

这肯定是 None (违约),或 list 属于 str .

此属性在调用后无效 PipeEngine.start() .

wireshark(1) 了解更多详细信息。

class WrpcapSink

Packet 在低入口 pcap 文件。

忽略高条目上的所有消息。

注解

由于 pcap 格式,所有数据包 must 属于同一链接类型。此类不会改变包以符合预期的链接类型。

参数
  • fname (str) -- 要写入数据包的文件名。

  • linktype (None or int) -- 见 linktype .

linktype

设置显式链接类型 (DLT_ )对于数据包。这一定是 intNone .

这和 wrpcap() linktype 参数。

如果 None (默认),链接类型将在第一个数据包上自动检测。此字段将 not 更新此自动检测的结果。

此属性在调用后无效 PipeEngine.start() .

创建自定义接收器

要创建自定义接收器,必须扩展 Sink 实施 push() 和/或 high_push() .

这是的简化版本 ConsoleSink

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

PipeEngine类

这个 PipeEngine 类是Pipetool系统的核心类。它必须初始化并传递所有源的列表。

有两种方法可以传递源:

  • 初始化期间: p = PipeEngine(source1, source2, ...)

  • 使用 add(source) 方法

A PipeEngine 类必须以开头 .start() 功能。可能是强制停止 .stop() 或完全停止 .wait_and_stop()

只有当资源耗尽(没有要发送的数据)时,才可以执行干净的停止操作。

它可以用 .graph() 方法。看见 help(do_graph) 用于可用关键字参数的列表。

scapy高级pipetool对象

注解

与前面的对象不同,这些对象不在 scapy.pipetool 但在 scapy.scapypipes

现在您已经了解了默认的pipetool对象,下面是一些基于数据包功能的更高级的对象。

  • 嗅探源:从接口读取数据包并将其发送到低出口。

  • rdpcapsource:从pcap文件中读取数据包,将它们发送到低出口。

  • InjectSink:低输入接收的数据包被注入(发送)到接口。

  • WRPcapSink:低输入接收的数据包被写入PCAP文件

  • udpdrain:通过udp发送高入口接收的udp有效负载(复杂,请看 help(UDPDrain)

  • fdsourcesink:使用文件描述符作为源和接收器

  • tcpconnectpipe:tcp连接到addr:port并将其用作源和接收器

  • tcplistenpipe:tcp侦听 [ADDR:] 端口和使用第一个连接作为源和接收器(复杂,请看 help(TCPListenPipe)

触发

有些特殊的排水管存在:触发排水管。

触发漏极是一种特殊的漏极,当接收到数据时,它不仅通过,而且发送一个“触发”输入,由下一个触发漏极(如果存在)接收和处理。

例如,下面是一个基本的triggerdrain用法:

>>> 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()

有几个触发排水管,它们非常明确。强烈建议使用 help([the class])

  • TriggeredMessage:在触发时发送预加载的消息,并在链中触发

  • triggerdrain:满足条件时传递消息和触发器

  • 触发阀:让消息交替通过或不通过,在触发器上更改

  • triggeredqueueingvalve:让消息交替通过或排队,在触发器上更改

  • TriggeredSwitch:让消息交替高或低,在触发器上更改