高级用法

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 它用于指示方法是一种状态,对于特殊状态,可以将initial、final、stop和error可选参数设置为非零。

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

  • ATMT.action 将方法绑定到转换,并在进行转换时运行。

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

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

这个简单的自动机可以用下图来描述:

_images/ATMT_HelloWorld.png

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

>>> HelloWorld.graph()

备注

An Automaton can be reset using restart(). It is then possible to run it again.

警告

Remember to call destroy() once you're done using an Automaton. (especially on PyPy)

变化状态

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

它可以这样运行,例如:

>>> atmt = TFTP_read("my_file", "192.168.1.128")
>>> atmt.run()
>>> atmt.destroy()

详细文件

装饰者

州装饰师

状态是通过 ATMT.state 功能。可以有4个可选参数, initialfinalstoperror ,当设置为 True ,表示状态是初始、最终、停止或错误状态。

备注

这个 initial 启动自动机时调用状态。这个 final step会告诉自动装置已经到了尽头。如果你打电话 atmt.stop() ,自动机将移动到 stop 不管它现在的状态是什么。这个 error 州政府会将自动装置标记为出错。如果没有 stop 状态已指定,调用 stopforcestop 将是等效的。

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(stop=1)
    def STOP(self):
        print("SHUTTING DOWN...")
        # e.g. close sockets...

    @ATMT.condition(STOP)
    def is_stopping(self):
        raise self.END()

    @ATMT.state(error=1)
    def ERROR(self):
        return "Partial result, or explanation"
# [...]

以TCP客户端为例:

_images/ATMT_TCP_client.svg

这个 START 事件是 initial=1 , the STOP 事件是 stop=1 以及 CLOSED 事件是 final=1 .

过渡装饰

Transitions are methods decorated by the result of one of ATMT.condition, ATMT.receive_condition, ATMT.timeout, ATMT.timer. They all take as argument the state method they are related to. ATMT.timeout and ATMT.timer also have a mandatory timeout parameter to provide the timeout value in seconds. The difference between ATMT.timeout and ATMT.timer is that ATMT.timeout gets triggered only once. ATMT.timer get reloaded automatically, which is useful for sending keep-alive packets. ATMT.condition and ATMT.receive_condition have an optional prio parameter so that the order in which conditions are evaluated can be forced. The default priority is 0. Transitions with the same priority level are called in an undetermined order.

当自动机切换到给定状态时,将执行状态的方法。然后在特定时刻调用转换方法,直到触发新的状态(例如 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。一个动作方法可以被修饰多次以绑定到许多转换。

from random import random

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!...
>>> a.destroy()

备注

如果要将参数传递给操作,可以使用 action_parameters 函数,同时引发下一个状态。

在下面的示例中, send_copy 操作接受传递的参数 is_fin ::

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

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

    @ATMT.receive_condition(WAITING)
    def is_fin(self, pkt):
        if pkt[TCP].flags.F:
            raise self.FIN_RECEIVED().action_parameters(pkt)

    @ATMT.action(is_fin)
    def send_copy(self, pkt):
        send(pkt)

超载方法

有两种方法是要重载的挂钩:

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

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

Timer configuration

Some protocols allow timer configuration. In order to configure timeout values during class initialization one may use timer_by_name() method, which returns Timer object associated with the given function name:

class Example(Automaton):
    def __init__(self, *args, **kwargs):
        super(Example, self).__init__(*args, **kwargs)
        timer = self.timer_by_name("waiting_timeout")
        timer.set(1)

    @ATMT.state(initial=1)
    def WAITING(self):
        pass

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

    @ATMT.timeout(WAITING, 10.0)
    def waiting_timeout(self):
        raise self.END()

PipeTools

斯卡皮的 pipetool 是一个智能管道系统,允许执行复杂的流数据管理。

目标是创建一个步骤序列,其中包含一个或多个输入和一个或多个输出,中间有一组块。PipeTools可以处理各种数据源(和输出),例如用户输入、pcap输入、嗅探、wireshark。。。管道系统是通过手动链接其所有零件来实现的。可以在运行或为同一个源设置多个排水管时动态添加元素。

备注

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

演示:嗅探,匿名,发送给Wireshark

下面的代码将在默认接口上嗅探数据包,匿名化源和目标IP地址,并将其全部导入Wireshark。例如,在网上发布例子时很有用。

source = SniffSource(iface=conf.iface)
wire = WiresharkSink()
def transf(pkt):
    if not pkt or IP not in pkt:
        return pkt
    pkt[IP].src = "1.1.1.1"
    pkt[IP].dst = "2.2.2.2"
    return pkt

source > TransformDrain(transf) > wire
p = PipeEngine(source)
p.start()
p.wait_and_stop()

引擎非常简单:

_images/pipetool_demo.svg

让我们运行它:

_images/pipetool_demo.gif

类类型

用于数据管理的对象有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")

start() 用于启动 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 如上图所示,它位于下运河上,穿过 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相同,但写在较高的通道上

  • PeriodicSource :在低通道上定期生成消息。

  • 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

排水沟

默认排水类

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

  • Drain :最基本的排水管。如果链接正确,将传递低和高入口。

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

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

  • DownDrain 从高到高重复消息

创建自定义排水沟

要创建自定义排水,必须扩展 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() .

默认接收器类
  • ConsoleSink : Print messages on low and high entries to stdout

  • RawConsoleSink :在低和高条目上打印消息,使用操作系统写入

  • TermSink :在单独的终端上打印低和高条目上的消息

  • QueueSink : Collects messages on the low and high entries into a Queue

创建自定义接收器

要创建自定义接收器,必须扩展 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对象,下面是一些基于数据包功能的更高级的对象。

  • SniffSource 从一个低的接口发送数据包。

  • RdpcapSource :从PCAP文件读取数据包将其发送到低出口。

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

  • WrpcapSink :低输入时接收到的数据包写入PCAP文件

  • UDPDrain :在高输入时接收到的UDP有效负载通过UDP发送(复杂,请查看 help(UDPDrain)

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

  • TCPConnectPipe :TCP连接到地址:港口和把它当作源和汇

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