高级用法

译者:草帽小子_DJ

来源:Python Scapy(2.3.1)文档学习(四):高级用法

原文:Advanced usage

协议:CC BY-NC-SA 2.5

ASN.1 和 SNMP

什么是ASN.1 ?

注意:这只是我对ASN.1的个人观点,我会尽可能的做简单的解释。至于更多的理论或者学术观点,我相信你会在互联网上找到更好的。

ASN.1(抽象语法标记)是一种对数据进行表示、编码、传输和解码的数据格式。它用一种独立的方式给数据编码,用指定的编码规则给数据编码。

最常用的编码规则是BER(基本编码规则)和DER(识别名编码规则),两者看起来是一样的,但是后者特殊在它保证了生成的编码的唯一性,当谈到加密,哈希和签名时,这个属性非常有意思。

ASN.1提供了基本的对象:整数,多种类型的字符串,浮点数,布尔值,容器,等等。它们组成了通用类。一种给定的协议能提供组成其他对象的上下文类。比如,SNMP定义了PDU_SETPDU_GET对象,还有其他的应用和私有类。

每一个对象将会给一个标签用来定义编码规则。从1开始用于通由类,1是布尔值,2是整型,3是一个字节字符串,6是OID,48是一个序列。标签来自Context类,从0xa0开始。当从0xa0遇到一个对象标签,我们将需要知道能够解码的context。比如说,在SNMP的context下,0xa0是一个PDU_GET对象,而在X509的context下,它是一个证书版本的容器。

其他对象通过组装基本的对象产生。新的结构是是用先前已经定义或者存在的序列和阵列组成。最终的对象(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,它提供的标签是最为通用的标签。每个新的context(SNMP,X509)都将继承它,并添加自己的标签。

  1. class ASN1_Class_UNIVERSAL(ASN1_Class):
  2. name = "UNIVERSAL"
  3. # [...]
  4. BOOLEAN = 1
  5. INTEGER = 2
  6. BIT_STRING = 3
  7. # [...]
  8. class ASN1_Class_SNMP(ASN1_Class_UNIVERSAL):
  9. name="SNMP"
  10. PDU_GET = 0xa0
  11. PDU_NEXT = 0xa1
  12. PDU_RESPONSE = 0xa2
  13. class ASN1_Class_X509(ASN1_Class_UNIVERSAL):
  14. name="X509"
  15. CONT0 = 0xa0
  16. CONT1 = 0xa1
  17. # [...]

所有的ASN.1对象都被简单的Python实例表示,并隐藏的原始的值。简单的逻辑被ASN1_Object所继承处理。因此他们相当简单。

  1. class ASN1_INTEGER(ASN1_Object):
  2. tag = ASN1_Class_UNIVERSAL.INTEGER
  3. class ASN1_STRING(ASN1_Object):
  4. tag = ASN1_Class_UNIVERSAL.STRING
  5. class ASN1_BIT_STRING(ASN1_STRING):
  6. tag = ASN1_Class_UNIVERSAL.BIT_STRING

这些实例可以组装并创建一个ASN.1树:

  1. >>> x=ASN1_SEQUENCE([ASN1_INTEGER(7),ASN1_STRING("egg"),ASN1_SEQUENCE([ASN1_BOOLEAN(False)])])
  2. >>> x
  3. <ASN1_SEQUENCE[[<ASN1_INTEGER[7]>, <ASN1_STRING['egg']>, <ASN1_SEQUENCE[[<ASN1_BOOLEAN[False]>]]>]]>
  4. >>> x.show()
  5. # ASN1_SEQUENCE:
  6. <ASN1_INTEGER[7]>
  7. <ASN1_STRING['egg']>
  8. # ASN1_SEQUENCE:
  9. <ASN1_BOOLEAN[False]>

编码引擎

作为标准,ASN.1和编码是独立的。我们只看到怎样生成一个组合的ASN.1对象,解码或者编码它我们只需要选择一个解码规则。Scapy目前只提供BER编码规则(事实上,它可能是DER规则,DER看起来像是BER,除了一小部分编码通过授权才可以得到)。我称它为ASN.1编解码器。

编码和解码都是编解码器的类方法提供的。比如说BERcodec_INTEGER 提供了一个enc()和一个dec()类方法可以将编码的字符串和其类型的值之间进行转换。它们都继承自BERcodec_Object,它能解码任何类型。

  1. >>> BERcodec_INTEGER.enc(7)
  2. '\x02\x01\x07'
  3. >>> BERcodec_BIT_STRING.enc("egg")
  4. '\x03\x03egg'
  5. >>> BERcodec_STRING.enc("egg")
  6. '\x04\x03egg'
  7. >>> BERcodec_STRING.dec('\x04\x03egg')
  8. (<ASN1_STRING['egg']>, '')
  9. >>> BERcodec_STRING.dec('\x03\x03egg')
  10. Traceback (most recent call last):
  11. File "<console>", line 1, in ?
  12. File "/usr/bin/scapy", line 2099, in dec
  13. return cls.do_dec(s, context, safe)
  14. File "/usr/bin/scapy", line 2178, in do_dec
  15. l,s,t = cls.check_type_check_len(s)
  16. File "/usr/bin/scapy", line 2076, in check_type_check_len
  17. l,s3 = cls.check_type_get_len(s)
  18. File "/usr/bin/scapy", line 2069, in check_type_get_len
  19. s2 = cls.check_type(s)
  20. File "/usr/bin/scapy", line 2065, in check_type
  21. (cls.__name__, ord(s[0]), ord(s[0]),cls.tag), remaining=s)
  22. BER_BadTag_Decoding_Error: BERcodec_STRING: Got tag [3/0x3] while expecting <ASN1Tag STRING[4]>
  23. ### Already decoded ###
  24. None
  25. ### Remaining ###
  26. '\x03\x03egg'
  27. >>> BERcodec_Object.dec('\x03\x03egg')
  28. (<ASN1_BIT_STRING['egg']>, '')

ASN.1对象使用它们的enc()方法解码。这个方法必须被我们要使用的编解码器所调用,所有的便把解码器都被ASN1_Codecs对象所引用。str()也能被用到,在这种情况下,默认的编解码器(conf.ASN1_default_codec)也会被用到。

  1. >>> x.enc(ASN1_Codecs.BER)
  2. '0\r\x02\x01\x07\x04\x03egg0\x03\x01\x01\x00'
  3. >>> str(x)
  4. '0\r\x02\x01\x07\x04\x03egg0\x03\x01\x01\x00'
  5. >>> xx,remain = BERcodec_Object.dec(_)
  6. >>> xx.show()
  7. # ASN1_SEQUENCE:
  8. <ASN1_INTEGER[7L]>
  9. <ASN1_STRING['egg']>
  10. # ASN1_SEQUENCE:
  11. <ASN1_BOOLEAN[0L]>
  12. >>> remain
  13. ''

默认情况下,解码器使用Universal类进行解码,这就意味着在Context类中定义的对象将不会被解码,这有一个比较好的原因:这个解码取决于context

  1. >>> cert="""
  2. ... MIIF5jCCA86gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMC
  3. ... VVMxHTAbBgNVBAoTFEFPTCBUaW1lIFdhcm5lciBJbmMuMRwwGgYDVQQLExNB
  4. ... bWVyaWNhIE9ubGluZSBJbmMuMTcwNQYDVQQDEy5BT0wgVGltZSBXYXJuZXIg
  5. ... Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyOTA2MDAw
  6. ... MFoXDTM3MDkyODIzNDMwMFowgYMxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRB
  7. ... T0wgVGltZSBXYXJuZXIgSW5jLjEcMBoGA1UECxMTQW1lcmljYSBPbmxpbmUg
  8. ... SW5jLjE3MDUGA1UEAxMuQU9MIFRpbWUgV2FybmVyIFJvb3QgQ2VydGlmaWNh
  9. ... dGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
  10. ... ggIBALQ3WggWmRToVbEbJGv8x4vmh6mJ7ouZzU9AhqS2TcnZsdw8TQ2FTBVs
  11. ... RotSeJ/4I/1n9SQ6aF3Q92RhQVSji6UI0ilbm2BPJoPRYxJWSXakFsKlnUWs
  12. ... i4SVqBax7J/qJBrvuVdcmiQhLE0OcR+mrF1FdAOYxFSMFkpBd4aVdQxHAWZg
  13. ... /BXxD+r1FHjHDtdugRxev17nOirYlxcwfACtCJ0zr7iZYYCLqJV+FNwSbKTQ
  14. ... 2O9ASQI2+W6p1h2WVgSysy0WVoaP2SBXgM1nEG2wTPDaRrbqJS5Gr42whTg0
  15. ... ixQmgiusrpkLjhTXUr2eacOGAgvqdnUxCc4zGSGFQ+aJLZ8lN2fxI2rSAG2X
  16. ... +Z/nKcrdH9cG6rjJuQkhn8g/BsXS6RJGAE57COtCPStIbp1n3UsC5ETzkxml
  17. ... J85per5n0/xQpCyrw2u544BMzwVhSyvcG7mm0tCq9Stz+86QNZ8MUhy/XCFh
  18. ... EVsVS6kkUfykXPcXnbDS+gfpj1bkGoxoigTTfFrjnqKhynFbotSg5ymFXQNo
  19. ... Kk/SBtc9+cMDLz9l+WceR0DTYw/j1Y75hauXTLPXJuuWCpTehTacyH+BCQJJ
  20. ... Kg71ZDIMgtG6aoIbs0t0EfOMd9afv9w3pKdVBC/UMejTRrkDfNoSTllkt1Ex
  21. ... MVCgyhwn2RAurda9EGYrw7AiShJbAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMB
  22. ... Af8wHQYDVR0OBBYEFE9pbQN+nZ8HGEO8txBO1b+pxCAoMB8GA1UdIwQYMBaA
  23. ... FE9pbQN+nZ8HGEO8txBO1b+pxCAoMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG
  24. ... 9w0BAQUFAAOCAgEAO/Ouyuguh4X7ZVnnrREUpVe8WJ8kEle7+z802u6teio0
  25. ... cnAxa8cZmIDJgt43d15Ui47y6mdPyXSEkVYJ1eV6moG2gcKtNuTxVBFT8zRF
  26. ... ASbI5Rq8NEQh3q0l/HYWdyGQgJhXnU7q7C+qPBR7V8F+GBRn7iTGvboVsNIY
  27. ... vbdVgaxTwOjdaRITQrcCtQVBynlQboIOcXKTRuidDV29rs4prWPVVRaAMCf/
  28. ... drr3uNZK49m1+VLQTkCpx+XCMseqdiThawVQ68W/ClTluUI8JPu3B5wwn3la
  29. ... 5uBAUhX0/Kr0VvlEl4ftDmVyXr4m+02kLQgH3thcoNyBM5kYJRF3p+v9WAks
  30. ... mWsbivNSPxpNSGDxoPYzAlOL7SUJuA0t7Zdz7NeWH45gDtoQmy8YJPamTQr5
  31. ... O8t1wswvziRpyQoijlmn94IM19drNZxDAGrElWe6nEXLuA4399xOAU++CrYD
  32. ... 062KRffaJ00psUjf5BHklka9bAI+1lHIlRcBFanyqqryvy9lG2/QuRqT9Y41
  33. ... xICHPpQvZuTpqP9BnHAqTyo5GJUefvthATxRCC4oGKQWDzH9OmwjkyB24f0H
  34. ... hdFbP9IcczLd+rn4jM8Ch3qaluTtT4mNU0OrDhPAARW0eTjb/G49nlG2uBOL
  35. ... Z8/5fNkiHfZdxRwBL5joeiQYvITX+txyW/fBOmg=
  36. ... """.decode("base64")
  37. >>> (dcert,remain) = BERcodec_Object.dec(cert)
  38. Traceback (most recent call last):
  39. File "<console>", line 1, in ?
  40. File "/usr/bin/scapy", line 2099, in dec
  41. return cls.do_dec(s, context, safe)
  42. File "/usr/bin/scapy", line 2094, in do_dec
  43. return codec.dec(s,context,safe)
  44. File "/usr/bin/scapy", line 2099, in dec
  45. return cls.do_dec(s, context, safe)
  46. File "/usr/bin/scapy", line 2218, in do_dec
  47. o,s = BERcodec_Object.dec(s, context, safe)
  48. File "/usr/bin/scapy", line 2099, in dec
  49. return cls.do_dec(s, context, safe)
  50. File "/usr/bin/scapy", line 2094, in do_dec
  51. return codec.dec(s,context,safe)
  52. File "/usr/bin/scapy", line 2099, in dec
  53. return cls.do_dec(s, context, safe)
  54. File "/usr/bin/scapy", line 2218, in do_dec
  55. o,s = BERcodec_Object.dec(s, context, safe)
  56. File "/usr/bin/scapy", line 2099, in dec
  57. return cls.do_dec(s, context, safe)
  58. File "/usr/bin/scapy", line 2092, in do_dec
  59. raise BER_Decoding_Error("Unknown prefix [%02x] for [%r]" % (p,t), remaining=s)
  60. BER_Decoding_Error: Unknown prefix [a0] for ['\xa0\x03\x02\x01\x02\x02\x01\x010\r\x06\t*\x86H...']
  61. ### Already decoded ###
  62. [[]]
  63. ### Remaining ###
  64. '\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类必须被指定:

  1. >>> (dcert,remain) = BERcodec_Object.dec(cert, context=ASN1_Class_X509)
  2. >>> dcert.show()
  3. # ASN1_SEQUENCE:
  4. # ASN1_SEQUENCE:
  5. # ASN1_X509_CONT0:
  6. <ASN1_INTEGER[2L]>
  7. <ASN1_INTEGER[1L]>
  8. # ASN1_SEQUENCE:
  9. <ASN1_OID['.1.2.840.113549.1.1.5']>
  10. <ASN1_NULL[0L]>
  11. # ASN1_SEQUENCE:
  12. # ASN1_SET:
  13. # ASN1_SEQUENCE:
  14. <ASN1_OID['.2.5.4.6']>
  15. <ASN1_PRINTABLE_STRING['US']>
  16. # ASN1_SET:
  17. # ASN1_SEQUENCE:
  18. <ASN1_OID['.2.5.4.10']>
  19. <ASN1_PRINTABLE_STRING['AOL Time Warner Inc.']>
  20. # ASN1_SET:
  21. # ASN1_SEQUENCE:
  22. <ASN1_OID['.2.5.4.11']>
  23. <ASN1_PRINTABLE_STRING['America Online Inc.']>
  24. # ASN1_SET:
  25. # ASN1_SEQUENCE:
  26. <ASN1_OID['.2.5.4.3']>
  27. <ASN1_PRINTABLE_STRING['AOL Time Warner Root Certification Authority 2']>
  28. # ASN1_SEQUENCE:
  29. <ASN1_UTC_TIME['020529060000Z']>
  30. <ASN1_UTC_TIME['370928234300Z']>
  31. # ASN1_SEQUENCE:
  32. # ASN1_SET:
  33. # ASN1_SEQUENCE:
  34. <ASN1_OID['.2.5.4.6']>
  35. <ASN1_PRINTABLE_STRING['US']>
  36. # ASN1_SET:
  37. # ASN1_SEQUENCE:
  38. <ASN1_OID['.2.5.4.10']>
  39. <ASN1_PRINTABLE_STRING['AOL Time Warner Inc.']>
  40. # ASN1_SET:
  41. # ASN1_SEQUENCE:
  42. <ASN1_OID['.2.5.4.11']>
  43. <ASN1_PRINTABLE_STRING['America Online Inc.']>
  44. # ASN1_SET:
  45. # ASN1_SEQUENCE:
  46. <ASN1_OID['.2.5.4.3']>
  47. <ASN1_PRINTABLE_STRING['AOL Time Warner Root Certification Authority 2']>
  48. # ASN1_SEQUENCE:
  49. # ASN1_SEQUENCE:
  50. <ASN1_OID['.1.2.840.113549.1.1.1']>
  51. <ASN1_NULL[0L]>
  52. <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']>
  53. # ASN1_X509_CONT3:
  54. # ASN1_SEQUENCE:
  55. # ASN1_SEQUENCE:
  56. <ASN1_OID['.2.5.29.19']>
  57. <ASN1_BOOLEAN[-1L]>
  58. <ASN1_STRING['0\x03\x01\x01\xff']>
  59. # ASN1_SEQUENCE:
  60. <ASN1_OID['.2.5.29.14']>
  61. <ASN1_STRING['\x04\x14Oim\x03~\x9d\x9f\x07\x18C\xbc\xb7\x10N\xd5\xbf\xa9\xc4 (']>
  62. # ASN1_SEQUENCE:
  63. <ASN1_OID['.2.5.29.35']>
  64. <ASN1_STRING['0\x16\x80\x14Oim\x03~\x9d\x9f\x07\x18C\xbc\xb7\x10N\xd5\xbf\xa9\xc4 (']>
  65. # ASN1_SEQUENCE:
  66. <ASN1_OID['.2.5.29.15']>
  67. <ASN1_BOOLEAN[-1L]>
  68. <ASN1_STRING['\x03\x02\x01\x86']>
  69. # ASN1_SEQUENCE:
  70. <ASN1_OID['.1.2.840.113549.1.1.5']>
  71. <ASN1_NULL[0L]>
  72. <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字段树,然后在同一个层面上每一个字段名将会做成一个正常的数据包提供(比如说:为了访问SNMP数据包的版本字段,你不用知道它包装了多少层容器)。

每一个 ASN.1字段都会通过它的标签连接到ASN.1对象。

ASN.1数据包

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

一个完整的例子:SNMP

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

  1. class ASN1_Class_SNMP(ASN1_Class_UNIVERSAL):
  2. name="SNMP"
  3. PDU_GET = 0xa0
  4. PDU_NEXT = 0xa1
  5. PDU_RESPONSE = 0xa2
  6. PDU_SET = 0xa3
  7. PDU_TRAPv1 = 0xa4
  8. PDU_BULK = 0xa5
  9. PDU_INFORM = 0xa6
  10. PDU_TRAPv2 = 0xa7

这个对象是PDU,实际上是一个序列容器的新名称,(这通常是在context对象的情况下:他们只是旧的容器有了新的名称),这意味着创建一个相应的ASN.1对象和BER编解码器是很容易的:

  1. class ASN1_SNMP_PDU_GET(ASN1_SEQUENCE):
  2. tag = ASN1_Class_SNMP.PDU_GET
  3. class ASN1_SNMP_PDU_NEXT(ASN1_SEQUENCE):
  4. tag = ASN1_Class_SNMP.PDU_NEXT
  5. # [...]
  6. class BERcodec_SNMP_PDU_GET(BERcodec_SEQUENCE):
  7. tag = ASN1_Class_SNMP.PDU_GET
  8. class BERcodec_SNMP_PDU_NEXT(BERcodec_SEQUENCE):
  9. tag = ASN1_Class_SNMP.PDU_NEXT
  10. # [...]

元类提供的魔法基于一切都是自动注册和ASN.1对象和BER编解码器都能找到对方的事实。

ASN.1的字段也是不重要的:

  1. class ASN1F_SNMP_PDU_GET(ASN1F_SEQUENCE):
  2. ASN1_tag = ASN1_Class_SNMP.PDU_GET
  3. class ASN1F_SNMP_PDU_NEXT(ASN1F_SEQUENCE):
  4. ASN1_tag = ASN1_Class_SNMP.PDU_NEXT
  5. # [...]

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

  1. SNMP_error = { 0: "no_error",
  2. 1: "too_big",
  3. # [...]
  4. }
  5. SNMP_trap_types = { 0: "cold_start",
  6. 1: "warm_start",
  7. # [...]
  8. }
  9. class SNMPvarbind(ASN1_Packet):
  10. ASN1_codec = ASN1_Codecs.BER
  11. ASN1_root = ASN1F_SEQUENCE( ASN1F_OID("oid","1.3"),
  12. ASN1F_field("value",ASN1_NULL(0))
  13. )
  14. class SNMPget(ASN1_Packet):
  15. ASN1_codec = ASN1_Codecs.BER
  16. ASN1_root = ASN1F_SNMP_PDU_GET( ASN1F_INTEGER("id",0),
  17. ASN1F_enum_INTEGER("error",0, SNMP_error),
  18. ASN1F_INTEGER("error_index",0),
  19. ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind)
  20. )
  21. class SNMPnext(ASN1_Packet):
  22. ASN1_codec = ASN1_Codecs.BER
  23. ASN1_root = ASN1F_SNMP_PDU_NEXT( ASN1F_INTEGER("id",0),
  24. ASN1F_enum_INTEGER("error",0, SNMP_error),
  25. ASN1F_INTEGER("error_index",0),
  26. ASN1F_SEQUENCE_OF("varbindlist", [], SNMPvarbind)
  27. )
  28. # [...]
  29. class SNMP(ASN1_Packet):
  30. ASN1_codec = ASN1_Codecs.BER
  31. ASN1_root = ASN1F_SEQUENCE(
  32. ASN1F_enum_INTEGER("version", 1, {0:"v1", 1:"v2c", 2:"v2", 3:"v3"}),
  33. ASN1F_STRING("community","public"),
  34. ASN1F_CHOICE("PDU", SNMPget(),
  35. SNMPget, SNMPnext, SNMPresponse, SNMPset,
  36. SNMPtrapv1, SNMPbulk, SNMPinform, SNMPtrapv2)
  37. )
  38. def answers(self, other):
  39. return ( isinstance(self.PDU, SNMPresponse) and
  40. ( isinstance(other.PDU, SNMPget) or
  41. isinstance(other.PDU, SNMPnext) or
  42. isinstance(other.PDU, SNMPset) ) and
  43. self.PDU.id == other.PDU.id )
  44. # [...]
  45. bind_layers( UDP, SNMP, sport=161)
  46. bind_layers( UDP, SNMP, dport=161)

这些不会有太大的困难,如果你认为不可能有这么短就能实现一个SNMP编解码器,我可能会删减很多,请看看完整的源代码。

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

  1. >>> a=SNMP(version=3, PDU=SNMPget(varbindlist=[SNMPvarbind(oid="1.2.3",value=5),
  2. ... SNMPvarbind(oid="3.2.1",value="hello")]))
  3. >>> a.show()
  4. ###[ SNMP ]###
  5. version= v3
  6. community= 'public'
  7. \PDU\
  8. |###[ SNMPget ]###
  9. | id= 0
  10. | error= no_error
  11. | error_index= 0
  12. | \varbindlist\
  13. | |###[ SNMPvarbind ]###
  14. | | oid= '1.2.3'
  15. | | value= 5
  16. | |###[ SNMPvarbind ]###
  17. | | oid= '3.2.1'
  18. | | value= 'hello'
  19. >>> hexdump(a)
  20. 0000 30 2E 02 01 03 04 06 70 75 62 6C 69 63 A0 21 02 0......public.!.
  21. 0010 01 00 02 01 00 02 01 00 30 16 30 07 06 02 2A 03 ........0.0...*.
  22. 0020 02 01 05 30 0B 06 02 7A 01 04 05 68 65 6C 6C 6F ...0...z...hello
  23. >>> send(IP(dst="1.2.3.4")/UDP()/SNMP())
  24. .
  25. Sent 1 packets.
  26. >>> SNMP(str(a)).show()
  27. ###[ SNMP ]###
  28. version= <ASN1_INTEGER[3L]>
  29. community= <ASN1_STRING['public']>
  30. \PDU\
  31. |###[ SNMPget ]###
  32. | id= <ASN1_INTEGER[0L]>
  33. | error= <ASN1_INTEGER[0L]>
  34. | error_index= <ASN1_INTEGER[0L]>
  35. | \varbindlist\
  36. | |###[ SNMPvarbind ]###
  37. | | oid= <ASN1_OID['.1.2.3']>
  38. | | value= <ASN1_INTEGER[5L]>
  39. | |###[ SNMPvarbind ]###
  40. | | oid= <ASN1_OID['.3.2.1']>
  41. | | value= <ASN1_STRING['hello']>

从MIB解析OID

关于OID对象

OID对象是通过ASN1_OID类产生的:

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

加载一个MIB

Scapy可以解析MIB文件并注意到OID和它的名称之间的映射。

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

Scapy的MIB数据库

所有的MIB信息都被存储在cong.mib对象中,这些对象用来寻找一个OID的名称。

  1. >>> conf.mib.sha1_with_rsa_signature
  2. '1.2.840.113549.1.1.5'

或者解析一个OID:

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

甚至可以图形化表示它:

  1. >>> conf.mib._make_graph()

自动机

Scapy能够创建一个简单的网络自动机。Scapy不会拘泥于一个特定的模型,像是Moore和Mealy自动机。它为你提供了灵活的方法去选择你想要的。

Scapy中的自动机是确定性的。他有不同的状态,一个开始状态和一些结束,错误状态,他们从一种状态过渡到另一种状态。过渡可以在一种特殊的状态下过渡,在一个特定的数据包或者超时过渡。当一个过渡被接受了,一个或者多个动作将会运行,一个动作可以被绑定在多个过渡中。参数可以通过从状态到过渡和从过渡到状态和动作中传递。

从一个开发者的角度看,状态,过渡和动作都是来自自动机子类的方法。他们都被包装起来提供元信息以供自动机工作。

第一个例子

让我们开始一个简单的例子。我按照惯例用字幕大写编写状态,但是每一个有效的Python语法都会工作的很好。

  1. class HelloWorld(Automaton):
  2. @ATMT.state(initial=1)
  3. def BEGIN(self):
  4. print "State=BEGIN"
  5. @ATMT.condition(BEGIN)
  6. def wait_for_nothing(self):
  7. print "Wait for nothing..."
  8. raise self.END()
  9. @ATMT.action(wait_for_nothing)
  10. def on_nothing(self):
  11. print "Action on 'nothing' condition"
  12. @ATMT.state(final=1)
  13. def END(self):
  14. print "State=END"

在这个例子中,我们可以看到三个装饰器:

ATMT.state被用来表示一个方法就是一个状态,并且能用initial, finalerror可选参数来设置非零的特殊状态。

ATMT.condition用来表示一个方法,当自动机的状态到达指示的状态时将会运行。参数是代表这个状态的函数的名称。

ATMT.action绑定到一个过渡的方法,当一个过渡被接受了该函数就会运行。

运行这个例子将会得到下面这个结果:

  1. >>> a=HelloWorld()
  2. >>> a.run()
  3. State=BEGIN
  4. Wait for nothing...
  5. Action on 'nothing' condition
  6. State=END

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

高级用法 - 图1

这个图可以用下面的代码自动画出:

  1. >>> HelloWorld.graph()

改变状态

ATMT.state装饰器将方法转换成一个返回一个异常的函数。如果你抛出这个异常,自动机的状态将会被改变。如果这个改变发生在一个过渡中,绑定在这个过渡上的函数将会被调用。给定函数的参数替换的方法将被保留,并最终传递到该方法中。这个异常有一个方法action_parameters能在抛出异常前被调用,他将存储参数传递到所有绑定在当前过渡上的动作。

作为一个例子,让我们来考虑一下下面的状态:

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

这个状态将会到达下面的代码:

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

让我们假设我们想绑定一个动作到这个状态,这也将需要一些参数:

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

这个条件应该被满足:

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

真正的例子

这里有一个来自Scapy的真正的例子。他实现了一个TFTP客户端,能够发送和读取请求。

高级用法 - 图2

  1. class TFTP_read(Automaton):
  2. def parse_args(self, filename, server, sport = None, port=69, **kargs):
  3. Automaton.parse_args(self, **kargs)
  4. self.filename = filename
  5. self.server = server
  6. self.port = port
  7. self.sport = sport
  8. def master_filter(self, pkt):
  9. return ( IP in pkt and pkt[IP].src == self.server and UDP in pkt
  10. and pkt[UDP].dport == self.my_tid
  11. and (self.server_tid is None or pkt[UDP].sport == self.server_tid) )
  12. # BEGIN
  13. @ATMT.state(initial=1)
  14. def BEGIN(self):
  15. self.blocksize=512
  16. self.my_tid = self.sport or RandShort()._fix()
  17. bind_bottom_up(UDP, TFTP, dport=self.my_tid)
  18. self.server_tid = None
  19. self.res = ""
  20. self.l3 = IP(dst=self.server)/UDP(sport=self.my_tid, dport=self.port)/TFTP()
  21. self.last_packet = self.l3/TFTP_RRQ(filename=self.filename, mode="octet")
  22. self.send(self.last_packet)
  23. self.awaiting=1
  24. raise self.WAITING()
  25. # WAITING
  26. @ATMT.state()
  27. def WAITING(self):
  28. pass
  29. @ATMT.receive_condition(WAITING)
  30. def receive_data(self, pkt):
  31. if TFTP_DATA in pkt and pkt[TFTP_DATA].block == self.awaiting:
  32. if self.server_tid is None:
  33. self.server_tid = pkt[UDP].sport
  34. self.l3[UDP].dport = self.server_tid
  35. raise self.RECEIVING(pkt)
  36. @ATMT.action(receive_data)
  37. def send_ack(self):
  38. self.last_packet = self.l3 / TFTP_ACK(block = self.awaiting)
  39. self.send(self.last_packet)
  40. @ATMT.receive_condition(WAITING, prio=1)
  41. def receive_error(self, pkt):
  42. if TFTP_ERROR in pkt:
  43. raise self.ERROR(pkt)
  44. @ATMT.timeout(WAITING, 3)
  45. def timeout_waiting(self):
  46. raise self.WAITING()
  47. @ATMT.action(timeout_waiting)
  48. def retransmit_last_packet(self):
  49. self.send(self.last_packet)
  50. # RECEIVED
  51. @ATMT.state()
  52. def RECEIVING(self, pkt):
  53. recvd = pkt[Raw].load
  54. self.res += recvd
  55. self.awaiting += 1
  56. if len(recvd) == self.blocksize:
  57. raise self.WAITING()
  58. raise self.END()
  59. # ERROR
  60. @ATMT.state(error=1)
  61. def ERROR(self,pkt):
  62. split_bottom_up(UDP, TFTP, dport=self.my_tid)
  63. return pkt[TFTP_ERROR].summary()
  64. #END
  65. @ATMT.state(final=1)
  66. def END(self):
  67. split_bottom_up(UDP, TFTP, dport=self.my_tid)
  68. return self.res

他运行起来像是这样,这是实例:

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

详细的文档

装饰器

状态的装饰器

状态是被ATMT.state函数结果装饰过的方法。他有三个可选的参数,initial,finalerror,当我们设置为True时,就意味着这个状态是一个initialfinal或者error状态。

  1. class Example(Automaton):
  2. @ATMT.state(initial=1)
  3. def BEGIN(self):
  4. pass
  5. @ATMT.state()
  6. def SOME_STATE(self):
  7. pass
  8. @ATMT.state(final=1)
  9. def END(self):
  10. return "Result of the automaton: 42"
  11. @ATMT.state(error=1)
  12. def ERROR(self):
  13. return "Partial result, or explanation"
  14. # [...]
过渡装饰器

过渡是被ATMT.condition, ATMT.receive_condition, ATMT.timeout之一的函数结果装饰过的方法。他们都作为他们关联的状态方法的参数。ATMT.timeout也有一个强制性的参数timeout用来提供超时的时间秒值。ATMT.conditionATMT.receive_condition有一个可选的prio参数,因此在这种情况下的调用的顺序是可以被强制提高优先级的。默认的优先级是0。相同的优先级的过渡调用的顺序是不确定的。

当自动机切换到一个给定的状态,这个状态的方法将会被执行,然后过渡方法将会在特殊的时刻被调用,直到触发一个新的状态。(有时候像是抛出self.MY_NEW_STATE())。首先,在状态方法返回正确之后,ATMT.condition装饰的方法通过提升优先级运行,然后每一个时刻通过主要的过滤器收到和接受的数据包所有的ATMT.receive_condition装饰过的方法都会通过提升优先级运行。当一个超时的数据包进入当前的空间,相应的ATMT.timeout装饰过的方法将会被调用。

  1. class Example(Automaton):
  2. @ATMT.state()
  3. def WAITING(self):
  4. pass
  5. @ATMT.condition(WAITING)
  6. def it_is_raining(self):
  7. if not self.have_umbrella:
  8. raise self.ERROR_WET()
  9. @ATMT.receive_condition(WAITING, prio=1)
  10. def it_is_ICMP(self, pkt):
  11. if ICMP in pkt:
  12. raise self.RECEIVED_ICMP(pkt)
  13. @ATMT.receive_condition(WAITING, prio=2)
  14. def it_is_IP(self, pkt):
  15. if IP in pkt:
  16. raise self.RECEIVED_IP(pkt)
  17. @ATMT.timeout(WAITING, 10.0)
  18. def waiting_timeout(self):
  19. raise self.ERROR_TIMEOUT()
动作装饰器

动作是被ATMT.action函数结果装饰过的方法。这个函数接受过渡方法绑定他作为第一个参数,并且可选的优先级prio作为第二个参数,默认的优先级是0。一个动作方法能被装饰很多次并且被绑定在很多过渡上。

  1. class Example(Automaton):
  2. @ATMT.state(initial=1)
  3. def BEGIN(self):
  4. pass
  5. @ATMT.state(final=1)
  6. def END(self):
  7. pass
  8. @ATMT.condition(BEGIN, prio=1)
  9. def maybe_go_to_end(self):
  10. if random() > 0.5:
  11. raise self.END()
  12. @ATMT.condition(BEGIN, prio=2)
  13. def certainly_go_to_end(self):
  14. raise self.END()
  15. @ATMT.action(maybe_go_to_end)
  16. def maybe_action(self):
  17. print "We are lucky..."
  18. @ATMT.action(certainly_go_to_end)
  19. def certainly_action(self):
  20. print "We are not lucky..."
  21. @ATMT.action(maybe_go_to_end, prio=1)
  22. @ATMT.action(certainly_go_to_end, prio=1)
  23. def always_action(self):
  24. 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!...

重载方法

两种方法通过hooks重载:

parse_args()方法是通过__init__()run()提供参数被调用的。使用这些来确定你的自动机的行为。

master_filter()方法被每一时刻嗅探到的数据包所调用,如果自动机感兴趣的话。当工作在一个特殊的协议上时,这将确保这些数据包属于你连接到的一部分,所以你不必在每一个过渡中做明确的检查。